Project import generated by Copybara.
GitOrigin-RevId: 2a5d58025f6fc085a99d2d716ea5016e37f32f4a
Change-Id: Id6eefad21277e8f30daf237dd72c86d0b75c82d7
diff --git a/.dockerignore b/.dockerignore
index bc549d9..8388195 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -3,14 +3,16 @@
!common/
common/target/
!nearby/.cargo
+!nearby/build_scripts/
!nearby/connections/
!nearby/crypto/
!nearby/presence/
nearby/presence/cmake-build
-!nearby/scripts/
!nearby/src/
+!nearby/sharing/
!nearby/Cargo.lock
!nearby/Cargo.toml
+!/nearby/CMakeLists.txt
!nearby/deny.toml
!nearby/rustfmt.toml
!third_party
diff --git a/.gitignore b/.gitignore
index 5c2ad01..b49ad4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,10 @@
nearby/presence/lib/
target/
nearby/connections/ukey2/ukey2_c_ffi/cpp/build/
+nearby/connections/ukey2/ukey2_c_ffi/cpp/bin/
nearby/presence/ldt_np_jni/java/LdtNpJni/build/
+nearby/presence/ldt_np_jni/java/LdtNpJni/bin/
**/auth_token.txt
/bazel-*
/.clwb/.bazelproject
+/common/1p_internal/ph_client_sys/portable_ph_fake/cmake-build-debug/
diff --git a/BUILD b/BUILD
index 5456139..2d6ccb5 100644
--- a/BUILD
+++ b/BUILD
@@ -82,7 +82,7 @@
rust_library(
name = "np_ffi_core",
srcs = glob(include = ["nearby/presence/np_ffi_core/src/**/*.rs"]),
- crate_features = ["crypto_provider/raw_private_key_permit"],
+ crate_features = ["crypto_provider/raw_private_key_permit", "rustcrypto"],
proc_macro_deps = ["@crate_index//:strum_macros"],
deps = [
":array_view",
@@ -96,6 +96,7 @@
":np_hkdf",
"@crate_index//:lazy_static",
"@crate_index//:strum",
+ "@crate_index//:tinyvec",
],
)
@@ -136,7 +137,6 @@
":crypto_provider",
":ldt",
":ldt_np_adv",
- ":np_ed25519",
":np_hkdf",
":sink",
":xts_aes",
@@ -144,22 +144,8 @@
"@crate_index//:nom",
"@crate_index//:strum",
"@crate_index//:tinyvec",
- "@crate_index//:itertools"
- ],
-)
-
-rust_library(
- name = "np_ed25519",
- srcs = glob(
- include = ["nearby/presence/np_ed25519/src/**/*.rs"],
- ),
- deps = [
- ":array_view",
- ":crypto_provider",
- ":ldt",
- ":sink",
- ":xts_aes",
- "@crate_index//:tinyvec",
+ "@crate_index//:itertools",
+ "@crate_index//:thiserror"
],
)
diff --git a/Dockerfile b/Dockerfile
index 9122d14..f5d2a54 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,10 +16,17 @@
# install system deps
RUN apt-get update && apt-get install -y build-essential wget vim libstdc++-10-dev \
-clang git checkinstall zlib1g-dev curl protobuf-compiler cmake \
-pkg-config libdbus-1-dev ninja-build libssl-dev default-jre
+git checkinstall zlib1g-dev curl protobuf-compiler cmake \
+pkg-config libdbus-1-dev ninja-build libssl-dev default-jre default-jdk \
+lsb-release wget software-properties-common gnupg clang
RUN apt upgrade -y
+# Install node and npm following official instructions : https://github.com/nodesource/distributions/blob/master/README.md
+RUN apt purge -y nodejs && apt autoremove
+RUN curl -fsSL https://deb.nodesource.com/setup_18.x -o nodesource_setup.sh
+RUN bash nodesource_setup.sh
+RUN apt install -y nodejs
+
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
RUN apt-get -y install ./google-chrome-stable_current_amd64.deb
RUN export CHROME_BIN="/usr/bin/google-chrome-stable"
@@ -28,12 +35,12 @@
ENV CXX=/usr/bin/clang++
# install cargo with default settings
-RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.77.1
+RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
-RUN rustup install stable
-RUN rustup toolchain install nightly --force
-RUN rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
-RUN rustup target add wasm32-unknown-unknown thumbv7m-none-eabi
+RUN rustup toolchain install 1.79.0 --component llvm-tools-preview
+RUN rustup toolchain install nightly-2024-09-03 --component rust-src --force
+RUN rustup target add wasm32-unknown-unknown # used by np_web
+RUN rustup target add thumbv7m-none-eabi
RUN cargo install --locked cargo-deny --color never 2>&1
RUN cargo install --locked cargo-llvm-cov --color never 2>&1
@@ -70,4 +77,4 @@
# when the image runs build and test everything to ensure env is setup correctly
WORKDIR /beto-core/nearby
-CMD ["cargo", "run", "--", "check-everything"]
+CMD ["cargo", "run", "--", "run-all-checks"]
diff --git a/MODULE.bazel b/MODULE.bazel
index 20bd352..ee7a554 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -7,7 +7,7 @@
bazel_dep(name = "platforms", version = "0.0.8")
bazel_dep(name = "bazel_skylib", version = "1.5.0")
-bazel_dep(name = "rules_rust", version = "0.42.1")
+bazel_dep(name = "rules_rust", version = "0.54.1")
git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(name = "com_google_absl", remote = "https://github.com/abseil/abseil-cpp.git", commit = "d87dc03cee90a0cac2dbf254217b346ca693bb83")
@@ -15,7 +15,7 @@
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(
edition = "2021",
- versions = ["1.77.1"],
+ versions = ["1.83.0"],
)
use_repo(rust, "rust_toolchains")
register_toolchains("@rust_toolchains//:all")
@@ -31,4 +31,4 @@
"//:bazel_placeholder/Cargo.toml",
],
)
-use_repo(crate, "crate_index")
\ No newline at end of file
+use_repo(crate, "crate_index")
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index b75433b..0e288f6 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -1,4238 +1,837 @@
{
- "lockFileVersion": 6,
- "moduleFileHash": "78832c474ed0f0b328c52d96604ff16b984ece2e83e6aea659f994755450d661",
- "flags": {
- "cmdRegistries": [
- "https://bcr.bazel.build/"
- ],
- "cmdModuleOverrides": {},
- "allowedYankedVersions": [],
- "envVarAllowedYankedVersions": "",
- "ignoreDevDependency": false,
- "directDependenciesMode": "WARNING",
- "compatibilityMode": "ERROR"
+ "lockFileVersion": 16,
+ "registryFileHashes": {
+ "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
+ "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
+ "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589",
+ "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0",
+ "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb",
+ "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16",
+ "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915",
+ "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed",
+ "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da",
+ "https://bcr.bazel.build/modules/apple_support/1.13.0/MODULE.bazel": "7c8cdea7e031b7f9f67f0b497adf6d2c6a2675e9304ca93a9af6ed84eef5a524",
+ "https://bcr.bazel.build/modules/apple_support/1.13.0/source.json": "aef5da52fdcfa9173e02c0cb772c85be5b01b9d49f97f9bb0fe3efe738938ba4",
+ "https://bcr.bazel.build/modules/aspect_bazel_lib/1.31.2/MODULE.bazel": "7bee702b4862612f29333590f4b658a5832d433d6f8e4395f090e8f4e85d442f",
+ "https://bcr.bazel.build/modules/aspect_bazel_lib/1.38.0/MODULE.bazel": "6307fec451ba9962c1c969eb516ebfe1e46528f7fa92e1c9ac8646bef4cdaa3f",
+ "https://bcr.bazel.build/modules/aspect_bazel_lib/1.40.3/MODULE.bazel": "668e6bcb4d957fc0e284316dba546b705c8d43c857f87119619ee83c4555b859",
+ "https://bcr.bazel.build/modules/aspect_bazel_lib/1.40.3/source.json": "f5a28b1320e5f444e798b4afc1465c8b720bfaec7522cca38a23583dffe85e6d",
+ "https://bcr.bazel.build/modules/aspect_rules_js/1.33.1/MODULE.bazel": "db3e7f16e471cf6827059d03af7c21859e7a0d2bc65429a3a11f005d46fc501b",
+ "https://bcr.bazel.build/modules/aspect_rules_js/1.39.0/MODULE.bazel": "aece421d479e3c31dc3e5f6d49a12acc2700457c03c556650ec7a0ff23fc0d95",
+ "https://bcr.bazel.build/modules/aspect_rules_js/1.39.0/source.json": "a8f93e4ad8843e8aa407fa5fd7c8b63a63846c0ce255371ff23384582813b13d",
+ "https://bcr.bazel.build/modules/aspect_rules_lint/0.12.0/MODULE.bazel": "e767c5dbfeb254ec03275a7701b5cfde2c4d2873676804bc7cb27ddff3728fed",
+ "https://bcr.bazel.build/modules/aspect_rules_lint/0.12.0/source.json": "9a3668e1ee219170e22c0e7f3ab959724c6198fdd12cd503fa10b1c6923a2559",
+ "https://bcr.bazel.build/modules/bazel_features/0.1.0/MODULE.bazel": "47011d645b0f949f42ee67f2e8775188a9cf4a0a1528aa2fa4952f2fd00906fd",
+ "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd",
+ "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
+ "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d",
+ "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d",
+ "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a",
+ "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58",
+ "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b",
+ "https://bcr.bazel.build/modules/bazel_features/1.21.0/source.json": "3e8379efaaef53ce35b7b8ba419df829315a880cb0a030e5bb45c96d6d5ecb5f",
+ "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
+ "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b",
+ "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953",
+ "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
+ "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
+ "https://bcr.bazel.build/modules/gazelle/0.27.0/MODULE.bazel": "3446abd608295de6d90b4a8a118ed64a9ce11dcb3dda2dc3290a22056bd20996",
+ "https://bcr.bazel.build/modules/gazelle/0.30.0/MODULE.bazel": "f888a1effe338491f35f0e0e85003b47bb9d8295ccba73c37e07702d8d31c65b",
+ "https://bcr.bazel.build/modules/gazelle/0.30.0/source.json": "7af0779f99120aafc73be127615d224f26da2fc5a606b52bdffb221fd9efb737",
+ "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb",
+ "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
+ "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6",
+ "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4",
+ "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
+ "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075",
+ "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d",
+ "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
+ "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
+ "https://bcr.bazel.build/modules/platforms/0.0.10/source.json": "f22828ff4cf021a6b577f1bf6341cb9dcd7965092a439f64fc1bb3b7a5ae4bd5",
+ "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
+ "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
+ "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
+ "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
+ "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
+ "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
+ "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c",
+ "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d",
+ "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df",
+ "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e",
+ "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981",
+ "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
+ "https://bcr.bazel.build/modules/protobuf/3.19.2/MODULE.bazel": "532ffe5f2186b69fdde039efe6df13ba726ff338c6bc82275ad433013fa10573",
+ "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858",
+ "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
+ "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022",
+ "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206",
+ "https://bcr.bazel.build/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4",
+ "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8",
+ "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e",
+ "https://bcr.bazel.build/modules/rules_buf/0.1.1/MODULE.bazel": "6189aec18a4f7caff599ad41b851ab7645d4f1e114aa6431acf9b0666eb92162",
+ "https://bcr.bazel.build/modules/rules_buf/0.1.1/source.json": "021363d254f7438f3f10725355969c974bb2c67e0c28667782ade31a9cdb747f",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.16/source.json": "227e83737046aa4f50015da48e98e0d8ab42fd0ec74d8d653b6cc9f9a357f200",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
+ "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
+ "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
+ "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
+ "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e",
+ "https://bcr.bazel.build/modules/rules_go/0.33.0/MODULE.bazel": "a2b11b64cd24bf94f57454f53288a5dacfe6cb86453eee7761b7637728c1910c",
+ "https://bcr.bazel.build/modules/rules_go/0.38.1/MODULE.bazel": "fb8e73dd3b6fc4ff9d260ceacd830114891d49904f5bda1c16bc147bcc254f71",
+ "https://bcr.bazel.build/modules/rules_go/0.39.1/MODULE.bazel": "d34fb2a249403a5f4339c754f1e63dc9e5ad70b47c5e97faee1441fc6636cd61",
+ "https://bcr.bazel.build/modules/rules_go/0.39.1/source.json": "f21e042154010ae2c944ab230d572b17d71cdb27c5255806d61df6ccaed4354c",
+ "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
+ "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
+ "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39",
+ "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6",
+ "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31",
+ "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a",
+ "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6",
+ "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
+ "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2",
+ "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe",
+ "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2",
+ "https://bcr.bazel.build/modules/rules_java/8.6.1/source.json": "f18d9ad3c4c54945bf422ad584fa6c5ca5b3116ff55a5b1bc77e5c1210be5960",
+ "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
+ "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909",
+ "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036",
+ "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d",
+ "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4",
+ "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0",
+ "https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197",
+ "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59",
+ "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3",
+ "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5",
+ "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
+ "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
+ "https://bcr.bazel.build/modules/rules_license/0.0.8/MODULE.bazel": "5669c6fe49b5134dbf534db681ad3d67a2d49cfc197e4a95f1ca2fd7f3aebe96",
+ "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c",
+ "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb",
+ "https://bcr.bazel.build/modules/rules_nodejs/5.8.2/MODULE.bazel": "6bc03c8f37f69401b888023bf511cb6ee4781433b0cb56236b2e55a21e3a026a",
+ "https://bcr.bazel.build/modules/rules_nodejs/5.8.2/source.json": "6e82cf5753d835ea18308200bc79b9c2e782efe2e2a4edc004a9162ca93382ca",
+ "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
+ "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff",
+ "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a",
+ "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06",
+ "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7",
+ "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73",
+ "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2",
+ "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1",
+ "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f",
+ "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300",
+ "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382",
+ "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed",
+ "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
+ "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
+ "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
+ "https://bcr.bazel.build/modules/rules_python/0.40.0/source.json": "939d4bd2e3110f27bfb360292986bb79fd8dcefb874358ccd6cdaa7bda029320",
+ "https://bcr.bazel.build/modules/rules_rust/0.54.1/MODULE.bazel": "388547bb0cd6a751437bb15c94c6725226f50100eec576e4354c3a8b48c754fb",
+ "https://bcr.bazel.build/modules/rules_rust/0.54.1/source.json": "9c5481b1abe4943457e6b2a475592d2e504b6b4355df603f24f64cde0a7f0f2d",
+ "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
+ "https://bcr.bazel.build/modules/rules_shell/0.2.0/source.json": "7f27af3c28037d9701487c4744b5448d26537cc66cdef0d8df7ae85411f8de95",
+ "https://bcr.bazel.build/modules/stardoc/0.5.0/MODULE.bazel": "f9f1f46ba8d9c3362648eea571c6f9100680efc44913618811b58cc9c02cd678",
+ "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
+ "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
+ "https://bcr.bazel.build/modules/stardoc/0.5.4/MODULE.bazel": "6569966df04610b8520957cb8e97cf2e9faac2c0309657c537ab51c16c18a2a4",
+ "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef",
+ "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c",
+ "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7",
+ "https://bcr.bazel.build/modules/stardoc/0.7.1/source.json": "b6500ffcd7b48cd72c29bb67bcac781e12701cc0d6d55d266a652583cfcdab01",
+ "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
+ "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
+ "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27",
+ "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79",
+ "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d",
+ "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198"
},
- "localOverrideHashes": {
- "bazel_tools": "1ae69322ac3823527337acf02016e8ee95813d8d356f47060255b8956fa642f0"
- },
- "moduleDepGraph": {
- "<root>": {
- "name": "",
- "version": "",
- "key": "<root>",
- "repoName": "",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [
- "@rust_toolchains//:all"
- ],
- "extensionUsages": [
- {
- "extensionBzlFile": "//:MODULE.bazel",
- "extensionName": "_repo_rules",
- "usingModule": "<root>",
- "location": {
- "file": "@@//:MODULE.bazel",
- "line": 0,
- "column": 0
- },
- "imports": {
- "com_google_absl": "com_google_absl"
- },
- "devImports": [],
- "tags": [
- {
- "tagName": "@bazel_tools//tools/build_defs/repo:git.bzl%git_repository",
- "attributeValues": {
- "remote": "https://github.com/abseil/abseil-cpp.git",
- "commit": "d87dc03cee90a0cac2dbf254217b346ca693bb83",
- "name": "com_google_absl"
- },
- "devDependency": false,
- "location": {
- "file": "@@//:MODULE.bazel",
- "line": 13,
- "column": 15
- }
- }
- ],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@rules_rust//rust:extensions.bzl",
- "extensionName": "rust",
- "usingModule": "<root>",
- "location": {
- "file": "@@//:MODULE.bazel",
- "line": 15,
- "column": 21
- },
- "imports": {
- "rust_toolchains": "rust_toolchains"
- },
- "devImports": [],
- "tags": [
- {
- "tagName": "toolchain",
- "attributeValues": {
- "edition": "2021",
- "versions": [
- "1.77.1"
- ]
- },
- "devDependency": false,
- "location": {
- "file": "@@//:MODULE.bazel",
- "line": 16,
- "column": 15
- }
- }
- ],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@rules_rust//crate_universe:extension.bzl",
- "extensionName": "crate",
- "usingModule": "<root>",
- "location": {
- "file": "@@//:MODULE.bazel",
- "line": 23,
- "column": 22
- },
- "imports": {
- "crate_index": "crate_index"
- },
- "devImports": [],
- "tags": [
- {
- "tagName": "from_cargo",
- "attributeValues": {
- "name": "crate_index",
- "cargo_lockfile": "//:bazel_placeholder/Cargo.lock",
- "manifests": [
- "//:bazel_placeholder/Cargo.toml"
- ]
- },
- "devDependency": false,
- "location": {
- "file": "@@//:MODULE.bazel",
- "line": 27,
- "column": 17
- }
- }
- ],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "platforms": "platforms@0.0.8",
- "bazel_skylib": "bazel_skylib@1.5.0",
- "rules_rust": "rules_rust@0.42.1",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- }
- },
- "platforms@0.0.8": {
- "name": "platforms",
- "version": "0.0.8",
- "key": "platforms@0.0.8",
- "repoName": "platforms",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "rules_license": "rules_license@0.0.8",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz"
- ],
- "integrity": "sha256-gVBAZgU4ns7LbaB8vLUJ1WN6OrmiS8abEQFTE2fYnXQ=",
- "strip_prefix": "",
- "remote_patches": {},
- "remote_patch_strip": 0
- }
- }
- },
- "bazel_skylib@1.5.0": {
- "name": "bazel_skylib",
- "version": "1.5.0",
- "key": "bazel_skylib@1.5.0",
- "repoName": "bazel_skylib",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [
- "//toolchains/unittest:cmd_toolchain",
- "//toolchains/unittest:bash_toolchain"
- ],
- "extensionUsages": [],
- "deps": {
- "platforms": "platforms@0.0.8",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz"
- ],
- "integrity": "sha256-zVWgYudjuTSZIfD124w5MyiNyLpPdt2UFqrGis7jy5Q=",
- "strip_prefix": "",
- "remote_patches": {},
- "remote_patch_strip": 0
- }
- }
- },
- "rules_rust@0.42.1": {
- "name": "rules_rust",
- "version": "0.42.1",
- "key": "rules_rust@0.42.1",
- "repoName": "rules_rust",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [
- "@rust_toolchains//:all"
- ],
- "extensionUsages": [
- {
- "extensionBzlFile": "@rules_rust//rust/private:extensions.bzl",
- "extensionName": "i",
- "usingModule": "rules_rust@0.42.1",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_rust/0.42.1/MODULE.bazel",
- "line": 54,
- "column": 30
- },
- "imports": {
- "bazelci_rules": "bazelci_rules",
- "cargo_bazel.buildifier-darwin-amd64": "cargo_bazel.buildifier-darwin-amd64",
- "cargo_bazel.buildifier-darwin-arm64": "cargo_bazel.buildifier-darwin-arm64",
- "cargo_bazel.buildifier-linux-amd64": "cargo_bazel.buildifier-linux-amd64",
- "cargo_bazel.buildifier-linux-arm64": "cargo_bazel.buildifier-linux-arm64",
- "cargo_bazel.buildifier-windows-amd64.exe": "cargo_bazel.buildifier-windows-amd64.exe",
- "com_google_googleapis": "com_google_googleapis",
- "cui": "cui",
- "cui__anyhow-1.0.75": "cui__anyhow-1.0.75",
- "cui__camino-1.1.6": "cui__camino-1.1.6",
- "cui__cargo-lock-9.0.0": "cui__cargo-lock-9.0.0",
- "cui__cargo-platform-0.1.4": "cui__cargo-platform-0.1.4",
- "cui__cargo_metadata-0.18.1": "cui__cargo_metadata-0.18.1",
- "cui__cargo_toml-0.19.2": "cui__cargo_toml-0.19.2",
- "cui__cfg-expr-0.15.5": "cui__cfg-expr-0.15.5",
- "cui__clap-4.3.11": "cui__clap-4.3.11",
- "cui__crates-index-2.2.0": "cui__crates-index-2.2.0",
- "cui__hex-0.4.3": "cui__hex-0.4.3",
- "cui__indoc-2.0.4": "cui__indoc-2.0.4",
- "cui__itertools-0.12.0": "cui__itertools-0.12.0",
- "cui__maplit-1.0.2": "cui__maplit-1.0.2",
- "cui__normpath-1.1.1": "cui__normpath-1.1.1",
- "cui__pathdiff-0.2.1": "cui__pathdiff-0.2.1",
- "cui__regex-1.10.2": "cui__regex-1.10.2",
- "cui__semver-1.0.20": "cui__semver-1.0.20",
- "cui__serde-1.0.190": "cui__serde-1.0.190",
- "cui__serde_json-1.0.108": "cui__serde_json-1.0.108",
- "cui__serde_starlark-0.1.14": "cui__serde_starlark-0.1.14",
- "cui__sha2-0.10.8": "cui__sha2-0.10.8",
- "cui__spdx-0.10.3": "cui__spdx-0.10.3",
- "cui__spectral-0.6.0": "cui__spectral-0.6.0",
- "cui__tempfile-3.8.1": "cui__tempfile-3.8.1",
- "cui__tera-1.19.1": "cui__tera-1.19.1",
- "cui__textwrap-0.16.0": "cui__textwrap-0.16.0",
- "cui__toml-0.8.10": "cui__toml-0.8.10",
- "cui__tracing-0.1.40": "cui__tracing-0.1.40",
- "cui__tracing-subscriber-0.3.17": "cui__tracing-subscriber-0.3.17",
- "generated_inputs_in_external_repo": "generated_inputs_in_external_repo",
- "libc": "libc",
- "llvm-raw": "llvm-raw",
- "rrra__anyhow-1.0.71": "rrra__anyhow-1.0.71",
- "rrra__clap-4.3.11": "rrra__clap-4.3.11",
- "rrra__env_logger-0.10.0": "rrra__env_logger-0.10.0",
- "rrra__itertools-0.11.0": "rrra__itertools-0.11.0",
- "rrra__log-0.4.19": "rrra__log-0.4.19",
- "rrra__serde-1.0.171": "rrra__serde-1.0.171",
- "rrra__serde_json-1.0.102": "rrra__serde_json-1.0.102",
- "rules_rust_bindgen__bindgen-0.69.1": "rules_rust_bindgen__bindgen-0.69.1",
- "rules_rust_bindgen__bindgen-cli-0.69.1": "rules_rust_bindgen__bindgen-cli-0.69.1",
- "rules_rust_bindgen__clang-sys-1.6.1": "rules_rust_bindgen__clang-sys-1.6.1",
- "rules_rust_bindgen__clap-4.3.3": "rules_rust_bindgen__clap-4.3.3",
- "rules_rust_bindgen__clap_complete-4.3.1": "rules_rust_bindgen__clap_complete-4.3.1",
- "rules_rust_bindgen__env_logger-0.10.0": "rules_rust_bindgen__env_logger-0.10.0",
- "rules_rust_prost": "rules_rust_prost",
- "rules_rust_prost__h2-0.3.19": "rules_rust_prost__h2-0.3.19",
- "rules_rust_prost__heck": "rules_rust_prost__heck",
- "rules_rust_prost__prost-0.11.9": "rules_rust_prost__prost-0.11.9",
- "rules_rust_prost__prost-types-0.11.9": "rules_rust_prost__prost-types-0.11.9",
- "rules_rust_prost__protoc-gen-prost-0.2.2": "rules_rust_prost__protoc-gen-prost-0.2.2",
- "rules_rust_prost__protoc-gen-tonic-0.2.2": "rules_rust_prost__protoc-gen-tonic-0.2.2",
- "rules_rust_prost__tokio-1.28.2": "rules_rust_prost__tokio-1.28.2",
- "rules_rust_prost__tokio-stream-0.1.14": "rules_rust_prost__tokio-stream-0.1.14",
- "rules_rust_prost__tonic-0.9.2": "rules_rust_prost__tonic-0.9.2",
- "rules_rust_proto__grpc-0.6.2": "rules_rust_proto__grpc-0.6.2",
- "rules_rust_proto__grpc-compiler-0.6.2": "rules_rust_proto__grpc-compiler-0.6.2",
- "rules_rust_proto__log-0.4.17": "rules_rust_proto__log-0.4.17",
- "rules_rust_proto__protobuf-2.8.2": "rules_rust_proto__protobuf-2.8.2",
- "rules_rust_proto__protobuf-codegen-2.8.2": "rules_rust_proto__protobuf-codegen-2.8.2",
- "rules_rust_proto__tls-api-0.1.22": "rules_rust_proto__tls-api-0.1.22",
- "rules_rust_proto__tls-api-stub-0.1.22": "rules_rust_proto__tls-api-stub-0.1.22",
- "rules_rust_test_load_arbitrary_tool": "rules_rust_test_load_arbitrary_tool",
- "rules_rust_tinyjson": "rules_rust_tinyjson",
- "rules_rust_toolchain_test_target_json": "rules_rust_toolchain_test_target_json",
- "rules_rust_wasm_bindgen__anyhow-1.0.71": "rules_rust_wasm_bindgen__anyhow-1.0.71",
- "rules_rust_wasm_bindgen__assert_cmd-1.0.8": "rules_rust_wasm_bindgen__assert_cmd-1.0.8",
- "rules_rust_wasm_bindgen__diff-0.1.13": "rules_rust_wasm_bindgen__diff-0.1.13",
- "rules_rust_wasm_bindgen__docopt-1.1.1": "rules_rust_wasm_bindgen__docopt-1.1.1",
- "rules_rust_wasm_bindgen__env_logger-0.8.4": "rules_rust_wasm_bindgen__env_logger-0.8.4",
- "rules_rust_wasm_bindgen__log-0.4.19": "rules_rust_wasm_bindgen__log-0.4.19",
- "rules_rust_wasm_bindgen__predicates-1.0.8": "rules_rust_wasm_bindgen__predicates-1.0.8",
- "rules_rust_wasm_bindgen__rayon-1.7.0": "rules_rust_wasm_bindgen__rayon-1.7.0",
- "rules_rust_wasm_bindgen__rouille-3.6.2": "rules_rust_wasm_bindgen__rouille-3.6.2",
- "rules_rust_wasm_bindgen__serde-1.0.171": "rules_rust_wasm_bindgen__serde-1.0.171",
- "rules_rust_wasm_bindgen__serde_derive-1.0.171": "rules_rust_wasm_bindgen__serde_derive-1.0.171",
- "rules_rust_wasm_bindgen__serde_json-1.0.102": "rules_rust_wasm_bindgen__serde_json-1.0.102",
- "rules_rust_wasm_bindgen__tempfile-3.6.0": "rules_rust_wasm_bindgen__tempfile-3.6.0",
- "rules_rust_wasm_bindgen__ureq-2.8.0": "rules_rust_wasm_bindgen__ureq-2.8.0",
- "rules_rust_wasm_bindgen__walrus-0.20.3": "rules_rust_wasm_bindgen__walrus-0.20.3",
- "rules_rust_wasm_bindgen__wasm-bindgen-0.2.91": "rules_rust_wasm_bindgen__wasm-bindgen-0.2.91",
- "rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.91": "rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.91",
- "rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.91": "rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.91",
- "rules_rust_wasm_bindgen__wasmparser-0.102.0": "rules_rust_wasm_bindgen__wasmparser-0.102.0",
- "rules_rust_wasm_bindgen__wasmprinter-0.2.60": "rules_rust_wasm_bindgen__wasmprinter-0.2.60",
- "rules_rust_wasm_bindgen_cli": "rules_rust_wasm_bindgen_cli"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@rules_rust//rust:extensions.bzl",
- "extensionName": "rust",
- "usingModule": "rules_rust@0.42.1",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_rust/0.42.1/MODULE.bazel",
- "line": 153,
- "column": 21
- },
- "imports": {
- "rust_toolchains": "rust_toolchains"
- },
- "devImports": [],
- "tags": [
- {
- "tagName": "toolchain",
- "attributeValues": {
- "edition": "2021"
- },
- "devDependency": false,
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_rust/0.42.1/MODULE.bazel",
- "line": 154,
- "column": 15
- }
- }
- ],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@rules_rust//rust:extensions.bzl",
- "extensionName": "rust_host_tools",
- "usingModule": "rules_rust@0.42.1",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_rust/0.42.1/MODULE.bazel",
- "line": 176,
- "column": 32
- },
- "imports": {
- "rust_host_tools": "rust_host_tools"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@rules_rust//crate_universe/private/module_extensions:cargo_bazel_bootstrap.bzl",
- "extensionName": "cargo_bazel_bootstrap",
- "usingModule": "rules_rust@0.42.1",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_rust/0.42.1/MODULE.bazel",
- "line": 179,
- "column": 38
- },
- "imports": {
- "cargo_bazel_bootstrap": "cargo_bazel_bootstrap"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "bazel_features": "bazel_features@1.9.1",
- "bazel_skylib": "bazel_skylib@1.5.0",
- "platforms": "platforms@0.0.8",
- "rules_cc": "rules_cc@0.0.9",
- "rules_license": "rules_license@0.0.8",
- "rules_proto": "rules_proto@5.3.0-21.7",
- "build_bazel_apple_support": "apple_support@1.13.0",
- "com_google_protobuf": "protobuf@21.7",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/rules_rust/releases/download/0.42.1/rules_rust-v0.42.1.tar.gz"
- ],
- "integrity": "sha256-JLN47ZcAbx9wEr5Jiib4HduZATGLiDgK7oUi/fvotzU=",
- "strip_prefix": "",
- "remote_patches": {},
- "remote_patch_strip": 0
- }
- }
- },
- "bazel_tools@_": {
- "name": "bazel_tools",
- "version": "",
- "key": "bazel_tools@_",
- "repoName": "bazel_tools",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [
- "@local_config_cc_toolchains//:all",
- "@local_config_sh//:local_sh_toolchain"
- ],
- "extensionUsages": [
- {
- "extensionBzlFile": "@bazel_tools//tools/cpp:cc_configure.bzl",
- "extensionName": "cc_configure_extension",
- "usingModule": "bazel_tools@_",
- "location": {
- "file": "@@bazel_tools//:MODULE.bazel",
- "line": 18,
- "column": 29
- },
- "imports": {
- "local_config_cc": "local_config_cc",
- "local_config_cc_toolchains": "local_config_cc_toolchains"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@bazel_tools//tools/osx:xcode_configure.bzl",
- "extensionName": "xcode_configure_extension",
- "usingModule": "bazel_tools@_",
- "location": {
- "file": "@@bazel_tools//:MODULE.bazel",
- "line": 22,
- "column": 32
- },
- "imports": {
- "local_config_xcode": "local_config_xcode"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@rules_java//java:extensions.bzl",
- "extensionName": "toolchains",
- "usingModule": "bazel_tools@_",
- "location": {
- "file": "@@bazel_tools//:MODULE.bazel",
- "line": 25,
- "column": 32
- },
- "imports": {
- "local_jdk": "local_jdk",
- "remote_java_tools": "remote_java_tools",
- "remote_java_tools_linux": "remote_java_tools_linux",
- "remote_java_tools_windows": "remote_java_tools_windows",
- "remote_java_tools_darwin_x86_64": "remote_java_tools_darwin_x86_64",
- "remote_java_tools_darwin_arm64": "remote_java_tools_darwin_arm64"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@bazel_tools//tools/sh:sh_configure.bzl",
- "extensionName": "sh_configure_extension",
- "usingModule": "bazel_tools@_",
- "location": {
- "file": "@@bazel_tools//:MODULE.bazel",
- "line": 36,
- "column": 39
- },
- "imports": {
- "local_config_sh": "local_config_sh"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@bazel_tools//tools/test:extensions.bzl",
- "extensionName": "remote_coverage_tools_extension",
- "usingModule": "bazel_tools@_",
- "location": {
- "file": "@@bazel_tools//:MODULE.bazel",
- "line": 40,
- "column": 48
- },
- "imports": {
- "remote_coverage_tools": "remote_coverage_tools"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@bazel_tools//tools/android:android_extensions.bzl",
- "extensionName": "remote_android_tools_extensions",
- "usingModule": "bazel_tools@_",
- "location": {
- "file": "@@bazel_tools//:MODULE.bazel",
- "line": 43,
- "column": 42
- },
- "imports": {
- "android_gmaven_r8": "android_gmaven_r8",
- "android_tools": "android_tools"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@buildozer//:buildozer_binary.bzl",
- "extensionName": "buildozer_binary",
- "usingModule": "bazel_tools@_",
- "location": {
- "file": "@@bazel_tools//:MODULE.bazel",
- "line": 47,
- "column": 33
- },
- "imports": {
- "buildozer_binary": "buildozer_binary"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "rules_cc": "rules_cc@0.0.9",
- "rules_java": "rules_java@7.4.0",
- "rules_license": "rules_license@0.0.8",
- "rules_proto": "rules_proto@5.3.0-21.7",
- "rules_python": "rules_python@0.22.1",
- "buildozer": "buildozer@6.4.0.2",
- "platforms": "platforms@0.0.8",
- "com_google_protobuf": "protobuf@21.7",
- "zlib": "zlib@1.3",
- "build_bazel_apple_support": "apple_support@1.13.0",
- "local_config_platform": "local_config_platform@_"
- }
- },
- "local_config_platform@_": {
- "name": "local_config_platform",
- "version": "",
- "key": "local_config_platform@_",
- "repoName": "local_config_platform",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "platforms": "platforms@0.0.8",
- "bazel_tools": "bazel_tools@_"
- }
- },
- "rules_license@0.0.8": {
- "name": "rules_license",
- "version": "0.0.8",
- "key": "rules_license@0.0.8",
- "repoName": "rules_license",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/rules_license/releases/download/0.0.8/rules_license-0.0.8.tar.gz"
- ],
- "integrity": "sha256-JBsG8wl/0Yb/RogyFQ1swUIkfcQqMqrvtW0AmYlf0ik=",
- "strip_prefix": "",
- "remote_patches": {},
- "remote_patch_strip": 0
- }
- }
- },
- "bazel_features@1.9.1": {
- "name": "bazel_features",
- "version": "1.9.1",
- "key": "bazel_features@1.9.1",
- "repoName": "bazel_features",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [
- {
- "extensionBzlFile": "@bazel_features//private:extensions.bzl",
- "extensionName": "version_extension",
- "usingModule": "bazel_features@1.9.1",
- "location": {
- "file": "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel",
- "line": 15,
- "column": 24
- },
- "imports": {
- "bazel_features_globals": "bazel_features_globals",
- "bazel_features_version": "bazel_features_version"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "bazel_skylib": "bazel_skylib@1.5.0",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazel-contrib/bazel_features/releases/download/v1.9.1/bazel_features-v1.9.1.tar.gz"
- ],
- "integrity": "sha256-13h9oomn+0lzUiEa0gDsn2mIIqngdXpJdv2fcT/zcrM=",
- "strip_prefix": "bazel_features-1.9.1",
- "remote_patches": {
- "https://bcr.bazel.build/modules/bazel_features/1.9.1/patches/module_dot_bazel_version.patch": "sha256-a2ofwS5r2Qq+WxzVa7sLbRXhfT3JoYxSlUVQH/nL454="
- },
- "remote_patch_strip": 1
- }
- }
- },
- "rules_cc@0.0.9": {
- "name": "rules_cc",
- "version": "0.0.9",
- "key": "rules_cc@0.0.9",
- "repoName": "rules_cc",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [
- "@local_config_cc_toolchains//:all"
- ],
- "extensionUsages": [
- {
- "extensionBzlFile": "@bazel_tools//tools/cpp:cc_configure.bzl",
- "extensionName": "cc_configure_extension",
- "usingModule": "rules_cc@0.0.9",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel",
- "line": 9,
- "column": 29
- },
- "imports": {
- "local_config_cc_toolchains": "local_config_cc_toolchains"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "platforms": "platforms@0.0.8",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/rules_cc/releases/download/0.0.9/rules_cc-0.0.9.tar.gz"
- ],
- "integrity": "sha256-IDeHW5pEVtzkp50RKorohbvEqtlo5lh9ym5k86CQDN8=",
- "strip_prefix": "rules_cc-0.0.9",
- "remote_patches": {
- "https://bcr.bazel.build/modules/rules_cc/0.0.9/patches/module_dot_bazel_version.patch": "sha256-mM+qzOI0SgAdaJBlWOSMwMPKpaA9b7R37Hj/tp5bb4g="
- },
- "remote_patch_strip": 0
- }
- }
- },
- "rules_proto@5.3.0-21.7": {
- "name": "rules_proto",
- "version": "5.3.0-21.7",
- "key": "rules_proto@5.3.0-21.7",
- "repoName": "rules_proto",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "bazel_skylib": "bazel_skylib@1.5.0",
- "com_google_protobuf": "protobuf@21.7",
- "rules_cc": "rules_cc@0.0.9",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz"
- ],
- "integrity": "sha256-3D+yBqLLNEG0heseQjFlsjEjWh6psDG0Qzz3vB+kYN0=",
- "strip_prefix": "rules_proto-5.3.0-21.7",
- "remote_patches": {},
- "remote_patch_strip": 0
- }
- }
- },
- "apple_support@1.13.0": {
- "name": "apple_support",
- "version": "1.13.0",
- "key": "apple_support@1.13.0",
- "repoName": "build_bazel_apple_support",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [
- "@local_config_apple_cc_toolchains//:all"
- ],
- "extensionUsages": [
- {
- "extensionBzlFile": "@build_bazel_apple_support//crosstool:setup.bzl",
- "extensionName": "apple_cc_configure_extension",
- "usingModule": "apple_support@1.13.0",
- "location": {
- "file": "https://bcr.bazel.build/modules/apple_support/1.13.0/MODULE.bazel",
- "line": 19,
- "column": 35
- },
- "imports": {
- "local_config_apple_cc": "local_config_apple_cc",
- "local_config_apple_cc_toolchains": "local_config_apple_cc_toolchains"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "bazel_skylib": "bazel_skylib@1.5.0",
- "platforms": "platforms@0.0.8",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/apple_support/releases/download/1.13.0/apple_support.1.13.0.tar.gz"
- ],
- "integrity": "sha256-HEAx5ytFagSNgXf1mlWBgIwHWF+p4lXG9f77h1KvfkA=",
- "strip_prefix": "",
- "remote_patches": {
- "https://bcr.bazel.build/modules/apple_support/1.13.0/patches/module_dot_bazel_version.patch": "sha256-OqLgfAMNy6ZUF/WaVkNXzB/KcCYLlHLspYNk67mcASA="
- },
- "remote_patch_strip": 1
- }
- }
- },
- "protobuf@21.7": {
- "name": "protobuf",
- "version": "21.7",
- "key": "protobuf@21.7",
- "repoName": "protobuf",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [
- {
- "extensionBzlFile": "@rules_jvm_external//:extensions.bzl",
- "extensionName": "maven",
- "usingModule": "protobuf@21.7",
- "location": {
- "file": "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel",
- "line": 22,
- "column": 22
- },
- "imports": {
- "maven": "maven"
- },
- "devImports": [],
- "tags": [
- {
- "tagName": "install",
- "attributeValues": {
- "name": "maven",
- "artifacts": [
- "com.google.code.findbugs:jsr305:3.0.2",
- "com.google.code.gson:gson:2.8.9",
- "com.google.errorprone:error_prone_annotations:2.3.2",
- "com.google.j2objc:j2objc-annotations:1.3",
- "com.google.guava:guava:31.1-jre",
- "com.google.guava:guava-testlib:31.1-jre",
- "com.google.truth:truth:1.1.2",
- "junit:junit:4.13.2",
- "org.mockito:mockito-core:4.3.1"
- ]
- },
- "devDependency": false,
- "location": {
- "file": "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel",
- "line": 24,
- "column": 14
- }
- }
- ],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "bazel_skylib": "bazel_skylib@1.5.0",
- "rules_python": "rules_python@0.22.1",
- "rules_cc": "rules_cc@0.0.9",
- "rules_proto": "rules_proto@5.3.0-21.7",
- "rules_java": "rules_java@7.4.0",
- "rules_pkg": "rules_pkg@0.7.0",
- "com_google_abseil": "abseil-cpp@20211102.0",
- "zlib": "zlib@1.3",
- "upb": "upb@0.0.0-20220923-a547704",
- "rules_jvm_external": "rules_jvm_external@4.4.2",
- "com_google_googletest": "googletest@1.11.0",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/protocolbuffers/protobuf/releases/download/v21.7/protobuf-all-21.7.zip"
- ],
- "integrity": "sha256-VJOiH17T/FAuZv7GuUScBqVRztYwAvpIkDxA36jeeko=",
- "strip_prefix": "protobuf-21.7",
- "remote_patches": {
- "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_module_dot_bazel.patch": "sha256-q3V2+eq0v2XF0z8z+V+QF4cynD6JvHI1y3kI/+rzl5s=",
- "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_module_dot_bazel_for_examples.patch": "sha256-O7YP6s3lo/1opUiO0jqXYORNHdZ/2q3hjz1QGy8QdIU=",
- "https://bcr.bazel.build/modules/protobuf/21.7/patches/relative_repo_names.patch": "sha256-RK9RjW8T5UJNG7flIrnFiNE9vKwWB+8uWWtJqXYT0w4=",
- "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_missing_files.patch": "sha256-Hyne4DG2u5bXcWHNxNMirA2QFAe/2Cl8oMm1XJdkQIY="
- },
- "remote_patch_strip": 1
- }
- }
- },
- "rules_java@7.4.0": {
- "name": "rules_java",
- "version": "7.4.0",
- "key": "rules_java@7.4.0",
- "repoName": "rules_java",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [
- "//toolchains:all",
- "@local_jdk//:runtime_toolchain_definition",
- "@local_jdk//:bootstrap_runtime_toolchain_definition",
- "@remotejdk11_linux_toolchain_config_repo//:all",
- "@remotejdk11_linux_aarch64_toolchain_config_repo//:all",
- "@remotejdk11_linux_ppc64le_toolchain_config_repo//:all",
- "@remotejdk11_linux_s390x_toolchain_config_repo//:all",
- "@remotejdk11_macos_toolchain_config_repo//:all",
- "@remotejdk11_macos_aarch64_toolchain_config_repo//:all",
- "@remotejdk11_win_toolchain_config_repo//:all",
- "@remotejdk11_win_arm64_toolchain_config_repo//:all",
- "@remotejdk17_linux_toolchain_config_repo//:all",
- "@remotejdk17_linux_aarch64_toolchain_config_repo//:all",
- "@remotejdk17_linux_ppc64le_toolchain_config_repo//:all",
- "@remotejdk17_linux_s390x_toolchain_config_repo//:all",
- "@remotejdk17_macos_toolchain_config_repo//:all",
- "@remotejdk17_macos_aarch64_toolchain_config_repo//:all",
- "@remotejdk17_win_toolchain_config_repo//:all",
- "@remotejdk17_win_arm64_toolchain_config_repo//:all",
- "@remotejdk21_linux_toolchain_config_repo//:all",
- "@remotejdk21_linux_aarch64_toolchain_config_repo//:all",
- "@remotejdk21_macos_toolchain_config_repo//:all",
- "@remotejdk21_macos_aarch64_toolchain_config_repo//:all",
- "@remotejdk21_win_toolchain_config_repo//:all"
- ],
- "extensionUsages": [
- {
- "extensionBzlFile": "@rules_java//java:extensions.bzl",
- "extensionName": "toolchains",
- "usingModule": "rules_java@7.4.0",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_java/7.4.0/MODULE.bazel",
- "line": 19,
- "column": 27
- },
- "imports": {
- "remote_java_tools": "remote_java_tools",
- "remote_java_tools_linux": "remote_java_tools_linux",
- "remote_java_tools_windows": "remote_java_tools_windows",
- "remote_java_tools_darwin_x86_64": "remote_java_tools_darwin_x86_64",
- "remote_java_tools_darwin_arm64": "remote_java_tools_darwin_arm64",
- "local_jdk": "local_jdk",
- "remotejdk11_linux_toolchain_config_repo": "remotejdk11_linux_toolchain_config_repo",
- "remotejdk11_linux_aarch64_toolchain_config_repo": "remotejdk11_linux_aarch64_toolchain_config_repo",
- "remotejdk11_linux_ppc64le_toolchain_config_repo": "remotejdk11_linux_ppc64le_toolchain_config_repo",
- "remotejdk11_linux_s390x_toolchain_config_repo": "remotejdk11_linux_s390x_toolchain_config_repo",
- "remotejdk11_macos_toolchain_config_repo": "remotejdk11_macos_toolchain_config_repo",
- "remotejdk11_macos_aarch64_toolchain_config_repo": "remotejdk11_macos_aarch64_toolchain_config_repo",
- "remotejdk11_win_toolchain_config_repo": "remotejdk11_win_toolchain_config_repo",
- "remotejdk11_win_arm64_toolchain_config_repo": "remotejdk11_win_arm64_toolchain_config_repo",
- "remotejdk17_linux_toolchain_config_repo": "remotejdk17_linux_toolchain_config_repo",
- "remotejdk17_linux_aarch64_toolchain_config_repo": "remotejdk17_linux_aarch64_toolchain_config_repo",
- "remotejdk17_linux_ppc64le_toolchain_config_repo": "remotejdk17_linux_ppc64le_toolchain_config_repo",
- "remotejdk17_linux_s390x_toolchain_config_repo": "remotejdk17_linux_s390x_toolchain_config_repo",
- "remotejdk17_macos_toolchain_config_repo": "remotejdk17_macos_toolchain_config_repo",
- "remotejdk17_macos_aarch64_toolchain_config_repo": "remotejdk17_macos_aarch64_toolchain_config_repo",
- "remotejdk17_win_toolchain_config_repo": "remotejdk17_win_toolchain_config_repo",
- "remotejdk17_win_arm64_toolchain_config_repo": "remotejdk17_win_arm64_toolchain_config_repo",
- "remotejdk21_linux_toolchain_config_repo": "remotejdk21_linux_toolchain_config_repo",
- "remotejdk21_linux_aarch64_toolchain_config_repo": "remotejdk21_linux_aarch64_toolchain_config_repo",
- "remotejdk21_macos_toolchain_config_repo": "remotejdk21_macos_toolchain_config_repo",
- "remotejdk21_macos_aarch64_toolchain_config_repo": "remotejdk21_macos_aarch64_toolchain_config_repo",
- "remotejdk21_win_toolchain_config_repo": "remotejdk21_win_toolchain_config_repo"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "platforms": "platforms@0.0.8",
- "rules_cc": "rules_cc@0.0.9",
- "bazel_skylib": "bazel_skylib@1.5.0",
- "rules_proto": "rules_proto@5.3.0-21.7",
- "rules_license": "rules_license@0.0.8",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/rules_java/releases/download/7.4.0/rules_java-7.4.0.tar.gz"
- ],
- "integrity": "sha256-l27wi0nJKXQfIBeQ5Z44B8cq2B9CjIvJU82+/1/tFes=",
- "strip_prefix": "",
- "remote_patches": {},
- "remote_patch_strip": 0
- }
- }
- },
- "rules_python@0.22.1": {
- "name": "rules_python",
- "version": "0.22.1",
- "key": "rules_python@0.22.1",
- "repoName": "rules_python",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [
- "@bazel_tools//tools/python:autodetecting_toolchain"
- ],
- "extensionUsages": [
- {
- "extensionBzlFile": "@rules_python//python/extensions/private:internal_deps.bzl",
- "extensionName": "internal_deps",
- "usingModule": "rules_python@0.22.1",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel",
- "line": 14,
- "column": 30
- },
- "imports": {
- "pypi__build": "pypi__build",
- "pypi__click": "pypi__click",
- "pypi__colorama": "pypi__colorama",
- "pypi__importlib_metadata": "pypi__importlib_metadata",
- "pypi__installer": "pypi__installer",
- "pypi__more_itertools": "pypi__more_itertools",
- "pypi__packaging": "pypi__packaging",
- "pypi__pep517": "pypi__pep517",
- "pypi__pip": "pypi__pip",
- "pypi__pip_tools": "pypi__pip_tools",
- "pypi__setuptools": "pypi__setuptools",
- "pypi__tomli": "pypi__tomli",
- "pypi__wheel": "pypi__wheel",
- "pypi__zipp": "pypi__zipp",
- "pypi__coverage_cp310_aarch64-apple-darwin": "pypi__coverage_cp310_aarch64-apple-darwin",
- "pypi__coverage_cp310_aarch64-unknown-linux-gnu": "pypi__coverage_cp310_aarch64-unknown-linux-gnu",
- "pypi__coverage_cp310_x86_64-apple-darwin": "pypi__coverage_cp310_x86_64-apple-darwin",
- "pypi__coverage_cp310_x86_64-unknown-linux-gnu": "pypi__coverage_cp310_x86_64-unknown-linux-gnu",
- "pypi__coverage_cp311_aarch64-unknown-linux-gnu": "pypi__coverage_cp311_aarch64-unknown-linux-gnu",
- "pypi__coverage_cp311_x86_64-apple-darwin": "pypi__coverage_cp311_x86_64-apple-darwin",
- "pypi__coverage_cp311_x86_64-unknown-linux-gnu": "pypi__coverage_cp311_x86_64-unknown-linux-gnu",
- "pypi__coverage_cp38_aarch64-apple-darwin": "pypi__coverage_cp38_aarch64-apple-darwin",
- "pypi__coverage_cp38_aarch64-unknown-linux-gnu": "pypi__coverage_cp38_aarch64-unknown-linux-gnu",
- "pypi__coverage_cp38_x86_64-apple-darwin": "pypi__coverage_cp38_x86_64-apple-darwin",
- "pypi__coverage_cp38_x86_64-unknown-linux-gnu": "pypi__coverage_cp38_x86_64-unknown-linux-gnu",
- "pypi__coverage_cp39_aarch64-apple-darwin": "pypi__coverage_cp39_aarch64-apple-darwin",
- "pypi__coverage_cp39_aarch64-unknown-linux-gnu": "pypi__coverage_cp39_aarch64-unknown-linux-gnu",
- "pypi__coverage_cp39_x86_64-apple-darwin": "pypi__coverage_cp39_x86_64-apple-darwin",
- "pypi__coverage_cp39_x86_64-unknown-linux-gnu": "pypi__coverage_cp39_x86_64-unknown-linux-gnu"
- },
- "devImports": [],
- "tags": [
- {
- "tagName": "install",
- "attributeValues": {},
- "devDependency": false,
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel",
- "line": 15,
- "column": 22
- }
- }
- ],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@rules_python//python/extensions:python.bzl",
- "extensionName": "python",
- "usingModule": "rules_python@0.22.1",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel",
- "line": 50,
- "column": 23
- },
- "imports": {
- "pythons_hub": "pythons_hub"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "platforms": "platforms@0.0.8",
- "bazel_skylib": "bazel_skylib@1.5.0",
- "rules_proto": "rules_proto@5.3.0-21.7",
- "com_google_protobuf": "protobuf@21.7",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/rules_python/releases/download/0.22.1/rules_python-0.22.1.tar.gz"
- ],
- "integrity": "sha256-pWQP3dS+sD6MH95e1xYMC6a9R359BIZhwwwGk2om/WM=",
- "strip_prefix": "rules_python-0.22.1",
- "remote_patches": {
- "https://bcr.bazel.build/modules/rules_python/0.22.1/patches/module_dot_bazel_version.patch": "sha256-3+VLDH9gYDzNI4eOW7mABC/LKxh1xqF6NhacLbNTucs="
- },
- "remote_patch_strip": 1
- }
- }
- },
- "buildozer@6.4.0.2": {
- "name": "buildozer",
- "version": "6.4.0.2",
- "key": "buildozer@6.4.0.2",
- "repoName": "buildozer",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [
- {
- "extensionBzlFile": "@buildozer//:buildozer_binary.bzl",
- "extensionName": "buildozer_binary",
- "usingModule": "buildozer@6.4.0.2",
- "location": {
- "file": "https://bcr.bazel.build/modules/buildozer/6.4.0.2/MODULE.bazel",
- "line": 7,
- "column": 33
- },
- "imports": {
- "buildozer_binary": "buildozer_binary"
- },
- "devImports": [],
- "tags": [
- {
- "tagName": "buildozer",
- "attributeValues": {
- "sha256": {
- "darwin-amd64": "d29e347ecd6b5673d72cb1a8de05bf1b06178dd229ff5eb67fad5100c840cc8e",
- "darwin-arm64": "9b9e71bdbec5e7223871e913b65d12f6d8fa026684daf991f00e52ed36a6978d",
- "linux-amd64": "8dfd6345da4e9042daa738d7fdf34f699c5dfce4632f7207956fceedd8494119",
- "linux-arm64": "6559558fded658c8fa7432a9d011f7c4dcbac6b738feae73d2d5c352e5f605fa",
- "windows-amd64": "e7f05bf847f7c3689dd28926460ce6e1097ae97380ac8e6ae7147b7b706ba19b"
- },
- "version": "6.4.0"
- },
- "devDependency": false,
- "location": {
- "file": "https://bcr.bazel.build/modules/buildozer/6.4.0.2/MODULE.bazel",
- "line": 8,
- "column": 27
- }
- }
- ],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/fmeum/buildozer/releases/download/v6.4.0.2/buildozer-v6.4.0.2.tar.gz"
- ],
- "integrity": "sha256-k7tFKQMR2AygxpmZfH0yEPnQmF3efFgD9rBPkj+Yz/8=",
- "strip_prefix": "buildozer-6.4.0.2",
- "remote_patches": {
- "https://bcr.bazel.build/modules/buildozer/6.4.0.2/patches/module_dot_bazel_version.patch": "sha256-gKANF2HMilj7bWmuXs4lbBIAAansuWC4IhWGB/CerjU="
- },
- "remote_patch_strip": 1
- }
- }
- },
- "zlib@1.3": {
- "name": "zlib",
- "version": "1.3",
- "key": "zlib@1.3",
- "repoName": "zlib",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "platforms": "platforms@0.0.8",
- "rules_cc": "rules_cc@0.0.9",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/madler/zlib/releases/download/v1.3/zlib-1.3.tar.gz"
- ],
- "integrity": "sha256-/wukwpIBPbwnUws6geH5qBPNOd4Byl4Pi/NVcC76WT4=",
- "strip_prefix": "zlib-1.3",
- "remote_patches": {
- "https://bcr.bazel.build/modules/zlib/1.3/patches/add_build_file.patch": "sha256-Ei+FYaaOo7A3jTKunMEodTI0Uw5NXQyZEcboMC8JskY=",
- "https://bcr.bazel.build/modules/zlib/1.3/patches/module_dot_bazel.patch": "sha256-fPWLM+2xaF/kuy+kZc1YTfW6hNjrkG400Ho7gckuyJk="
- },
- "remote_patch_strip": 0
- }
- }
- },
- "rules_pkg@0.7.0": {
- "name": "rules_pkg",
- "version": "0.7.0",
- "key": "rules_pkg@0.7.0",
- "repoName": "rules_pkg",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "rules_python": "rules_python@0.22.1",
- "bazel_skylib": "bazel_skylib@1.5.0",
- "rules_license": "rules_license@0.0.8",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz"
- ],
- "integrity": "sha256-iimOgydi7aGDBZfWT+fbWBeKqEzVkm121bdE1lWJQcI=",
- "strip_prefix": "",
- "remote_patches": {
- "https://bcr.bazel.build/modules/rules_pkg/0.7.0/patches/module_dot_bazel.patch": "sha256-4OaEPZwYF6iC71ZTDg6MJ7LLqX7ZA0/kK4mT+4xKqiE="
- },
- "remote_patch_strip": 0
- }
- }
- },
- "abseil-cpp@20211102.0": {
- "name": "abseil-cpp",
- "version": "20211102.0",
- "key": "abseil-cpp@20211102.0",
- "repoName": "abseil-cpp",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "rules_cc": "rules_cc@0.0.9",
- "platforms": "platforms@0.0.8",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/abseil/abseil-cpp/archive/refs/tags/20211102.0.tar.gz"
- ],
- "integrity": "sha256-3PcbnLqNwMqZQMSzFqDHlr6Pq0KwcLtrfKtitI8OZsQ=",
- "strip_prefix": "abseil-cpp-20211102.0",
- "remote_patches": {
- "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/patches/module_dot_bazel.patch": "sha256-4izqopgGCey4jVZzl/w3M2GVPNohjh2B5TmbThZNvPY="
- },
- "remote_patch_strip": 0
- }
- }
- },
- "upb@0.0.0-20220923-a547704": {
- "name": "upb",
- "version": "0.0.0-20220923-a547704",
- "key": "upb@0.0.0-20220923-a547704",
- "repoName": "upb",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "bazel_skylib": "bazel_skylib@1.5.0",
- "rules_proto": "rules_proto@5.3.0-21.7",
- "com_google_protobuf": "protobuf@21.7",
- "com_google_absl": "abseil-cpp@20211102.0",
- "platforms": "platforms@0.0.8",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/protocolbuffers/upb/archive/a5477045acaa34586420942098f5fecd3570f577.tar.gz"
- ],
- "integrity": "sha256-z39x6v+QskwaKLSWRan/A6mmwecTQpHOcJActj5zZLU=",
- "strip_prefix": "upb-a5477045acaa34586420942098f5fecd3570f577",
- "remote_patches": {
- "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/patches/module_dot_bazel.patch": "sha256-wH4mNS6ZYy+8uC0HoAft/c7SDsq2Kxf+J8dUakXhaB0="
- },
- "remote_patch_strip": 0
- }
- }
- },
- "rules_jvm_external@4.4.2": {
- "name": "rules_jvm_external",
- "version": "4.4.2",
- "key": "rules_jvm_external@4.4.2",
- "repoName": "rules_jvm_external",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [
- {
- "extensionBzlFile": "@rules_jvm_external//:non-module-deps.bzl",
- "extensionName": "non_module_deps",
- "usingModule": "rules_jvm_external@4.4.2",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel",
- "line": 9,
- "column": 32
- },
- "imports": {
- "io_bazel_rules_kotlin": "io_bazel_rules_kotlin"
- },
- "devImports": [],
- "tags": [],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- },
- {
- "extensionBzlFile": "@rules_jvm_external//:extensions.bzl",
- "extensionName": "maven",
- "usingModule": "rules_jvm_external@4.4.2",
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel",
- "line": 16,
- "column": 22
- },
- "imports": {
- "rules_jvm_external_deps": "rules_jvm_external_deps"
- },
- "devImports": [],
- "tags": [
- {
- "tagName": "install",
- "attributeValues": {
- "name": "rules_jvm_external_deps",
- "artifacts": [
- "com.google.cloud:google-cloud-core:1.93.10",
- "com.google.cloud:google-cloud-storage:1.113.4",
- "com.google.code.gson:gson:2.9.0",
- "org.apache.maven:maven-artifact:3.8.6",
- "software.amazon.awssdk:s3:2.17.183"
- ],
- "lock_file": "@rules_jvm_external//:rules_jvm_external_deps_install.json"
- },
- "devDependency": false,
- "location": {
- "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel",
- "line": 18,
- "column": 14
- }
- }
- ],
- "hasDevUseExtension": false,
- "hasNonDevUseExtension": true
- }
- ],
- "deps": {
- "bazel_skylib": "bazel_skylib@1.5.0",
- "io_bazel_stardoc": "stardoc@0.5.1",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.4.2.zip"
- ],
- "integrity": "sha256-c1YC9QgT6y6pPKP15DsZWb2AshO4NqB6YqKddXZwt3s=",
- "strip_prefix": "rules_jvm_external-4.4.2",
- "remote_patches": {},
- "remote_patch_strip": 0
- }
- }
- },
- "googletest@1.11.0": {
- "name": "googletest",
- "version": "1.11.0",
- "key": "googletest@1.11.0",
- "repoName": "googletest",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "com_google_absl": "abseil-cpp@20211102.0",
- "platforms": "platforms@0.0.8",
- "rules_cc": "rules_cc@0.0.9",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz"
- ],
- "integrity": "sha256-tIcL8SH/d5W6INILzdhie44Ijy0dqymaAxwQNO3ck9U=",
- "strip_prefix": "googletest-release-1.11.0",
- "remote_patches": {
- "https://bcr.bazel.build/modules/googletest/1.11.0/patches/module_dot_bazel.patch": "sha256-HuahEdI/n8KCI071sN3CEziX+7qP/Ec77IWayYunLP0="
- },
- "remote_patch_strip": 0
- }
- }
- },
- "stardoc@0.5.1": {
- "name": "stardoc",
- "version": "0.5.1",
- "key": "stardoc@0.5.1",
- "repoName": "stardoc",
- "executionPlatformsToRegister": [],
- "toolchainsToRegister": [],
- "extensionUsages": [],
- "deps": {
- "bazel_skylib": "bazel_skylib@1.5.0",
- "rules_java": "rules_java@7.4.0",
- "bazel_tools": "bazel_tools@_",
- "local_config_platform": "local_config_platform@_"
- },
- "repoSpec": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/stardoc/releases/download/0.5.1/stardoc-0.5.1.tar.gz"
- ],
- "integrity": "sha256-qoFNrgrEALurLoiB+ZFcb0fElmS/CHxAmhX5BDjSwj4=",
- "strip_prefix": "",
- "remote_patches": {
- "https://bcr.bazel.build/modules/stardoc/0.5.1/patches/module_dot_bazel.patch": "sha256-UAULCuTpJE7SG0YrR9XLjMfxMRmbP+za3uW9ONZ5rjI="
- },
- "remote_patch_strip": 0
- }
- }
- }
- },
+ "selectedYankedVersions": {},
"moduleExtensions": {
- "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": {
+ "@@apple_support+//crosstool:setup.bzl%apple_cc_configure_extension": {
"general": {
- "bzlTransitiveDigest": "TMkUP4/N3ZORvZrcDg9FxSoW9r/7+uDVH/SI2biRyJg=",
+ "bzlTransitiveDigest": "toI2CJrfSjyrO+VKLMSvyJeQBo38LpM/Y452BrL9PRE=",
+ "usagesDigest": "2g11pC3meeC9i6QJ70IQ9kqRygrhz9bj/s9la710uQE=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
- "local_config_apple_cc": {
- "bzlFile": "@@apple_support~//crosstool:setup.bzl",
- "ruleClassName": "_apple_cc_autoconf",
- "attributes": {}
- },
"local_config_apple_cc_toolchains": {
- "bzlFile": "@@apple_support~//crosstool:setup.bzl",
- "ruleClassName": "_apple_cc_autoconf_toolchains",
+ "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf_toolchains",
+ "attributes": {}
+ },
+ "local_config_apple_cc": {
+ "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf",
"attributes": {}
}
},
"recordedRepoMappingEntries": [
[
- "apple_support~",
- "bazel_tools",
- "bazel_tools"
- ]
- ]
- }
- },
- "@@bazel_features~//private:extensions.bzl%version_extension": {
- "general": {
- "bzlTransitiveDigest": "3FcE0iMy2yYKEbEO19f72k9dzcpRUXHH+igow5yVy8g=",
- "recordedFileInputs": {},
- "recordedDirentsInputs": {},
- "envVariables": {},
- "generatedRepoSpecs": {
- "bazel_features_version": {
- "bzlFile": "@@bazel_features~//private:version_repo.bzl",
- "ruleClassName": "version_repo",
- "attributes": {}
- },
- "bazel_features_globals": {
- "bzlFile": "@@bazel_features~//private:globals_repo.bzl",
- "ruleClassName": "globals_repo",
- "attributes": {
- "globals": {
- "RunEnvironmentInfo": "5.3.0",
- "DefaultInfo": "0.0.1",
- "__TestingOnly_NeverAvailable": "1000000000.0.0"
- }
- }
- }
- },
- "recordedRepoMappingEntries": [
- [
- "bazel_features~",
- "bazel_tools",
- "bazel_tools"
- ]
- ]
- }
- },
- "@@bazel_tools//tools/cpp:cc_configure.bzl%cc_configure_extension": {
- "general": {
- "bzlTransitiveDigest": "PHpT2yqMGms2U4L3E/aZ+WcQalmZWm+ILdP3yiLsDhA=",
- "recordedFileInputs": {},
- "recordedDirentsInputs": {},
- "envVariables": {},
- "generatedRepoSpecs": {
- "local_config_cc": {
- "bzlFile": "@@bazel_tools//tools/cpp:cc_configure.bzl",
- "ruleClassName": "cc_autoconf",
- "attributes": {}
- },
- "local_config_cc_toolchains": {
- "bzlFile": "@@bazel_tools//tools/cpp:cc_configure.bzl",
- "ruleClassName": "cc_autoconf_toolchains",
- "attributes": {}
- }
- },
- "recordedRepoMappingEntries": [
- [
- "bazel_tools",
- "bazel_tools",
- "bazel_tools"
- ]
- ]
- }
- },
- "@@bazel_tools//tools/osx:xcode_configure.bzl%xcode_configure_extension": {
- "general": {
- "bzlTransitiveDigest": "Qh2bWTU6QW6wkrd87qrU4YeY+SG37Nvw3A0PR4Y0L2Y=",
- "recordedFileInputs": {},
- "recordedDirentsInputs": {},
- "envVariables": {},
- "generatedRepoSpecs": {
- "local_config_xcode": {
- "bzlFile": "@@bazel_tools//tools/osx:xcode_configure.bzl",
- "ruleClassName": "xcode_autoconf",
- "attributes": {
- "xcode_locator": "@bazel_tools//tools/osx:xcode_locator.m",
- "remote_xcode": ""
- }
- }
- },
- "recordedRepoMappingEntries": []
- }
- },
- "@@bazel_tools//tools/sh:sh_configure.bzl%sh_configure_extension": {
- "general": {
- "bzlTransitiveDigest": "hp4NgmNjEg5+xgvzfh6L83bt9/aiiWETuNpwNuF1MSU=",
- "recordedFileInputs": {},
- "recordedDirentsInputs": {},
- "envVariables": {},
- "generatedRepoSpecs": {
- "local_config_sh": {
- "bzlFile": "@@bazel_tools//tools/sh:sh_configure.bzl",
- "ruleClassName": "sh_config",
- "attributes": {}
- }
- },
- "recordedRepoMappingEntries": []
- }
- },
- "@@rules_java~//java:extensions.bzl%toolchains": {
- "general": {
- "bzlTransitiveDigest": "tJHbmWnq7m+9eUBnUdv7jZziQ26FmcGL9C5/hU3Q9UQ=",
- "recordedFileInputs": {},
- "recordedDirentsInputs": {},
- "envVariables": {},
- "generatedRepoSpecs": {
- "remotejdk21_linux_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux//:jdk\",\n)\n"
- }
- },
- "remotejdk17_linux_s390x_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_s390x//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_s390x//:jdk\",\n)\n"
- }
- },
- "remotejdk17_macos_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos//:jdk\",\n)\n"
- }
- },
- "remotejdk21_macos_aarch64_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos_aarch64//:jdk\",\n)\n"
- }
- },
- "remotejdk17_linux_aarch64_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_aarch64//:jdk\",\n)\n"
- }
- },
- "remotejdk21_macos_aarch64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n",
- "sha256": "e8260516de8b60661422a725f1df2c36ef888f6fb35393566b00e7325db3d04e",
- "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-macosx_aarch64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_aarch64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_aarch64.tar.gz"
- ]
- }
- },
- "remotejdk17_linux_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux//:jdk\",\n)\n"
- }
- },
- "remotejdk17_macos_aarch64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n",
- "sha256": "314b04568ec0ae9b36ba03c9cbd42adc9e1265f74678923b19297d66eb84dcca",
- "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64.tar.gz"
- ]
- }
- },
- "remote_java_tools_windows": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fe2f88169696d6c6fc6e90ba61bb46be7d0ae3693cbafdf336041bf56679e8d1",
- "urls": [
- "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_windows-v13.4.zip",
- "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_windows-v13.4.zip"
- ]
- }
- },
- "remotejdk11_win": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n",
- "sha256": "43408193ce2fa0862819495b5ae8541085b95660153f2adcf91a52d3a1710e83",
- "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-win_x64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-win_x64.zip",
- "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-win_x64.zip"
- ]
- }
- },
- "remotejdk11_win_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win//:jdk\",\n)\n"
- }
- },
- "remotejdk11_linux_aarch64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n",
- "sha256": "54174439f2b3fddd11f1048c397fe7bb45d4c9d66d452d6889b013d04d21c4de",
- "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-linux_aarch64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_aarch64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_aarch64.tar.gz"
- ]
- }
- },
- "remotejdk17_linux": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n",
- "sha256": "b9482f2304a1a68a614dfacddcf29569a72f0fac32e6c74f83dc1b9a157b8340",
- "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-linux_x64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_x64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_x64.tar.gz"
- ]
- }
- },
- "remotejdk11_linux_s390x_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_s390x//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_s390x//:jdk\",\n)\n"
- }
- },
- "remotejdk11_linux_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux//:jdk\",\n)\n"
- }
- },
- "remotejdk11_macos": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n",
- "sha256": "bcaab11cfe586fae7583c6d9d311c64384354fb2638eb9a012eca4c3f1a1d9fd",
- "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-macosx_x64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_x64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_x64.tar.gz"
- ]
- }
- },
- "remotejdk11_win_arm64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n",
- "sha256": "b8a28e6e767d90acf793ea6f5bed0bb595ba0ba5ebdf8b99f395266161e53ec2",
- "strip_prefix": "jdk-11.0.13+8",
- "urls": [
- "https://mirror.bazel.build/aka.ms/download-jdk/microsoft-jdk-11.0.13.8.1-windows-aarch64.zip"
- ]
- }
- },
- "remotejdk17_macos": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n",
- "sha256": "640453e8afe8ffe0fb4dceb4535fb50db9c283c64665eebb0ba68b19e65f4b1f",
- "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-macosx_x64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_x64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_x64.tar.gz"
- ]
- }
- },
- "remotejdk21_macos": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n",
- "sha256": "3ad8fe288eb57d975c2786ae453a036aa46e47ab2ac3d81538ebae2a54d3c025",
- "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-macosx_x64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_x64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_x64.tar.gz"
- ]
- }
- },
- "remotejdk21_macos_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos//:jdk\",\n)\n"
- }
- },
- "remotejdk17_macos_aarch64_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos_aarch64//:jdk\",\n)\n"
- }
- },
- "remotejdk17_win": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n",
- "sha256": "192f2afca57701de6ec496234f7e45d971bf623ff66b8ee4a5c81582054e5637",
- "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-win_x64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_x64.zip",
- "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_x64.zip"
- ]
- }
- },
- "remotejdk11_macos_aarch64_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos_aarch64//:jdk\",\n)\n"
- }
- },
- "remotejdk11_linux_ppc64le_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_ppc64le//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_ppc64le//:jdk\",\n)\n"
- }
- },
- "remotejdk21_linux": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n",
- "sha256": "5ad730fbee6bb49bfff10bf39e84392e728d89103d3474a7e5def0fd134b300a",
- "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-linux_x64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_x64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_x64.tar.gz"
- ]
- }
- },
- "remote_java_tools_linux": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ba10f09a138cf185d04cbc807d67a3da42ab13d618c5d1ce20d776e199c33a39",
- "urls": [
- "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_linux-v13.4.zip",
- "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_linux-v13.4.zip"
- ]
- }
- },
- "remotejdk21_win": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n",
- "sha256": "f7cc15ca17295e69c907402dfe8db240db446e75d3b150da7bf67243cded93de",
- "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-win_x64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-win_x64.zip",
- "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-win_x64.zip"
- ]
- }
- },
- "remotejdk21_linux_aarch64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n",
- "sha256": "ce7df1af5d44a9f455617c4b8891443fbe3e4b269c777d8b82ed66f77167cfe0",
- "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-linux_aarch64",
- "urls": [
- "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_aarch64.tar.gz",
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_aarch64.tar.gz"
- ]
- }
- },
- "remotejdk11_linux_aarch64_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_aarch64//:jdk\",\n)\n"
- }
- },
- "remotejdk11_linux_s390x": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n",
- "sha256": "a58fc0361966af0a5d5a31a2d8a208e3c9bb0f54f345596fd80b99ea9a39788b",
- "strip_prefix": "jdk-11.0.15+10",
- "urls": [
- "https://mirror.bazel.build/github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.15_10.tar.gz",
- "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.15_10.tar.gz"
- ]
- }
- },
- "remotejdk17_linux_aarch64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n",
- "sha256": "6531cef61e416d5a7b691555c8cf2bdff689201b8a001ff45ab6740062b44313",
- "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64.tar.gz"
- ]
- }
- },
- "remotejdk17_win_arm64_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win_arm64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win_arm64//:jdk\",\n)\n"
- }
- },
- "remotejdk11_linux": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n",
- "sha256": "a34b404f87a08a61148b38e1416d837189e1df7a040d949e743633daf4695a3c",
- "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-linux_x64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_x64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_x64.tar.gz"
- ]
- }
- },
- "remotejdk11_macos_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos//:jdk\",\n)\n"
- }
- },
- "remotejdk17_linux_ppc64le_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_ppc64le//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_ppc64le//:jdk\",\n)\n"
- }
- },
- "remotejdk17_win_arm64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n",
- "sha256": "6802c99eae0d788e21f52d03cab2e2b3bf42bc334ca03cbf19f71eb70ee19f85",
- "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-win_aarch64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_aarch64.zip",
- "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_aarch64.zip"
- ]
- }
- },
- "remote_java_tools_darwin_arm64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "076a7e198ad077f8c7d997986ef5102427fae6bbfce7a7852d2e080ed8767528",
- "urls": [
- "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_darwin_arm64-v13.4.zip",
- "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_darwin_arm64-v13.4.zip"
- ]
- }
- },
- "remotejdk17_linux_ppc64le": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n",
- "sha256": "00a4c07603d0218cd678461b5b3b7e25b3253102da4022d31fc35907f21a2efd",
- "strip_prefix": "jdk-17.0.8.1+1",
- "urls": [
- "https://mirror.bazel.build/github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_ppc64le_linux_hotspot_17.0.8.1_1.tar.gz",
- "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_ppc64le_linux_hotspot_17.0.8.1_1.tar.gz"
- ]
- }
- },
- "remotejdk21_linux_aarch64_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux_aarch64//:jdk\",\n)\n"
- }
- },
- "remotejdk11_win_arm64_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win_arm64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win_arm64//:jdk\",\n)\n"
- }
- },
- "local_jdk": {
- "bzlFile": "@@rules_java~//toolchains:local_java_repository.bzl",
- "ruleClassName": "_local_java_repository_rule",
- "attributes": {
- "java_home": "",
- "version": "",
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = {RUNTIME_VERSION},\n)\n"
- }
- },
- "remote_java_tools_darwin_x86_64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4523aec4d09c587091a2dae6f5c9bc6922c220f3b6030e5aba9c8f015913cc65",
- "urls": [
- "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_darwin_x86_64-v13.4.zip",
- "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_darwin_x86_64-v13.4.zip"
- ]
- }
- },
- "remote_java_tools": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e025fd260ac39b47c111f5212d64ec0d00d85dec16e49368aae82fc626a940cf",
- "urls": [
- "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools-v13.4.zip",
- "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools-v13.4.zip"
- ]
- }
- },
- "remotejdk17_linux_s390x": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n",
- "sha256": "ffacba69c6843d7ca70d572489d6cc7ab7ae52c60f0852cedf4cf0d248b6fc37",
- "strip_prefix": "jdk-17.0.8.1+1",
- "urls": [
- "https://mirror.bazel.build/github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_s390x_linux_hotspot_17.0.8.1_1.tar.gz",
- "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_s390x_linux_hotspot_17.0.8.1_1.tar.gz"
- ]
- }
- },
- "remotejdk17_win_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win//:jdk\",\n)\n"
- }
- },
- "remotejdk11_linux_ppc64le": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n",
- "sha256": "a8fba686f6eb8ae1d1a9566821dbd5a85a1108b96ad857fdbac5c1e4649fc56f",
- "strip_prefix": "jdk-11.0.15+10",
- "urls": [
- "https://mirror.bazel.build/github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.15_10.tar.gz",
- "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.15_10.tar.gz"
- ]
- }
- },
- "remotejdk11_macos_aarch64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n",
- "sha256": "7632bc29f8a4b7d492b93f3bc75a7b61630894db85d136456035ab2a24d38885",
- "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-macosx_aarch64",
- "urls": [
- "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_aarch64.tar.gz",
- "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_aarch64.tar.gz"
- ]
- }
- },
- "remotejdk21_win_toolchain_config_repo": {
- "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl",
- "ruleClassName": "_toolchain_config",
- "attributes": {
- "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_win//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_win//:jdk\",\n)\n"
- }
- }
- },
- "recordedRepoMappingEntries": [
- [
- "rules_java~",
+ "apple_support+",
"bazel_tools",
"bazel_tools"
],
[
- "rules_java~",
- "remote_java_tools",
- "rules_java~~toolchains~remote_java_tools"
+ "bazel_tools",
+ "rules_cc",
+ "rules_cc+"
]
]
}
},
- "@@rules_rust~//rust:extensions.bzl%rust": {
+ "@@aspect_bazel_lib+//lib:extensions.bzl%toolchains": {
"general": {
- "bzlTransitiveDigest": "AIzky/8mTX1LH5J1xwGS2WPTgEh9lI036W+TrK7PEkw=",
+ "bzlTransitiveDigest": "TGnRoh+5JjQRL6rkWCQneJpM89XjhPyydRXWIn0HmDw=",
+ "usagesDigest": "HyCD/AMcHKcynL86oRSbi4rhw9cjPb8yfXrC363gBKE=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
- "rust_windows_x86_64__wasm32-wasi__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "copy_directory_darwin_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_directory_toolchain.bzl%copy_directory_platform_repo",
"attributes": {
- "exec_triple": "x86_64-pc-windows-msvc",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-wasi",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "darwin_amd64"
}
},
- "rust_darwin_aarch64__wasm32-wasi__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "copy_directory_darwin_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_directory_toolchain.bzl%copy_directory_platform_repo",
"attributes": {
- "exec_triple": "aarch64-apple-darwin",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-wasi",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "darwin_arm64"
}
},
- "rust_darwin_x86_64__wasm32-wasi__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "copy_directory_freebsd_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_directory_toolchain.bzl%copy_directory_platform_repo",
"attributes": {
- "exec_triple": "x86_64-apple-darwin",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-wasi",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "freebsd_amd64"
}
},
- "rust_freebsd_x86_64__wasm32-unknown-unknown__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "copy_directory_linux_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_directory_toolchain.bzl%copy_directory_platform_repo",
"attributes": {
- "exec_triple": "x86_64-unknown-freebsd",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-unknown-unknown",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "linux_amd64"
}
},
- "rust_freebsd_x86_64__wasm32-wasi__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "copy_directory_linux_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_directory_toolchain.bzl%copy_directory_platform_repo",
"attributes": {
- "toolchain": "@rust_freebsd_x86_64__wasm32-wasi__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ]
+ "platform": "linux_arm64"
}
},
- "rust_linux_aarch64__aarch64-unknown-linux-gnu__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "copy_directory_windows_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_directory_toolchain.bzl%copy_directory_platform_repo",
"attributes": {
- "exec_triple": "aarch64-unknown-linux-gnu",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "aarch64-unknown-linux-gnu",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "windows_amd64"
}
},
- "rust_linux_aarch64__aarch64-unknown-linux-gnu__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "copy_directory_toolchains": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_directory_toolchain.bzl%copy_directory_toolchains_repo",
"attributes": {
- "toolchain": "@rust_linux_aarch64__aarch64-unknown-linux-gnu__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ],
- "target_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ]
+ "user_repository_name": "copy_directory"
}
},
- "rust_windows_x86_64": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_set_repository",
+ "copy_to_directory_darwin_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_to_directory_toolchain.bzl%copy_to_directory_platform_repo",
"attributes": {
- "toolchains": [
- "@rust_windows_x86_64__x86_64-pc-windows-msvc__stable//:toolchain",
- "@rust_windows_x86_64__wasm32-unknown-unknown__stable//:toolchain",
- "@rust_windows_x86_64__wasm32-wasi__stable//:toolchain"
- ]
+ "platform": "darwin_amd64"
}
},
- "rust_linux_aarch64__wasm32-unknown-unknown__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "copy_to_directory_darwin_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_to_directory_toolchain.bzl%copy_to_directory_platform_repo",
"attributes": {
- "toolchain": "@rust_linux_aarch64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ]
+ "platform": "darwin_arm64"
}
},
- "rust_windows_aarch64__wasm32-wasi__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "copy_to_directory_freebsd_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_to_directory_toolchain.bzl%copy_to_directory_platform_repo",
"attributes": {
- "exec_triple": "aarch64-pc-windows-msvc",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-wasi",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "freebsd_amd64"
}
},
- "rustfmt_nightly-2024-04-09__x86_64-unknown-freebsd": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "copy_to_directory_linux_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_to_directory_toolchain.bzl%copy_to_directory_platform_repo",
"attributes": {
- "toolchain": "@rustfmt_nightly-2024-04-09__x86_64-unknown-freebsd_tools//:rustfmt_toolchain",
- "toolchain_type": "@rules_rust//rust/rustfmt:toolchain_type",
- "target_settings": [],
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ],
- "target_compatible_with": []
+ "platform": "linux_amd64"
}
},
- "rustfmt_nightly-2024-04-09__aarch64-apple-darwin": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "copy_to_directory_linux_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_to_directory_toolchain.bzl%copy_to_directory_platform_repo",
"attributes": {
- "toolchain": "@rustfmt_nightly-2024-04-09__aarch64-apple-darwin_tools//:rustfmt_toolchain",
- "toolchain_type": "@rules_rust//rust/rustfmt:toolchain_type",
- "target_settings": [],
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ],
- "target_compatible_with": []
+ "platform": "linux_arm64"
}
},
- "rust_linux_x86_64__x86_64-unknown-linux-gnu__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "copy_to_directory_windows_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_to_directory_toolchain.bzl%copy_to_directory_platform_repo",
"attributes": {
- "exec_triple": "x86_64-unknown-linux-gnu",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "x86_64-unknown-linux-gnu",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "windows_amd64"
}
},
- "rust_windows_aarch64__aarch64-pc-windows-msvc__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "copy_to_directory_toolchains": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:copy_to_directory_toolchain.bzl%copy_to_directory_toolchains_repo",
"attributes": {
- "exec_triple": "aarch64-pc-windows-msvc",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "aarch64-pc-windows-msvc",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "user_repository_name": "copy_to_directory"
}
},
- "rust_windows_aarch64": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_set_repository",
+ "jq_darwin_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:jq_toolchain.bzl%jq_platform_repo",
"attributes": {
- "toolchains": [
- "@rust_windows_aarch64__aarch64-pc-windows-msvc__stable//:toolchain",
- "@rust_windows_aarch64__wasm32-unknown-unknown__stable//:toolchain",
- "@rust_windows_aarch64__wasm32-wasi__stable//:toolchain"
- ]
+ "platform": "darwin_amd64",
+ "version": "1.6"
}
},
- "rust_linux_x86_64__wasm32-unknown-unknown__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "jq_darwin_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:jq_toolchain.bzl%jq_platform_repo",
"attributes": {
- "toolchain": "@rust_linux_x86_64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ]
+ "platform": "darwin_arm64",
+ "version": "1.6"
}
},
- "rust_windows_x86_64__x86_64-pc-windows-msvc__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "jq_linux_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:jq_toolchain.bzl%jq_platform_repo",
"attributes": {
- "exec_triple": "x86_64-pc-windows-msvc",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "x86_64-pc-windows-msvc",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "linux_amd64",
+ "version": "1.6"
}
},
- "rustfmt_nightly-2024-04-09__x86_64-unknown-freebsd_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rustfmt_toolchain_tools_repository",
+ "jq_windows_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:jq_toolchain.bzl%jq_platform_repo",
"attributes": {
- "version": "nightly",
- "iso_date": "2024-04-09",
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {},
- "exec_triple": "x86_64-unknown-freebsd"
+ "platform": "windows_amd64",
+ "version": "1.6"
}
},
- "rust_freebsd_x86_64__x86_64-unknown-freebsd__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
- "attributes": {
- "exec_triple": "x86_64-unknown-freebsd",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "x86_64-unknown-freebsd",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
- }
+ "jq": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:jq_toolchain.bzl%jq_host_alias_repo",
+ "attributes": {}
},
- "rust_darwin_x86_64__wasm32-unknown-unknown__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "jq_toolchains": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:jq_toolchain.bzl%jq_toolchains_repo",
"attributes": {
- "toolchain": "@rust_darwin_x86_64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ]
+ "user_repository_name": "jq"
}
},
- "rust_darwin_aarch64__wasm32-unknown-unknown__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "yq_darwin_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:yq_toolchain.bzl%yq_platform_repo",
"attributes": {
- "toolchain": "@rust_darwin_aarch64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ]
+ "platform": "darwin_amd64",
+ "version": "4.25.2"
}
},
- "rust_darwin_aarch64__aarch64-apple-darwin__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "yq_darwin_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:yq_toolchain.bzl%yq_platform_repo",
"attributes": {
- "toolchain": "@rust_darwin_aarch64__aarch64-apple-darwin__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ],
- "target_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ]
+ "platform": "darwin_arm64",
+ "version": "4.25.2"
}
},
- "rust_analyzer_1.77.1_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_analyzer_toolchain_tools_repository",
+ "yq_linux_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:yq_toolchain.bzl%yq_platform_repo",
"attributes": {
- "version": "1.77.1",
- "iso_date": "",
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "linux_amd64",
+ "version": "4.25.2"
}
},
- "rust_windows_x86_64__wasm32-wasi__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "yq_linux_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:yq_toolchain.bzl%yq_platform_repo",
"attributes": {
- "toolchain": "@rust_windows_x86_64__wasm32-wasi__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ]
+ "platform": "linux_arm64",
+ "version": "4.25.2"
}
},
- "rust_darwin_x86_64": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_set_repository",
+ "yq_linux_s390x": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:yq_toolchain.bzl%yq_platform_repo",
"attributes": {
- "toolchains": [
- "@rust_darwin_x86_64__x86_64-apple-darwin__stable//:toolchain",
- "@rust_darwin_x86_64__wasm32-unknown-unknown__stable//:toolchain",
- "@rust_darwin_x86_64__wasm32-wasi__stable//:toolchain"
- ]
+ "platform": "linux_s390x",
+ "version": "4.25.2"
}
},
- "rust_windows_aarch64__wasm32-wasi__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "yq_linux_ppc64le": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:yq_toolchain.bzl%yq_platform_repo",
"attributes": {
- "toolchain": "@rust_windows_aarch64__wasm32-wasi__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ]
+ "platform": "linux_ppc64le",
+ "version": "4.25.2"
}
},
- "rust_darwin_x86_64__wasm32-unknown-unknown__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "yq_windows_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:yq_toolchain.bzl%yq_platform_repo",
"attributes": {
- "exec_triple": "x86_64-apple-darwin",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-unknown-unknown",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "windows_amd64",
+ "version": "4.25.2"
}
},
- "rustfmt_nightly-2024-04-09__x86_64-pc-windows-msvc_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rustfmt_toolchain_tools_repository",
- "attributes": {
- "version": "nightly",
- "iso_date": "2024-04-09",
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {},
- "exec_triple": "x86_64-pc-windows-msvc"
- }
+ "yq": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:yq_toolchain.bzl%yq_host_alias_repo",
+ "attributes": {}
},
- "rust_darwin_aarch64__wasm32-unknown-unknown__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "yq_toolchains": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:yq_toolchain.bzl%yq_toolchains_repo",
"attributes": {
- "exec_triple": "aarch64-apple-darwin",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-unknown-unknown",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "user_repository_name": "yq"
}
},
- "rust_windows_x86_64__wasm32-unknown-unknown__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "coreutils_darwin_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo",
"attributes": {
- "exec_triple": "x86_64-pc-windows-msvc",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-unknown-unknown",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "darwin_amd64",
+ "version": "0.0.16"
}
},
- "rust_linux_aarch64__wasm32-wasi__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "coreutils_darwin_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo",
"attributes": {
- "toolchain": "@rust_linux_aarch64__wasm32-wasi__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ]
+ "platform": "darwin_arm64",
+ "version": "0.0.16"
}
},
- "rust_darwin_aarch64": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_set_repository",
+ "coreutils_linux_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo",
"attributes": {
- "toolchains": [
- "@rust_darwin_aarch64__aarch64-apple-darwin__stable//:toolchain",
- "@rust_darwin_aarch64__wasm32-unknown-unknown__stable//:toolchain",
- "@rust_darwin_aarch64__wasm32-wasi__stable//:toolchain"
- ]
+ "platform": "linux_amd64",
+ "version": "0.0.16"
}
},
- "rustfmt_nightly-2024-04-09__aarch64-apple-darwin_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rustfmt_toolchain_tools_repository",
+ "coreutils_linux_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo",
"attributes": {
- "version": "nightly",
- "iso_date": "2024-04-09",
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {},
- "exec_triple": "aarch64-apple-darwin"
+ "platform": "linux_arm64",
+ "version": "0.0.16"
}
},
- "rust_analyzer_1.77.1": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "coreutils_windows_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo",
"attributes": {
- "toolchain": "@rust_analyzer_1.77.1_tools//:rust_analyzer_toolchain",
- "toolchain_type": "@rules_rust//rust/rust_analyzer:toolchain_type",
- "exec_compatible_with": [],
- "target_compatible_with": []
+ "platform": "windows_amd64",
+ "version": "0.0.16"
}
},
- "rust_darwin_x86_64__x86_64-apple-darwin__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "coreutils_toolchains": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_toolchains_repo",
"attributes": {
- "toolchain": "@rust_darwin_x86_64__x86_64-apple-darwin__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ],
- "target_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ]
+ "user_repository_name": "coreutils"
}
},
- "rust_linux_x86_64__wasm32-unknown-unknown__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "expand_template_darwin_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:expand_template_toolchain.bzl%expand_template_platform_repo",
"attributes": {
- "exec_triple": "x86_64-unknown-linux-gnu",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-unknown-unknown",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "darwin_amd64"
}
},
- "rust_freebsd_x86_64__wasm32-wasi__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "expand_template_darwin_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:expand_template_toolchain.bzl%expand_template_platform_repo",
"attributes": {
- "exec_triple": "x86_64-unknown-freebsd",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-wasi",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "darwin_arm64"
}
},
- "rust_darwin_x86_64__wasm32-wasi__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "expand_template_freebsd_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:expand_template_toolchain.bzl%expand_template_platform_repo",
"attributes": {
- "toolchain": "@rust_darwin_x86_64__wasm32-wasi__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ]
+ "platform": "freebsd_amd64"
}
},
- "rust_freebsd_x86_64__wasm32-unknown-unknown__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
+ "expand_template_linux_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:expand_template_toolchain.bzl%expand_template_platform_repo",
"attributes": {
- "toolchain": "@rust_freebsd_x86_64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ]
+ "platform": "linux_amd64"
}
},
- "rust_freebsd_x86_64": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_set_repository",
- "attributes": {
- "toolchains": [
- "@rust_freebsd_x86_64__x86_64-unknown-freebsd__stable//:toolchain",
- "@rust_freebsd_x86_64__wasm32-unknown-unknown__stable//:toolchain",
- "@rust_freebsd_x86_64__wasm32-wasi__stable//:toolchain"
- ]
- }
- },
- "rust_linux_x86_64__wasm32-wasi__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
- "attributes": {
- "exec_triple": "x86_64-unknown-linux-gnu",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-wasi",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
- }
- },
- "rust_windows_x86_64__wasm32-unknown-unknown__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rust_windows_x86_64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ]
- }
- },
- "rust_linux_x86_64__x86_64-unknown-linux-gnu__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rust_linux_x86_64__x86_64-unknown-linux-gnu__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ],
- "target_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ]
- }
- },
- "rustfmt_nightly-2024-04-09__aarch64-unknown-linux-gnu": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rustfmt_nightly-2024-04-09__aarch64-unknown-linux-gnu_tools//:rustfmt_toolchain",
- "toolchain_type": "@rules_rust//rust/rustfmt:toolchain_type",
- "target_settings": [],
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ],
- "target_compatible_with": []
- }
- },
- "rust_linux_x86_64": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_set_repository",
- "attributes": {
- "toolchains": [
- "@rust_linux_x86_64__x86_64-unknown-linux-gnu__stable//:toolchain",
- "@rust_linux_x86_64__wasm32-unknown-unknown__stable//:toolchain",
- "@rust_linux_x86_64__wasm32-wasi__stable//:toolchain"
- ]
- }
- },
- "rust_windows_x86_64__x86_64-pc-windows-msvc__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rust_windows_x86_64__x86_64-pc-windows-msvc__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ],
- "target_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ]
- }
- },
- "rust_linux_x86_64__wasm32-wasi__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rust_linux_x86_64__wasm32-wasi__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ]
- }
- },
- "rust_windows_aarch64__wasm32-unknown-unknown__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
- "attributes": {
- "exec_triple": "aarch64-pc-windows-msvc",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-unknown-unknown",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
- }
- },
- "rust_darwin_aarch64__aarch64-apple-darwin__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
- "attributes": {
- "exec_triple": "aarch64-apple-darwin",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "aarch64-apple-darwin",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
- }
- },
- "rust_linux_aarch64__wasm32-wasi__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
- "attributes": {
- "exec_triple": "aarch64-unknown-linux-gnu",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-wasi",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
- }
- },
- "rustfmt_nightly-2024-04-09__aarch64-pc-windows-msvc": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rustfmt_nightly-2024-04-09__aarch64-pc-windows-msvc_tools//:rustfmt_toolchain",
- "toolchain_type": "@rules_rust//rust/rustfmt:toolchain_type",
- "target_settings": [],
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ],
- "target_compatible_with": []
- }
- },
- "rustfmt_nightly-2024-04-09__aarch64-unknown-linux-gnu_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rustfmt_toolchain_tools_repository",
- "attributes": {
- "version": "nightly",
- "iso_date": "2024-04-09",
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {},
- "exec_triple": "aarch64-unknown-linux-gnu"
- }
- },
- "rustfmt_nightly-2024-04-09__x86_64-apple-darwin": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rustfmt_nightly-2024-04-09__x86_64-apple-darwin_tools//:rustfmt_toolchain",
- "toolchain_type": "@rules_rust//rust/rustfmt:toolchain_type",
- "target_settings": [],
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ],
- "target_compatible_with": []
- }
- },
- "rustfmt_nightly-2024-04-09__aarch64-pc-windows-msvc_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rustfmt_toolchain_tools_repository",
- "attributes": {
- "version": "nightly",
- "iso_date": "2024-04-09",
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {},
- "exec_triple": "aarch64-pc-windows-msvc"
- }
- },
- "rust_windows_aarch64__wasm32-unknown-unknown__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rust_windows_aarch64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ]
- }
- },
- "rustfmt_nightly-2024-04-09__x86_64-apple-darwin_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rustfmt_toolchain_tools_repository",
- "attributes": {
- "version": "nightly",
- "iso_date": "2024-04-09",
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {},
- "exec_triple": "x86_64-apple-darwin"
- }
- },
- "rust_linux_aarch64": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_set_repository",
- "attributes": {
- "toolchains": [
- "@rust_linux_aarch64__aarch64-unknown-linux-gnu__stable//:toolchain",
- "@rust_linux_aarch64__wasm32-unknown-unknown__stable//:toolchain",
- "@rust_linux_aarch64__wasm32-wasi__stable//:toolchain"
- ]
- }
- },
- "rustfmt_nightly-2024-04-09__x86_64-unknown-linux-gnu": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rustfmt_nightly-2024-04-09__x86_64-unknown-linux-gnu_tools//:rustfmt_toolchain",
- "toolchain_type": "@rules_rust//rust/rustfmt:toolchain_type",
- "target_settings": [],
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ],
- "target_compatible_with": []
- }
- },
- "rust_darwin_aarch64__wasm32-wasi__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rust_darwin_aarch64__wasm32-wasi__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ],
- "target_compatible_with": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ]
- }
- },
- "rustfmt_nightly-2024-04-09__x86_64-pc-windows-msvc": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rustfmt_nightly-2024-04-09__x86_64-pc-windows-msvc_tools//:rustfmt_toolchain",
- "toolchain_type": "@rules_rust//rust/rustfmt:toolchain_type",
- "target_settings": [],
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ],
- "target_compatible_with": []
- }
- },
- "rustfmt_nightly-2024-04-09__x86_64-unknown-linux-gnu_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rustfmt_toolchain_tools_repository",
- "attributes": {
- "version": "nightly",
- "iso_date": "2024-04-09",
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {},
- "exec_triple": "x86_64-unknown-linux-gnu"
- }
- },
- "rust_windows_aarch64__aarch64-pc-windows-msvc__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rust_windows_aarch64__aarch64-pc-windows-msvc__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ],
- "target_compatible_with": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ]
- }
- },
- "rust_freebsd_x86_64__x86_64-unknown-freebsd__stable": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "toolchain_repository_proxy",
- "attributes": {
- "toolchain": "@rust_freebsd_x86_64__x86_64-unknown-freebsd__stable_tools//:rust_toolchain",
- "target_settings": [
- "@rules_rust//rust/toolchain/channel:stable"
- ],
- "toolchain_type": "@rules_rust//rust:toolchain",
- "exec_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ],
- "target_compatible_with": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ]
- }
- },
- "rust_toolchains": {
- "bzlFile": "@@rules_rust~//rust/private:repository_utils.bzl",
- "ruleClassName": "toolchain_repository_hub",
+ "expand_template_linux_arm64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:expand_template_toolchain.bzl%expand_template_platform_repo",
"attributes": {
- "toolchain_names": [
- "rust_analyzer_1.77.1",
- "rust_darwin_aarch64__aarch64-apple-darwin__stable",
- "rust_darwin_aarch64__wasm32-unknown-unknown__stable",
- "rust_darwin_aarch64__wasm32-wasi__stable",
- "rustfmt_nightly-2024-04-09__aarch64-apple-darwin",
- "rust_windows_aarch64__aarch64-pc-windows-msvc__stable",
- "rust_windows_aarch64__wasm32-unknown-unknown__stable",
- "rust_windows_aarch64__wasm32-wasi__stable",
- "rustfmt_nightly-2024-04-09__aarch64-pc-windows-msvc",
- "rust_linux_aarch64__aarch64-unknown-linux-gnu__stable",
- "rust_linux_aarch64__wasm32-unknown-unknown__stable",
- "rust_linux_aarch64__wasm32-wasi__stable",
- "rustfmt_nightly-2024-04-09__aarch64-unknown-linux-gnu",
- "rust_darwin_x86_64__x86_64-apple-darwin__stable",
- "rust_darwin_x86_64__wasm32-unknown-unknown__stable",
- "rust_darwin_x86_64__wasm32-wasi__stable",
- "rustfmt_nightly-2024-04-09__x86_64-apple-darwin",
- "rust_windows_x86_64__x86_64-pc-windows-msvc__stable",
- "rust_windows_x86_64__wasm32-unknown-unknown__stable",
- "rust_windows_x86_64__wasm32-wasi__stable",
- "rustfmt_nightly-2024-04-09__x86_64-pc-windows-msvc",
- "rust_freebsd_x86_64__x86_64-unknown-freebsd__stable",
- "rust_freebsd_x86_64__wasm32-unknown-unknown__stable",
- "rust_freebsd_x86_64__wasm32-wasi__stable",
- "rustfmt_nightly-2024-04-09__x86_64-unknown-freebsd",
- "rust_linux_x86_64__x86_64-unknown-linux-gnu__stable",
- "rust_linux_x86_64__wasm32-unknown-unknown__stable",
- "rust_linux_x86_64__wasm32-wasi__stable",
- "rustfmt_nightly-2024-04-09__x86_64-unknown-linux-gnu"
- ],
- "toolchain_labels": {
- "rust_analyzer_1.77.1": "@rust_analyzer_1.77.1_tools//:rust_analyzer_toolchain",
- "rust_darwin_aarch64__aarch64-apple-darwin__stable": "@rust_darwin_aarch64__aarch64-apple-darwin__stable_tools//:rust_toolchain",
- "rust_darwin_aarch64__wasm32-unknown-unknown__stable": "@rust_darwin_aarch64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "rust_darwin_aarch64__wasm32-wasi__stable": "@rust_darwin_aarch64__wasm32-wasi__stable_tools//:rust_toolchain",
- "rustfmt_nightly-2024-04-09__aarch64-apple-darwin": "@rustfmt_nightly-2024-04-09__aarch64-apple-darwin_tools//:rustfmt_toolchain",
- "rust_windows_aarch64__aarch64-pc-windows-msvc__stable": "@rust_windows_aarch64__aarch64-pc-windows-msvc__stable_tools//:rust_toolchain",
- "rust_windows_aarch64__wasm32-unknown-unknown__stable": "@rust_windows_aarch64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "rust_windows_aarch64__wasm32-wasi__stable": "@rust_windows_aarch64__wasm32-wasi__stable_tools//:rust_toolchain",
- "rustfmt_nightly-2024-04-09__aarch64-pc-windows-msvc": "@rustfmt_nightly-2024-04-09__aarch64-pc-windows-msvc_tools//:rustfmt_toolchain",
- "rust_linux_aarch64__aarch64-unknown-linux-gnu__stable": "@rust_linux_aarch64__aarch64-unknown-linux-gnu__stable_tools//:rust_toolchain",
- "rust_linux_aarch64__wasm32-unknown-unknown__stable": "@rust_linux_aarch64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "rust_linux_aarch64__wasm32-wasi__stable": "@rust_linux_aarch64__wasm32-wasi__stable_tools//:rust_toolchain",
- "rustfmt_nightly-2024-04-09__aarch64-unknown-linux-gnu": "@rustfmt_nightly-2024-04-09__aarch64-unknown-linux-gnu_tools//:rustfmt_toolchain",
- "rust_darwin_x86_64__x86_64-apple-darwin__stable": "@rust_darwin_x86_64__x86_64-apple-darwin__stable_tools//:rust_toolchain",
- "rust_darwin_x86_64__wasm32-unknown-unknown__stable": "@rust_darwin_x86_64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "rust_darwin_x86_64__wasm32-wasi__stable": "@rust_darwin_x86_64__wasm32-wasi__stable_tools//:rust_toolchain",
- "rustfmt_nightly-2024-04-09__x86_64-apple-darwin": "@rustfmt_nightly-2024-04-09__x86_64-apple-darwin_tools//:rustfmt_toolchain",
- "rust_windows_x86_64__x86_64-pc-windows-msvc__stable": "@rust_windows_x86_64__x86_64-pc-windows-msvc__stable_tools//:rust_toolchain",
- "rust_windows_x86_64__wasm32-unknown-unknown__stable": "@rust_windows_x86_64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "rust_windows_x86_64__wasm32-wasi__stable": "@rust_windows_x86_64__wasm32-wasi__stable_tools//:rust_toolchain",
- "rustfmt_nightly-2024-04-09__x86_64-pc-windows-msvc": "@rustfmt_nightly-2024-04-09__x86_64-pc-windows-msvc_tools//:rustfmt_toolchain",
- "rust_freebsd_x86_64__x86_64-unknown-freebsd__stable": "@rust_freebsd_x86_64__x86_64-unknown-freebsd__stable_tools//:rust_toolchain",
- "rust_freebsd_x86_64__wasm32-unknown-unknown__stable": "@rust_freebsd_x86_64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "rust_freebsd_x86_64__wasm32-wasi__stable": "@rust_freebsd_x86_64__wasm32-wasi__stable_tools//:rust_toolchain",
- "rustfmt_nightly-2024-04-09__x86_64-unknown-freebsd": "@rustfmt_nightly-2024-04-09__x86_64-unknown-freebsd_tools//:rustfmt_toolchain",
- "rust_linux_x86_64__x86_64-unknown-linux-gnu__stable": "@rust_linux_x86_64__x86_64-unknown-linux-gnu__stable_tools//:rust_toolchain",
- "rust_linux_x86_64__wasm32-unknown-unknown__stable": "@rust_linux_x86_64__wasm32-unknown-unknown__stable_tools//:rust_toolchain",
- "rust_linux_x86_64__wasm32-wasi__stable": "@rust_linux_x86_64__wasm32-wasi__stable_tools//:rust_toolchain",
- "rustfmt_nightly-2024-04-09__x86_64-unknown-linux-gnu": "@rustfmt_nightly-2024-04-09__x86_64-unknown-linux-gnu_tools//:rustfmt_toolchain"
- },
- "toolchain_types": {
- "rust_analyzer_1.77.1": "@rules_rust//rust/rust_analyzer:toolchain_type",
- "rust_darwin_aarch64__aarch64-apple-darwin__stable": "@rules_rust//rust:toolchain",
- "rust_darwin_aarch64__wasm32-unknown-unknown__stable": "@rules_rust//rust:toolchain",
- "rust_darwin_aarch64__wasm32-wasi__stable": "@rules_rust//rust:toolchain",
- "rustfmt_nightly-2024-04-09__aarch64-apple-darwin": "@rules_rust//rust/rustfmt:toolchain_type",
- "rust_windows_aarch64__aarch64-pc-windows-msvc__stable": "@rules_rust//rust:toolchain",
- "rust_windows_aarch64__wasm32-unknown-unknown__stable": "@rules_rust//rust:toolchain",
- "rust_windows_aarch64__wasm32-wasi__stable": "@rules_rust//rust:toolchain",
- "rustfmt_nightly-2024-04-09__aarch64-pc-windows-msvc": "@rules_rust//rust/rustfmt:toolchain_type",
- "rust_linux_aarch64__aarch64-unknown-linux-gnu__stable": "@rules_rust//rust:toolchain",
- "rust_linux_aarch64__wasm32-unknown-unknown__stable": "@rules_rust//rust:toolchain",
- "rust_linux_aarch64__wasm32-wasi__stable": "@rules_rust//rust:toolchain",
- "rustfmt_nightly-2024-04-09__aarch64-unknown-linux-gnu": "@rules_rust//rust/rustfmt:toolchain_type",
- "rust_darwin_x86_64__x86_64-apple-darwin__stable": "@rules_rust//rust:toolchain",
- "rust_darwin_x86_64__wasm32-unknown-unknown__stable": "@rules_rust//rust:toolchain",
- "rust_darwin_x86_64__wasm32-wasi__stable": "@rules_rust//rust:toolchain",
- "rustfmt_nightly-2024-04-09__x86_64-apple-darwin": "@rules_rust//rust/rustfmt:toolchain_type",
- "rust_windows_x86_64__x86_64-pc-windows-msvc__stable": "@rules_rust//rust:toolchain",
- "rust_windows_x86_64__wasm32-unknown-unknown__stable": "@rules_rust//rust:toolchain",
- "rust_windows_x86_64__wasm32-wasi__stable": "@rules_rust//rust:toolchain",
- "rustfmt_nightly-2024-04-09__x86_64-pc-windows-msvc": "@rules_rust//rust/rustfmt:toolchain_type",
- "rust_freebsd_x86_64__x86_64-unknown-freebsd__stable": "@rules_rust//rust:toolchain",
- "rust_freebsd_x86_64__wasm32-unknown-unknown__stable": "@rules_rust//rust:toolchain",
- "rust_freebsd_x86_64__wasm32-wasi__stable": "@rules_rust//rust:toolchain",
- "rustfmt_nightly-2024-04-09__x86_64-unknown-freebsd": "@rules_rust//rust/rustfmt:toolchain_type",
- "rust_linux_x86_64__x86_64-unknown-linux-gnu__stable": "@rules_rust//rust:toolchain",
- "rust_linux_x86_64__wasm32-unknown-unknown__stable": "@rules_rust//rust:toolchain",
- "rust_linux_x86_64__wasm32-wasi__stable": "@rules_rust//rust:toolchain",
- "rustfmt_nightly-2024-04-09__x86_64-unknown-linux-gnu": "@rules_rust//rust/rustfmt:toolchain_type"
- },
- "exec_compatible_with": {
- "rust_analyzer_1.77.1": [],
- "rust_darwin_aarch64__aarch64-apple-darwin__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ],
- "rust_darwin_aarch64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ],
- "rust_darwin_aarch64__wasm32-wasi__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ],
- "rustfmt_nightly-2024-04-09__aarch64-apple-darwin": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ],
- "rust_windows_aarch64__aarch64-pc-windows-msvc__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ],
- "rust_windows_aarch64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ],
- "rust_windows_aarch64__wasm32-wasi__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ],
- "rustfmt_nightly-2024-04-09__aarch64-pc-windows-msvc": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ],
- "rust_linux_aarch64__aarch64-unknown-linux-gnu__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ],
- "rust_linux_aarch64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ],
- "rust_linux_aarch64__wasm32-wasi__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ],
- "rustfmt_nightly-2024-04-09__aarch64-unknown-linux-gnu": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ],
- "rust_darwin_x86_64__x86_64-apple-darwin__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ],
- "rust_darwin_x86_64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ],
- "rust_darwin_x86_64__wasm32-wasi__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ],
- "rustfmt_nightly-2024-04-09__x86_64-apple-darwin": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ],
- "rust_windows_x86_64__x86_64-pc-windows-msvc__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ],
- "rust_windows_x86_64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ],
- "rust_windows_x86_64__wasm32-wasi__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ],
- "rustfmt_nightly-2024-04-09__x86_64-pc-windows-msvc": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ],
- "rust_freebsd_x86_64__x86_64-unknown-freebsd__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ],
- "rust_freebsd_x86_64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ],
- "rust_freebsd_x86_64__wasm32-wasi__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ],
- "rustfmt_nightly-2024-04-09__x86_64-unknown-freebsd": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ],
- "rust_linux_x86_64__x86_64-unknown-linux-gnu__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ],
- "rust_linux_x86_64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ],
- "rust_linux_x86_64__wasm32-wasi__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ],
- "rustfmt_nightly-2024-04-09__x86_64-unknown-linux-gnu": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ]
- },
- "target_compatible_with": {
- "rust_analyzer_1.77.1": [],
- "rust_darwin_aarch64__aarch64-apple-darwin__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:osx"
- ],
- "rust_darwin_aarch64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ],
- "rust_darwin_aarch64__wasm32-wasi__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ],
- "rustfmt_nightly-2024-04-09__aarch64-apple-darwin": [],
- "rust_windows_aarch64__aarch64-pc-windows-msvc__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:windows"
- ],
- "rust_windows_aarch64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ],
- "rust_windows_aarch64__wasm32-wasi__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ],
- "rustfmt_nightly-2024-04-09__aarch64-pc-windows-msvc": [],
- "rust_linux_aarch64__aarch64-unknown-linux-gnu__stable": [
- "@platforms//cpu:aarch64",
- "@platforms//os:linux"
- ],
- "rust_linux_aarch64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ],
- "rust_linux_aarch64__wasm32-wasi__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ],
- "rustfmt_nightly-2024-04-09__aarch64-unknown-linux-gnu": [],
- "rust_darwin_x86_64__x86_64-apple-darwin__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:osx"
- ],
- "rust_darwin_x86_64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ],
- "rust_darwin_x86_64__wasm32-wasi__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ],
- "rustfmt_nightly-2024-04-09__x86_64-apple-darwin": [],
- "rust_windows_x86_64__x86_64-pc-windows-msvc__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:windows"
- ],
- "rust_windows_x86_64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ],
- "rust_windows_x86_64__wasm32-wasi__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ],
- "rustfmt_nightly-2024-04-09__x86_64-pc-windows-msvc": [],
- "rust_freebsd_x86_64__x86_64-unknown-freebsd__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:freebsd"
- ],
- "rust_freebsd_x86_64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ],
- "rust_freebsd_x86_64__wasm32-wasi__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ],
- "rustfmt_nightly-2024-04-09__x86_64-unknown-freebsd": [],
- "rust_linux_x86_64__x86_64-unknown-linux-gnu__stable": [
- "@platforms//cpu:x86_64",
- "@platforms//os:linux"
- ],
- "rust_linux_x86_64__wasm32-unknown-unknown__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:none"
- ],
- "rust_linux_x86_64__wasm32-wasi__stable": [
- "@platforms//cpu:wasm32",
- "@platforms//os:wasi"
- ],
- "rustfmt_nightly-2024-04-09__x86_64-unknown-linux-gnu": []
- }
+ "platform": "linux_arm64"
}
},
- "rust_linux_aarch64__wasm32-unknown-unknown__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "expand_template_windows_amd64": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:expand_template_toolchain.bzl%expand_template_platform_repo",
"attributes": {
- "exec_triple": "aarch64-unknown-linux-gnu",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "wasm32-unknown-unknown",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "platform": "windows_amd64"
}
},
- "rust_darwin_x86_64__x86_64-apple-darwin__stable_tools": {
- "bzlFile": "@@rules_rust~//rust:repositories.bzl",
- "ruleClassName": "rust_toolchain_tools_repository",
+ "expand_template_toolchains": {
+ "repoRuleId": "@@aspect_bazel_lib+//lib/private:expand_template_toolchain.bzl%expand_template_toolchains_repo",
"attributes": {
- "exec_triple": "x86_64-apple-darwin",
- "allocator_library": "@rules_rust//ffi/cc/allocator_library",
- "global_allocator_library": "@rules_rust//ffi/cc/global_allocator_library",
- "target_triple": "x86_64-apple-darwin",
- "iso_date": "",
- "version": "1.77.1",
- "rustfmt_version": "nightly/2024-04-09",
- "edition": "2021",
- "dev_components": false,
- "extra_rustc_flags": [],
- "extra_exec_rustc_flags": [],
- "opt_level": {},
- "sha256s": {},
- "urls": [
- "https://static.rust-lang.org/dist/{}.tar.xz"
- ],
- "auth": {}
+ "user_repository_name": "expand_template"
}
}
},
"recordedRepoMappingEntries": [
[
- "bazel_features~",
- "bazel_features_globals",
- "bazel_features~~version_extension~bazel_features_globals"
+ "aspect_bazel_lib+",
+ "aspect_bazel_lib",
+ "aspect_bazel_lib+"
],
[
- "bazel_features~",
- "bazel_features_version",
- "bazel_features~~version_extension~bazel_features_version"
- ],
- [
- "rules_rust~",
- "bazel_features",
- "bazel_features~"
- ],
- [
- "rules_rust~",
+ "aspect_bazel_lib+",
"bazel_skylib",
- "bazel_skylib~"
+ "bazel_skylib+"
],
[
- "rules_rust~",
+ "aspect_bazel_lib+",
"bazel_tools",
"bazel_tools"
- ],
- [
- "rules_rust~",
- "rules_rust",
- "rules_rust~"
]
]
}
},
- "@@rules_rust~//rust/private:extensions.bzl%i": {
+ "@@platforms//host:extension.bzl%host_platform": {
"general": {
- "bzlTransitiveDigest": "X2v+7Bz11W5htCVO7xqy67eK7NWv0mmFRB4EQTVUZOY=",
+ "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=",
+ "usagesDigest": "SeQiIN/f8/Qt9vYQk7qcXp4I4wJeEC0RnQDiaaJ4tb8=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
- "rules_rust_prost__tracing-0.1.37": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "host_platform": {
+ "repoRuleId": "@@platforms//host:extension.bzl%host_platform_repo",
+ "attributes": {}
+ }
+ },
+ "recordedRepoMappingEntries": []
+ }
+ },
+ "@@rules_buf+//buf:extensions.bzl%ext": {
+ "general": {
+ "bzlTransitiveDigest": "3jGepUu1j86kWsTP3Fgogw/XfktHd4UIQt8zj494n/Y=",
+ "usagesDigest": "RTc2BMQ2b0wGU8CRvN3EoPz34m3LMe+K/oSkFkN83+M=",
+ "recordedFileInputs": {},
+ "recordedDirentsInputs": {},
+ "envVariables": {},
+ "generatedRepoSpecs": {
+ "rules_buf_toolchains": {
+ "repoRuleId": "@@rules_buf+//buf/internal:toolchain.bzl%buf_download_releases",
"attributes": {
- "sha256": "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8",
- "type": "tar.gz",
+ "version": "v1.27.0"
+ }
+ }
+ },
+ "recordedRepoMappingEntries": [
+ [
+ "rules_buf+",
+ "bazel_tools",
+ "bazel_tools"
+ ]
+ ]
+ }
+ },
+ "@@rules_go+//go:extensions.bzl%go_sdk": {
+ "general": {
+ "bzlTransitiveDigest": "GI0gnOeyAURBWF+T+482mWnxAoSjspZNDIVvAHGR7Yk=",
+ "usagesDigest": "G0DymwAVABR+Olml5OAfLhVRqUVCU372GHdSQxQ1PJw=",
+ "recordedFileInputs": {},
+ "recordedDirentsInputs": {},
+ "envVariables": {},
+ "generatedRepoSpecs": {
+ "go_default_sdk": {
+ "repoRuleId": "@@rules_go+//go/private:sdk.bzl%go_download_sdk_rule",
+ "attributes": {
+ "goos": "",
+ "goarch": "",
+ "sdks": {},
"urls": [
- "https://static.crates.io/crates/tracing/0.1.37/download"
+ "https://dl.google.com/go/{}"
],
- "strip_prefix": "tracing-0.1.37",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tracing-0.1.37.bazel"
+ "version": "1.19.8"
}
},
- "rules_rust_wasm_bindgen__walrus-0.20.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "go_toolchains": {
+ "repoRuleId": "@@rules_go+//go/private:sdk.bzl%go_multiple_toolchains",
"attributes": {
- "sha256": "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/walrus/0.20.3/download"
+ "prefixes": [
+ "_0000_go_default_sdk_"
],
- "strip_prefix": "walrus-0.20.3",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.walrus-0.20.3.bazel"
+ "geese": [
+ ""
+ ],
+ "goarchs": [
+ ""
+ ],
+ "sdk_repos": [
+ "go_default_sdk"
+ ],
+ "sdk_types": [
+ "remote"
+ ],
+ "sdk_versions": [
+ "1.19.8"
+ ]
+ }
+ }
+ },
+ "recordedRepoMappingEntries": [
+ [
+ "rules_go+",
+ "bazel_tools",
+ "bazel_tools"
+ ]
+ ]
+ }
+ },
+ "@@rules_java+//java:rules_java_deps.bzl%compatibility_proxy": {
+ "general": {
+ "bzlTransitiveDigest": "84xJEZ1jnXXwo8BXMprvBm++rRt4jsTu9liBxz0ivps=",
+ "usagesDigest": "jTQDdLDxsS43zuRmg1faAjIEPWdLAbDAowI1pInQSoo=",
+ "recordedFileInputs": {},
+ "recordedDirentsInputs": {},
+ "envVariables": {},
+ "generatedRepoSpecs": {
+ "compatibility_proxy": {
+ "repoRuleId": "@@rules_java+//java:rules_java_deps.bzl%_compatibility_proxy_repo_rule",
+ "attributes": {}
+ }
+ },
+ "recordedRepoMappingEntries": [
+ [
+ "rules_java+",
+ "bazel_tools",
+ "bazel_tools"
+ ]
+ ]
+ }
+ },
+ "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": {
+ "general": {
+ "bzlTransitiveDigest": "sFhcgPbDQehmbD1EOXzX4H1q/CD5df8zwG4kp4jbvr8=",
+ "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=",
+ "recordedFileInputs": {},
+ "recordedDirentsInputs": {},
+ "envVariables": {},
+ "generatedRepoSpecs": {
+ "com_github_jetbrains_kotlin_git": {
+ "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository",
+ "attributes": {
+ "urls": [
+ "https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip"
+ ],
+ "sha256": "93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88"
}
},
- "rules_rust_wasm_bindgen__unicode-bidi-0.3.13": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "com_github_jetbrains_kotlin": {
+ "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository",
"attributes": {
- "sha256": "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-bidi/0.3.13/download"
- ],
- "strip_prefix": "unicode-bidi-0.3.13",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.unicode-bidi-0.3.13.bazel"
+ "git_repository_name": "com_github_jetbrains_kotlin_git",
+ "compiler_version": "1.9.23"
}
},
- "rules_rust_wasm_bindgen__windows_x86_64_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "com_github_google_ksp": {
+ "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository",
"attributes": {
- "sha256": "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1",
- "type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/windows_x86_64_gnu/0.48.0/download"
+ "https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip"
],
- "strip_prefix": "windows_x86_64_gnu-0.48.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows_x86_64_gnu-0.48.0.bazel"
+ "sha256": "ee0618755913ef7fd6511288a232e8fad24838b9af6ea73972a76e81053c8c2d",
+ "strip_version": "1.9.23-1.0.20"
}
},
- "cui__rustix-0.37.23": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "com_github_pinterest_ktlint": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file",
"attributes": {
- "sha256": "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06",
- "type": "tar.gz",
+ "sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985",
"urls": [
- "https://static.crates.io/crates/rustix/0.37.23/download"
+ "https://github.com/pinterest/ktlint/releases/download/1.3.0/ktlint"
],
- "strip_prefix": "rustix-0.37.23",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rustix-0.37.23.bazel"
- }
- },
- "cui__fuchsia-cprng-0.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fuchsia-cprng/0.1.1/download"
- ],
- "strip_prefix": "fuchsia-cprng-0.1.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.fuchsia-cprng-0.1.1.bazel"
- }
- },
- "cui__url-2.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/url/2.4.0/download"
- ],
- "strip_prefix": "url-2.4.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.url-2.4.0.bazel"
- }
- },
- "cui__ryu-1.0.14": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ryu/1.0.14/download"
- ],
- "strip_prefix": "ryu-1.0.14",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.ryu-1.0.14.bazel"
- }
- },
- "rules_rust_prost__protoc-gen-prost-0.2.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "patch_args": [
- "-p1"
- ],
- "patches": [
- "@@rules_rust~//proto/prost/private/3rdparty/patches:protoc-gen-prost.patch"
- ],
- "sha256": "a81e3a9bb429fec47008b209896f0b9ab99fbcbc1c3733b385d43fbfd64dd2ca",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/protoc-gen-prost/0.2.2/download"
- ],
- "strip_prefix": "protoc-gen-prost-0.2.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.protoc-gen-prost-0.2.2.bazel"
- }
- },
- "rules_rust_bindgen__cfg-if-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cfg-if/1.0.0/download"
- ],
- "strip_prefix": "cfg-if-1.0.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
- }
- },
- "rules_rust_prost__protoc-gen-tonic-0.2.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "725a07a704f9cf7a956b302c21d81b5516ed5ee6cfbbf827edb69beeaae6cc30",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/protoc-gen-tonic/0.2.2/download"
- ],
- "strip_prefix": "protoc-gen-tonic-0.2.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.protoc-gen-tonic-0.2.2.bazel"
- }
- },
- "cui__iana-time-zone-haiku-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/iana-time-zone-haiku/0.1.2/download"
- ],
- "strip_prefix": "iana-time-zone-haiku-0.1.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.iana-time-zone-haiku-0.1.2.bazel"
- }
- },
- "cui__windows_x86_64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.48.0.bazel"
- }
- },
- "rules_rust_prost__percent-encoding-2.3.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/percent-encoding/2.3.0/download"
- ],
- "strip_prefix": "percent-encoding-2.3.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.percent-encoding-2.3.0.bazel"
- }
- },
- "cui__fastrand-2.0.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fastrand/2.0.1/download"
- ],
- "strip_prefix": "fastrand-2.0.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.fastrand-2.0.1.bazel"
- }
- },
- "cui__flate2-1.0.28": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/flate2/1.0.28/download"
- ],
- "strip_prefix": "flate2-1.0.28",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.flate2-1.0.28.bazel"
- }
- },
- "rules_rust_prost__cc-1.0.79": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cc/1.0.79/download"
- ],
- "strip_prefix": "cc-1.0.79",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.cc-1.0.79.bazel"
- }
- },
- "rrra__winapi-0.3.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi/0.3.9/download"
- ],
- "strip_prefix": "winapi-0.3.9",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
- }
- },
- "cui__windows-targets-0.48.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-targets/0.48.1/download"
- ],
- "strip_prefix": "windows-targets-0.48.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows-targets-0.48.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__ppv-lite86-0.2.17": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ppv-lite86/0.2.17/download"
- ],
- "strip_prefix": "ppv-lite86-0.2.17",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.ppv-lite86-0.2.17.bazel"
- }
- },
- "cui__smawk-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/smawk/0.3.1/download"
- ],
- "strip_prefix": "smawk-0.3.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.smawk-0.3.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__heck-0.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/heck/0.3.3/download"
- ],
- "strip_prefix": "heck-0.3.3",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.heck-0.3.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen__unicode-ident-1.0.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-ident/1.0.10/download"
- ],
- "strip_prefix": "unicode-ident-1.0.10",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.unicode-ident-1.0.10.bazel"
- }
- },
- "cui__clap_derive-4.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap_derive/4.3.2/download"
- ],
- "strip_prefix": "clap_derive-4.3.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.clap_derive-4.3.2.bazel"
- }
- },
- "cui__libm-0.2.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/libm/0.2.7/download"
- ],
- "strip_prefix": "libm-0.2.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.libm-0.2.7.bazel"
- }
- },
- "cui__deranged-0.3.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/deranged/0.3.9/download"
- ],
- "strip_prefix": "deranged-0.3.9",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.deranged-0.3.9.bazel"
- }
- },
- "cui__gix-negotiate-0.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6f1697bf9911c6d1b8d709b9e6ef718cb5ea5821a1b7991520125a8134448004",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-negotiate/0.8.0/download"
- ],
- "strip_prefix": "gix-negotiate-0.8.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-negotiate-0.8.0.bazel"
- }
- },
- "rules_rust_proto__autocfg-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/autocfg/1.1.0/download"
- ],
- "strip_prefix": "autocfg-1.1.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.autocfg-1.1.0.bazel"
- }
- },
- "cui__io-lifetimes-1.0.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/io-lifetimes/1.0.11/download"
- ],
- "strip_prefix": "io-lifetimes-1.0.11",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.io-lifetimes-1.0.11.bazel"
- }
- },
- "rules_rust_proto__cfg-if-0.1.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cfg-if/0.1.10/download"
- ],
- "strip_prefix": "cfg-if-0.1.10",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.cfg-if-0.1.10.bazel"
- }
- },
- "rules_rust_prost__proc-macro2-1.0.60": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/proc-macro2/1.0.60/download"
- ],
- "strip_prefix": "proc-macro2-1.0.60",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.proc-macro2-1.0.60.bazel"
- }
- },
- "rules_rust_bindgen__clap_complete-4.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7f6b5c519bab3ea61843a7923d074b04245624bb84a64a8c150f5deb014e388b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap_complete/4.3.1/download"
- ],
- "strip_prefix": "clap_complete-4.3.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.clap_complete-4.3.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__time-core-0.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/time-core/0.1.1/download"
- ],
- "strip_prefix": "time-core-0.1.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.time-core-0.1.1.bazel"
- }
- },
- "cui__num-0.1.42": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num/0.1.42/download"
- ],
- "strip_prefix": "num-0.1.42",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.num-0.1.42.bazel"
- }
- },
- "rules_rust_wasm_bindgen__tiny_http-0.12.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tiny_http/0.12.0/download"
- ],
- "strip_prefix": "tiny_http-0.12.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.tiny_http-0.12.0.bazel"
- }
- },
- "rules_rust_bindgen__windows-sys-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-sys/0.48.0/download"
- ],
- "strip_prefix": "windows-sys-0.48.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.windows-sys-0.48.0.bazel"
- }
- },
- "rules_rust_bindgen__libc-0.2.146": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/libc/0.2.146/download"
- ],
- "strip_prefix": "libc-0.2.146",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.libc-0.2.146.bazel"
- }
- },
- "rules_rust_wasm_bindgen__iana-time-zone-haiku-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/iana-time-zone-haiku/0.1.2/download"
- ],
- "strip_prefix": "iana-time-zone-haiku-0.1.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.iana-time-zone-haiku-0.1.2.bazel"
- }
- },
- "rrra__memchr-2.5.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/memchr/2.5.0/download"
- ],
- "strip_prefix": "memchr-2.5.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.memchr-2.5.0.bazel"
- }
- },
- "cui__getrandom-0.2.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/getrandom/0.2.10/download"
- ],
- "strip_prefix": "getrandom-0.2.10",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.getrandom-0.2.10.bazel"
- }
- },
- "rules_rust_prost__bitflags-1.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bitflags/1.3.2/download"
- ],
- "strip_prefix": "bitflags-1.3.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
- }
- },
- "cui__sha1_smol-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/sha1_smol/1.0.0/download"
- ],
- "strip_prefix": "sha1_smol-1.0.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.sha1_smol-1.0.0.bazel"
- }
- },
- "cargo_bazel.buildifier-darwin-amd64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_file",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/buildtools/releases/download/5.0.1/buildifier-darwin-amd64"
- ],
- "sha256": "2cb0a54683633ef6de4e0491072e22e66ac9c6389051432b76200deeeeaf93fb",
- "downloaded_file_path": "buildifier.exe",
"executable": true
}
},
- "rules_rust_proto__iovec-0.1.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_android": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e",
- "type": "tar.gz",
+ "sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806",
+ "strip_prefix": "rules_android-0.1.1",
"urls": [
- "https://static.crates.io/crates/iovec/0.1.4/download"
- ],
- "strip_prefix": "iovec-0.1.4",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.iovec-0.1.4.bazel"
+ "https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"
+ ]
+ }
+ }
+ },
+ "recordedRepoMappingEntries": [
+ [
+ "rules_kotlin+",
+ "bazel_tools",
+ "bazel_tools"
+ ]
+ ]
+ }
+ },
+ "@@rules_nodejs+//nodejs:extensions.bzl%node": {
+ "general": {
+ "bzlTransitiveDigest": "btnelILPo3ngQN9vWtsQMclvJZPf3X2vcGTjmW7Owy8=",
+ "usagesDigest": "CtwJeycIo1YVyKAUrO/7bkpB6yqctQd8XUnRtqUbwRI=",
+ "recordedFileInputs": {},
+ "recordedDirentsInputs": {},
+ "envVariables": {},
+ "generatedRepoSpecs": {
+ "nodejs_linux_amd64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories",
+ "attributes": {
+ "platform": "linux_amd64",
+ "node_version": "16.19.0"
}
},
- "rules_rust_proto__byteorder-1.4.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "nodejs_linux_arm64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories",
"attributes": {
- "sha256": "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/byteorder/1.4.3/download"
- ],
- "strip_prefix": "byteorder-1.4.3",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.byteorder-1.4.3.bazel"
+ "platform": "linux_arm64",
+ "node_version": "16.19.0"
}
},
- "cui__chrono-0.4.26": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "nodejs_linux_s390x": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories",
"attributes": {
- "sha256": "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/chrono/0.4.26/download"
- ],
- "strip_prefix": "chrono-0.4.26",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.chrono-0.4.26.bazel"
+ "platform": "linux_s390x",
+ "node_version": "16.19.0"
}
},
- "rules_rust_proto__redox_syscall-0.1.57": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "nodejs_linux_ppc64le": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories",
"attributes": {
- "sha256": "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/redox_syscall/0.1.57/download"
- ],
- "strip_prefix": "redox_syscall-0.1.57",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.redox_syscall-0.1.57.bazel"
+ "platform": "linux_ppc64le",
+ "node_version": "16.19.0"
}
},
- "rules_rust_bindgen__proc-macro2-1.0.60": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "nodejs_darwin_amd64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories",
"attributes": {
- "sha256": "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/proc-macro2/1.0.60/download"
- ],
- "strip_prefix": "proc-macro2-1.0.60",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.proc-macro2-1.0.60.bazel"
+ "platform": "darwin_amd64",
+ "node_version": "16.19.0"
}
},
- "rrra__windows_i686_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "nodejs_darwin_arm64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories",
"attributes": {
- "sha256": "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_msvc-0.48.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.windows_i686_msvc-0.48.0.bazel"
+ "platform": "darwin_arm64",
+ "node_version": "16.19.0"
}
},
- "cui__overload-0.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "nodejs_windows_amd64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories",
"attributes": {
- "sha256": "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/overload/0.1.1/download"
- ],
- "strip_prefix": "overload-0.1.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.overload-0.1.1.bazel"
+ "platform": "windows_amd64",
+ "node_version": "16.19.0"
}
},
- "rules_rust_bindgen__clap_derive-4.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "nodejs": {
+ "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias",
"attributes": {
- "sha256": "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f",
+ "user_node_repository_name": "nodejs"
+ }
+ },
+ "nodejs_host": {
+ "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias",
+ "attributes": {
+ "user_node_repository_name": "nodejs"
+ }
+ },
+ "nodejs_toolchains": {
+ "repoRuleId": "@@rules_nodejs+//nodejs/private:toolchains_repo.bzl%toolchains_repo",
+ "attributes": {
+ "user_node_repository_name": "nodejs"
+ }
+ }
+ },
+ "recordedRepoMappingEntries": [
+ [
+ "rules_nodejs+",
+ "bazel_skylib",
+ "bazel_skylib+"
+ ],
+ [
+ "rules_nodejs+",
+ "bazel_tools",
+ "bazel_tools"
+ ]
+ ]
+ }
+ },
+ "@@rules_rust+//rust/private:extensions.bzl%i": {
+ "general": {
+ "bzlTransitiveDigest": "YnEaUAWyKpeyzWk0X4zLTbz7nEMDH6rRgpxwobGF/jo=",
+ "usagesDigest": "9lU8iZ3WLB7+RkWSk13CwAoTAFTYTBzfdF5J5OFFVuQ=",
+ "recordedFileInputs": {},
+ "recordedDirentsInputs": {},
+ "envVariables": {},
+ "generatedRepoSpecs": {
+ "rules_rust_tinyjson": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a",
+ "url": "https://static.crates.io/crates/tinyjson/tinyjson-2.5.1.crate",
+ "strip_prefix": "tinyjson-2.5.1",
+ "type": "tar.gz",
+ "build_file": "@@rules_rust+//util/process_wrapper:BUILD.tinyjson.bazel"
+ }
+ },
+ "cui": {
+ "repoRuleId": "@@rules_rust+//crate_universe/private:crates_vendor.bzl%crates_vendor_remote_repository",
+ "attributes": {
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.bazel",
+ "defs_module": "@@rules_rust+//crate_universe/3rdparty/crates:defs.bzl"
+ }
+ },
+ "cui__adler-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/clap_derive/4.3.2/download"
+ "https://static.crates.io/crates/adler/1.0.2/download"
],
- "strip_prefix": "clap_derive-4.3.2",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.clap_derive-4.3.2.bazel"
+ "strip_prefix": "adler-1.0.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.adler-1.0.2.bazel"
+ }
+ },
+ "cui__ahash-0.8.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ahash/0.8.11/download"
+ ],
+ "strip_prefix": "ahash-0.8.11",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.ahash-0.8.11.bazel"
+ }
+ },
+ "cui__aho-corasick-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/aho-corasick/1.0.2/download"
+ ],
+ "strip_prefix": "aho-corasick-1.0.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.aho-corasick-1.0.2.bazel"
+ }
+ },
+ "cui__allocator-api2-0.2.18": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/allocator-api2/0.2.18/download"
+ ],
+ "strip_prefix": "allocator-api2-0.2.18",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.allocator-api2-0.2.18.bazel"
}
},
"cui__anstream-0.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"sha256": "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163",
"type": "tar.gz",
@@ -4240,2186 +839,11 @@
"https://static.crates.io/crates/anstream/0.3.2/download"
],
"strip_prefix": "anstream-0.3.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.anstream-0.3.2.bazel"
- }
- },
- "cui__bitflags-1.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bitflags/1.3.2/download"
- ],
- "strip_prefix": "bitflags-1.3.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
- }
- },
- "rules_rust_prost__smallvec-1.10.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/smallvec/1.10.0/download"
- ],
- "strip_prefix": "smallvec-1.10.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.smallvec-1.10.0.bazel"
- }
- },
- "rules_rust_prost__windows_x86_64_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_gnu/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_gnu-0.48.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.windows_x86_64_gnu-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__atty-0.2.14": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/atty/0.2.14/download"
- ],
- "strip_prefix": "atty-0.2.14",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.atty-0.2.14.bazel"
- }
- },
- "cui__walkdir-2.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/walkdir/2.3.3/download"
- ],
- "strip_prefix": "walkdir-2.3.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.walkdir-2.3.3.bazel"
- }
- },
- "rrra__aho-corasick-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/aho-corasick/1.0.2/download"
- ],
- "strip_prefix": "aho-corasick-1.0.2",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.aho-corasick-1.0.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rustls-0.21.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustls/0.21.8/download"
- ],
- "strip_prefix": "rustls-0.21.8",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rustls-0.21.8.bazel"
- }
- },
- "cui__gix-refspec-0.18.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0895cb7b1e70f3c3bd4550c329e9f5caf2975f97fcd4238e05754e72208ef61e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-refspec/0.18.0/download"
- ],
- "strip_prefix": "gix-refspec-0.18.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-refspec-0.18.0.bazel"
- }
- },
- "cui__semver-1.0.20": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/semver/1.0.20/download"
- ],
- "strip_prefix": "semver-1.0.20",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.semver-1.0.20.bazel"
- }
- },
- "rules_rust_proto__num_cpus-1.15.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num_cpus/1.15.0/download"
- ],
- "strip_prefix": "num_cpus-1.15.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.num_cpus-1.15.0.bazel"
- }
- },
- "rules_rust_bindgen__humantime-2.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/humantime/2.1.0/download"
- ],
- "strip_prefix": "humantime-2.1.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.humantime-2.1.0.bazel"
- }
- },
- "rules_rust_bindgen__bitflags-2.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bitflags/2.4.1/download"
- ],
- "strip_prefix": "bitflags-2.4.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.bitflags-2.4.1.bazel"
- }
- },
- "rrra__regex-syntax-0.7.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-syntax/0.7.4/download"
- ],
- "strip_prefix": "regex-syntax-0.7.4",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.regex-syntax-0.7.4.bazel"
- }
- },
- "rules_rust_prost__autocfg-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/autocfg/1.1.0/download"
- ],
- "strip_prefix": "autocfg-1.1.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.autocfg-1.1.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__sct-0.7.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/sct/0.7.1/download"
- ],
- "strip_prefix": "sct-0.7.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.sct-0.7.1.bazel"
- }
- },
- "rrra__winapi-util-0.1.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-util/0.1.5/download"
- ],
- "strip_prefix": "winapi-util-0.1.5",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-util-0.1.5.bazel"
- }
- },
- "rules_rust_wasm_bindgen__strsim-0.10.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/strsim/0.10.0/download"
- ],
- "strip_prefix": "strsim-0.10.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.strsim-0.10.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__untrusted-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/untrusted/0.9.0/download"
- ],
- "strip_prefix": "untrusted-0.9.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.untrusted-0.9.0.bazel"
- }
- },
- "rules_rust_proto__slab-0.4.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/slab/0.4.7/download"
- ],
- "strip_prefix": "slab-0.4.7",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.slab-0.4.7.bazel"
- }
- },
- "rules_rust_wasm_bindgen__crossbeam-epoch-0.9.15": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-epoch/0.9.15/download"
- ],
- "strip_prefix": "crossbeam-epoch-0.9.15",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.crossbeam-epoch-0.9.15.bazel"
- }
- },
- "rrra__termcolor-1.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/termcolor/1.2.0/download"
- ],
- "strip_prefix": "termcolor-1.2.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.termcolor-1.2.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__errno-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno/0.3.1/download"
- ],
- "strip_prefix": "errno-0.3.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.errno-0.3.1.bazel"
- }
- },
- "rules_rust_bindgen__unicode-width-0.1.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-width/0.1.10/download"
- ],
- "strip_prefix": "unicode-width-0.1.10",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.unicode-width-0.1.10.bazel"
- }
- },
- "rules_rust_proto__crossbeam-queue-0.2.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-queue/0.2.3/download"
- ],
- "strip_prefix": "crossbeam-queue-0.2.3",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.crossbeam-queue-0.2.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen__crossbeam-deque-0.8.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-deque/0.8.3/download"
- ],
- "strip_prefix": "crossbeam-deque-0.8.3",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.crossbeam-deque-0.8.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasi-0.11.0-wasi-snapshot-preview1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasi/0.11.0+wasi-snapshot-preview1/download"
- ],
- "strip_prefix": "wasi-0.11.0+wasi-snapshot-preview1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasi-0.11.0+wasi-snapshot-preview1.bazel"
- }
- },
- "rrra__colorchoice-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/colorchoice/1.0.0/download"
- ],
- "strip_prefix": "colorchoice-1.0.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.colorchoice-1.0.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__regex-1.9.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex/1.9.1/download"
- ],
- "strip_prefix": "regex-1.9.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.regex-1.9.1.bazel"
- }
- },
- "rrra__windows_x86_64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.48.0.bazel"
- }
- },
- "rules_rust_prost__slab-0.4.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/slab/0.4.8/download"
- ],
- "strip_prefix": "slab-0.4.8",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.slab-0.4.8.bazel"
- }
- },
- "rrra__clap-4.3.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap/4.3.11/download"
- ],
- "strip_prefix": "clap-4.3.11",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.clap-4.3.11.bazel"
- }
- },
- "cui__adler-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/adler/1.0.2/download"
- ],
- "strip_prefix": "adler-1.0.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.adler-1.0.2.bazel"
- }
- },
- "cross_x86_64-apple-darwin": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/rust-embedded/cross/releases/download/v0.2.1/cross-v0.2.1-x86_64-apple-darwin.tar.gz"
- ],
- "sha256": "589da89453291dc26f0b10b521cdadb98376d495645b210574bd9ca4ec8cfa2c",
- "build_file_content": "exports_files(glob([\"**\"]), visibility = [\"//visibility:public\"])"
- }
- },
- "rules_rust_prost__rustix-0.37.20": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustix/0.37.20/download"
- ],
- "strip_prefix": "rustix-0.37.20",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.rustix-0.37.20.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-macro-support-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-macro-support/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-macro-support-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-macro-support-0.2.91.bazel"
- }
- },
- "rules_rust_prost__fnv-1.0.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fnv/1.0.7/download"
- ],
- "strip_prefix": "fnv-1.0.7",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.fnv-1.0.7.bazel"
- }
- },
- "cui__windows_i686_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_msvc-0.48.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows_i686_msvc-0.48.0.bazel"
- }
- },
- "cui__jwalk-0.8.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2735847566356cd2179a2a38264839308f7079fa96e6bd5a42d740460e003c56",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/jwalk/0.8.1/download"
- ],
- "strip_prefix": "jwalk-0.8.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.jwalk-0.8.1.bazel"
- }
- },
- "rules_rust_prost__getrandom-0.2.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/getrandom/0.2.10/download"
- ],
- "strip_prefix": "getrandom-0.2.10",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.getrandom-0.2.10.bazel"
- }
- },
- "rules_rust_wasm_bindgen__redox_syscall-0.2.16": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/redox_syscall/0.2.16/download"
- ],
- "strip_prefix": "redox_syscall-0.2.16",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.redox_syscall-0.2.16.bazel"
- }
- },
- "rules_rust_prost__httpdate-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/httpdate/1.0.2/download"
- ],
- "strip_prefix": "httpdate-1.0.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.httpdate-1.0.2.bazel"
- }
- },
- "cargo_bazel.buildifier-darwin-arm64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_file",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/buildtools/releases/download/5.0.1/buildifier-darwin-arm64"
- ],
- "sha256": "4da23315f0dccabf878c8227fddbccf35545b23b3cb6225bfcf3107689cc4364",
- "downloaded_file_path": "buildifier.exe",
- "executable": true
- }
- },
- "cui__cargo_toml-0.19.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a98356df42a2eb1bd8f1793ae4ee4de48e384dd974ce5eac8eee802edb7492be",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cargo_toml/0.19.2/download"
- ],
- "strip_prefix": "cargo_toml-0.19.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.cargo_toml-0.19.2.bazel"
- }
- },
- "rules_rust_prost__num_cpus-1.15.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num_cpus/1.15.0/download"
- ],
- "strip_prefix": "num_cpus-1.15.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.num_cpus-1.15.0.bazel"
- }
- },
- "rules_rust_bindgen__lazycell-1.3.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/lazycell/1.3.0/download"
- ],
- "strip_prefix": "lazycell-1.3.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.lazycell-1.3.0.bazel"
- }
- },
- "cui__tracing-subscriber-0.3.17": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tracing-subscriber/0.3.17/download"
- ],
- "strip_prefix": "tracing-subscriber-0.3.17",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.tracing-subscriber-0.3.17.bazel"
- }
- },
- "rules_rust_prost__bytes-1.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bytes/1.4.0/download"
- ],
- "strip_prefix": "bytes-1.4.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.bytes-1.4.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__mime_guess-2.0.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/mime_guess/2.0.4/download"
- ],
- "strip_prefix": "mime_guess-2.0.4",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.mime_guess-2.0.4.bazel"
- }
- },
- "rules_rust_proto__protobuf-codegen-2.8.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3d74b9cbbf2ac9a7169c85a3714ec16c51ee9ec7cfd511549527e9a7df720795",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/protobuf-codegen/2.8.2/download"
- ],
- "strip_prefix": "protobuf-codegen-2.8.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.protobuf-codegen-2.8.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-encoder-0.29.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-encoder/0.29.0/download"
- ],
- "strip_prefix": "wasm-encoder-0.29.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-encoder-0.29.0.bazel"
- }
- },
- "cui__regex-syntax-0.8.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-syntax/0.8.2/download"
- ],
- "strip_prefix": "regex-syntax-0.8.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.regex-syntax-0.8.2.bazel"
- }
- },
- "rules_rust_bindgen__clap_lex-0.5.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap_lex/0.5.0/download"
- ],
- "strip_prefix": "clap_lex-0.5.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.clap_lex-0.5.0.bazel"
- }
- },
- "rules_rust_prost__http-body-0.4.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/http-body/0.4.5/download"
- ],
- "strip_prefix": "http-body-0.4.5",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.http-body-0.4.5.bazel"
- }
- },
- "rules_rust_bindgen__utf8parse-0.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/utf8parse/0.2.1/download"
- ],
- "strip_prefix": "utf8parse-0.2.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.utf8parse-0.2.1.bazel"
- }
- },
- "rules_rust_proto__lazy_static-1.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/lazy_static/1.4.0/download"
- ],
- "strip_prefix": "lazy_static-1.4.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.lazy_static-1.4.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__windows_x86_64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.48.0.bazel"
- }
- },
- "rules_rust_prost__fixedbitset-0.4.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fixedbitset/0.4.2/download"
- ],
- "strip_prefix": "fixedbitset-0.4.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.fixedbitset-0.4.2.bazel"
- }
- },
- "rrra__winapi-i686-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "rules_rust_prost__regex-1.8.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex/1.8.4/download"
- ],
- "strip_prefix": "regex-1.8.4",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.regex-1.8.4.bazel"
- }
- },
- "cui__winapi-0.3.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi/0.3.9/download"
- ],
- "strip_prefix": "winapi-0.3.9",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
- }
- },
- "cui__syn-2.0.32": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/syn/2.0.32/download"
- ],
- "strip_prefix": "syn-2.0.32",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.syn-2.0.32.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-externref-xform-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "12b6ac5fca1d0992d2328147488169ea166bfe899c88f8ad06cf583f4c492fcf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-externref-xform/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-externref-xform-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-externref-xform-0.2.91.bazel"
- }
- },
- "rules_rust_prost__rustversion-1.0.12": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustversion/1.0.12/download"
- ],
- "strip_prefix": "rustversion-1.0.12",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.rustversion-1.0.12.bazel"
- }
- },
- "rules_rust_prost__tokio-macros-2.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-macros/2.1.0/download"
- ],
- "strip_prefix": "tokio-macros-2.1.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tokio-macros-2.1.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasmprinter-0.2.60": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b76cb909fe3d9b0de58cee1f4072247e680ff5cc1558ccad2790a9de14a23993",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasmprinter/0.2.60/download"
- ],
- "strip_prefix": "wasmprinter-0.2.60",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasmprinter-0.2.60.bazel"
- }
- },
- "rules_rust_proto__scoped-tls-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/scoped-tls/0.1.2/download"
- ],
- "strip_prefix": "scoped-tls-0.1.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.scoped-tls-0.1.2.bazel"
- }
- },
- "cui__gix-macros-0.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9d8acb5ee668d55f0f2d19a320a3f9ef67a6999ad483e11135abcc2464ed18b6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-macros/0.1.0/download"
- ],
- "strip_prefix": "gix-macros-0.1.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-macros-0.1.0.bazel"
- }
- },
- "rrra__ryu-1.0.14": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ryu/1.0.14/download"
- ],
- "strip_prefix": "ryu-1.0.14",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.ryu-1.0.14.bazel"
- }
- },
- "rrra__serde-1.0.171": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde/1.0.171/download"
- ],
- "strip_prefix": "serde-1.0.171",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.serde-1.0.171.bazel"
- }
- },
- "rules_rust_prost__lock_api-0.4.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/lock_api/0.4.10/download"
- ],
- "strip_prefix": "lock_api-0.4.10",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.lock_api-0.4.10.bazel"
- }
- },
- "rules_rust_bindgen__glob-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/glob/0.3.1/download"
- ],
- "strip_prefix": "glob-0.3.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.glob-0.3.1.bazel"
- }
- },
- "rules_rust_prost__itertools-0.10.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/itertools/0.10.5/download"
- ],
- "strip_prefix": "itertools-0.10.5",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.itertools-0.10.5.bazel"
- }
- },
- "cui__redox_syscall-0.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/redox_syscall/0.4.1/download"
- ],
- "strip_prefix": "redox_syscall-0.4.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.redox_syscall-0.4.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__id-arena-2.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/id-arena/2.2.1/download"
- ],
- "strip_prefix": "id-arena-2.2.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.id-arena-2.2.1.bazel"
- }
- },
- "cui__normpath-1.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/normpath/1.1.1/download"
- ],
- "strip_prefix": "normpath-1.1.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.normpath-1.1.1.bazel"
- }
- },
- "rules_rust_bindgen__lazy_static-1.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/lazy_static/1.4.0/download"
- ],
- "strip_prefix": "lazy_static-1.4.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.lazy_static-1.4.0.bazel"
- }
- },
- "rules_rust_prost__axum-0.6.18": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/axum/0.6.18/download"
- ],
- "strip_prefix": "axum-0.6.18",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.axum-0.6.18.bazel"
- }
- },
- "rules_rust_prost__parking_lot-0.12.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/parking_lot/0.12.1/download"
- ],
- "strip_prefix": "parking_lot-0.12.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.parking_lot-0.12.1.bazel"
- }
- },
- "cui__cargo-platform-0.1.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cargo-platform/0.1.4/download"
- ],
- "strip_prefix": "cargo-platform-0.1.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.cargo-platform-0.1.4.bazel"
- }
- },
- "cui__slug-0.1.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/slug/0.1.4/download"
- ],
- "strip_prefix": "slug-0.1.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.slug-0.1.4.bazel"
- }
- },
- "rules_rust_prost__errno-dragonfly-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno-dragonfly/0.1.2/download"
- ],
- "strip_prefix": "errno-dragonfly-0.1.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.errno-dragonfly-0.1.2.bazel"
- }
- },
- "cui__gix-url-0.24.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6125ecf46e8c68bf7202da6cad239831daebf0247ffbab30210d72f3856e420f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-url/0.24.0/download"
- ],
- "strip_prefix": "gix-url-0.24.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-url-0.24.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__percent-encoding-2.3.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/percent-encoding/2.3.0/download"
- ],
- "strip_prefix": "percent-encoding-2.3.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.percent-encoding-2.3.0.bazel"
- }
- },
- "cui__clap_builder-4.3.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap_builder/4.3.11/download"
- ],
- "strip_prefix": "clap_builder-4.3.11",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.clap_builder-4.3.11.bazel"
- }
- },
- "cui__tracing-core-0.1.32": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tracing-core/0.1.32/download"
- ],
- "strip_prefix": "tracing-core-0.1.32",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.tracing-core-0.1.32.bazel"
- }
- },
- "rules_rust_proto__fuchsia-zircon-sys-0.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fuchsia-zircon-sys/0.3.3/download"
- ],
- "strip_prefix": "fuchsia-zircon-sys-0.3.3",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.fuchsia-zircon-sys-0.3.3.bazel"
- }
- },
- "rules_rust_proto__safemem-0.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/safemem/0.3.3/download"
- ],
- "strip_prefix": "safemem-0.3.3",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.safemem-0.3.3.bazel"
- }
- },
- "cui__windows_x86_64_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_gnu/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_gnu-0.48.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows_x86_64_gnu-0.48.0.bazel"
- }
- },
- "cui__gix-actor-0.27.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "08c60e982c5290897122d4e2622447f014a2dadd5a18cb73d50bb91b31645e27",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-actor/0.27.0/download"
- ],
- "strip_prefix": "gix-actor-0.27.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-actor-0.27.0.bazel"
- }
- },
- "cui__unic-ucd-version-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unic-ucd-version/0.9.0/download"
- ],
- "strip_prefix": "unic-ucd-version-0.9.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unic-ucd-version-0.9.0.bazel"
- }
- },
- "com_google_googleapis": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/googleapis/googleapis/archive/18becb1d1426feb7399db144d7beeb3284f1ccb0.zip"
- ],
- "strip_prefix": "googleapis-18becb1d1426feb7399db144d7beeb3284f1ccb0",
- "sha256": "b8c487191eb942361af905e40172644eab490190e717c3d09bf83e87f3994fff"
- }
- },
- "cui__either-1.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/either/1.9.0/download"
- ],
- "strip_prefix": "either-1.9.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.either-1.9.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__gimli-0.26.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gimli/0.26.2/download"
- ],
- "strip_prefix": "gimli-0.26.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.gimli-0.26.2.bazel"
- }
- },
- "cui__parking_lot-0.12.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/parking_lot/0.12.1/download"
- ],
- "strip_prefix": "parking_lot-0.12.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.parking_lot-0.12.1.bazel"
- }
- },
- "cui__globwalk-0.8.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/globwalk/0.8.1/download"
- ],
- "strip_prefix": "globwalk-0.8.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.globwalk-0.8.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-wasm-interpreter-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "682940195a701dbf887f20017418b8cac916a37b3f91ededec33226619e973c1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-wasm-interpreter/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-wasm-interpreter-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-wasm-interpreter-0.2.91.bazel"
- }
- },
- "rules_rust_wasm_bindgen__ring-0.17.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ring/0.17.5/download"
- ],
- "strip_prefix": "ring-0.17.5",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.ring-0.17.5.bazel"
- }
- },
- "rules_rust_prost__memchr-2.5.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/memchr/2.5.0/download"
- ],
- "strip_prefix": "memchr-2.5.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.memchr-2.5.0.bazel"
- }
- },
- "cui__crates-index-2.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "33bc10579ea08741ae173928194b6c42c90b295d51ddd0d18238eaf15502ac87",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crates-index/2.2.0/download"
- ],
- "strip_prefix": "crates-index-2.2.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.crates-index-2.2.0.bazel"
- }
- },
- "rules_rust_proto__winapi-0.3.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi/0.3.9/download"
- ],
- "strip_prefix": "winapi-0.3.9",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
- }
- },
- "rules_rust_wasm_bindgen__crossbeam-channel-0.5.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-channel/0.5.8/download"
- ],
- "strip_prefix": "crossbeam-channel-0.5.8",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.crossbeam-channel-0.5.8.bazel"
- }
- },
- "rules_rust_wasm_bindgen__windows-sys-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-sys/0.48.0/download"
- ],
- "strip_prefix": "windows-sys-0.48.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows-sys-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__flate2-1.0.28": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/flate2/1.0.28/download"
- ],
- "strip_prefix": "flate2-1.0.28",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.flate2-1.0.28.bazel"
- }
- },
- "rules_rust_proto__semver-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/semver/0.9.0/download"
- ],
- "strip_prefix": "semver-0.9.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.semver-0.9.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__scopeguard-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/scopeguard/1.1.0/download"
- ],
- "strip_prefix": "scopeguard-1.1.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.scopeguard-1.1.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__fastrand-1.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fastrand/1.9.0/download"
- ],
- "strip_prefix": "fastrand-1.9.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.fastrand-1.9.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__num_threads-0.1.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num_threads/0.1.6/download"
- ],
- "strip_prefix": "num_threads-0.1.6",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.num_threads-0.1.6.bazel"
- }
- },
- "cui__rayon-core-1.12.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rayon-core/1.12.0/download"
- ],
- "strip_prefix": "rayon-core-1.12.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rayon-core-1.12.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__lazy_static-1.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/lazy_static/1.4.0/download"
- ],
- "strip_prefix": "lazy_static-1.4.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.lazy_static-1.4.0.bazel"
- }
- },
- "cui__thread_local-1.1.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/thread_local/1.1.4/download"
- ],
- "strip_prefix": "thread_local-1.1.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.thread_local-1.1.4.bazel"
- }
- },
- "rules_rust_wasm_bindgen__threadpool-1.8.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/threadpool/1.8.1/download"
- ],
- "strip_prefix": "threadpool-1.8.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.threadpool-1.8.1.bazel"
- }
- },
- "cui__linux-raw-sys-0.4.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/linux-raw-sys/0.4.10/download"
- ],
- "strip_prefix": "linux-raw-sys-0.4.10",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.linux-raw-sys-0.4.10.bazel"
- }
- },
- "rules_rust_bindgen__anstyle-wincon-1.0.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anstyle-wincon/1.0.1/download"
- ],
- "strip_prefix": "anstyle-wincon-1.0.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.anstyle-wincon-1.0.1.bazel"
- }
- },
- "rrra__windows_x86_64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_msvc-0.48.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.windows_x86_64_msvc-0.48.0.bazel"
- }
- },
- "cui__rand_core-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand_core/0.3.1/download"
- ],
- "strip_prefix": "rand_core-0.3.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rand_core-0.3.1.bazel"
- }
- },
- "cui__rayon-1.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rayon/1.8.0/download"
- ],
- "strip_prefix": "rayon-1.8.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rayon-1.8.0.bazel"
- }
- },
- "cui__tempfile-3.8.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tempfile/3.8.1/download"
- ],
- "strip_prefix": "tempfile-3.8.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.tempfile-3.8.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__windows_aarch64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_msvc-0.48.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows_aarch64_msvc-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__multipart-0.18.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/multipart/0.18.0/download"
- ],
- "strip_prefix": "multipart-0.18.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.multipart-0.18.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__android_system_properties-0.1.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/android_system_properties/0.1.5/download"
- ],
- "strip_prefix": "android_system_properties-0.1.5",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.android_system_properties-0.1.5.bazel"
- }
- },
- "cui__gix-ref-0.37.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "22e6b749660b613641769edc1954132eb8071a13c32224891686091bef078de4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-ref/0.37.0/download"
- ],
- "strip_prefix": "gix-ref-0.37.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-ref-0.37.0.bazel"
- }
- },
- "cui__rand-0.8.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand/0.8.5/download"
- ],
- "strip_prefix": "rand-0.8.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rand-0.8.5.bazel"
- }
- },
- "cui__num-integer-0.1.45": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num-integer/0.1.45/download"
- ],
- "strip_prefix": "num-integer-0.1.45",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.num-integer-0.1.45.bazel"
- }
- },
- "rules_rust_bindgen__anstyle-query-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anstyle-query/1.0.0/download"
- ],
- "strip_prefix": "anstyle-query-1.0.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.anstyle-query-1.0.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__hermit-abi-0.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hermit-abi/0.3.2/download"
- ],
- "strip_prefix": "hermit-abi-0.3.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.hermit-abi-0.3.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__getrandom-0.2.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/getrandom/0.2.10/download"
- ],
- "strip_prefix": "getrandom-0.2.10",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.getrandom-0.2.10.bazel"
- }
- },
- "rules_rust_proto__smallvec-0.6.14": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/smallvec/0.6.14/download"
- ],
- "strip_prefix": "smallvec-0.6.14",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.smallvec-0.6.14.bazel"
- }
- },
- "rules_rust_prost__httparse-1.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/httparse/1.8.0/download"
- ],
- "strip_prefix": "httparse-1.8.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.httparse-1.8.0.bazel"
- }
- },
- "rules_rust_bindgen__shlex-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/shlex/1.1.0/download"
- ],
- "strip_prefix": "shlex-1.1.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.shlex-1.1.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__predicates-1.0.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/predicates/1.0.8/download"
- ],
- "strip_prefix": "predicates-1.0.8",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.predicates-1.0.8.bazel"
- }
- },
- "rules_rust_proto__scopeguard-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/scopeguard/1.1.0/download"
- ],
- "strip_prefix": "scopeguard-1.1.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.scopeguard-1.1.0.bazel"
- }
- },
- "rrra__windows-targets-0.48.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-targets/0.48.1/download"
- ],
- "strip_prefix": "windows-targets-0.48.1",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.windows-targets-0.48.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__serde_json-1.0.102": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde_json/1.0.102/download"
- ],
- "strip_prefix": "serde_json-1.0.102",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.serde_json-1.0.102.bazel"
- }
- },
- "rrra__clap_builder-4.3.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap_builder/4.3.11/download"
- ],
- "strip_prefix": "clap_builder-4.3.11",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.clap_builder-4.3.11.bazel"
- }
- },
- "rules_rust_prost__windows-sys-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-sys/0.48.0/download"
- ],
- "strip_prefix": "windows-sys-0.48.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.windows-sys-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__windows_i686_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_gnu/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_gnu-0.48.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows_i686_gnu-0.48.0.bazel"
- }
- },
- "cui__gix-lock-10.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "47fc96fa8b6b6d33555021907c81eb3b27635daecf6e630630bdad44f8feaa95",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-lock/10.0.0/download"
- ],
- "strip_prefix": "gix-lock-10.0.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-lock-10.0.0.bazel"
- }
- },
- "rules_rust_prost__indexmap-1.9.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/indexmap/1.9.3/download"
- ],
- "strip_prefix": "indexmap-1.9.3",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.indexmap-1.9.3.bazel"
- }
- },
- "cui__num-iter-0.1.43": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num-iter/0.1.43/download"
- ],
- "strip_prefix": "num-iter-0.1.43",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.num-iter-0.1.43.bazel"
- }
- },
- "rules_rust_wasm_bindgen__ryu-1.0.14": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ryu/1.0.14/download"
- ],
- "strip_prefix": "ryu-1.0.14",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.ryu-1.0.14.bazel"
- }
- },
- "rules_rust_prost__lazy_static-1.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/lazy_static/1.4.0/download"
- ],
- "strip_prefix": "lazy_static-1.4.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.lazy_static-1.4.0.bazel"
- }
- },
- "rules_rust_prost__multimap-0.8.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/multimap/0.8.3/download"
- ],
- "strip_prefix": "multimap-0.8.3",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.multimap-0.8.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen__difference-2.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/difference/2.0.0/download"
- ],
- "strip_prefix": "difference-2.0.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.difference-2.0.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__unicode-segmentation-1.10.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-segmentation/1.10.1/download"
- ],
- "strip_prefix": "unicode-segmentation-1.10.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.unicode-segmentation-1.10.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__proc-macro2-1.0.64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/proc-macro2/1.0.64/download"
- ],
- "strip_prefix": "proc-macro2-1.0.64",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.proc-macro2-1.0.64.bazel"
- }
- },
- "rrra__cc-1.0.79": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cc/1.0.79/download"
- ],
- "strip_prefix": "cc-1.0.79",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.cc-1.0.79.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rustls-webpki-0.101.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustls-webpki/0.101.7/download"
- ],
- "strip_prefix": "rustls-webpki-0.101.7",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rustls-webpki-0.101.7.bazel"
- }
- },
- "rules_rust_prost": {
- "bzlFile": "@@rules_rust~//crate_universe/private:crates_vendor.bzl",
- "ruleClassName": "crates_vendor_remote_repository",
- "attributes": {
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.bazel",
- "defs_module": "@@rules_rust~//proto/prost/private/3rdparty/crates:defs.bzl"
- }
- },
- "rules_rust_bindgen__quote-1.0.28": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/quote/1.0.28/download"
- ],
- "strip_prefix": "quote-1.0.28",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.quote-1.0.28.bazel"
- }
- },
- "cui__anstyle-query-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anstyle-query/1.0.0/download"
- ],
- "strip_prefix": "anstyle-query-1.0.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.anstyle-query-1.0.0.bazel"
- }
- },
- "cui__bumpalo-3.13.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bumpalo/3.13.0/download"
- ],
- "strip_prefix": "bumpalo-3.13.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.bumpalo-3.13.0.bazel"
- }
- },
- "rules_rust_prost__cfg-if-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cfg-if/1.0.0/download"
- ],
- "strip_prefix": "cfg-if-1.0.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
- }
- },
- "rules_rust_bindgen__anstyle-parse-0.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anstyle-parse/0.2.0/download"
- ],
- "strip_prefix": "anstyle-parse-0.2.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.anstyle-parse-0.2.0.bazel"
- }
- },
- "rules_rust_bindgen__bindgen-0.69.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bindgen/0.69.1/download"
- ],
- "strip_prefix": "bindgen-0.69.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.bindgen-0.69.1.bazel"
- }
- },
- "cui__num-complex-0.1.43": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num-complex/0.1.43/download"
- ],
- "strip_prefix": "num-complex-0.1.43",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.num-complex-0.1.43.bazel"
- }
- },
- "rules_rust_prost__pin-project-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/pin-project/1.1.0/download"
- ],
- "strip_prefix": "pin-project-1.1.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.pin-project-1.1.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__quote-1.0.29": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/quote/1.0.29/download"
- ],
- "strip_prefix": "quote-1.0.29",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.quote-1.0.29.bazel"
- }
- },
- "cui__parse-zoneinfo-0.3.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/parse-zoneinfo/0.3.0/download"
- ],
- "strip_prefix": "parse-zoneinfo-0.3.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.parse-zoneinfo-0.3.0.bazel"
- }
- },
- "cui__unicode-bidi-0.3.13": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-bidi/0.3.13/download"
- ],
- "strip_prefix": "unicode-bidi-0.3.13",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unicode-bidi-0.3.13.bazel"
- }
- },
- "cui__gix-traverse-0.33.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "22ef04ab3643acba289b5cedd25d6f53c0430770b1d689d1d654511e6fb81ba0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-traverse/0.33.0/download"
- ],
- "strip_prefix": "gix-traverse-0.33.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-traverse-0.33.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__stable_deref_trait-1.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/stable_deref_trait/1.2.0/download"
- ],
- "strip_prefix": "stable_deref_trait-1.2.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.stable_deref_trait-1.2.0.bazel"
- }
- },
- "rules_rust_proto__ws2_32-sys-0.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ws2_32-sys/0.2.1/download"
- ],
- "strip_prefix": "ws2_32-sys-0.2.1",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.ws2_32-sys-0.2.1.bazel"
- }
- },
- "cui__miniz_oxide-0.7.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/miniz_oxide/0.7.1/download"
- ],
- "strip_prefix": "miniz_oxide-0.7.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.miniz_oxide-0.7.1.bazel"
- }
- },
- "rules_rust_bindgen__io-lifetimes-1.0.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/io-lifetimes/1.0.11/download"
- ],
- "strip_prefix": "io-lifetimes-1.0.11",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.io-lifetimes-1.0.11.bazel"
- }
- },
- "cui__unic-char-range-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unic-char-range/0.9.0/download"
- ],
- "strip_prefix": "unic-char-range-0.9.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unic-char-range-0.9.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__leb128-0.2.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/leb128/0.2.5/download"
- ],
- "strip_prefix": "leb128-0.2.5",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.leb128-0.2.5.bazel"
- }
- },
- "rules_rust_wasm_bindgen__predicates-core-1.0.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/predicates-core/1.0.6/download"
- ],
- "strip_prefix": "predicates-core-1.0.6",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.predicates-core-1.0.6.bazel"
- }
- },
- "cui__windows_aarch64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_msvc-0.48.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows_aarch64_msvc-0.48.0.bazel"
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.anstream-0.3.2.bazel"
}
},
"cui__anstyle-1.0.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"sha256": "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd",
"type": "tar.gz",
@@ -6427,77 +851,35 @@
"https://static.crates.io/crates/anstyle/1.0.1/download"
],
"strip_prefix": "anstyle-1.0.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.anstyle-1.0.1.bazel"
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.anstyle-1.0.1.bazel"
}
},
- "rules_rust_wasm_bindgen__wasm-bindgen-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__anstyle-parse-0.2.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f",
+ "sha256": "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/wasm-bindgen/0.2.91/download"
+ "https://static.crates.io/crates/anstyle-parse/0.2.1/download"
],
- "strip_prefix": "wasm-bindgen-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-0.2.91.bazel"
+ "strip_prefix": "anstyle-parse-0.2.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.anstyle-parse-0.2.1.bazel"
}
},
- "rules_rust_prost__winapi-i686-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__anstyle-query-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
+ "sha256": "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
+ "https://static.crates.io/crates/anstyle-query/1.0.0/download"
],
- "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
+ "strip_prefix": "anstyle-query-1.0.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.anstyle-query-1.0.0.bazel"
}
},
- "cui__regex-automata-0.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-automata/0.3.3/download"
- ],
- "strip_prefix": "regex-automata-0.3.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.regex-automata-0.3.3.bazel"
- }
- },
- "rrra__windows_aarch64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_msvc-0.48.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.windows_aarch64_msvc-0.48.0.bazel"
- }
- },
- "rules_rust_prost__which-4.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/which/4.4.0/download"
- ],
- "strip_prefix": "which-4.4.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.which-4.4.0.bazel"
- }
- },
- "rrra__anstyle-wincon-1.0.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__anstyle-wincon-1.0.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"sha256": "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188",
"type": "tar.gz",
@@ -6505,1805 +887,23 @@
"https://static.crates.io/crates/anstyle-wincon/1.0.1/download"
],
"strip_prefix": "anstyle-wincon-1.0.1",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-wincon-1.0.1.bazel"
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.anstyle-wincon-1.0.1.bazel"
}
},
- "rules_rust_wasm_bindgen__adler-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__anyhow-1.0.89": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe",
+ "sha256": "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/adler/1.0.2/download"
+ "https://static.crates.io/crates/anyhow/1.0.89/download"
],
- "strip_prefix": "adler-1.0.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.adler-1.0.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__log-0.4.19": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/log/0.4.19/download"
- ],
- "strip_prefix": "log-0.4.19",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.log-0.4.19.bazel"
- }
- },
- "rules_rust_bindgen__heck-0.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/heck/0.4.1/download"
- ],
- "strip_prefix": "heck-0.4.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.heck-0.4.1.bazel"
- }
- },
- "cui__digest-0.10.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/digest/0.10.7/download"
- ],
- "strip_prefix": "digest-0.10.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.digest-0.10.7.bazel"
- }
- },
- "cui__equivalent-1.0.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/equivalent/1.0.1/download"
- ],
- "strip_prefix": "equivalent-1.0.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.equivalent-1.0.1.bazel"
- }
- },
- "cui": {
- "bzlFile": "@@rules_rust~//crate_universe/private:crates_vendor.bzl",
- "ruleClassName": "crates_vendor_remote_repository",
- "attributes": {
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.bazel",
- "defs_module": "@@rules_rust~//crate_universe/3rdparty/crates:defs.bzl"
- }
- },
- "rules_rust_wasm_bindgen__memchr-2.5.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/memchr/2.5.0/download"
- ],
- "strip_prefix": "memchr-2.5.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.memchr-2.5.0.bazel"
- }
- },
- "rrra__once_cell-1.18.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/once_cell/1.18.0/download"
- ],
- "strip_prefix": "once_cell-1.18.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.once_cell-1.18.0.bazel"
- }
- },
- "rules_rust_proto__tokio-tls-api-0.1.22": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "68d0e040d5b1f4cfca70ec4f371229886a5de5bb554d272a4a8da73004a7b2c9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-tls-api/0.1.22/download"
- ],
- "strip_prefix": "tokio-tls-api-0.1.22",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-tls-api-0.1.22.bazel"
- }
- },
- "rules_rust_bindgen__is-terminal-0.4.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/is-terminal/0.4.7/download"
- ],
- "strip_prefix": "is-terminal-0.4.7",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.is-terminal-0.4.7.bazel"
- }
- },
- "cui__autocfg-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/autocfg/1.1.0/download"
- ],
- "strip_prefix": "autocfg-1.1.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.autocfg-1.1.0.bazel"
- }
- },
- "rules_rust_prost__tokio-util-0.7.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-util/0.7.8/download"
- ],
- "strip_prefix": "tokio-util-0.7.8",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tokio-util-0.7.8.bazel"
- }
- },
- "rules_rust_prost__tokio-io-timeout-1.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-io-timeout/1.2.0/download"
- ],
- "strip_prefix": "tokio-io-timeout-1.2.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tokio-io-timeout-1.2.0.bazel"
- }
- },
- "cui__num-traits-0.2.15": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num-traits/0.2.15/download"
- ],
- "strip_prefix": "num-traits-0.2.15",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.num-traits-0.2.15.bazel"
- }
- },
- "rules_rust_proto__winapi-build-0.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-build/0.1.1/download"
- ],
- "strip_prefix": "winapi-build-0.1.1",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.winapi-build-0.1.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__base64-0.13.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/base64/0.13.1/download"
- ],
- "strip_prefix": "base64-0.13.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.base64-0.13.1.bazel"
- }
- },
- "rules_rust_proto__parking_lot-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/parking_lot/0.9.0/download"
- ],
- "strip_prefix": "parking_lot-0.9.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.parking_lot-0.9.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__humantime-2.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/humantime/2.1.0/download"
- ],
- "strip_prefix": "humantime-2.1.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.humantime-2.1.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rand_chacha-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand_chacha/0.3.1/download"
- ],
- "strip_prefix": "rand_chacha-0.3.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rand_chacha-0.3.1.bazel"
- }
- },
- "cui__strsim-0.10.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/strsim/0.10.0/download"
- ],
- "strip_prefix": "strsim-0.10.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.strsim-0.10.0.bazel"
- }
- },
- "rules_rust_prost__windows_aarch64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.48.0.bazel"
- }
- },
- "cui__cfg-if-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cfg-if/1.0.0/download"
- ],
- "strip_prefix": "cfg-if-1.0.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
- }
- },
- "cui__errno-dragonfly-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno-dragonfly/0.1.2/download"
- ],
- "strip_prefix": "errno-dragonfly-0.1.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.errno-dragonfly-0.1.2.bazel"
- }
- },
- "rules_rust_bindgen__regex-syntax-0.7.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-syntax/0.7.2/download"
- ],
- "strip_prefix": "regex-syntax-0.7.2",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.regex-syntax-0.7.2.bazel"
- }
- },
- "cui__proc-macro2-1.0.64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/proc-macro2/1.0.64/download"
- ],
- "strip_prefix": "proc-macro2-1.0.64",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.proc-macro2-1.0.64.bazel"
- }
- },
- "cui__gix-prompt-0.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5c9a913769516f5e9d937afac206fb76428e3d7238e538845842887fda584678",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-prompt/0.7.0/download"
- ],
- "strip_prefix": "gix-prompt-0.7.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-prompt-0.7.0.bazel"
- }
- },
- "cui__thiserror-impl-1.0.50": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/thiserror-impl/1.0.50/download"
- ],
- "strip_prefix": "thiserror-impl-1.0.50",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.thiserror-impl-1.0.50.bazel"
- }
- },
- "rules_rust_prost__either-1.8.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/either/1.8.1/download"
- ],
- "strip_prefix": "either-1.8.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.either-1.8.1.bazel"
- }
- },
- "rules_rust_bindgen__bindgen-cli-0.69.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "integrity": "sha256-iFZe4JEQqZ54KZiX+/7VA7mqAwZThu6MGBl/yvIotQE=",
- "type": "tar.gz",
- "urls": [
- "https://crates.io/api/v1/crates/bindgen-cli/0.69.1/download"
- ],
- "strip_prefix": "bindgen-cli-0.69.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty:BUILD.bindgen-cli.bazel"
- }
- },
- "cui__thiserror-1.0.50": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/thiserror/1.0.50/download"
- ],
- "strip_prefix": "thiserror-1.0.50",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.thiserror-1.0.50.bazel"
- }
- },
- "rules_rust_proto__mio-uds-0.6.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/mio-uds/0.6.8/download"
- ],
- "strip_prefix": "mio-uds-0.6.8",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.mio-uds-0.6.8.bazel"
- }
- },
- "rules_rust_proto__tokio-fs-0.1.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-fs/0.1.7/download"
- ],
- "strip_prefix": "tokio-fs-0.1.7",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-fs-0.1.7.bazel"
- }
- },
- "rules_rust_bindgen__linux-raw-sys-0.3.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/linux-raw-sys/0.3.8/download"
- ],
- "strip_prefix": "linux-raw-sys-0.3.8",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.linux-raw-sys-0.3.8.bazel"
- }
- },
- "rules_rust_wasm_bindgen__regex-automata-0.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-automata/0.3.3/download"
- ],
- "strip_prefix": "regex-automata-0.3.3",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.regex-automata-0.3.3.bazel"
- }
- },
- "cui__typenum-1.16.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/typenum/1.16.0/download"
- ],
- "strip_prefix": "typenum-1.16.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.typenum-1.16.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rand-0.8.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand/0.8.5/download"
- ],
- "strip_prefix": "rand-0.8.5",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rand-0.8.5.bazel"
- }
- },
- "cui__errno-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno/0.3.1/download"
- ],
- "strip_prefix": "errno-0.3.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.errno-0.3.1.bazel"
- }
- },
- "cui__num-rational-0.1.42": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num-rational/0.1.42/download"
- ],
- "strip_prefix": "num-rational-0.1.42",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.num-rational-0.1.42.bazel"
- }
- },
- "rules_rust_wasm_bindgen__difflib-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/difflib/0.4.0/download"
- ],
- "strip_prefix": "difflib-0.4.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.difflib-0.4.0.bazel"
- }
- },
- "cui__sha2-0.10.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/sha2/0.10.8/download"
- ],
- "strip_prefix": "sha2-0.10.8",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.sha2-0.10.8.bazel"
- }
- },
- "cui__clru-0.6.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clru/0.6.1/download"
- ],
- "strip_prefix": "clru-0.6.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.clru-0.6.1.bazel"
- }
- },
- "cui__rand-0.4.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand/0.4.6/download"
- ],
- "strip_prefix": "rand-0.4.6",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rand-0.4.6.bazel"
- }
- },
- "rrra__io-lifetimes-1.0.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/io-lifetimes/1.0.11/download"
- ],
- "strip_prefix": "io-lifetimes-1.0.11",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.io-lifetimes-1.0.11.bazel"
- }
- },
- "cui__phf_shared-0.11.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/phf_shared/0.11.2/download"
- ],
- "strip_prefix": "phf_shared-0.11.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.phf_shared-0.11.2.bazel"
- }
- },
- "rrra__bitflags-1.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bitflags/1.3.2/download"
- ],
- "strip_prefix": "bitflags-1.3.2",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
- }
- },
- "rules_rust_prost__redox_syscall-0.3.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/redox_syscall/0.3.5/download"
- ],
- "strip_prefix": "redox_syscall-0.3.5",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.redox_syscall-0.3.5.bazel"
- }
- },
- "cui__gix-packetline-blocking-0.16.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7d8395f7501c84d6a1fe902035fdfd8cd86d89e2dd6be0200ec1a72fd3c92d39",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-packetline-blocking/0.16.6/download"
- ],
- "strip_prefix": "gix-packetline-blocking-0.16.6",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-packetline-blocking-0.16.6.bazel"
- }
- },
- "rules_rust_proto__fnv-1.0.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fnv/1.0.7/download"
- ],
- "strip_prefix": "fnv-1.0.7",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.fnv-1.0.7.bazel"
- }
- },
- "cui__windows_aarch64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.48.0.bazel"
- }
- },
- "rules_rust_prost__tracing-core-0.1.31": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tracing-core/0.1.31/download"
- ],
- "strip_prefix": "tracing-core-0.1.31",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tracing-core-0.1.31.bazel"
- }
- },
- "rrra__env_logger-0.10.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/env_logger/0.10.0/download"
- ],
- "strip_prefix": "env_logger-0.10.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.env_logger-0.10.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__aho-corasick-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/aho-corasick/1.0.2/download"
- ],
- "strip_prefix": "aho-corasick-1.0.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.aho-corasick-1.0.2.bazel"
- }
- },
- "cui__time-0.3.30": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/time/0.3.30/download"
- ],
- "strip_prefix": "time-0.3.30",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.time-0.3.30.bazel"
- }
- },
- "rules_rust_proto__grpc-0.6.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2aaf1d741fe6f3413f1f9f71b99f5e4e26776d563475a8a53ce53a73a8534c1d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/grpc/0.6.2/download"
- ],
- "strip_prefix": "grpc-0.6.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.grpc-0.6.2.bazel"
- }
- },
- "rules_rust_bindgen__unicode-ident-1.0.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-ident/1.0.9/download"
- ],
- "strip_prefix": "unicode-ident-1.0.9",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.unicode-ident-1.0.9.bazel"
- }
- },
- "rules_rust_prost__log-0.4.19": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/log/0.4.19/download"
- ],
- "strip_prefix": "log-0.4.19",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.log-0.4.19.bazel"
- }
- },
- "cui__ucd-trie-0.1.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ucd-trie/0.1.6/download"
- ],
- "strip_prefix": "ucd-trie-0.1.6",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.ucd-trie-0.1.6.bazel"
- }
- },
- "cui__gix-pack-0.43.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7536203a45b31e1bc5694bbf90ba8da1b736c77040dd6a520db369f371eb1ab3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-pack/0.43.0/download"
- ],
- "strip_prefix": "gix-pack-0.43.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-pack-0.43.0.bazel"
- }
- },
- "rules_rust_prost__prettyplease-0.1.25": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/prettyplease/0.1.25/download"
- ],
- "strip_prefix": "prettyplease-0.1.25",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.prettyplease-0.1.25.bazel"
- }
- },
- "cui__toml-0.7.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/toml/0.7.6/download"
- ],
- "strip_prefix": "toml-0.7.6",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.toml-0.7.6.bazel"
- }
- },
- "rules_rust_prost__tempfile-3.6.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tempfile/3.6.0/download"
- ],
- "strip_prefix": "tempfile-3.6.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tempfile-3.6.0.bazel"
- }
- },
- "rules_rust_prost__tokio-stream-0.1.14": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-stream/0.1.14/download"
- ],
- "strip_prefix": "tokio-stream-0.1.14",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tokio-stream-0.1.14.bazel"
- }
- },
- "cui__unic-ucd-segment-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unic-ucd-segment/0.9.0/download"
- ],
- "strip_prefix": "unic-ucd-segment-0.9.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unic-ucd-segment-0.9.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__android-tzdata-0.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/android-tzdata/0.1.1/download"
- ],
- "strip_prefix": "android-tzdata-0.1.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.android-tzdata-0.1.1.bazel"
- }
- },
- "generated_inputs_in_external_repo": {
- "bzlFile": "@@rules_rust~//test/generated_inputs:external_repo.bzl",
- "ruleClassName": "_generated_inputs_in_external_repo",
- "attributes": {}
- },
- "cui__gix-submodule-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "dd0150e82e9282d3f2ab2dd57a22f9f6c3447b9d9856e5321ac92d38e3e0e2b7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-submodule/0.4.0/download"
- ],
- "strip_prefix": "gix-submodule-0.4.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-submodule-0.4.0.bazel"
- }
- },
- "cui__serde_spanned-0.6.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde_spanned/0.6.5/download"
- ],
- "strip_prefix": "serde_spanned-0.6.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.serde_spanned-0.6.5.bazel"
- }
- },
- "rules_rust_proto__kernel32-sys-0.2.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/kernel32-sys/0.2.2/download"
- ],
- "strip_prefix": "kernel32-sys-0.2.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.kernel32-sys-0.2.2.bazel"
- }
- },
- "rules_rust_prost__mime-0.3.17": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/mime/0.3.17/download"
- ],
- "strip_prefix": "mime-0.3.17",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.mime-0.3.17.bazel"
- }
- },
- "cui__gix-quote-0.4.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "475c86a97dd0127ba4465fbb239abac9ea10e68301470c9791a6dd5351cdc905",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-quote/0.4.7/download"
- ],
- "strip_prefix": "gix-quote-0.4.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-quote-0.4.7.bazel"
- }
- },
- "rrra__linux-raw-sys-0.3.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/linux-raw-sys/0.3.8/download"
- ],
- "strip_prefix": "linux-raw-sys-0.3.8",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.linux-raw-sys-0.3.8.bazel"
- }
- },
- "cui__memmap2-0.7.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/memmap2/0.7.1/download"
- ],
- "strip_prefix": "memmap2-0.7.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.memmap2-0.7.1.bazel"
- }
- },
- "rules_rust_proto__tokio-reactor-0.1.12": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-reactor/0.1.12/download"
- ],
- "strip_prefix": "tokio-reactor-0.1.12",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-reactor-0.1.12.bazel"
- }
- },
- "rules_rust_wasm_bindgen__equivalent-1.0.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/equivalent/1.0.1/download"
- ],
- "strip_prefix": "equivalent-1.0.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.equivalent-1.0.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__fallible-iterator-0.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fallible-iterator/0.2.0/download"
- ],
- "strip_prefix": "fallible-iterator-0.2.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.fallible-iterator-0.2.0.bazel"
- }
- },
- "cui__pest_derive-2.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/pest_derive/2.7.0/download"
- ],
- "strip_prefix": "pest_derive-2.7.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.pest_derive-2.7.0.bazel"
- }
- },
- "rules_rust_prost__once_cell-1.18.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/once_cell/1.18.0/download"
- ],
- "strip_prefix": "once_cell-1.18.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.once_cell-1.18.0.bazel"
- }
- },
- "rules_rust_proto__fuchsia-zircon-0.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fuchsia-zircon/0.3.3/download"
- ],
- "strip_prefix": "fuchsia-zircon-0.3.3",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.fuchsia-zircon-0.3.3.bazel"
- }
- },
- "cui__hermit-abi-0.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hermit-abi/0.3.2/download"
- ],
- "strip_prefix": "hermit-abi-0.3.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.hermit-abi-0.3.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__regex-syntax-0.7.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-syntax/0.7.4/download"
- ],
- "strip_prefix": "regex-syntax-0.7.4",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.regex-syntax-0.7.4.bazel"
- }
- },
- "rules_rust_bindgen__errno-dragonfly-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno-dragonfly/0.1.2/download"
- ],
- "strip_prefix": "errno-dragonfly-0.1.2",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.errno-dragonfly-0.1.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__miniz_oxide-0.7.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/miniz_oxide/0.7.1/download"
- ],
- "strip_prefix": "miniz_oxide-0.7.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.miniz_oxide-0.7.1.bazel"
- }
- },
- "rules_rust_bindgen__windows_x86_64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.48.0.bazel"
- }
- },
- "rules_rust_bindgen__syn-2.0.18": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/syn/2.0.18/download"
- ],
- "strip_prefix": "syn-2.0.18",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.syn-2.0.18.bazel"
- }
- },
- "rules_rust_proto__tokio-io-0.1.13": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-io/0.1.13/download"
- ],
- "strip_prefix": "tokio-io-0.1.13",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-io-0.1.13.bazel"
- }
- },
- "cui__gix-utils-0.1.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b85d89dc728613e26e0ed952a19583744e7f5240fcd4aa30d6c824ffd8b52f0f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-utils/0.1.5/download"
- ],
- "strip_prefix": "gix-utils-0.1.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-utils-0.1.5.bazel"
- }
- },
- "rules_rust_wasm_bindgen__unicase-2.6.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicase/2.6.0/download"
- ],
- "strip_prefix": "unicase-2.6.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.unicase-2.6.0.bazel"
- }
- },
- "rules_rust_bindgen__cc-1.0.79": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cc/1.0.79/download"
- ],
- "strip_prefix": "cc-1.0.79",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.cc-1.0.79.bazel"
- }
- },
- "rrra__unicode-ident-1.0.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-ident/1.0.10/download"
- ],
- "strip_prefix": "unicode-ident-1.0.10",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.unicode-ident-1.0.10.bazel"
- }
- },
- "rules_rust_proto__crossbeam-epoch-0.8.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-epoch/0.8.2/download"
- ],
- "strip_prefix": "crossbeam-epoch-0.8.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.crossbeam-epoch-0.8.2.bazel"
- }
- },
- "cui__clap_lex-0.5.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap_lex/0.5.0/download"
- ],
- "strip_prefix": "clap_lex-0.5.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.clap_lex-0.5.0.bazel"
- }
- },
- "cui__indexmap-2.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/indexmap/2.1.0/download"
- ],
- "strip_prefix": "indexmap-2.1.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.indexmap-2.1.0.bazel"
- }
- },
- "cui__hex-0.4.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hex/0.4.3/download"
- ],
- "strip_prefix": "hex-0.4.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.hex-0.4.3.bazel"
- }
- },
- "rules_rust_prost__quote-1.0.28": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/quote/1.0.28/download"
- ],
- "strip_prefix": "quote-1.0.28",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.quote-1.0.28.bazel"
- }
- },
- "rules_rust_wasm_bindgen__windows-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows/0.48.0/download"
- ],
- "strip_prefix": "windows-0.48.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows-0.48.0.bazel"
- }
- },
- "rules_rust_proto__bitflags-1.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bitflags/1.3.2/download"
- ],
- "strip_prefix": "bitflags-1.3.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__unicode-normalization-0.1.22": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-normalization/0.1.22/download"
- ],
- "strip_prefix": "unicode-normalization-0.1.22",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.unicode-normalization-0.1.22.bazel"
- }
- },
- "cargo_bazel.buildifier-linux-arm64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_file",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/buildtools/releases/download/5.0.1/buildifier-linux-arm64"
- ],
- "sha256": "c657c628fca72b7e0446f1a542231722a10ba4321597bd6f6249a5da6060b6ff",
- "downloaded_file_path": "buildifier.exe",
- "executable": true
- }
- },
- "rules_rust_wasm_bindgen__anyhow-1.0.71": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anyhow/1.0.71/download"
- ],
- "strip_prefix": "anyhow-1.0.71",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.anyhow-1.0.71.bazel"
- }
- },
- "rules_rust_bindgen__memchr-2.5.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/memchr/2.5.0/download"
- ],
- "strip_prefix": "memchr-2.5.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.memchr-2.5.0.bazel"
- }
- },
- "rules_rust_prost__parking_lot_core-0.9.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/parking_lot_core/0.9.8/download"
- ],
- "strip_prefix": "parking_lot_core-0.9.8",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.parking_lot_core-0.9.8.bazel"
- }
- },
- "rules_rust_proto__bytes-0.4.12": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bytes/0.4.12/download"
- ],
- "strip_prefix": "bytes-0.4.12",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.bytes-0.4.12.bazel"
- }
- },
- "cui__iana-time-zone-0.1.57": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/iana-time-zone/0.1.57/download"
- ],
- "strip_prefix": "iana-time-zone-0.1.57",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.iana-time-zone-0.1.57.bazel"
- }
- },
- "cui__toml_edit-0.19.13": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5f8751d9c1b03c6500c387e96f81f815a4f8e72d142d2d4a9ffa6fedd51ddee7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/toml_edit/0.19.13/download"
- ],
- "strip_prefix": "toml_edit-0.19.13",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.toml_edit-0.19.13.bazel"
- }
- },
- "rules_rust_prost__matchit-0.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/matchit/0.7.0/download"
- ],
- "strip_prefix": "matchit-0.7.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.matchit-0.7.0.bazel"
- }
- },
- "cui__gix-chunk-0.4.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5b42ea64420f7994000130328f3c7a2038f639120518870436d31b8bde704493",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-chunk/0.4.4/download"
- ],
- "strip_prefix": "gix-chunk-0.4.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-chunk-0.4.4.bazel"
- }
- },
- "rules_rust_prost__sync_wrapper-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/sync_wrapper/0.1.2/download"
- ],
- "strip_prefix": "sync_wrapper-0.1.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.sync_wrapper-0.1.2.bazel"
- }
- },
- "cui__idna-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/idna/0.4.0/download"
- ],
- "strip_prefix": "idna-0.4.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.idna-0.4.0.bazel"
- }
- },
- "cui__wasm-bindgen-macro-support-0.2.87": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-macro-support/0.2.87/download"
- ],
- "strip_prefix": "wasm-bindgen-macro-support-0.2.87",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.wasm-bindgen-macro-support-0.2.87.bazel"
- }
- },
- "rules_rust_wasm_bindgen__errno-dragonfly-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno-dragonfly/0.1.2/download"
- ],
- "strip_prefix": "errno-dragonfly-0.1.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.errno-dragonfly-0.1.2.bazel"
- }
- },
- "rules_rust_prost__hyper-timeout-0.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hyper-timeout/0.4.1/download"
- ],
- "strip_prefix": "hyper-timeout-0.4.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.hyper-timeout-0.4.1.bazel"
- }
- },
- "rules_rust_bindgen__rustc-hash-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustc-hash/1.1.0/download"
- ],
- "strip_prefix": "rustc-hash-1.1.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.rustc-hash-1.1.0.bazel"
- }
- },
- "rules_rust_prost__http-0.2.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/http/0.2.9/download"
- ],
- "strip_prefix": "http-0.2.9",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.http-0.2.9.bazel"
- }
- },
- "cui__crossbeam-epoch-0.9.15": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-epoch/0.9.15/download"
- ],
- "strip_prefix": "crossbeam-epoch-0.9.15",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.crossbeam-epoch-0.9.15.bazel"
- }
- },
- "cui__gix-config-value-0.14.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ea7505b97f4d8e7933e29735a568ba2f86d8de466669d9f0e8321384f9972f47",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-config-value/0.14.0/download"
- ],
- "strip_prefix": "gix-config-value-0.14.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-config-value-0.14.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__chrono-0.4.26": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/chrono/0.4.26/download"
- ],
- "strip_prefix": "chrono-0.4.26",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.chrono-0.4.26.bazel"
- }
- },
- "cui__same-file-1.0.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/same-file/1.0.6/download"
- ],
- "strip_prefix": "same-file-1.0.6",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.same-file-1.0.6.bazel"
- }
- },
- "cui__linux-raw-sys-0.3.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/linux-raw-sys/0.3.8/download"
- ],
- "strip_prefix": "linux-raw-sys-0.3.8",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.linux-raw-sys-0.3.8.bazel"
- }
- },
- "rules_rust_bindgen__termcolor-1.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/termcolor/1.2.0/download"
- ],
- "strip_prefix": "termcolor-1.2.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.termcolor-1.2.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rand_core-0.6.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand_core/0.6.4/download"
- ],
- "strip_prefix": "rand_core-0.6.4",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rand_core-0.6.4.bazel"
- }
- },
- "cui__crossbeam-channel-0.5.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-channel/0.5.8/download"
- ],
- "strip_prefix": "crossbeam-channel-0.5.8",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.crossbeam-channel-0.5.8.bazel"
- }
- },
- "cui__cc-1.0.79": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cc/1.0.79/download"
- ],
- "strip_prefix": "cc-1.0.79",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.cc-1.0.79.bazel"
- }
- },
- "rules_rust_prost__rand-0.8.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand/0.8.5/download"
- ],
- "strip_prefix": "rand-0.8.5",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.rand-0.8.5.bazel"
- }
- },
- "cui__gix-validate-0.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e05cab2b03a45b866156e052aa38619f4ece4adcb2f79978bfc249bc3b21b8c5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-validate/0.8.0/download"
- ],
- "strip_prefix": "gix-validate-0.8.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-validate-0.8.0.bazel"
- }
- },
- "rules_rust_prost__anyhow-1.0.71": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anyhow/1.0.71/download"
- ],
- "strip_prefix": "anyhow-1.0.71",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.anyhow-1.0.71.bazel"
- }
- },
- "cui__is-terminal-0.4.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/is-terminal/0.4.7/download"
- ],
- "strip_prefix": "is-terminal-0.4.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.is-terminal-0.4.7.bazel"
- }
- },
- "cui__unicode-width-0.1.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-width/0.1.10/download"
- ],
- "strip_prefix": "unicode-width-0.1.10",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unicode-width-0.1.10.bazel"
- }
- },
- "rrra__humantime-2.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/humantime/2.1.0/download"
- ],
- "strip_prefix": "humantime-2.1.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.humantime-2.1.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__libc-0.2.150": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/libc/0.2.150/download"
- ],
- "strip_prefix": "libc-0.2.150",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.libc-0.2.150.bazel"
- }
- },
- "rules_rust_bindgen__env_logger-0.10.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/env_logger/0.10.0/download"
- ],
- "strip_prefix": "env_logger-0.10.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.env_logger-0.10.0.bazel"
- }
- },
- "cui__toml-0.8.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/toml/0.8.10/download"
- ],
- "strip_prefix": "toml-0.8.10",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.toml-0.8.10.bazel"
- }
- },
- "rules_rust_prost__tracing-attributes-0.1.26": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tracing-attributes/0.1.26/download"
- ],
- "strip_prefix": "tracing-attributes-0.1.26",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tracing-attributes-0.1.26.bazel"
- }
- },
- "rules_rust_prost__instant-0.1.12": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/instant/0.1.12/download"
- ],
- "strip_prefix": "instant-0.1.12",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.instant-0.1.12.bazel"
- }
- },
- "rules_rust_wasm_bindgen__indexmap-2.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/indexmap/2.0.0/download"
- ],
- "strip_prefix": "indexmap-2.0.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.indexmap-2.0.0.bazel"
- }
- },
- "cui__windows_i686_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_gnu/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_gnu-0.48.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows_i686_gnu-0.48.0.bazel"
- }
- },
- "rrra__proc-macro2-1.0.64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/proc-macro2/1.0.64/download"
- ],
- "strip_prefix": "proc-macro2-1.0.64",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.proc-macro2-1.0.64.bazel"
- }
- },
- "rules_rust_wasm_bindgen__predicates-tree-1.0.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/predicates-tree/1.0.9/download"
- ],
- "strip_prefix": "predicates-tree-1.0.9",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.predicates-tree-1.0.9.bazel"
- }
- },
- "rrra__errno-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno/0.3.1/download"
- ],
- "strip_prefix": "errno-0.3.1",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.errno-0.3.1.bazel"
- }
- },
- "cui__num_threads-0.1.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num_threads/0.1.6/download"
- ],
- "strip_prefix": "num_threads-0.1.6",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.num_threads-0.1.6.bazel"
+ "strip_prefix": "anyhow-1.0.89",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.anyhow-1.0.89.bazel"
}
},
"cui__arc-swap-1.6.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"sha256": "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6",
"type": "tar.gz",
@@ -8311,2983 +911,23 @@
"https://static.crates.io/crates/arc-swap/1.6.0/download"
],
"strip_prefix": "arc-swap-1.6.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.arc-swap-1.6.0.bazel"
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.arc-swap-1.6.0.bazel"
}
},
- "rules_rust_proto__tokio-uds-0.2.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__arrayvec-0.7.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0",
+ "sha256": "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/tokio-uds/0.2.7/download"
+ "https://static.crates.io/crates/arrayvec/0.7.4/download"
],
- "strip_prefix": "tokio-uds-0.2.7",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-uds-0.2.7.bazel"
+ "strip_prefix": "arrayvec-0.7.4",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.arrayvec-0.7.4.bazel"
}
},
- "rules_rust_wasm_bindgen__webpki-roots-0.25.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/webpki-roots/0.25.2/download"
- ],
- "strip_prefix": "webpki-roots-0.25.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.webpki-roots-0.25.2.bazel"
- }
- },
- "cui__gix-features-0.35.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9b9ff423ae4983f762659040d13dd7a5defbd54b6a04ac3cc7347741cec828cd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-features/0.35.0/download"
- ],
- "strip_prefix": "gix-features-0.35.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-features-0.35.0.bazel"
- }
- },
- "cui__lock_api-0.4.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/lock_api/0.4.11/download"
- ],
- "strip_prefix": "lock_api-0.4.11",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.lock_api-0.4.11.bazel"
- }
- },
- "cui__android-tzdata-0.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/android-tzdata/0.1.1/download"
- ],
- "strip_prefix": "android-tzdata-0.1.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.android-tzdata-0.1.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__winapi-i686-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "rules_rust_prost__futures-task-0.3.28": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/futures-task/0.3.28/download"
- ],
- "strip_prefix": "futures-task-0.3.28",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.futures-task-0.3.28.bazel"
- }
- },
- "cui__serde-1.0.190": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde/1.0.190/download"
- ],
- "strip_prefix": "serde-1.0.190",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.serde-1.0.190.bazel"
- }
- },
- "rules_rust_wasm_bindgen__ascii-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ascii/1.1.0/download"
- ],
- "strip_prefix": "ascii-1.1.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.ascii-1.1.0.bazel"
- }
- },
- "rules_rust_prost__prost-types-0.11.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/prost-types/0.11.9/download"
- ],
- "strip_prefix": "prost-types-0.11.9",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.prost-types-0.11.9.bazel"
- }
- },
- "rules_rust_wasm_bindgen__bstr-0.2.17": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bstr/0.2.17/download"
- ],
- "strip_prefix": "bstr-0.2.17",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.bstr-0.2.17.bazel"
- }
- },
- "rules_rust_proto__rustc_version-0.2.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustc_version/0.2.3/download"
- ],
- "strip_prefix": "rustc_version-0.2.3",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.rustc_version-0.2.3.bazel"
- }
- },
- "cui__aho-corasick-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/aho-corasick/1.0.2/download"
- ],
- "strip_prefix": "aho-corasick-1.0.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.aho-corasick-1.0.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__tinyvec-1.6.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tinyvec/1.6.0/download"
- ],
- "strip_prefix": "tinyvec-1.6.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.tinyvec-1.6.0.bazel"
- }
- },
- "rules_rust_bindgen__windows_x86_64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_msvc-0.48.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.windows_x86_64_msvc-0.48.0.bazel"
- }
- },
- "rules_rust_proto__winapi-x86_64-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "cui__winnow-0.5.18": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "176b6138793677221d420fd2f0aeeced263f197688b36484660da767bca2fa32",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winnow/0.5.18/download"
- ],
- "strip_prefix": "winnow-0.5.18",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.winnow-0.5.18.bazel"
- }
- },
- "rules_rust_wasm_bindgen__crossbeam-utils-0.8.16": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-utils/0.8.16/download"
- ],
- "strip_prefix": "crossbeam-utils-0.8.16",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.crossbeam-utils-0.8.16.bazel"
- }
- },
- "cui__memchr-2.6.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/memchr/2.6.4/download"
- ],
- "strip_prefix": "memchr-2.6.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.memchr-2.6.4.bazel"
- }
- },
- "rrra__serde_derive-1.0.171": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde_derive/1.0.171/download"
- ],
- "strip_prefix": "serde_derive-1.0.171",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.serde_derive-1.0.171.bazel"
- }
- },
- "cui__bitflags-2.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bitflags/2.4.1/download"
- ],
- "strip_prefix": "bitflags-2.4.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.bitflags-2.4.1.bazel"
- }
- },
- "rrra__windows_aarch64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.48.0.bazel"
- }
- },
- "rules_rust_prost__pin-project-lite-0.2.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/pin-project-lite/0.2.9/download"
- ],
- "strip_prefix": "pin-project-lite-0.2.9",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.pin-project-lite-0.2.9.bazel"
- }
- },
- "rules_rust_proto__void-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/void/1.0.2/download"
- ],
- "strip_prefix": "void-1.0.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.void-1.0.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "806a045c4ec4ef7c3ad86dc27bcb641b84d9eeb3846200f56d7ab0885241d654",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-cli-support/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-cli-support-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-cli-support-0.2.91.bazel"
- }
- },
- "rules_rust_prost__regex-syntax-0.7.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-syntax/0.7.2/download"
- ],
- "strip_prefix": "regex-syntax-0.7.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.regex-syntax-0.7.2.bazel"
- }
- },
- "cui__pest_generator-2.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/pest_generator/2.7.0/download"
- ],
- "strip_prefix": "pest_generator-2.7.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.pest_generator-2.7.0.bazel"
- }
- },
- "cui__chrono-tz-0.8.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e23185c0e21df6ed832a12e2bda87c7d1def6842881fb634a8511ced741b0d76",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/chrono-tz/0.8.4/download"
- ],
- "strip_prefix": "chrono-tz-0.8.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.chrono-tz-0.8.4.bazel"
- }
- },
- "cross_x86_64-pc-windows-msvc": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/rust-embedded/cross/releases/download/v0.2.1/cross-v0.2.1-x86_64-pc-windows-msvc.tar.gz"
- ],
- "sha256": "3af59ff5a2229f92b54df937c50a9a88c96dffc8ac3dde520a38fdf046d656c4",
- "build_file_content": "exports_files(glob([\"**\"]), visibility = [\"//visibility:public\"])"
- }
- },
- "rules_rust_prost__heck": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8",
- "type": "tar.gz",
- "urls": [
- "https://crates.io/api/v1/crates/heck/0.4.1/download"
- ],
- "strip_prefix": "heck-0.4.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.heck-0.4.1.bazel"
- }
- },
- "rules_rust_prost__prost-build-0.11.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/prost-build/0.11.9/download"
- ],
- "strip_prefix": "prost-build-0.11.9",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.prost-build-0.11.9.bazel"
- }
- },
- "cui__gix-discover-0.25.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "69507643d75a0ea9a402fcf73ced517d2b95cc95385904ac09d03e0b952fde33",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-discover/0.25.0/download"
- ],
- "strip_prefix": "gix-discover-0.25.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-discover-0.25.0.bazel"
- }
- },
- "rules_rust_proto__libc-0.2.139": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/libc/0.2.139/download"
- ],
- "strip_prefix": "libc-0.2.139",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.libc-0.2.139.bazel"
- }
- },
- "cui__unic-common-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unic-common/0.9.0/download"
- ],
- "strip_prefix": "unic-common-0.9.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unic-common-0.9.0.bazel"
- }
- },
- "rules_rust_prost__tower-0.4.13": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tower/0.4.13/download"
- ],
- "strip_prefix": "tower-0.4.13",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tower-0.4.13.bazel"
- }
- },
- "rules_rust_wasm_bindgen__bitflags-1.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bitflags/1.3.2/download"
- ],
- "strip_prefix": "bitflags-1.3.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
- }
- },
- "cui__utf8parse-0.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/utf8parse/0.2.1/download"
- ],
- "strip_prefix": "utf8parse-0.2.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.utf8parse-0.2.1.bazel"
- }
- },
- "rules_rust_tinyjson": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a",
- "url": "https://crates.io/api/v1/crates/tinyjson/2.5.1/download",
- "strip_prefix": "tinyjson-2.5.1",
- "type": "tar.gz",
- "build_file": "@@rules_rust~//util/process_wrapper:BUILD.tinyjson.bazel"
- }
- },
- "rules_rust_wasm_bindgen__bumpalo-3.13.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bumpalo/3.13.0/download"
- ],
- "strip_prefix": "bumpalo-3.13.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.bumpalo-3.13.0.bazel"
- }
- },
- "cui__pin-project-lite-0.2.13": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/pin-project-lite/0.2.13/download"
- ],
- "strip_prefix": "pin-project-lite-0.2.13",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.pin-project-lite-0.2.13.bazel"
- }
- },
- "cui__generic-array-0.14.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/generic-array/0.14.7/download"
- ],
- "strip_prefix": "generic-array-0.14.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.generic-array-0.14.7.bazel"
- }
- },
- "cross_x86_64-unknown-linux-gnu": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "urls": [
- "https://github.com/rust-embedded/cross/releases/download/v0.2.1/cross-v0.2.1-x86_64-unknown-linux-gnu.tar.gz"
- ],
- "sha256": "06dcce3248488e95fbb368d14bef17fa8e77461d5055fbd5193538574820f413",
- "build_file_content": "exports_files(glob([\"**\"]), visibility = [\"//visibility:public\"])"
- }
- },
- "rules_rust_proto__tokio-codec-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-codec/0.1.2/download"
- ],
- "strip_prefix": "tokio-codec-0.1.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-codec-0.1.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__ureq-2.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ureq/2.8.0/download"
- ],
- "strip_prefix": "ureq-2.8.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.ureq-2.8.0.bazel"
- }
- },
- "cui__parking_lot_core-0.9.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/parking_lot_core/0.9.9/download"
- ],
- "strip_prefix": "parking_lot_core-0.9.9",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.parking_lot_core-0.9.9.bazel"
- }
- },
- "cui__core-foundation-sys-0.8.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/core-foundation-sys/0.8.4/download"
- ],
- "strip_prefix": "core-foundation-sys-0.8.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.core-foundation-sys-0.8.4.bazel"
- }
- },
- "rrra__quote-1.0.29": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/quote/1.0.29/download"
- ],
- "strip_prefix": "quote-1.0.29",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.quote-1.0.29.bazel"
- }
- },
- "rules_rust_proto__protobuf-2.8.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "patch_args": [
- "-p1"
- ],
- "patches": [
- "@@rules_rust~//proto/protobuf/3rdparty/patches:protobuf-2.8.2.patch"
- ],
- "sha256": "70731852eec72c56d11226c8a5f96ad5058a3dab73647ca5f7ee351e464f2571",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/protobuf/2.8.2/download"
- ],
- "strip_prefix": "protobuf-2.8.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.protobuf-2.8.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-shared/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-shared-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-shared-0.2.91.bazel"
- }
- },
- "rules_rust_wasm_bindgen__httpdate-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/httpdate/1.0.2/download"
- ],
- "strip_prefix": "httpdate-1.0.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.httpdate-1.0.2.bazel"
- }
- },
- "cui__gix-object-0.37.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1e7e19616c67967374137bae83e950e9b518a9ea8a605069bd6716ada357fd6f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-object/0.37.0/download"
- ],
- "strip_prefix": "gix-object-0.37.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-object-0.37.0.bazel"
- }
- },
- "cui__crossbeam-queue-0.3.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-queue/0.3.8/download"
- ],
- "strip_prefix": "crossbeam-queue-0.3.8",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.crossbeam-queue-0.3.8.bazel"
- }
- },
- "rules_rust_wasm_bindgen__iana-time-zone-0.1.57": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/iana-time-zone/0.1.57/download"
- ],
- "strip_prefix": "iana-time-zone-0.1.57",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.iana-time-zone-0.1.57.bazel"
- }
- },
- "rules_rust_bindgen__winapi-x86_64-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "cui__deunicode-0.4.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/deunicode/0.4.3/download"
- ],
- "strip_prefix": "deunicode-0.4.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.deunicode-0.4.3.bazel"
- }
- },
- "cui__wasm-bindgen-macro-0.2.87": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-macro/0.2.87/download"
- ],
- "strip_prefix": "wasm-bindgen-macro-0.2.87",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.wasm-bindgen-macro-0.2.87.bazel"
- }
- },
- "rules_rust_prost__pin-utils-0.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/pin-utils/0.1.0/download"
- ],
- "strip_prefix": "pin-utils-0.1.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.pin-utils-0.1.0.bazel"
- }
- },
- "cui__gix-hashtable-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "409268480841ad008e81c17ca5a293393fbf9f2b6c2f85b8ab9de1f0c5176a16",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-hashtable/0.4.0/download"
- ],
- "strip_prefix": "gix-hashtable-0.4.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-hashtable-0.4.0.bazel"
- }
- },
- "rules_rust_bindgen__errno-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno/0.3.1/download"
- ],
- "strip_prefix": "errno-0.3.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.errno-0.3.1.bazel"
- }
- },
- "cui__fnv-1.0.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fnv/1.0.7/download"
- ],
- "strip_prefix": "fnv-1.0.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.fnv-1.0.7.bazel"
- }
- },
- "cui__js-sys-0.3.64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/js-sys/0.3.64/download"
- ],
- "strip_prefix": "js-sys-0.3.64",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.js-sys-0.3.64.bazel"
- }
- },
- "rules_rust_toolchain_test_target_json": {
- "bzlFile": "@@rules_rust~//test/unit/toolchain:toolchain_test_utils.bzl",
- "ruleClassName": "rules_rust_toolchain_test_target_json_repository",
- "attributes": {
- "target_json": "@@rules_rust~//test/unit/toolchain:toolchain-test-triple.json"
- }
- },
- "rules_rust_bindgen__once_cell-1.18.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/once_cell/1.18.0/download"
- ],
- "strip_prefix": "once_cell-1.18.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.once_cell-1.18.0.bazel"
- }
- },
- "rules_rust_prost__prost-0.11.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/prost/0.11.9/download"
- ],
- "strip_prefix": "prost-0.11.9",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.prost-0.11.9.bazel"
- }
- },
- "rules_rust_proto__slab-0.3.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/slab/0.3.0/download"
- ],
- "strip_prefix": "slab-0.3.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.slab-0.3.0.bazel"
- }
- },
- "rules_rust_prost__rand_core-0.6.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand_core/0.6.4/download"
- ],
- "strip_prefix": "rand_core-0.6.4",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.rand_core-0.6.4.bazel"
- }
- },
- "rules_rust_bindgen__bitflags-1.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bitflags/1.3.2/download"
- ],
- "strip_prefix": "bitflags-1.3.2",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
- }
- },
- "cui__wasi-0.11.0-wasi-snapshot-preview1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasi/0.11.0+wasi-snapshot-preview1/download"
- ],
- "strip_prefix": "wasi-0.11.0+wasi-snapshot-preview1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.wasi-0.11.0+wasi-snapshot-preview1.bazel"
- }
- },
- "rules_rust_bindgen__windows_i686_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_gnu/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_gnu-0.48.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.windows_i686_gnu-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__alloc-no-stdlib-2.0.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/alloc-no-stdlib/2.0.4/download"
- ],
- "strip_prefix": "alloc-no-stdlib-2.0.4",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.alloc-no-stdlib-2.0.4.bazel"
- }
- },
- "rules_rust_wasm_bindgen__env_logger-0.8.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/env_logger/0.8.4/download"
- ],
- "strip_prefix": "env_logger-0.8.4",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.env_logger-0.8.4.bazel"
- }
- },
- "cui__smol_str-0.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/smol_str/0.2.0/download"
- ],
- "strip_prefix": "smol_str-0.2.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.smol_str-0.2.0.bazel"
- }
- },
- "cui__memoffset-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/memoffset/0.9.0/download"
- ],
- "strip_prefix": "memoffset-0.9.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.memoffset-0.9.0.bazel"
- }
- },
- "cui__log-0.4.19": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/log/0.4.19/download"
- ],
- "strip_prefix": "log-0.4.19",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.log-0.4.19.bazel"
- }
- },
- "cui__wasm-bindgen-backend-0.2.87": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-backend/0.2.87/download"
- ],
- "strip_prefix": "wasm-bindgen-backend-0.2.87",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.wasm-bindgen-backend-0.2.87.bazel"
- }
- },
- "cui__pest-2.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/pest/2.7.0/download"
- ],
- "strip_prefix": "pest-2.7.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.pest-2.7.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__docopt-1.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/docopt/1.1.1/download"
- ],
- "strip_prefix": "docopt-1.1.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.docopt-1.1.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rustc-demangle-0.1.23": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustc-demangle/0.1.23/download"
- ],
- "strip_prefix": "rustc-demangle-0.1.23",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rustc-demangle-0.1.23.bazel"
- }
- },
- "rules_rust_prost__rand_chacha-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand_chacha/0.3.1/download"
- ],
- "strip_prefix": "rand_chacha-0.3.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.rand_chacha-0.3.1.bazel"
- }
- },
- "cui__syn-1.0.109": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/syn/1.0.109/download"
- ],
- "strip_prefix": "syn-1.0.109",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.syn-1.0.109.bazel"
- }
- },
- "cui__pathdiff-0.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/pathdiff/0.2.1/download"
- ],
- "strip_prefix": "pathdiff-0.2.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.pathdiff-0.2.1.bazel"
- }
- },
- "cargo_bazel.buildifier-linux-amd64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_file",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/buildtools/releases/download/5.0.1/buildifier-linux-amd64"
- ],
- "sha256": "3ed7358c7c6a1ca216dc566e9054fd0b97a1482cb0b7e61092be887d42615c5d",
- "downloaded_file_path": "buildifier.exe",
- "executable": true
- }
- },
- "rules_rust_wasm_bindgen__either-1.8.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/either/1.8.1/download"
- ],
- "strip_prefix": "either-1.8.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.either-1.8.1.bazel"
- }
- },
- "rules_rust_prost__windows_aarch64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_msvc-0.48.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.windows_aarch64_msvc-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__crc32fast-1.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crc32fast/1.3.2/download"
- ],
- "strip_prefix": "crc32fast-1.3.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.crc32fast-1.3.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-backend-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-backend/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-backend-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-backend-0.2.91.bazel"
- }
- },
- "cui__encoding_rs-0.8.33": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/encoding_rs/0.8.33/download"
- ],
- "strip_prefix": "encoding_rs-0.8.33",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.encoding_rs-0.8.33.bazel"
- }
- },
- "rules_rust_prost__windows_i686_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_msvc-0.48.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.windows_i686_msvc-0.48.0.bazel"
- }
- },
- "rules_rust_proto__hermit-abi-0.2.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hermit-abi/0.2.6/download"
- ],
- "strip_prefix": "hermit-abi-0.2.6",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.hermit-abi-0.2.6.bazel"
- }
- },
- "rules_rust_prost__want-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/want/0.3.1/download"
- ],
- "strip_prefix": "want-0.3.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.want-0.3.1.bazel"
- }
- },
- "cui__gix-glob-0.13.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a9d76e85f11251dcf751d2c5e918a14f562db5be6f727fd24775245653e9b19d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-glob/0.13.0/download"
- ],
- "strip_prefix": "gix-glob-0.13.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-glob-0.13.0.bazel"
- }
- },
- "rules_rust_proto__tokio-timer-0.2.13": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-timer/0.2.13/download"
- ],
- "strip_prefix": "tokio-timer-0.2.13",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-timer-0.2.13.bazel"
- }
- },
- "cui__itoa-1.0.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/itoa/1.0.8/download"
- ],
- "strip_prefix": "itoa-1.0.8",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.itoa-1.0.8.bazel"
- }
- },
- "rules_rust_proto__cloudabi-0.0.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cloudabi/0.0.3/download"
- ],
- "strip_prefix": "cloudabi-0.0.3",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.cloudabi-0.0.3.bazel"
- }
- },
- "cui__serde_json-1.0.108": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde_json/1.0.108/download"
- ],
- "strip_prefix": "serde_json-1.0.108",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.serde_json-1.0.108.bazel"
- }
- },
- "rules_rust_bindgen__log-0.4.19": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/log/0.4.19/download"
- ],
- "strip_prefix": "log-0.4.19",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.log-0.4.19.bazel"
- }
- },
- "rules_rust_wasm_bindgen__termcolor-1.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/termcolor/1.2.0/download"
- ],
- "strip_prefix": "termcolor-1.2.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.termcolor-1.2.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__hermit-abi-0.1.19": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hermit-abi/0.1.19/download"
- ],
- "strip_prefix": "hermit-abi-0.1.19",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.hermit-abi-0.1.19.bazel"
- }
- },
- "cui__bstr-1.6.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/bstr/1.6.0/download"
- ],
- "strip_prefix": "bstr-1.6.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.bstr-1.6.0.bazel"
- }
- },
- "cui__gix-diff-0.36.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "788ddb152c388206e81f36bcbb574e7ed7827c27d8fa62227b34edc333d8928c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-diff/0.36.0/download"
- ],
- "strip_prefix": "gix-diff-0.36.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-diff-0.36.0.bazel"
- }
- },
- "cui__gix-index-0.25.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f54d63a9d13c13088f41f5a3accbec284e492ac8f4f707fcc307c139622e17b7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-index/0.25.0/download"
- ],
- "strip_prefix": "gix-index-0.25.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-index-0.25.0.bazel"
- }
- },
- "rules_rust_prost__windows_i686_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_gnu/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_gnu-0.48.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.windows_i686_gnu-0.48.0.bazel"
- }
- },
- "rules_rust_proto__lock_api-0.3.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/lock_api/0.3.4/download"
- ],
- "strip_prefix": "lock_api-0.3.4",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.lock_api-0.3.4.bazel"
- }
- },
- "cui__filetime-0.2.22": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/filetime/0.2.22/download"
- ],
- "strip_prefix": "filetime-0.2.22",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.filetime-0.2.22.bazel"
- }
- },
- "cui__tracing-log-0.1.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tracing-log/0.1.4/download"
- ],
- "strip_prefix": "tracing-log-0.1.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.tracing-log-0.1.4.bazel"
- }
- },
- "cui__rustix-0.38.21": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustix/0.38.21/download"
- ],
- "strip_prefix": "rustix-0.38.21",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rustix-0.38.21.bazel"
- }
- },
- "cui__indoc-2.0.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/indoc/2.0.4/download"
- ],
- "strip_prefix": "indoc-2.0.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.indoc-2.0.4.bazel"
- }
- },
- "cui__unicode-bom-2.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "98e90c70c9f0d4d1ee6d0a7d04aa06cb9bbd53d8cfbdd62a0269a7c2eb640552",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-bom/2.0.2/download"
- ],
- "strip_prefix": "unicode-bom-2.0.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unicode-bom-2.0.2.bazel"
- }
- },
- "cui__smallvec-1.11.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/smallvec/1.11.0/download"
- ],
- "strip_prefix": "smallvec-1.11.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.smallvec-1.11.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__redox_syscall-0.3.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/redox_syscall/0.3.5/download"
- ],
- "strip_prefix": "redox_syscall-0.3.5",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.redox_syscall-0.3.5.bazel"
- }
- },
- "cui__ignore-0.4.18": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ignore/0.4.18/download"
- ],
- "strip_prefix": "ignore-0.4.18",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.ignore-0.4.18.bazel"
- }
- },
- "cui__textwrap-0.16.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/textwrap/0.16.0/download"
- ],
- "strip_prefix": "textwrap-0.16.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.textwrap-0.16.0.bazel"
- }
- },
- "rules_rust_bindgen__winapi-i686-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-wasm-conventions-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4e6b653f6820409609bda0f176e6949302307af7a7b9479cd4d4b1bdc31eb9cd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-wasm-conventions/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-wasm-conventions-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-wasm-conventions-0.2.91.bazel"
- }
- },
- "cui__valuable-0.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/valuable/0.1.0/download"
- ],
- "strip_prefix": "valuable-0.1.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.valuable-0.1.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__form_urlencoded-1.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/form_urlencoded/1.2.0/download"
- ],
- "strip_prefix": "form_urlencoded-1.2.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.form_urlencoded-1.2.0.bazel"
- }
- },
- "rules_rust_proto__cfg-if-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cfg-if/1.0.0/download"
- ],
- "strip_prefix": "cfg-if-1.0.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
- }
- },
- "rules_rust_proto__tokio-core-0.1.18": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-core/0.1.18/download"
- ],
- "strip_prefix": "tokio-core-0.1.18",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-core-0.1.18.bazel"
- }
- },
- "rules_rust_prost__prost-derive-0.11.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/prost-derive/0.11.9/download"
- ],
- "strip_prefix": "prost-derive-0.11.9",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.prost-derive-0.11.9.bazel"
- }
- },
- "cui__wasm-bindgen-shared-0.2.87": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-shared/0.2.87/download"
- ],
- "strip_prefix": "wasm-bindgen-shared-0.2.87",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.wasm-bindgen-shared-0.2.87.bazel"
- }
- },
- "rules_rust_proto__crossbeam-utils-0.7.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-utils/0.7.2/download"
- ],
- "strip_prefix": "crossbeam-utils-0.7.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.crossbeam-utils-0.7.2.bazel"
- }
- },
- "cui__spectral-0.6.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ae3c15181f4b14e52eeaac3efaeec4d2764716ce9c86da0c934c3e318649c5ba",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/spectral/0.6.0/download"
- ],
- "strip_prefix": "spectral-0.6.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.spectral-0.6.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__float-cmp-0.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/float-cmp/0.8.0/download"
- ],
- "strip_prefix": "float-cmp-0.8.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.float-cmp-0.8.0.bazel"
- }
- },
- "cui__gix-tempfile-10.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5ae0978f3e11dc57290ee75ac2477c815bca1ce2fa7ed5dc5f16db067410ac4d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-tempfile/10.0.0/download"
- ],
- "strip_prefix": "gix-tempfile-10.0.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-tempfile-10.0.0.bazel"
- }
- },
- "rules_rust_prost__tower-layer-0.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tower-layer/0.3.2/download"
- ],
- "strip_prefix": "tower-layer-0.3.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tower-layer-0.3.2.bazel"
- }
- },
- "cui__cfg-expr-0.15.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cfg-expr/0.15.5/download"
- ],
- "strip_prefix": "cfg-expr-0.15.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.cfg-expr-0.15.5.bazel"
- }
- },
- "cui__prodash-26.2.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "794b5bf8e2d19b53dcdcec3e4bba628e20f5b6062503ba89281fa7037dd7bbcf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/prodash/26.2.2/download"
- ],
- "strip_prefix": "prodash-26.2.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.prodash-26.2.2.bazel"
- }
- },
- "cui__winapi-i686-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "cui__gix-0.54.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ad6d32e74454459690d57d18ea4ebec1629936e6b130b51d12cb4a81630ac953",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix/0.54.1/download"
- ],
- "strip_prefix": "gix-0.54.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-0.54.1.bazel"
- }
- },
- "cui__gix-command-0.2.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3c576cfbf577f72c097b5f88aedea502cd62952bdc1fb3adcab4531d5525a4c7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-command/0.2.10/download"
- ],
- "strip_prefix": "gix-command-0.2.10",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-command-0.2.10.bazel"
- }
- },
- "rules_rust_wasm_bindgen__tinyvec_macros-0.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tinyvec_macros/0.1.1/download"
- ],
- "strip_prefix": "tinyvec_macros-0.1.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.tinyvec_macros-0.1.1.bazel"
- }
- },
- "cui__gix-odb-0.53.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "8d6a392c6ba3a2f133cdc63120e9bc7aec81eef763db372c817de31febfe64bf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-odb/0.53.0/download"
- ],
- "strip_prefix": "gix-odb-0.53.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-odb-0.53.0.bazel"
- }
- },
- "rules_rust_bindgen__rustix-0.37.20": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustix/0.37.20/download"
- ],
- "strip_prefix": "rustix-0.37.20",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.rustix-0.37.20.bazel"
- }
- },
- "rules_rust_bindgen__windows_i686_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_msvc-0.48.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.windows_i686_msvc-0.48.0.bazel"
- }
- },
- "rules_rust_bindgen__clap_builder-4.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap_builder/4.3.3/download"
- ],
- "strip_prefix": "clap_builder-4.3.3",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.clap_builder-4.3.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen_cli": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "80b674e1bda34888e132276ba600676cea158bdcd289bb7da5c25885f1a3a535",
- "urls": [
- "https://crates.io/api/v1/crates/wasm-bindgen-cli/0.2.91/download"
- ],
- "type": "tar.gz",
- "strip_prefix": "wasm-bindgen-cli-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty:BUILD.wasm-bindgen-cli.bazel",
- "patch_args": [
- "-p1"
- ],
- "patches": [
- "@@rules_rust~//wasm_bindgen/3rdparty/patches:resolver.patch"
- ]
- }
- },
- "rules_rust_proto__tokio-threadpool-0.1.18": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-threadpool/0.1.18/download"
- ],
- "strip_prefix": "tokio-threadpool-0.1.18",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-threadpool-0.1.18.bazel"
- }
- },
- "rules_rust_bindgen__annotate-snippets-0.9.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/annotate-snippets/0.9.1/download"
- ],
- "strip_prefix": "annotate-snippets-0.9.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.annotate-snippets-0.9.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__httparse-1.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/httparse/1.8.0/download"
- ],
- "strip_prefix": "httparse-1.8.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.httparse-1.8.0.bazel"
- }
- },
- "cui__powerfmt-0.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/powerfmt/0.2.0/download"
- ],
- "strip_prefix": "powerfmt-0.2.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.powerfmt-0.2.0.bazel"
- }
- },
- "rrra__strsim-0.10.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/strsim/0.10.0/download"
- ],
- "strip_prefix": "strsim-0.10.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.strsim-0.10.0.bazel"
- }
- },
- "rules_rust_prost__tonic-0.9.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tonic/0.9.2/download"
- ],
- "strip_prefix": "tonic-0.9.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tonic-0.9.2.bazel"
- }
- },
- "rules_rust_prost__async-trait-0.1.68": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/async-trait/0.1.68/download"
- ],
- "strip_prefix": "async-trait-0.1.68",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.async-trait-0.1.68.bazel"
- }
- },
- "rules_rust_wasm_bindgen__brotli-decompressor-2.5.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/brotli-decompressor/2.5.1/download"
- ],
- "strip_prefix": "brotli-decompressor-2.5.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.brotli-decompressor-2.5.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__windows_x86_64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_msvc-0.48.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows_x86_64_msvc-0.48.0.bazel"
- }
- },
- "cui__unicode-normalization-0.1.22": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-normalization/0.1.22/download"
- ],
- "strip_prefix": "unicode-normalization-0.1.22",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unicode-normalization-0.1.22.bazel"
- }
- },
- "rules_rust_prost__windows_x86_64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_msvc-0.48.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.windows_x86_64_msvc-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__idna-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/idna/0.4.0/download"
- ],
- "strip_prefix": "idna-0.4.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.idna-0.4.0.bazel"
- }
- },
- "rrra__regex-1.9.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex/1.9.1/download"
- ],
- "strip_prefix": "regex-1.9.1",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.regex-1.9.1.bazel"
- }
- },
- "cui__anstyle-parse-0.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anstyle-parse/0.2.1/download"
- ],
- "strip_prefix": "anstyle-parse-0.2.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.anstyle-parse-0.2.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wait-timeout-0.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wait-timeout/0.2.0/download"
- ],
- "strip_prefix": "wait-timeout-0.2.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wait-timeout-0.2.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__windows_aarch64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__quick-error-1.2.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/quick-error/1.2.3/download"
- ],
- "strip_prefix": "quick-error-1.2.3",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.quick-error-1.2.3.bazel"
- }
- },
- "rules_rust_bindgen__winapi-0.3.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi/0.3.9/download"
- ],
- "strip_prefix": "winapi-0.3.9",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
- }
- },
- "rules_rust_wasm_bindgen__core-foundation-sys-0.8.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/core-foundation-sys/0.8.4/download"
- ],
- "strip_prefix": "core-foundation-sys-0.8.4",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.core-foundation-sys-0.8.4.bazel"
- }
- },
- "rules_rust_prost__futures-core-0.3.28": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/futures-core/0.3.28/download"
- ],
- "strip_prefix": "futures-core-0.3.28",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.futures-core-0.3.28.bazel"
- }
- },
- "rrra__winapi-x86_64-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "rrra__anstyle-1.0.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anstyle/1.0.1/download"
- ],
- "strip_prefix": "anstyle-1.0.1",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-1.0.1.bazel"
- }
- },
- "cui__dunce-1.0.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/dunce/1.0.4/download"
- ],
- "strip_prefix": "dunce-1.0.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.dunce-1.0.4.bazel"
- }
- },
- "cui__phf_generator-0.11.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/phf_generator/0.11.2/download"
- ],
- "strip_prefix": "phf_generator-0.11.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.phf_generator-0.11.2.bazel"
- }
- },
- "rules_rust_prost__fastrand-1.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/fastrand/1.9.0/download"
- ],
- "strip_prefix": "fastrand-1.9.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.fastrand-1.9.0.bazel"
- }
- },
- "rules_rust_bindgen__windows_x86_64_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_gnu/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_gnu-0.48.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.windows_x86_64_gnu-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__memoffset-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/memoffset/0.9.0/download"
- ],
- "strip_prefix": "memoffset-0.9.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.memoffset-0.9.0.bazel"
- }
- },
- "rules_rust_bindgen__windows-targets-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-targets/0.48.0/download"
- ],
- "strip_prefix": "windows-targets-0.48.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.windows-targets-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__twoway-0.1.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/twoway/0.1.8/download"
- ],
- "strip_prefix": "twoway-0.1.8",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.twoway-0.1.8.bazel"
- }
- },
- "rules_rust_wasm_bindgen__linux-raw-sys-0.3.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/linux-raw-sys/0.3.8/download"
- ],
- "strip_prefix": "linux-raw-sys-0.3.8",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.linux-raw-sys-0.3.8.bazel"
- }
- },
- "cui__quote-1.0.29": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/quote/1.0.29/download"
- ],
- "strip_prefix": "quote-1.0.29",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.quote-1.0.29.bazel"
- }
- },
- "rules_rust_wasm_bindgen__safemem-0.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/safemem/0.3.3/download"
- ],
- "strip_prefix": "safemem-0.3.3",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.safemem-0.3.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen__assert_cmd-1.0.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/assert_cmd/1.0.8/download"
- ],
- "strip_prefix": "assert_cmd-1.0.8",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.assert_cmd-1.0.8.bazel"
- }
- },
- "cui__serde_starlark-0.1.14": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "29675b116dd4c7ab4012e00e71f6dee9ed8c731108468b4434779c6b9eec7957",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde_starlark/0.1.14/download"
- ],
- "strip_prefix": "serde_starlark-0.1.14",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.serde_starlark-0.1.14.bazel"
- }
- },
- "cui__ppv-lite86-0.2.17": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ppv-lite86/0.2.17/download"
- ],
- "strip_prefix": "ppv-lite86-0.2.17",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.ppv-lite86-0.2.17.bazel"
- }
- },
- "cui__rand_core-0.6.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand_core/0.6.4/download"
- ],
- "strip_prefix": "rand_core-0.6.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rand_core-0.6.4.bazel"
- }
- },
- "rules_rust_prost__wasi-0.11.0-wasi-snapshot-preview1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasi/0.11.0+wasi-snapshot-preview1/download"
- ],
- "strip_prefix": "wasi-0.11.0+wasi-snapshot-preview1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.wasi-0.11.0+wasi-snapshot-preview1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rustix-0.37.23": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustix/0.37.23/download"
- ],
- "strip_prefix": "rustix-0.37.23",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rustix-0.37.23.bazel"
- }
- },
- "rrra__clap_lex-0.5.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap_lex/0.5.0/download"
- ],
- "strip_prefix": "clap_lex-0.5.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.clap_lex-0.5.0.bazel"
- }
- },
- "rules_rust_prost__base64-0.21.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/base64/0.21.2/download"
- ],
- "strip_prefix": "base64-0.21.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.base64-0.21.2.bazel"
- }
- },
- "rules_rust_proto__log-0.3.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/log/0.3.9/download"
- ],
- "strip_prefix": "log-0.3.9",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.log-0.3.9.bazel"
- }
- },
- "rules_rust_wasm_bindgen__windows_i686_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_msvc-0.48.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows_i686_msvc-0.48.0.bazel"
- }
- },
- "cui__home-0.5.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/home/0.5.5/download"
- ],
- "strip_prefix": "home-0.5.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.home-0.5.5.bazel"
- }
- },
- "rules_rust_proto__memoffset-0.5.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/memoffset/0.5.6/download"
- ],
- "strip_prefix": "memoffset-0.5.6",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.memoffset-0.5.6.bazel"
- }
- },
- "cui__gix-attributes-0.19.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2451665e70709ba4753b623ef97511ee98c4a73816b2c5b5df25678d607ed820",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-attributes/0.19.0/download"
- ],
- "strip_prefix": "gix-attributes-0.19.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-attributes-0.19.0.bazel"
- }
- },
- "rules_rust_bindgen__clap-4.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap/4.3.3/download"
- ],
- "strip_prefix": "clap-4.3.3",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.clap-4.3.3.bazel"
- }
- },
- "rules_rust_prost__hyper-0.14.26": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hyper/0.14.26/download"
- ],
- "strip_prefix": "hyper-0.14.26",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.hyper-0.14.26.bazel"
- }
- },
- "rules_rust_wasm_bindgen__predicates-2.1.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/predicates/2.1.5/download"
- ],
- "strip_prefix": "predicates-2.1.5",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.predicates-2.1.5.bazel"
- }
- },
- "cui__windows_x86_64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_msvc-0.48.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows_x86_64_msvc-0.48.0.bazel"
- }
- },
- "cui__redox_syscall-0.3.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/redox_syscall/0.3.5/download"
- ],
- "strip_prefix": "redox_syscall-0.3.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.redox_syscall-0.3.5.bazel"
- }
- },
- "rules_rust_wasm_bindgen__indexmap-1.9.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/indexmap/1.9.3/download"
- ],
- "strip_prefix": "indexmap-1.9.3",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.indexmap-1.9.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen__once_cell-1.18.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/once_cell/1.18.0/download"
- ],
- "strip_prefix": "once_cell-1.18.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.once_cell-1.18.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__termtree-0.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/termtree/0.4.1/download"
- ],
- "strip_prefix": "termtree-0.4.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.termtree-0.4.1.bazel"
- }
- },
- "rules_rust_bindgen__anstream-0.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anstream/0.3.2/download"
- ],
- "strip_prefix": "anstream-0.3.2",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.anstream-0.3.2.bazel"
- }
- },
- "cui__gix-protocol-0.40.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "cc7b700dc20cc9be8a5130a1fd7e10c34117ffa7068431c8c24d963f0a2e0c9b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-protocol/0.40.0/download"
- ],
- "strip_prefix": "gix-protocol-0.40.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-protocol-0.40.0.bazel"
- }
- },
- "bazelci_rules": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "eca21884e6f66a88c358e580fd67a6b148d30ab57b1680f62a96c00f9bc6a07e",
- "strip_prefix": "bazelci_rules-1.0.0",
- "url": "https://github.com/bazelbuild/continuous-integration/releases/download/rules-1.0.0/bazelci_rules-1.0.0.tar.gz"
- }
- },
- "rules_rust_wasm_bindgen__doc-comment-0.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/doc-comment/0.3.3/download"
- ],
- "strip_prefix": "doc-comment-0.3.3",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.doc-comment-0.3.3.bazel"
- }
- },
- "cui__crc32fast-1.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crc32fast/1.3.2/download"
- ],
- "strip_prefix": "crc32fast-1.3.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.crc32fast-1.3.2.bazel"
- }
- },
- "rules_rust_bindgen__aho-corasick-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/aho-corasick/1.0.2/download"
- ],
- "strip_prefix": "aho-corasick-1.0.2",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.aho-corasick-1.0.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__walrus-macro-0.19.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/walrus-macro/0.19.0/download"
- ],
- "strip_prefix": "walrus-macro-0.19.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.walrus-macro-0.19.0.bazel"
- }
- },
- "cui__rdrand-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rdrand/0.4.0/download"
- ],
- "strip_prefix": "rdrand-0.4.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rdrand-0.4.0.bazel"
- }
- },
- "cui__cpufeatures-0.2.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cpufeatures/0.2.9/download"
- ],
- "strip_prefix": "cpufeatures-0.2.9",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.cpufeatures-0.2.9.bazel"
- }
- },
- "rules_rust_prost__mio-0.8.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/mio/0.8.8/download"
- ],
- "strip_prefix": "mio-0.8.8",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.mio-0.8.8.bazel"
- }
- },
- "rules_rust_proto__base64-0.9.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/base64/0.9.3/download"
- ],
- "strip_prefix": "base64-0.9.3",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.base64-0.9.3.bazel"
- }
- },
- "cui__rustc-serialize-0.3.25": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustc-serialize/0.3.25/download"
- ],
- "strip_prefix": "rustc-serialize-0.3.25",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rustc-serialize-0.3.25.bazel"
- }
- },
- "rrra__anyhow-1.0.71": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anyhow/1.0.71/download"
- ],
- "strip_prefix": "anyhow-1.0.71",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.anyhow-1.0.71.bazel"
- }
- },
- "cui__gix-path-0.10.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6a1d370115171e3ae03c5c6d4f7d096f2981a40ddccb98dfd704c773530ba73b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-path/0.10.0/download"
- ],
- "strip_prefix": "gix-path-0.10.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-path-0.10.0.bazel"
- }
- },
- "rules_rust_bindgen__hermit-abi-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hermit-abi/0.3.1/download"
- ],
- "strip_prefix": "hermit-abi-0.3.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.hermit-abi-0.3.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__cc-1.0.83": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cc/1.0.83/download"
- ],
- "strip_prefix": "cc-1.0.83",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.cc-1.0.83.bazel"
- }
- },
- "rrra__utf8parse-0.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/utf8parse/0.2.1/download"
- ],
- "strip_prefix": "utf8parse-0.2.1",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.utf8parse-0.2.1.bazel"
- }
- },
- "rules_rust_proto__futures-cpupool-0.1.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/futures-cpupool/0.1.8/download"
- ],
- "strip_prefix": "futures-cpupool-0.1.8",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.futures-cpupool-0.1.8.bazel"
- }
- },
- "cargo_bazel.buildifier-windows-amd64.exe": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_file",
- "attributes": {
- "urls": [
- "https://github.com/bazelbuild/buildtools/releases/download/5.0.1/buildifier-windows-amd64.exe"
- ],
- "sha256": "45e13b2951e4c611d346dacdaf0aafaa484045a3e7300fbc5dd01a896a688177",
- "downloaded_file_path": "buildifier.exe",
- "executable": true
- }
- },
- "cui__regex-1.10.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex/1.10.2/download"
- ],
- "strip_prefix": "regex-1.10.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.regex-1.10.2.bazel"
- }
- },
- "rrra__log-0.4.19": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/log/0.4.19/download"
- ],
- "strip_prefix": "log-0.4.19",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.log-0.4.19.bazel"
- }
- },
- "cui__cargo_metadata-0.18.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cargo_metadata/0.18.1/download"
- ],
- "strip_prefix": "cargo_metadata-0.18.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.cargo_metadata-0.18.1.bazel"
- }
- },
- "cui__gix-fs-0.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "09815faba62fe9b32d918b75a554686c98e43f7d48c43a80df58eb718e5c6635",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-fs/0.7.0/download"
- ],
- "strip_prefix": "gix-fs-0.7.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-fs-0.7.0.bazel"
- }
- },
- "cui__gix-sec-0.10.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "92b9542ac025a8c02ed5d17b3fc031a111a384e859d0be3532ec4d58c40a0f28",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-sec/0.10.0/download"
- ],
- "strip_prefix": "gix-sec-0.10.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-sec-0.10.0.bazel"
- }
- },
- "cui__gix-trace-0.1.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "96b6d623a1152c3facb79067d6e2ecdae48130030cf27d6eb21109f13bd7b836",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-trace/0.1.3/download"
- ],
- "strip_prefix": "gix-trace-0.1.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-trace-0.1.3.bazel"
- }
- },
- "cui__humansize-2.1.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/humansize/2.1.3/download"
- ],
- "strip_prefix": "humansize-2.1.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.humansize-2.1.3.bazel"
- }
- },
- "rules_rust_prost__winapi-x86_64-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__io-lifetimes-1.0.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/io-lifetimes/1.0.11/download"
- ],
- "strip_prefix": "io-lifetimes-1.0.11",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.io-lifetimes-1.0.11.bazel"
- }
- },
- "rules_rust_prost__tower-service-0.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tower-service/0.3.2/download"
- ],
- "strip_prefix": "tower-service-0.3.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tower-service-0.3.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__diff-0.1.13": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/diff/0.1.13/download"
- ],
- "strip_prefix": "diff-0.1.13",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.diff-0.1.13.bazel"
- }
- },
- "cui__rand_core-0.4.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand_core/0.4.2/download"
- ],
- "strip_prefix": "rand_core-0.4.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rand_core-0.4.2.bazel"
- }
- },
- "cui__phf-0.11.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/phf/0.11.2/download"
- ],
- "strip_prefix": "phf-0.11.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.phf-0.11.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-threads-xform-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "90a2e577034352f9aa9352730fcf2562c68957f2e9b9ee70ab6379510e49e2fe",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-threads-xform/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-threads-xform-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-threads-xform-0.2.91.bazel"
- }
- },
- "rules_rust_wasm_bindgen__winapi-0.3.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi/0.3.9/download"
- ],
- "strip_prefix": "winapi-0.3.9",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
- }
- },
- "cui__wasm-bindgen-0.2.87": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen/0.2.87/download"
- ],
- "strip_prefix": "wasm-bindgen-0.2.87",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.wasm-bindgen-0.2.87.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasmparser-0.102.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasmparser/0.102.0/download"
- ],
- "strip_prefix": "wasmparser-0.102.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasmparser-0.102.0.bazel"
- }
- },
- "cui__winapi-x86_64-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "rules_rust_proto__grpc-compiler-0.6.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "907274ce8ee7b40a0d0b0db09022ea22846a47cfb1fc8ad2c983c70001b4ffb1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/grpc-compiler/0.6.2/download"
- ],
- "strip_prefix": "grpc-compiler-0.6.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.grpc-compiler-0.6.2.bazel"
- }
- },
- "rrra__heck-0.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/heck/0.4.1/download"
- ],
- "strip_prefix": "heck-0.4.1",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.heck-0.4.1.bazel"
- }
- },
- "rules_rust_prost__hermit-abi-0.2.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hermit-abi/0.2.6/download"
- ],
- "strip_prefix": "hermit-abi-0.2.6",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.hermit-abi-0.2.6.bazel"
- }
- },
- "rules_rust_wasm_bindgen__autocfg-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__autocfg-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"sha256": "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa",
"type": "tar.gz",
@@ -11295,38 +935,1703 @@
"https://static.crates.io/crates/autocfg/1.1.0/download"
],
"strip_prefix": "autocfg-1.1.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.autocfg-1.1.0.bazel"
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.autocfg-1.1.0.bazel"
}
},
- "cui__version_check-0.9.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__bitflags-1.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f",
+ "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/version_check/0.9.4/download"
+ "https://static.crates.io/crates/bitflags/1.3.2/download"
],
- "strip_prefix": "version_check-0.9.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.version_check-0.9.4.bazel"
+ "strip_prefix": "bitflags-1.3.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
}
},
- "cui__gix-date-0.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__bitflags-2.4.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "fc7df669639582dc7c02737642f76890b03b5544e141caba68a7d6b4eb551e0d",
+ "sha256": "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/gix-date/0.8.0/download"
+ "https://static.crates.io/crates/bitflags/2.4.1/download"
],
- "strip_prefix": "gix-date-0.8.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-date-0.8.0.bazel"
+ "strip_prefix": "bitflags-2.4.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.bitflags-2.4.1.bazel"
+ }
+ },
+ "cui__block-buffer-0.10.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/block-buffer/0.10.4/download"
+ ],
+ "strip_prefix": "block-buffer-0.10.4",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.block-buffer-0.10.4.bazel"
+ }
+ },
+ "cui__bstr-1.6.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/bstr/1.6.0/download"
+ ],
+ "strip_prefix": "bstr-1.6.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.bstr-1.6.0.bazel"
+ }
+ },
+ "cui__camino-1.1.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/camino/1.1.9/download"
+ ],
+ "strip_prefix": "camino-1.1.9",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.camino-1.1.9.bazel"
+ }
+ },
+ "cui__cargo-lock-10.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "49f8d8bb8836f681fe20ad10faa7796a11e67dbb6125e5a38f88ddd725c217e8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cargo-lock/10.0.0/download"
+ ],
+ "strip_prefix": "cargo-lock-10.0.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.cargo-lock-10.0.0.bazel"
+ }
+ },
+ "cui__cargo-platform-0.1.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cargo-platform/0.1.7/download"
+ ],
+ "strip_prefix": "cargo-platform-0.1.7",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.cargo-platform-0.1.7.bazel"
+ }
+ },
+ "cui__cargo_metadata-0.18.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cargo_metadata/0.18.1/download"
+ ],
+ "strip_prefix": "cargo_metadata-0.18.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.cargo_metadata-0.18.1.bazel"
+ }
+ },
+ "cui__cargo_toml-0.20.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "88da5a13c620b4ca0078845707ea9c3faf11edbc3ffd8497d11d686211cd1ac0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cargo_toml/0.20.5/download"
+ ],
+ "strip_prefix": "cargo_toml-0.20.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.cargo_toml-0.20.5.bazel"
+ }
+ },
+ "cui__cfg-expr-0.17.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cfg-expr/0.17.0/download"
+ ],
+ "strip_prefix": "cfg-expr-0.17.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.cfg-expr-0.17.0.bazel"
+ }
+ },
+ "cui__cfg-if-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cfg-if/1.0.0/download"
+ ],
+ "strip_prefix": "cfg-if-1.0.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
+ }
+ },
+ "cui__clap-4.3.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/clap/4.3.11/download"
+ ],
+ "strip_prefix": "clap-4.3.11",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.clap-4.3.11.bazel"
+ }
+ },
+ "cui__clap_builder-4.3.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/clap_builder/4.3.11/download"
+ ],
+ "strip_prefix": "clap_builder-4.3.11",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.clap_builder-4.3.11.bazel"
+ }
+ },
+ "cui__clap_derive-4.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/clap_derive/4.3.2/download"
+ ],
+ "strip_prefix": "clap_derive-4.3.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.clap_derive-4.3.2.bazel"
+ }
+ },
+ "cui__clap_lex-0.5.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/clap_lex/0.5.0/download"
+ ],
+ "strip_prefix": "clap_lex-0.5.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.clap_lex-0.5.0.bazel"
+ }
+ },
+ "cui__clru-0.6.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/clru/0.6.1/download"
+ ],
+ "strip_prefix": "clru-0.6.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.clru-0.6.1.bazel"
+ }
+ },
+ "cui__colorchoice-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/colorchoice/1.0.0/download"
+ ],
+ "strip_prefix": "colorchoice-1.0.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.colorchoice-1.0.0.bazel"
+ }
+ },
+ "cui__cpufeatures-0.2.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cpufeatures/0.2.9/download"
+ ],
+ "strip_prefix": "cpufeatures-0.2.9",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.cpufeatures-0.2.9.bazel"
+ }
+ },
+ "cui__crates-index-3.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "45fbf3a2a2f3435363fb343f30ee31d9f63ea3862d6eab639446c1393d82cd32",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crates-index/3.2.0/download"
+ ],
+ "strip_prefix": "crates-index-3.2.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.crates-index-3.2.0.bazel"
+ }
+ },
+ "cui__crc32fast-1.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crc32fast/1.3.2/download"
+ ],
+ "strip_prefix": "crc32fast-1.3.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.crc32fast-1.3.2.bazel"
+ }
+ },
+ "cui__crossbeam-channel-0.5.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-channel/0.5.8/download"
+ ],
+ "strip_prefix": "crossbeam-channel-0.5.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.crossbeam-channel-0.5.8.bazel"
+ }
+ },
+ "cui__crossbeam-utils-0.8.16": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-utils/0.8.16/download"
+ ],
+ "strip_prefix": "crossbeam-utils-0.8.16",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.crossbeam-utils-0.8.16.bazel"
+ }
+ },
+ "cui__crypto-common-0.1.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crypto-common/0.1.6/download"
+ ],
+ "strip_prefix": "crypto-common-0.1.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.crypto-common-0.1.6.bazel"
+ }
+ },
+ "cui__digest-0.10.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/digest/0.10.7/download"
+ ],
+ "strip_prefix": "digest-0.10.7",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.digest-0.10.7.bazel"
+ }
+ },
+ "cui__dunce-1.0.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/dunce/1.0.4/download"
+ ],
+ "strip_prefix": "dunce-1.0.4",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.dunce-1.0.4.bazel"
+ }
+ },
+ "cui__either-1.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/either/1.9.0/download"
+ ],
+ "strip_prefix": "either-1.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.either-1.9.0.bazel"
+ }
+ },
+ "cui__encoding_rs-0.8.33": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/encoding_rs/0.8.33/download"
+ ],
+ "strip_prefix": "encoding_rs-0.8.33",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.encoding_rs-0.8.33.bazel"
+ }
+ },
+ "cui__equivalent-1.0.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/equivalent/1.0.1/download"
+ ],
+ "strip_prefix": "equivalent-1.0.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.equivalent-1.0.1.bazel"
+ }
+ },
+ "cui__errno-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/errno/0.3.9/download"
+ ],
+ "strip_prefix": "errno-0.3.9",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.errno-0.3.9.bazel"
+ }
+ },
+ "cui__faster-hex-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/faster-hex/0.9.0/download"
+ ],
+ "strip_prefix": "faster-hex-0.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.faster-hex-0.9.0.bazel"
+ }
+ },
+ "cui__fastrand-2.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fastrand/2.1.1/download"
+ ],
+ "strip_prefix": "fastrand-2.1.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.fastrand-2.1.1.bazel"
+ }
+ },
+ "cui__filetime-0.2.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/filetime/0.2.22/download"
+ ],
+ "strip_prefix": "filetime-0.2.22",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.filetime-0.2.22.bazel"
+ }
+ },
+ "cui__flate2-1.0.28": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/flate2/1.0.28/download"
+ ],
+ "strip_prefix": "flate2-1.0.28",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.flate2-1.0.28.bazel"
+ }
+ },
+ "cui__fnv-1.0.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fnv/1.0.7/download"
+ ],
+ "strip_prefix": "fnv-1.0.7",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.fnv-1.0.7.bazel"
+ }
+ },
+ "cui__form_urlencoded-1.2.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/form_urlencoded/1.2.1/download"
+ ],
+ "strip_prefix": "form_urlencoded-1.2.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.form_urlencoded-1.2.1.bazel"
+ }
+ },
+ "cui__generic-array-0.14.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/generic-array/0.14.7/download"
+ ],
+ "strip_prefix": "generic-array-0.14.7",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.generic-array-0.14.7.bazel"
+ }
+ },
+ "cui__gix-0.66.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9048b8d1ae2104f045cb37e5c450fc49d5d8af22609386bfc739c11ba88995eb",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix/0.66.0/download"
+ ],
+ "strip_prefix": "gix-0.66.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-0.66.0.bazel"
+ }
+ },
+ "cui__gix-actor-0.32.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fc19e312cd45c4a66cd003f909163dc2f8e1623e30a0c0c6df3776e89b308665",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-actor/0.32.0/download"
+ ],
+ "strip_prefix": "gix-actor-0.32.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-actor-0.32.0.bazel"
+ }
+ },
+ "cui__gix-attributes-0.22.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ebccbf25aa4a973dd352564a9000af69edca90623e8a16dad9cbc03713131311",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-attributes/0.22.5/download"
+ ],
+ "strip_prefix": "gix-attributes-0.22.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-attributes-0.22.5.bazel"
+ }
+ },
+ "cui__gix-bitmap-0.2.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a371db66cbd4e13f0ed9dc4c0fea712d7276805fccc877f77e96374d317e87ae",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-bitmap/0.2.11/download"
+ ],
+ "strip_prefix": "gix-bitmap-0.2.11",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-bitmap-0.2.11.bazel"
+ }
+ },
+ "cui__gix-chunk-0.4.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "45c8751169961ba7640b513c3b24af61aa962c967aaf04116734975cd5af0c52",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-chunk/0.4.8/download"
+ ],
+ "strip_prefix": "gix-chunk-0.4.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-chunk-0.4.8.bazel"
+ }
+ },
+ "cui__gix-command-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "dff2e692b36bbcf09286c70803006ca3fd56551a311de450be317a0ab8ea92e7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-command/0.3.9/download"
+ ],
+ "strip_prefix": "gix-command-0.3.9",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-command-0.3.9.bazel"
+ }
+ },
+ "cui__gix-commitgraph-0.24.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "133b06f67f565836ec0c473e2116a60fb74f80b6435e21d88013ac0e3c60fc78",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-commitgraph/0.24.3/download"
+ ],
+ "strip_prefix": "gix-commitgraph-0.24.3",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-commitgraph-0.24.3.bazel"
+ }
+ },
+ "cui__gix-config-0.40.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "78e797487e6ca3552491de1131b4f72202f282fb33f198b1c34406d765b42bb0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-config/0.40.0/download"
+ ],
+ "strip_prefix": "gix-config-0.40.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-config-0.40.0.bazel"
+ }
+ },
+ "cui__gix-config-value-0.14.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "03f76169faa0dec598eac60f83d7fcdd739ec16596eca8fb144c88973dbe6f8c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-config-value/0.14.8/download"
+ ],
+ "strip_prefix": "gix-config-value-0.14.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-config-value-0.14.8.bazel"
+ }
+ },
+ "cui__gix-credentials-0.24.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8ce391d305968782f1ae301c4a3d42c5701df7ff1d8bc03740300f6fd12bce78",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-credentials/0.24.5/download"
+ ],
+ "strip_prefix": "gix-credentials-0.24.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-credentials-0.24.5.bazel"
+ }
+ },
+ "cui__gix-date-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "35c84b7af01e68daf7a6bb8bb909c1ff5edb3ce4326f1f43063a5a96d3c3c8a5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-date/0.9.0/download"
+ ],
+ "strip_prefix": "gix-date-0.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-date-0.9.0.bazel"
+ }
+ },
+ "cui__gix-diff-0.46.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "92c9afd80fff00f8b38b1c1928442feb4cd6d2232a6ed806b6b193151a3d336c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-diff/0.46.0/download"
+ ],
+ "strip_prefix": "gix-diff-0.46.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-diff-0.46.0.bazel"
+ }
+ },
+ "cui__gix-discover-0.35.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0577366b9567376bc26e815fd74451ebd0e6218814e242f8e5b7072c58d956d2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-discover/0.35.0/download"
+ ],
+ "strip_prefix": "gix-discover-0.35.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-discover-0.35.0.bazel"
+ }
+ },
+ "cui__gix-features-0.38.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-features/0.38.2/download"
+ ],
+ "strip_prefix": "gix-features-0.38.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-features-0.38.2.bazel"
+ }
+ },
+ "cui__gix-filter-0.13.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4121790ae140066e5b953becc72e7496278138d19239be2e63b5067b0843119e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-filter/0.13.0/download"
+ ],
+ "strip_prefix": "gix-filter-0.13.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-filter-0.13.0.bazel"
+ }
+ },
+ "cui__gix-fs-0.11.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f2bfe6249cfea6d0c0e0990d5226a4cb36f030444ba9e35e0639275db8f98575",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-fs/0.11.3/download"
+ ],
+ "strip_prefix": "gix-fs-0.11.3",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-fs-0.11.3.bazel"
+ }
+ },
+ "cui__gix-glob-0.16.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "74908b4bbc0a0a40852737e5d7889f676f081e340d5451a16e5b4c50d592f111",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-glob/0.16.5/download"
+ ],
+ "strip_prefix": "gix-glob-0.16.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-glob-0.16.5.bazel"
+ }
+ },
+ "cui__gix-hash-0.14.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-hash/0.14.2/download"
+ ],
+ "strip_prefix": "gix-hash-0.14.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-hash-0.14.2.bazel"
+ }
+ },
+ "cui__gix-hashtable-0.5.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7ddf80e16f3c19ac06ce415a38b8591993d3f73aede049cb561becb5b3a8e242",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-hashtable/0.5.2/download"
+ ],
+ "strip_prefix": "gix-hashtable-0.5.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-hashtable-0.5.2.bazel"
+ }
+ },
+ "cui__gix-ignore-0.11.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e447cd96598460f5906a0f6c75e950a39f98c2705fc755ad2f2020c9e937fab7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-ignore/0.11.4/download"
+ ],
+ "strip_prefix": "gix-ignore-0.11.4",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-ignore-0.11.4.bazel"
+ }
+ },
+ "cui__gix-index-0.35.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0cd4203244444017682176e65fd0180be9298e58ed90bd4a8489a357795ed22d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-index/0.35.0/download"
+ ],
+ "strip_prefix": "gix-index-0.35.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-index-0.35.0.bazel"
+ }
+ },
+ "cui__gix-lock-14.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e3bc7fe297f1f4614774989c00ec8b1add59571dc9b024b4c00acb7dedd4e19d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-lock/14.0.0/download"
+ ],
+ "strip_prefix": "gix-lock-14.0.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-lock-14.0.0.bazel"
+ }
+ },
+ "cui__gix-negotiate-0.15.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b4063bf329a191a9e24b6f948a17ccf6698c0380297f5e169cee4f1d2ab9475b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-negotiate/0.15.0/download"
+ ],
+ "strip_prefix": "gix-negotiate-0.15.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-negotiate-0.15.0.bazel"
+ }
+ },
+ "cui__gix-object-0.44.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2f5b801834f1de7640731820c2df6ba88d95480dc4ab166a5882f8ff12b88efa",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-object/0.44.0/download"
+ ],
+ "strip_prefix": "gix-object-0.44.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-object-0.44.0.bazel"
+ }
+ },
+ "cui__gix-odb-0.63.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a3158068701c17df54f0ab2adda527f5a6aca38fd5fd80ceb7e3c0a2717ec747",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-odb/0.63.0/download"
+ ],
+ "strip_prefix": "gix-odb-0.63.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-odb-0.63.0.bazel"
+ }
+ },
+ "cui__gix-pack-0.53.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3223aa342eee21e1e0e403cad8ae9caf9edca55ef84c347738d10681676fd954",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-pack/0.53.0/download"
+ ],
+ "strip_prefix": "gix-pack-0.53.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-pack-0.53.0.bazel"
+ }
+ },
+ "cui__gix-packetline-0.17.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8c43ef4d5fe2fa222c606731c8bdbf4481413ee4ef46d61340ec39e4df4c5e49",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-packetline/0.17.6/download"
+ ],
+ "strip_prefix": "gix-packetline-0.17.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-packetline-0.17.6.bazel"
+ }
+ },
+ "cui__gix-packetline-blocking-0.17.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b9802304baa798dd6f5ff8008a2b6516d54b74a69ca2d3a2b9e2d6c3b5556b40",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-packetline-blocking/0.17.5/download"
+ ],
+ "strip_prefix": "gix-packetline-blocking-0.17.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-packetline-blocking-0.17.5.bazel"
+ }
+ },
+ "cui__gix-path-0.10.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ebfc4febd088abdcbc9f1246896e57e37b7a34f6909840045a1767c6dafac7af",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-path/0.10.11/download"
+ ],
+ "strip_prefix": "gix-path-0.10.11",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-path-0.10.11.bazel"
+ }
+ },
+ "cui__gix-pathspec-0.7.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5d23bf239532b4414d0e63b8ab3a65481881f7237ed9647bb10c1e3cc54c5ceb",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-pathspec/0.7.7/download"
+ ],
+ "strip_prefix": "gix-pathspec-0.7.7",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-pathspec-0.7.7.bazel"
+ }
+ },
+ "cui__gix-prompt-0.8.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "74fde865cdb46b30d8dad1293385d9bcf998d3a39cbf41bee67d0dab026fe6b1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-prompt/0.8.7/download"
+ ],
+ "strip_prefix": "gix-prompt-0.8.7",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-prompt-0.8.7.bazel"
+ }
+ },
+ "cui__gix-protocol-0.45.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "cc43a1006f01b5efee22a003928c9eb83dde2f52779ded9d4c0732ad93164e3e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-protocol/0.45.3/download"
+ ],
+ "strip_prefix": "gix-protocol-0.45.3",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-protocol-0.45.3.bazel"
+ }
+ },
+ "cui__gix-quote-0.4.12": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "cbff4f9b9ea3fa7a25a70ee62f545143abef624ac6aa5884344e70c8b0a1d9ff",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-quote/0.4.12/download"
+ ],
+ "strip_prefix": "gix-quote-0.4.12",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-quote-0.4.12.bazel"
+ }
+ },
+ "cui__gix-ref-0.47.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ae0d8406ebf9aaa91f55a57f053c5a1ad1a39f60fdf0303142b7be7ea44311e5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-ref/0.47.0/download"
+ ],
+ "strip_prefix": "gix-ref-0.47.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-ref-0.47.0.bazel"
+ }
+ },
+ "cui__gix-refspec-0.25.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ebb005f82341ba67615ffdd9f7742c87787544441c88090878393d0682869ca6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-refspec/0.25.0/download"
+ ],
+ "strip_prefix": "gix-refspec-0.25.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-refspec-0.25.0.bazel"
+ }
+ },
+ "cui__gix-revision-0.29.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ba4621b219ac0cdb9256883030c3d56a6c64a6deaa829a92da73b9a576825e1e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-revision/0.29.0/download"
+ ],
+ "strip_prefix": "gix-revision-0.29.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-revision-0.29.0.bazel"
+ }
+ },
+ "cui__gix-revwalk-0.15.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b41e72544b93084ee682ef3d5b31b1ba4d8fa27a017482900e5e044d5b1b3984",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-revwalk/0.15.0/download"
+ ],
+ "strip_prefix": "gix-revwalk-0.15.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-revwalk-0.15.0.bazel"
+ }
+ },
+ "cui__gix-sec-0.10.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0fe4d52f30a737bbece5276fab5d3a8b276dc2650df963e293d0673be34e7a5f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-sec/0.10.8/download"
+ ],
+ "strip_prefix": "gix-sec-0.10.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-sec-0.10.8.bazel"
+ }
+ },
+ "cui__gix-submodule-0.14.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "529d0af78cc2f372b3218f15eb1e3d1635a21c8937c12e2dd0b6fc80c2ca874b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-submodule/0.14.0/download"
+ ],
+ "strip_prefix": "gix-submodule-0.14.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-submodule-0.14.0.bazel"
+ }
+ },
+ "cui__gix-tempfile-14.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "046b4927969fa816a150a0cda2e62c80016fe11fb3c3184e4dddf4e542f108aa",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-tempfile/14.0.2/download"
+ ],
+ "strip_prefix": "gix-tempfile-14.0.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-tempfile-14.0.2.bazel"
+ }
+ },
+ "cui__gix-trace-0.1.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6cae0e8661c3ff92688ce1c8b8058b3efb312aba9492bbe93661a21705ab431b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-trace/0.1.10/download"
+ ],
+ "strip_prefix": "gix-trace-0.1.10",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-trace-0.1.10.bazel"
+ }
+ },
+ "cui__gix-transport-0.42.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "421dcccab01b41a15d97b226ad97a8f9262295044e34fbd37b10e493b0a6481f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-transport/0.42.3/download"
+ ],
+ "strip_prefix": "gix-transport-0.42.3",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-transport-0.42.3.bazel"
+ }
+ },
+ "cui__gix-traverse-0.41.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "030da39af94e4df35472e9318228f36530989327906f38e27807df305fccb780",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-traverse/0.41.0/download"
+ ],
+ "strip_prefix": "gix-traverse-0.41.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-traverse-0.41.0.bazel"
+ }
+ },
+ "cui__gix-url-0.27.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fd280c5e84fb22e128ed2a053a0daeacb6379469be6a85e3d518a0636e160c89",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-url/0.27.5/download"
+ ],
+ "strip_prefix": "gix-url-0.27.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-url-0.27.5.bazel"
+ }
+ },
+ "cui__gix-utils-0.1.12": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-utils/0.1.12/download"
+ ],
+ "strip_prefix": "gix-utils-0.1.12",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-utils-0.1.12.bazel"
+ }
+ },
+ "cui__gix-validate-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "81f2badbb64e57b404593ee26b752c26991910fd0d81fe6f9a71c1a8309b6c86",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-validate/0.9.0/download"
+ ],
+ "strip_prefix": "gix-validate-0.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-validate-0.9.0.bazel"
+ }
+ },
+ "cui__gix-worktree-0.36.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c312ad76a3f2ba8e865b360d5cb3aa04660971d16dec6dd0ce717938d903149a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gix-worktree/0.36.0/download"
+ ],
+ "strip_prefix": "gix-worktree-0.36.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.gix-worktree-0.36.0.bazel"
+ }
+ },
+ "cui__globset-0.4.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/globset/0.4.11/download"
+ ],
+ "strip_prefix": "globset-0.4.11",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.globset-0.4.11.bazel"
+ }
+ },
+ "cui__globwalk-0.8.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/globwalk/0.8.1/download"
+ ],
+ "strip_prefix": "globwalk-0.8.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.globwalk-0.8.1.bazel"
+ }
+ },
+ "cui__hashbrown-0.14.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hashbrown/0.14.3/download"
+ ],
+ "strip_prefix": "hashbrown-0.14.3",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.hashbrown-0.14.3.bazel"
+ }
+ },
+ "cui__hashbrown-0.15.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hashbrown/0.15.0/download"
+ ],
+ "strip_prefix": "hashbrown-0.15.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.hashbrown-0.15.0.bazel"
+ }
+ },
+ "cui__heck-0.4.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/heck/0.4.1/download"
+ ],
+ "strip_prefix": "heck-0.4.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.heck-0.4.1.bazel"
+ }
+ },
+ "cui__hermit-abi-0.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hermit-abi/0.3.2/download"
+ ],
+ "strip_prefix": "hermit-abi-0.3.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.hermit-abi-0.3.2.bazel"
+ }
+ },
+ "cui__hex-0.4.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hex/0.4.3/download"
+ ],
+ "strip_prefix": "hex-0.4.3",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.hex-0.4.3.bazel"
+ }
+ },
+ "cui__home-0.5.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/home/0.5.5/download"
+ ],
+ "strip_prefix": "home-0.5.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.home-0.5.5.bazel"
+ }
+ },
+ "cui__idna-0.5.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/idna/0.5.0/download"
+ ],
+ "strip_prefix": "idna-0.5.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.idna-0.5.0.bazel"
+ }
+ },
+ "cui__ignore-0.4.18": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ignore/0.4.18/download"
+ ],
+ "strip_prefix": "ignore-0.4.18",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.ignore-0.4.18.bazel"
+ }
+ },
+ "cui__indexmap-2.6.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/indexmap/2.6.0/download"
+ ],
+ "strip_prefix": "indexmap-2.6.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.indexmap-2.6.0.bazel"
+ }
+ },
+ "cui__indoc-2.0.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/indoc/2.0.5/download"
+ ],
+ "strip_prefix": "indoc-2.0.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.indoc-2.0.5.bazel"
+ }
+ },
+ "cui__io-lifetimes-1.0.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/io-lifetimes/1.0.11/download"
+ ],
+ "strip_prefix": "io-lifetimes-1.0.11",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.io-lifetimes-1.0.11.bazel"
+ }
+ },
+ "cui__is-terminal-0.4.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/is-terminal/0.4.7/download"
+ ],
+ "strip_prefix": "is-terminal-0.4.7",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.is-terminal-0.4.7.bazel"
+ }
+ },
+ "cui__itertools-0.13.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/itertools/0.13.0/download"
+ ],
+ "strip_prefix": "itertools-0.13.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.itertools-0.13.0.bazel"
+ }
+ },
+ "cui__itoa-1.0.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/itoa/1.0.8/download"
+ ],
+ "strip_prefix": "itoa-1.0.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.itoa-1.0.8.bazel"
+ }
+ },
+ "cui__jiff-0.1.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8a45489186a6123c128fdf6016183fcfab7113e1820eb813127e036e287233fb",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/jiff/0.1.13/download"
+ ],
+ "strip_prefix": "jiff-0.1.13",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.jiff-0.1.13.bazel"
+ }
+ },
+ "cui__jiff-tzdb-0.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/jiff-tzdb/0.1.1/download"
+ ],
+ "strip_prefix": "jiff-tzdb-0.1.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.jiff-tzdb-0.1.1.bazel"
+ }
+ },
+ "cui__jiff-tzdb-platform-0.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/jiff-tzdb-platform/0.1.1/download"
+ ],
+ "strip_prefix": "jiff-tzdb-platform-0.1.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.jiff-tzdb-platform-0.1.1.bazel"
+ }
+ },
+ "cui__kstring-2.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/kstring/2.0.2/download"
+ ],
+ "strip_prefix": "kstring-2.0.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.kstring-2.0.2.bazel"
+ }
+ },
+ "cui__lazy_static-1.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/lazy_static/1.4.0/download"
+ ],
+ "strip_prefix": "lazy_static-1.4.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.lazy_static-1.4.0.bazel"
+ }
+ },
+ "cui__libc-0.2.161": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/libc/0.2.161/download"
+ ],
+ "strip_prefix": "libc-0.2.161",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.libc-0.2.161.bazel"
+ }
+ },
+ "cui__linux-raw-sys-0.3.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/linux-raw-sys/0.3.8/download"
+ ],
+ "strip_prefix": "linux-raw-sys-0.3.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.linux-raw-sys-0.3.8.bazel"
+ }
+ },
+ "cui__linux-raw-sys-0.4.14": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/linux-raw-sys/0.4.14/download"
+ ],
+ "strip_prefix": "linux-raw-sys-0.4.14",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.linux-raw-sys-0.4.14.bazel"
+ }
+ },
+ "cui__lock_api-0.4.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/lock_api/0.4.11/download"
+ ],
+ "strip_prefix": "lock_api-0.4.11",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.lock_api-0.4.11.bazel"
+ }
+ },
+ "cui__log-0.4.19": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/log/0.4.19/download"
+ ],
+ "strip_prefix": "log-0.4.19",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.log-0.4.19.bazel"
+ }
+ },
+ "cui__maplit-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/maplit/1.0.2/download"
+ ],
+ "strip_prefix": "maplit-1.0.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.maplit-1.0.2.bazel"
+ }
+ },
+ "cui__maybe-async-0.2.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/maybe-async/0.2.7/download"
+ ],
+ "strip_prefix": "maybe-async-0.2.7",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.maybe-async-0.2.7.bazel"
+ }
+ },
+ "cui__memchr-2.6.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/memchr/2.6.4/download"
+ ],
+ "strip_prefix": "memchr-2.6.4",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.memchr-2.6.4.bazel"
+ }
+ },
+ "cui__memmap2-0.9.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/memmap2/0.9.5/download"
+ ],
+ "strip_prefix": "memmap2-0.9.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.memmap2-0.9.5.bazel"
+ }
+ },
+ "cui__miniz_oxide-0.7.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/miniz_oxide/0.7.1/download"
+ ],
+ "strip_prefix": "miniz_oxide-0.7.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.miniz_oxide-0.7.1.bazel"
+ }
+ },
+ "cui__normpath-1.3.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/normpath/1.3.0/download"
+ ],
+ "strip_prefix": "normpath-1.3.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.normpath-1.3.0.bazel"
+ }
+ },
+ "cui__nu-ansi-term-0.46.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/nu-ansi-term/0.46.0/download"
+ ],
+ "strip_prefix": "nu-ansi-term-0.46.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.nu-ansi-term-0.46.0.bazel"
+ }
+ },
+ "cui__once_cell-1.20.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/once_cell/1.20.2/download"
+ ],
+ "strip_prefix": "once_cell-1.20.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.once_cell-1.20.2.bazel"
+ }
+ },
+ "cui__overload-0.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/overload/0.1.1/download"
+ ],
+ "strip_prefix": "overload-0.1.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.overload-0.1.1.bazel"
+ }
+ },
+ "cui__parking_lot-0.12.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/parking_lot/0.12.1/download"
+ ],
+ "strip_prefix": "parking_lot-0.12.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.parking_lot-0.12.1.bazel"
+ }
+ },
+ "cui__parking_lot_core-0.9.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/parking_lot_core/0.9.9/download"
+ ],
+ "strip_prefix": "parking_lot_core-0.9.9",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.parking_lot_core-0.9.9.bazel"
+ }
+ },
+ "cui__pathdiff-0.2.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pathdiff/0.2.2/download"
+ ],
+ "strip_prefix": "pathdiff-0.2.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.pathdiff-0.2.2.bazel"
+ }
+ },
+ "cui__percent-encoding-2.3.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/percent-encoding/2.3.1/download"
+ ],
+ "strip_prefix": "percent-encoding-2.3.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.percent-encoding-2.3.1.bazel"
+ }
+ },
+ "cui__pest-2.7.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pest/2.7.0/download"
+ ],
+ "strip_prefix": "pest-2.7.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.pest-2.7.0.bazel"
+ }
+ },
+ "cui__pest_derive-2.7.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pest_derive/2.7.0/download"
+ ],
+ "strip_prefix": "pest_derive-2.7.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.pest_derive-2.7.0.bazel"
+ }
+ },
+ "cui__pest_generator-2.7.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pest_generator/2.7.0/download"
+ ],
+ "strip_prefix": "pest_generator-2.7.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.pest_generator-2.7.0.bazel"
+ }
+ },
+ "cui__pest_meta-2.7.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pest_meta/2.7.0/download"
+ ],
+ "strip_prefix": "pest_meta-2.7.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.pest_meta-2.7.0.bazel"
+ }
+ },
+ "cui__pin-project-lite-0.2.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pin-project-lite/0.2.13/download"
+ ],
+ "strip_prefix": "pin-project-lite-0.2.13",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.pin-project-lite-0.2.13.bazel"
+ }
+ },
+ "cui__proc-macro2-1.0.88": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/proc-macro2/1.0.88/download"
+ ],
+ "strip_prefix": "proc-macro2-1.0.88",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.proc-macro2-1.0.88.bazel"
+ }
+ },
+ "cui__prodash-28.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/prodash/28.0.0/download"
+ ],
+ "strip_prefix": "prodash-28.0.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.prodash-28.0.0.bazel"
+ }
+ },
+ "cui__quote-1.0.37": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/quote/1.0.37/download"
+ ],
+ "strip_prefix": "quote-1.0.37",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.quote-1.0.37.bazel"
+ }
+ },
+ "cui__redox_syscall-0.3.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/redox_syscall/0.3.5/download"
+ ],
+ "strip_prefix": "redox_syscall-0.3.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.redox_syscall-0.3.5.bazel"
+ }
+ },
+ "cui__redox_syscall-0.4.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/redox_syscall/0.4.1/download"
+ ],
+ "strip_prefix": "redox_syscall-0.4.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.redox_syscall-0.4.1.bazel"
+ }
+ },
+ "cui__regex-1.11.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex/1.11.0/download"
+ ],
+ "strip_prefix": "regex-1.11.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.regex-1.11.0.bazel"
+ }
+ },
+ "cui__regex-automata-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-automata/0.3.3/download"
+ ],
+ "strip_prefix": "regex-automata-0.3.3",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.regex-automata-0.3.3.bazel"
+ }
+ },
+ "cui__regex-automata-0.4.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-automata/0.4.8/download"
+ ],
+ "strip_prefix": "regex-automata-0.4.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.regex-automata-0.4.8.bazel"
+ }
+ },
+ "cui__regex-syntax-0.8.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-syntax/0.8.5/download"
+ ],
+ "strip_prefix": "regex-syntax-0.8.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.regex-syntax-0.8.5.bazel"
+ }
+ },
+ "cui__rustc-hash-2.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustc-hash/2.0.0/download"
+ ],
+ "strip_prefix": "rustc-hash-2.0.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.rustc-hash-2.0.0.bazel"
+ }
+ },
+ "cui__rustix-0.37.23": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustix/0.37.23/download"
+ ],
+ "strip_prefix": "rustix-0.37.23",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.rustix-0.37.23.bazel"
+ }
+ },
+ "cui__rustix-0.38.37": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustix/0.38.37/download"
+ ],
+ "strip_prefix": "rustix-0.38.37",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.rustix-0.38.37.bazel"
+ }
+ },
+ "cui__ryu-1.0.14": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ryu/1.0.14/download"
+ ],
+ "strip_prefix": "ryu-1.0.14",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.ryu-1.0.14.bazel"
+ }
+ },
+ "cui__same-file-1.0.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/same-file/1.0.6/download"
+ ],
+ "strip_prefix": "same-file-1.0.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.same-file-1.0.6.bazel"
}
},
"cui__scopeguard-1.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"sha256": "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49",
"type": "tar.gz",
@@ -11334,51 +2639,3534 @@
"https://static.crates.io/crates/scopeguard/1.2.0/download"
],
"strip_prefix": "scopeguard-1.2.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.scopeguard-1.2.0.bazel"
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.scopeguard-1.2.0.bazel"
}
},
- "rules_rust_bindgen__clang-sys-1.6.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__semver-1.0.23": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f",
+ "sha256": "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/clang-sys/1.6.1/download"
+ "https://static.crates.io/crates/semver/1.0.23/download"
],
- "strip_prefix": "clang-sys-1.6.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.clang-sys-1.6.1.bazel"
+ "strip_prefix": "semver-1.0.23",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.semver-1.0.23.bazel"
}
},
- "rrra__anstyle-parse-0.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__serde-1.0.210": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333",
+ "sha256": "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/anstyle-parse/0.2.1/download"
+ "https://static.crates.io/crates/serde/1.0.210/download"
],
- "strip_prefix": "anstyle-parse-0.2.1",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-parse-0.2.1.bazel"
+ "strip_prefix": "serde-1.0.210",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.serde-1.0.210.bazel"
}
},
- "rules_rust_wasm_bindgen__num_cpus-1.16.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "cui__serde_derive-1.0.210": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43",
+ "sha256": "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/num_cpus/1.16.0/download"
+ "https://static.crates.io/crates/serde_derive/1.0.210/download"
],
- "strip_prefix": "num_cpus-1.16.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.num_cpus-1.16.0.bazel"
+ "strip_prefix": "serde_derive-1.0.210",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.serde_derive-1.0.210.bazel"
+ }
+ },
+ "cui__serde_json-1.0.129": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6dbcf9b78a125ee667ae19388837dd12294b858d101fdd393cb9d5501ef09eb2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde_json/1.0.129/download"
+ ],
+ "strip_prefix": "serde_json-1.0.129",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.serde_json-1.0.129.bazel"
+ }
+ },
+ "cui__serde_spanned-0.6.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde_spanned/0.6.8/download"
+ ],
+ "strip_prefix": "serde_spanned-0.6.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.serde_spanned-0.6.8.bazel"
+ }
+ },
+ "cui__serde_starlark-0.1.16": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "43f25f26c1c853647016b862c1734e0ad68c4f9f752b5f792220d38b1369ed4a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde_starlark/0.1.16/download"
+ ],
+ "strip_prefix": "serde_starlark-0.1.16",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.serde_starlark-0.1.16.bazel"
+ }
+ },
+ "cui__sha1_smol-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/sha1_smol/1.0.0/download"
+ ],
+ "strip_prefix": "sha1_smol-1.0.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.sha1_smol-1.0.0.bazel"
+ }
+ },
+ "cui__sha2-0.10.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/sha2/0.10.8/download"
+ ],
+ "strip_prefix": "sha2-0.10.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.sha2-0.10.8.bazel"
+ }
+ },
+ "cui__sharded-slab-0.1.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/sharded-slab/0.1.7/download"
+ ],
+ "strip_prefix": "sharded-slab-0.1.7",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.sharded-slab-0.1.7.bazel"
+ }
+ },
+ "cui__shell-words-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/shell-words/1.1.0/download"
+ ],
+ "strip_prefix": "shell-words-1.1.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.shell-words-1.1.0.bazel"
+ }
+ },
+ "cui__smallvec-1.11.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/smallvec/1.11.0/download"
+ ],
+ "strip_prefix": "smallvec-1.11.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.smallvec-1.11.0.bazel"
+ }
+ },
+ "cui__smawk-0.3.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/smawk/0.3.1/download"
+ ],
+ "strip_prefix": "smawk-0.3.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.smawk-0.3.1.bazel"
+ }
+ },
+ "cui__smol_str-0.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/smol_str/0.2.0/download"
+ ],
+ "strip_prefix": "smol_str-0.2.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.smol_str-0.2.0.bazel"
+ }
+ },
+ "cui__spdx-0.10.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "47317bbaf63785b53861e1ae2d11b80d6b624211d42cb20efcd210ee6f8a14bc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/spdx/0.10.6/download"
+ ],
+ "strip_prefix": "spdx-0.10.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.spdx-0.10.6.bazel"
+ }
+ },
+ "cui__static_assertions-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/static_assertions/1.1.0/download"
+ ],
+ "strip_prefix": "static_assertions-1.1.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.static_assertions-1.1.0.bazel"
+ }
+ },
+ "cui__strsim-0.10.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/strsim/0.10.0/download"
+ ],
+ "strip_prefix": "strsim-0.10.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.strsim-0.10.0.bazel"
+ }
+ },
+ "cui__syn-1.0.109": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/syn/1.0.109/download"
+ ],
+ "strip_prefix": "syn-1.0.109",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.syn-1.0.109.bazel"
+ }
+ },
+ "cui__syn-2.0.79": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/syn/2.0.79/download"
+ ],
+ "strip_prefix": "syn-2.0.79",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.syn-2.0.79.bazel"
+ }
+ },
+ "cui__tempfile-3.13.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tempfile/3.13.0/download"
+ ],
+ "strip_prefix": "tempfile-3.13.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.tempfile-3.13.0.bazel"
+ }
+ },
+ "cui__tera-1.19.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tera/1.19.1/download"
+ ],
+ "strip_prefix": "tera-1.19.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.tera-1.19.1.bazel"
+ }
+ },
+ "cui__textwrap-0.16.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/textwrap/0.16.1/download"
+ ],
+ "strip_prefix": "textwrap-0.16.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.textwrap-0.16.1.bazel"
+ }
+ },
+ "cui__thiserror-1.0.50": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/thiserror/1.0.50/download"
+ ],
+ "strip_prefix": "thiserror-1.0.50",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.thiserror-1.0.50.bazel"
+ }
+ },
+ "cui__thiserror-impl-1.0.50": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/thiserror-impl/1.0.50/download"
+ ],
+ "strip_prefix": "thiserror-impl-1.0.50",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.thiserror-impl-1.0.50.bazel"
+ }
+ },
+ "cui__thread_local-1.1.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/thread_local/1.1.4/download"
+ ],
+ "strip_prefix": "thread_local-1.1.4",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.thread_local-1.1.4.bazel"
+ }
+ },
+ "cui__tinyvec-1.6.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tinyvec/1.6.0/download"
+ ],
+ "strip_prefix": "tinyvec-1.6.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.tinyvec-1.6.0.bazel"
+ }
+ },
+ "cui__tinyvec_macros-0.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tinyvec_macros/0.1.1/download"
+ ],
+ "strip_prefix": "tinyvec_macros-0.1.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.tinyvec_macros-0.1.1.bazel"
+ }
+ },
+ "cui__toml-0.8.19": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/toml/0.8.19/download"
+ ],
+ "strip_prefix": "toml-0.8.19",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.toml-0.8.19.bazel"
+ }
+ },
+ "cui__toml_datetime-0.6.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/toml_datetime/0.6.8/download"
+ ],
+ "strip_prefix": "toml_datetime-0.6.8",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.toml_datetime-0.6.8.bazel"
+ }
+ },
+ "cui__toml_edit-0.22.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/toml_edit/0.22.22/download"
+ ],
+ "strip_prefix": "toml_edit-0.22.22",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.toml_edit-0.22.22.bazel"
+ }
+ },
+ "cui__tracing-0.1.40": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tracing/0.1.40/download"
+ ],
+ "strip_prefix": "tracing-0.1.40",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.tracing-0.1.40.bazel"
+ }
+ },
+ "cui__tracing-attributes-0.1.27": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tracing-attributes/0.1.27/download"
+ ],
+ "strip_prefix": "tracing-attributes-0.1.27",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.tracing-attributes-0.1.27.bazel"
+ }
+ },
+ "cui__tracing-core-0.1.32": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tracing-core/0.1.32/download"
+ ],
+ "strip_prefix": "tracing-core-0.1.32",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.tracing-core-0.1.32.bazel"
+ }
+ },
+ "cui__tracing-log-0.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tracing-log/0.2.0/download"
+ ],
+ "strip_prefix": "tracing-log-0.2.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.tracing-log-0.2.0.bazel"
+ }
+ },
+ "cui__tracing-subscriber-0.3.18": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tracing-subscriber/0.3.18/download"
+ ],
+ "strip_prefix": "tracing-subscriber-0.3.18",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.tracing-subscriber-0.3.18.bazel"
+ }
+ },
+ "cui__typenum-1.16.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/typenum/1.16.0/download"
+ ],
+ "strip_prefix": "typenum-1.16.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.typenum-1.16.0.bazel"
+ }
+ },
+ "cui__ucd-trie-0.1.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ucd-trie/0.1.6/download"
+ ],
+ "strip_prefix": "ucd-trie-0.1.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.ucd-trie-0.1.6.bazel"
+ }
+ },
+ "cui__uluru-3.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "794a32261a1f5eb6a4462c81b59cec87b5c27d5deea7dd1ac8fc781c41d226db",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/uluru/3.0.0/download"
+ ],
+ "strip_prefix": "uluru-3.0.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.uluru-3.0.0.bazel"
+ }
+ },
+ "cui__unic-char-property-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unic-char-property/0.9.0/download"
+ ],
+ "strip_prefix": "unic-char-property-0.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unic-char-property-0.9.0.bazel"
+ }
+ },
+ "cui__unic-char-range-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unic-char-range/0.9.0/download"
+ ],
+ "strip_prefix": "unic-char-range-0.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unic-char-range-0.9.0.bazel"
+ }
+ },
+ "cui__unic-common-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unic-common/0.9.0/download"
+ ],
+ "strip_prefix": "unic-common-0.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unic-common-0.9.0.bazel"
+ }
+ },
+ "cui__unic-segment-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unic-segment/0.9.0/download"
+ ],
+ "strip_prefix": "unic-segment-0.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unic-segment-0.9.0.bazel"
+ }
+ },
+ "cui__unic-ucd-segment-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unic-ucd-segment/0.9.0/download"
+ ],
+ "strip_prefix": "unic-ucd-segment-0.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unic-ucd-segment-0.9.0.bazel"
+ }
+ },
+ "cui__unic-ucd-version-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unic-ucd-version/0.9.0/download"
+ ],
+ "strip_prefix": "unic-ucd-version-0.9.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unic-ucd-version-0.9.0.bazel"
+ }
+ },
+ "cui__unicode-bidi-0.3.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-bidi/0.3.13/download"
+ ],
+ "strip_prefix": "unicode-bidi-0.3.13",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unicode-bidi-0.3.13.bazel"
+ }
+ },
+ "cui__unicode-bom-2.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "98e90c70c9f0d4d1ee6d0a7d04aa06cb9bbd53d8cfbdd62a0269a7c2eb640552",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-bom/2.0.2/download"
+ ],
+ "strip_prefix": "unicode-bom-2.0.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unicode-bom-2.0.2.bazel"
+ }
+ },
+ "cui__unicode-ident-1.0.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-ident/1.0.10/download"
+ ],
+ "strip_prefix": "unicode-ident-1.0.10",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unicode-ident-1.0.10.bazel"
+ }
+ },
+ "cui__unicode-linebreak-0.1.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-linebreak/0.1.5/download"
+ ],
+ "strip_prefix": "unicode-linebreak-0.1.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unicode-linebreak-0.1.5.bazel"
+ }
+ },
+ "cui__unicode-normalization-0.1.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-normalization/0.1.22/download"
+ ],
+ "strip_prefix": "unicode-normalization-0.1.22",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unicode-normalization-0.1.22.bazel"
+ }
+ },
+ "cui__unicode-width-0.1.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-width/0.1.10/download"
+ ],
+ "strip_prefix": "unicode-width-0.1.10",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.unicode-width-0.1.10.bazel"
+ }
+ },
+ "cui__url-2.5.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/url/2.5.2/download"
+ ],
+ "strip_prefix": "url-2.5.2",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.url-2.5.2.bazel"
+ }
+ },
+ "cui__utf8parse-0.2.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/utf8parse/0.2.1/download"
+ ],
+ "strip_prefix": "utf8parse-0.2.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.utf8parse-0.2.1.bazel"
+ }
+ },
+ "cui__valuable-0.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/valuable/0.1.0/download"
+ ],
+ "strip_prefix": "valuable-0.1.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.valuable-0.1.0.bazel"
+ }
+ },
+ "cui__version_check-0.9.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/version_check/0.9.4/download"
+ ],
+ "strip_prefix": "version_check-0.9.4",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.version_check-0.9.4.bazel"
+ }
+ },
+ "cui__walkdir-2.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/walkdir/2.3.3/download"
+ ],
+ "strip_prefix": "walkdir-2.3.3",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.walkdir-2.3.3.bazel"
+ }
+ },
+ "cui__winapi-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi/0.3.9/download"
+ ],
+ "strip_prefix": "winapi-0.3.9",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
+ }
+ },
+ "cui__winapi-i686-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
+ ],
+ "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
+ }
+ },
+ "cui__winapi-util-0.1.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-util/0.1.5/download"
+ ],
+ "strip_prefix": "winapi-util-0.1.5",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.winapi-util-0.1.5.bazel"
+ }
+ },
+ "cui__winapi-x86_64-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
+ ],
+ "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
+ }
+ },
+ "cui__windows-sys-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-sys/0.48.0/download"
+ ],
+ "strip_prefix": "windows-sys-0.48.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows-sys-0.48.0.bazel"
+ }
+ },
+ "cui__windows-sys-0.52.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-sys/0.52.0/download"
+ ],
+ "strip_prefix": "windows-sys-0.52.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows-sys-0.52.0.bazel"
+ }
+ },
+ "cui__windows-sys-0.59.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-sys/0.59.0/download"
+ ],
+ "strip_prefix": "windows-sys-0.59.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows-sys-0.59.0.bazel"
+ }
+ },
+ "cui__windows-targets-0.48.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-targets/0.48.1/download"
+ ],
+ "strip_prefix": "windows-targets-0.48.1",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows-targets-0.48.1.bazel"
+ }
+ },
+ "cui__windows-targets-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-targets/0.52.6/download"
+ ],
+ "strip_prefix": "windows-targets-0.52.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows-targets-0.52.6.bazel"
+ }
+ },
+ "cui__windows_aarch64_gnullvm-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_gnullvm/0.48.0/download"
+ ],
+ "strip_prefix": "windows_aarch64_gnullvm-0.48.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.48.0.bazel"
+ }
+ },
+ "cui__windows_aarch64_gnullvm-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_gnullvm/0.52.6/download"
+ ],
+ "strip_prefix": "windows_aarch64_gnullvm-0.52.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.52.6.bazel"
+ }
+ },
+ "cui__windows_aarch64_msvc-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_msvc/0.48.0/download"
+ ],
+ "strip_prefix": "windows_aarch64_msvc-0.48.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_aarch64_msvc-0.48.0.bazel"
+ }
+ },
+ "cui__windows_aarch64_msvc-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_msvc/0.52.6/download"
+ ],
+ "strip_prefix": "windows_aarch64_msvc-0.52.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_aarch64_msvc-0.52.6.bazel"
+ }
+ },
+ "cui__windows_i686_gnu-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_gnu/0.48.0/download"
+ ],
+ "strip_prefix": "windows_i686_gnu-0.48.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_i686_gnu-0.48.0.bazel"
+ }
+ },
+ "cui__windows_i686_gnu-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_gnu/0.52.6/download"
+ ],
+ "strip_prefix": "windows_i686_gnu-0.52.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_i686_gnu-0.52.6.bazel"
+ }
+ },
+ "cui__windows_i686_gnullvm-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_gnullvm/0.52.6/download"
+ ],
+ "strip_prefix": "windows_i686_gnullvm-0.52.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_i686_gnullvm-0.52.6.bazel"
+ }
+ },
+ "cui__windows_i686_msvc-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_msvc/0.48.0/download"
+ ],
+ "strip_prefix": "windows_i686_msvc-0.48.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_i686_msvc-0.48.0.bazel"
+ }
+ },
+ "cui__windows_i686_msvc-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_msvc/0.52.6/download"
+ ],
+ "strip_prefix": "windows_i686_msvc-0.52.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_i686_msvc-0.52.6.bazel"
+ }
+ },
+ "cui__windows_x86_64_gnu-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnu/0.48.0/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnu-0.48.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_x86_64_gnu-0.48.0.bazel"
+ }
+ },
+ "cui__windows_x86_64_gnu-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnu/0.52.6/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnu-0.52.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_x86_64_gnu-0.52.6.bazel"
+ }
+ },
+ "cui__windows_x86_64_gnullvm-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnullvm/0.48.0/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnullvm-0.48.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.48.0.bazel"
+ }
+ },
+ "cui__windows_x86_64_gnullvm-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnullvm/0.52.6/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnullvm-0.52.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.52.6.bazel"
+ }
+ },
+ "cui__windows_x86_64_msvc-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_msvc/0.48.0/download"
+ ],
+ "strip_prefix": "windows_x86_64_msvc-0.48.0",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_x86_64_msvc-0.48.0.bazel"
+ }
+ },
+ "cui__windows_x86_64_msvc-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_msvc/0.52.6/download"
+ ],
+ "strip_prefix": "windows_x86_64_msvc-0.52.6",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.windows_x86_64_msvc-0.52.6.bazel"
+ }
+ },
+ "cui__winnow-0.6.20": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winnow/0.6.20/download"
+ ],
+ "strip_prefix": "winnow-0.6.20",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.winnow-0.6.20.bazel"
+ }
+ },
+ "cui__zerocopy-0.7.35": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/zerocopy/0.7.35/download"
+ ],
+ "strip_prefix": "zerocopy-0.7.35",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.zerocopy-0.7.35.bazel"
+ }
+ },
+ "cui__zerocopy-derive-0.7.35": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/zerocopy-derive/0.7.35/download"
+ ],
+ "strip_prefix": "zerocopy-derive-0.7.35",
+ "build_file": "@@rules_rust+//crate_universe/3rdparty/crates:BUILD.zerocopy-derive-0.7.35.bazel"
+ }
+ },
+ "cargo_bazel.buildifier-darwin-amd64": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file",
+ "attributes": {
+ "urls": [
+ "https://github.com/bazelbuild/buildtools/releases/download/v7.3.1/buildifier-darwin-amd64"
+ ],
+ "integrity": "sha256-N1+CMQPQFiCq7CCgwpxsvKmfT9ByWuMLk2VcZwT0TXE=",
+ "downloaded_file_path": "buildifier",
+ "executable": true
+ }
+ },
+ "cargo_bazel.buildifier-darwin-arm64": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file",
+ "attributes": {
+ "urls": [
+ "https://github.com/bazelbuild/buildtools/releases/download/v7.3.1/buildifier-darwin-arm64"
+ ],
+ "integrity": "sha256-Wmr8asegn1RVuguJvZnVriO0F03F3J1sDtXOjKrD+BM=",
+ "downloaded_file_path": "buildifier",
+ "executable": true
+ }
+ },
+ "cargo_bazel.buildifier-linux-amd64": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file",
+ "attributes": {
+ "urls": [
+ "https://github.com/bazelbuild/buildtools/releases/download/v7.3.1/buildifier-linux-amd64"
+ ],
+ "integrity": "sha256-VHTMUSinToBng9VAgfWBZixL6K5lAi9VfpKB7V3IgAk=",
+ "downloaded_file_path": "buildifier",
+ "executable": true
+ }
+ },
+ "cargo_bazel.buildifier-linux-arm64": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file",
+ "attributes": {
+ "urls": [
+ "https://github.com/bazelbuild/buildtools/releases/download/v7.3.1/buildifier-linux-arm64"
+ ],
+ "integrity": "sha256-C/hsS//69PCO7Xe95bIILkrlA5oR4uiwOYTBc8NKVhw=",
+ "downloaded_file_path": "buildifier",
+ "executable": true
+ }
+ },
+ "cargo_bazel.buildifier-linux-s390x": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file",
+ "attributes": {
+ "urls": [
+ "https://github.com/bazelbuild/buildtools/releases/download/v7.3.1/buildifier-linux-s390x"
+ ],
+ "integrity": "sha256-4tef9YhdRSdPdlMfGtvHtzoSn1nnZ/d36PveYz2dTi4=",
+ "downloaded_file_path": "buildifier",
+ "executable": true
+ }
+ },
+ "cargo_bazel.buildifier-windows-amd64.exe": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file",
+ "attributes": {
+ "urls": [
+ "https://github.com/bazelbuild/buildtools/releases/download/v7.3.1/buildifier-windows-amd64.exe"
+ ],
+ "integrity": "sha256-NwzVdgda0pkwqC9d4TLxod5AhMeEqCUUvU2oDIWs9Kg=",
+ "downloaded_file_path": "buildifier.exe",
+ "executable": true
+ }
+ },
+ "rules_rust_prost": {
+ "repoRuleId": "@@rules_rust+//crate_universe/private:crates_vendor.bzl%crates_vendor_remote_repository",
+ "attributes": {
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.bazel",
+ "defs_module": "@@rules_rust+//proto/prost/private/3rdparty/crates:defs.bzl"
+ }
+ },
+ "rules_rust_prost__addr2line-0.22.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/addr2line/0.22.0/download"
+ ],
+ "strip_prefix": "addr2line-0.22.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.addr2line-0.22.0.bazel"
+ }
+ },
+ "rules_rust_prost__adler-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/adler/1.0.2/download"
+ ],
+ "strip_prefix": "adler-1.0.2",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.adler-1.0.2.bazel"
+ }
+ },
+ "rules_rust_prost__aho-corasick-1.1.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/aho-corasick/1.1.3/download"
+ ],
+ "strip_prefix": "aho-corasick-1.1.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.aho-corasick-1.1.3.bazel"
+ }
+ },
+ "rules_rust_prost__anyhow-1.0.86": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/anyhow/1.0.86/download"
+ ],
+ "strip_prefix": "anyhow-1.0.86",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.anyhow-1.0.86.bazel"
+ }
+ },
+ "rules_rust_prost__async-stream-0.3.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/async-stream/0.3.5/download"
+ ],
+ "strip_prefix": "async-stream-0.3.5",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.async-stream-0.3.5.bazel"
+ }
+ },
+ "rules_rust_prost__async-stream-impl-0.3.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/async-stream-impl/0.3.5/download"
+ ],
+ "strip_prefix": "async-stream-impl-0.3.5",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.async-stream-impl-0.3.5.bazel"
+ }
+ },
+ "rules_rust_prost__async-trait-0.1.81": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/async-trait/0.1.81/download"
+ ],
+ "strip_prefix": "async-trait-0.1.81",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.async-trait-0.1.81.bazel"
+ }
+ },
+ "rules_rust_prost__atomic-waker-1.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/atomic-waker/1.1.2/download"
+ ],
+ "strip_prefix": "atomic-waker-1.1.2",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.atomic-waker-1.1.2.bazel"
+ }
+ },
+ "rules_rust_prost__autocfg-1.3.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/autocfg/1.3.0/download"
+ ],
+ "strip_prefix": "autocfg-1.3.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.autocfg-1.3.0.bazel"
+ }
+ },
+ "rules_rust_prost__axum-0.7.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/axum/0.7.5/download"
+ ],
+ "strip_prefix": "axum-0.7.5",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.axum-0.7.5.bazel"
+ }
+ },
+ "rules_rust_prost__axum-core-0.4.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/axum-core/0.4.3/download"
+ ],
+ "strip_prefix": "axum-core-0.4.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.axum-core-0.4.3.bazel"
+ }
+ },
+ "rules_rust_prost__backtrace-0.3.73": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/backtrace/0.3.73/download"
+ ],
+ "strip_prefix": "backtrace-0.3.73",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.backtrace-0.3.73.bazel"
+ }
+ },
+ "rules_rust_prost__base64-0.22.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/base64/0.22.1/download"
+ ],
+ "strip_prefix": "base64-0.22.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.base64-0.22.1.bazel"
+ }
+ },
+ "rules_rust_prost__bitflags-2.6.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/bitflags/2.6.0/download"
+ ],
+ "strip_prefix": "bitflags-2.6.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.bitflags-2.6.0.bazel"
+ }
+ },
+ "rules_rust_prost__byteorder-1.5.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/byteorder/1.5.0/download"
+ ],
+ "strip_prefix": "byteorder-1.5.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.byteorder-1.5.0.bazel"
+ }
+ },
+ "rules_rust_prost__bytes-1.7.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/bytes/1.7.1/download"
+ ],
+ "strip_prefix": "bytes-1.7.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.bytes-1.7.1.bazel"
+ }
+ },
+ "rules_rust_prost__cc-1.1.14": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cc/1.1.14/download"
+ ],
+ "strip_prefix": "cc-1.1.14",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.cc-1.1.14.bazel"
+ }
+ },
+ "rules_rust_prost__cfg-if-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cfg-if/1.0.0/download"
+ ],
+ "strip_prefix": "cfg-if-1.0.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
+ }
+ },
+ "rules_rust_prost__either-1.13.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/either/1.13.0/download"
+ ],
+ "strip_prefix": "either-1.13.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.either-1.13.0.bazel"
+ }
+ },
+ "rules_rust_prost__equivalent-1.0.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/equivalent/1.0.1/download"
+ ],
+ "strip_prefix": "equivalent-1.0.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.equivalent-1.0.1.bazel"
+ }
+ },
+ "rules_rust_prost__errno-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/errno/0.3.9/download"
+ ],
+ "strip_prefix": "errno-0.3.9",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.errno-0.3.9.bazel"
+ }
+ },
+ "rules_rust_prost__fastrand-2.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fastrand/2.1.1/download"
+ ],
+ "strip_prefix": "fastrand-2.1.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.fastrand-2.1.1.bazel"
+ }
+ },
+ "rules_rust_prost__fixedbitset-0.4.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fixedbitset/0.4.2/download"
+ ],
+ "strip_prefix": "fixedbitset-0.4.2",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.fixedbitset-0.4.2.bazel"
+ }
+ },
+ "rules_rust_prost__fnv-1.0.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fnv/1.0.7/download"
+ ],
+ "strip_prefix": "fnv-1.0.7",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.fnv-1.0.7.bazel"
+ }
+ },
+ "rules_rust_prost__futures-channel-0.3.30": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/futures-channel/0.3.30/download"
+ ],
+ "strip_prefix": "futures-channel-0.3.30",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.futures-channel-0.3.30.bazel"
+ }
+ },
+ "rules_rust_prost__futures-core-0.3.30": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/futures-core/0.3.30/download"
+ ],
+ "strip_prefix": "futures-core-0.3.30",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.futures-core-0.3.30.bazel"
+ }
+ },
+ "rules_rust_prost__futures-sink-0.3.30": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/futures-sink/0.3.30/download"
+ ],
+ "strip_prefix": "futures-sink-0.3.30",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.futures-sink-0.3.30.bazel"
+ }
+ },
+ "rules_rust_prost__futures-task-0.3.30": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/futures-task/0.3.30/download"
+ ],
+ "strip_prefix": "futures-task-0.3.30",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.futures-task-0.3.30.bazel"
+ }
+ },
+ "rules_rust_prost__futures-util-0.3.30": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/futures-util/0.3.30/download"
+ ],
+ "strip_prefix": "futures-util-0.3.30",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.futures-util-0.3.30.bazel"
+ }
+ },
+ "rules_rust_prost__getrandom-0.2.15": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/getrandom/0.2.15/download"
+ ],
+ "strip_prefix": "getrandom-0.2.15",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.getrandom-0.2.15.bazel"
+ }
+ },
+ "rules_rust_prost__gimli-0.29.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gimli/0.29.0/download"
+ ],
+ "strip_prefix": "gimli-0.29.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.gimli-0.29.0.bazel"
+ }
+ },
+ "rules_rust_prost__h2-0.4.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/h2/0.4.6/download"
+ ],
+ "strip_prefix": "h2-0.4.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.h2-0.4.6.bazel"
+ }
+ },
+ "rules_rust_prost__hashbrown-0.12.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hashbrown/0.12.3/download"
+ ],
+ "strip_prefix": "hashbrown-0.12.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.hashbrown-0.12.3.bazel"
+ }
+ },
+ "rules_rust_prost__hashbrown-0.14.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hashbrown/0.14.5/download"
+ ],
+ "strip_prefix": "hashbrown-0.14.5",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.hashbrown-0.14.5.bazel"
+ }
+ },
+ "rules_rust_prost__heck-0.5.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/heck/0.5.0/download"
+ ],
+ "strip_prefix": "heck-0.5.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.heck-0.5.0.bazel"
+ }
+ },
+ "rules_rust_prost__hermit-abi-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hermit-abi/0.3.9/download"
+ ],
+ "strip_prefix": "hermit-abi-0.3.9",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.hermit-abi-0.3.9.bazel"
+ }
+ },
+ "rules_rust_prost__http-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/http/1.1.0/download"
+ ],
+ "strip_prefix": "http-1.1.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.http-1.1.0.bazel"
+ }
+ },
+ "rules_rust_prost__http-body-1.0.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/http-body/1.0.1/download"
+ ],
+ "strip_prefix": "http-body-1.0.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.http-body-1.0.1.bazel"
+ }
+ },
+ "rules_rust_prost__http-body-util-0.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/http-body-util/0.1.2/download"
+ ],
+ "strip_prefix": "http-body-util-0.1.2",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.http-body-util-0.1.2.bazel"
+ }
+ },
+ "rules_rust_prost__httparse-1.9.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/httparse/1.9.4/download"
+ ],
+ "strip_prefix": "httparse-1.9.4",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.httparse-1.9.4.bazel"
+ }
+ },
+ "rules_rust_prost__httpdate-1.0.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/httpdate/1.0.3/download"
+ ],
+ "strip_prefix": "httpdate-1.0.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.httpdate-1.0.3.bazel"
+ }
+ },
+ "rules_rust_prost__hyper-1.4.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hyper/1.4.1/download"
+ ],
+ "strip_prefix": "hyper-1.4.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.hyper-1.4.1.bazel"
+ }
+ },
+ "rules_rust_prost__hyper-timeout-0.5.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hyper-timeout/0.5.1/download"
+ ],
+ "strip_prefix": "hyper-timeout-0.5.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.hyper-timeout-0.5.1.bazel"
+ }
+ },
+ "rules_rust_prost__hyper-util-0.1.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hyper-util/0.1.7/download"
+ ],
+ "strip_prefix": "hyper-util-0.1.7",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.hyper-util-0.1.7.bazel"
+ }
+ },
+ "rules_rust_prost__indexmap-1.9.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/indexmap/1.9.3/download"
+ ],
+ "strip_prefix": "indexmap-1.9.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.indexmap-1.9.3.bazel"
+ }
+ },
+ "rules_rust_prost__indexmap-2.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/indexmap/2.4.0/download"
+ ],
+ "strip_prefix": "indexmap-2.4.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.indexmap-2.4.0.bazel"
+ }
+ },
+ "rules_rust_prost__itertools-0.13.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/itertools/0.13.0/download"
+ ],
+ "strip_prefix": "itertools-0.13.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.itertools-0.13.0.bazel"
+ }
+ },
+ "rules_rust_prost__itoa-1.0.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/itoa/1.0.11/download"
+ ],
+ "strip_prefix": "itoa-1.0.11",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.itoa-1.0.11.bazel"
+ }
+ },
+ "rules_rust_prost__libc-0.2.158": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/libc/0.2.158/download"
+ ],
+ "strip_prefix": "libc-0.2.158",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.libc-0.2.158.bazel"
+ }
+ },
+ "rules_rust_prost__linux-raw-sys-0.4.14": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/linux-raw-sys/0.4.14/download"
+ ],
+ "strip_prefix": "linux-raw-sys-0.4.14",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.linux-raw-sys-0.4.14.bazel"
+ }
+ },
+ "rules_rust_prost__lock_api-0.4.12": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/lock_api/0.4.12/download"
+ ],
+ "strip_prefix": "lock_api-0.4.12",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.lock_api-0.4.12.bazel"
+ }
+ },
+ "rules_rust_prost__log-0.4.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/log/0.4.22/download"
+ ],
+ "strip_prefix": "log-0.4.22",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.log-0.4.22.bazel"
+ }
+ },
+ "rules_rust_prost__matchit-0.7.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/matchit/0.7.3/download"
+ ],
+ "strip_prefix": "matchit-0.7.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.matchit-0.7.3.bazel"
+ }
+ },
+ "rules_rust_prost__memchr-2.7.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/memchr/2.7.4/download"
+ ],
+ "strip_prefix": "memchr-2.7.4",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.memchr-2.7.4.bazel"
+ }
+ },
+ "rules_rust_prost__mime-0.3.17": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/mime/0.3.17/download"
+ ],
+ "strip_prefix": "mime-0.3.17",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.mime-0.3.17.bazel"
+ }
+ },
+ "rules_rust_prost__miniz_oxide-0.7.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/miniz_oxide/0.7.4/download"
+ ],
+ "strip_prefix": "miniz_oxide-0.7.4",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.miniz_oxide-0.7.4.bazel"
+ }
+ },
+ "rules_rust_prost__mio-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/mio/1.0.2/download"
+ ],
+ "strip_prefix": "mio-1.0.2",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.mio-1.0.2.bazel"
+ }
+ },
+ "rules_rust_prost__multimap-0.10.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/multimap/0.10.0/download"
+ ],
+ "strip_prefix": "multimap-0.10.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.multimap-0.10.0.bazel"
+ }
+ },
+ "rules_rust_prost__object-0.36.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/object/0.36.3/download"
+ ],
+ "strip_prefix": "object-0.36.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.object-0.36.3.bazel"
+ }
+ },
+ "rules_rust_prost__once_cell-1.19.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/once_cell/1.19.0/download"
+ ],
+ "strip_prefix": "once_cell-1.19.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.once_cell-1.19.0.bazel"
+ }
+ },
+ "rules_rust_prost__parking_lot-0.12.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/parking_lot/0.12.3/download"
+ ],
+ "strip_prefix": "parking_lot-0.12.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.parking_lot-0.12.3.bazel"
+ }
+ },
+ "rules_rust_prost__parking_lot_core-0.9.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/parking_lot_core/0.9.10/download"
+ ],
+ "strip_prefix": "parking_lot_core-0.9.10",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.parking_lot_core-0.9.10.bazel"
+ }
+ },
+ "rules_rust_prost__percent-encoding-2.3.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/percent-encoding/2.3.1/download"
+ ],
+ "strip_prefix": "percent-encoding-2.3.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.percent-encoding-2.3.1.bazel"
+ }
+ },
+ "rules_rust_prost__petgraph-0.6.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/petgraph/0.6.5/download"
+ ],
+ "strip_prefix": "petgraph-0.6.5",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.petgraph-0.6.5.bazel"
+ }
+ },
+ "rules_rust_prost__pin-project-1.1.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pin-project/1.1.5/download"
+ ],
+ "strip_prefix": "pin-project-1.1.5",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.pin-project-1.1.5.bazel"
+ }
+ },
+ "rules_rust_prost__pin-project-internal-1.1.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pin-project-internal/1.1.5/download"
+ ],
+ "strip_prefix": "pin-project-internal-1.1.5",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.pin-project-internal-1.1.5.bazel"
+ }
+ },
+ "rules_rust_prost__pin-project-lite-0.2.14": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pin-project-lite/0.2.14/download"
+ ],
+ "strip_prefix": "pin-project-lite-0.2.14",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.pin-project-lite-0.2.14.bazel"
+ }
+ },
+ "rules_rust_prost__pin-utils-0.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/pin-utils/0.1.0/download"
+ ],
+ "strip_prefix": "pin-utils-0.1.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.pin-utils-0.1.0.bazel"
+ }
+ },
+ "rules_rust_prost__ppv-lite86-0.2.20": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ppv-lite86/0.2.20/download"
+ ],
+ "strip_prefix": "ppv-lite86-0.2.20",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.ppv-lite86-0.2.20.bazel"
+ }
+ },
+ "rules_rust_prost__prettyplease-0.2.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/prettyplease/0.2.22/download"
+ ],
+ "strip_prefix": "prettyplease-0.2.22",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.prettyplease-0.2.22.bazel"
+ }
+ },
+ "rules_rust_prost__proc-macro2-1.0.86": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/proc-macro2/1.0.86/download"
+ ],
+ "strip_prefix": "proc-macro2-1.0.86",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.proc-macro2-1.0.86.bazel"
+ }
+ },
+ "rules_rust_prost__prost-0.13.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/prost/0.13.1/download"
+ ],
+ "strip_prefix": "prost-0.13.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.prost-0.13.1.bazel"
+ }
+ },
+ "rules_rust_prost__prost-build-0.13.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/prost-build/0.13.1/download"
+ ],
+ "strip_prefix": "prost-build-0.13.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.prost-build-0.13.1.bazel"
+ }
+ },
+ "rules_rust_prost__prost-derive-0.13.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/prost-derive/0.13.1/download"
+ ],
+ "strip_prefix": "prost-derive-0.13.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.prost-derive-0.13.1.bazel"
+ }
+ },
+ "rules_rust_prost__prost-types-0.13.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/prost-types/0.13.1/download"
+ ],
+ "strip_prefix": "prost-types-0.13.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.prost-types-0.13.1.bazel"
+ }
+ },
+ "rules_rust_prost__protoc-gen-prost-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "77eb17a7657a703f30cb9b7ba4d981e4037b8af2d819ab0077514b0bef537406",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/protoc-gen-prost/0.4.0/download"
+ ],
+ "strip_prefix": "protoc-gen-prost-0.4.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.protoc-gen-prost-0.4.0.bazel"
+ }
+ },
+ "rules_rust_prost__protoc-gen-tonic-0.4.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6ab6a0d73a0914752ed8fd7cc51afe169e28da87be3efef292de5676cc527634",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/protoc-gen-tonic/0.4.1/download"
+ ],
+ "strip_prefix": "protoc-gen-tonic-0.4.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.protoc-gen-tonic-0.4.1.bazel"
+ }
+ },
+ "rules_rust_prost__quote-1.0.37": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/quote/1.0.37/download"
+ ],
+ "strip_prefix": "quote-1.0.37",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.quote-1.0.37.bazel"
+ }
+ },
+ "rules_rust_prost__rand-0.8.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rand/0.8.5/download"
+ ],
+ "strip_prefix": "rand-0.8.5",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.rand-0.8.5.bazel"
+ }
+ },
+ "rules_rust_prost__rand_chacha-0.3.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rand_chacha/0.3.1/download"
+ ],
+ "strip_prefix": "rand_chacha-0.3.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.rand_chacha-0.3.1.bazel"
+ }
+ },
+ "rules_rust_prost__rand_core-0.6.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rand_core/0.6.4/download"
+ ],
+ "strip_prefix": "rand_core-0.6.4",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.rand_core-0.6.4.bazel"
+ }
+ },
+ "rules_rust_prost__redox_syscall-0.5.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/redox_syscall/0.5.3/download"
+ ],
+ "strip_prefix": "redox_syscall-0.5.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.redox_syscall-0.5.3.bazel"
+ }
+ },
+ "rules_rust_prost__regex-1.10.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex/1.10.6/download"
+ ],
+ "strip_prefix": "regex-1.10.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.regex-1.10.6.bazel"
+ }
+ },
+ "rules_rust_prost__regex-automata-0.4.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-automata/0.4.7/download"
+ ],
+ "strip_prefix": "regex-automata-0.4.7",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.regex-automata-0.4.7.bazel"
+ }
+ },
+ "rules_rust_prost__regex-syntax-0.8.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-syntax/0.8.4/download"
+ ],
+ "strip_prefix": "regex-syntax-0.8.4",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.regex-syntax-0.8.4.bazel"
+ }
+ },
+ "rules_rust_prost__rustc-demangle-0.1.24": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustc-demangle/0.1.24/download"
+ ],
+ "strip_prefix": "rustc-demangle-0.1.24",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.rustc-demangle-0.1.24.bazel"
+ }
+ },
+ "rules_rust_prost__rustix-0.38.34": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustix/0.38.34/download"
+ ],
+ "strip_prefix": "rustix-0.38.34",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.rustix-0.38.34.bazel"
+ }
+ },
+ "rules_rust_prost__rustversion-1.0.17": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustversion/1.0.17/download"
+ ],
+ "strip_prefix": "rustversion-1.0.17",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.rustversion-1.0.17.bazel"
+ }
+ },
+ "rules_rust_prost__scopeguard-1.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/scopeguard/1.2.0/download"
+ ],
+ "strip_prefix": "scopeguard-1.2.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.scopeguard-1.2.0.bazel"
+ }
+ },
+ "rules_rust_prost__serde-1.0.209": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde/1.0.209/download"
+ ],
+ "strip_prefix": "serde-1.0.209",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.serde-1.0.209.bazel"
+ }
+ },
+ "rules_rust_prost__serde_derive-1.0.209": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde_derive/1.0.209/download"
+ ],
+ "strip_prefix": "serde_derive-1.0.209",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.serde_derive-1.0.209.bazel"
+ }
+ },
+ "rules_rust_prost__shlex-1.3.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/shlex/1.3.0/download"
+ ],
+ "strip_prefix": "shlex-1.3.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.shlex-1.3.0.bazel"
+ }
+ },
+ "rules_rust_prost__signal-hook-registry-1.4.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/signal-hook-registry/1.4.2/download"
+ ],
+ "strip_prefix": "signal-hook-registry-1.4.2",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.signal-hook-registry-1.4.2.bazel"
+ }
+ },
+ "rules_rust_prost__slab-0.4.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/slab/0.4.9/download"
+ ],
+ "strip_prefix": "slab-0.4.9",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.slab-0.4.9.bazel"
+ }
+ },
+ "rules_rust_prost__smallvec-1.13.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/smallvec/1.13.2/download"
+ ],
+ "strip_prefix": "smallvec-1.13.2",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.smallvec-1.13.2.bazel"
+ }
+ },
+ "rules_rust_prost__socket2-0.5.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/socket2/0.5.7/download"
+ ],
+ "strip_prefix": "socket2-0.5.7",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.socket2-0.5.7.bazel"
+ }
+ },
+ "rules_rust_prost__syn-2.0.76": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/syn/2.0.76/download"
+ ],
+ "strip_prefix": "syn-2.0.76",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.syn-2.0.76.bazel"
+ }
+ },
+ "rules_rust_prost__sync_wrapper-0.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/sync_wrapper/0.1.2/download"
+ ],
+ "strip_prefix": "sync_wrapper-0.1.2",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.sync_wrapper-0.1.2.bazel"
+ }
+ },
+ "rules_rust_prost__sync_wrapper-1.0.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/sync_wrapper/1.0.1/download"
+ ],
+ "strip_prefix": "sync_wrapper-1.0.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.sync_wrapper-1.0.1.bazel"
+ }
+ },
+ "rules_rust_prost__tempfile-3.12.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tempfile/3.12.0/download"
+ ],
+ "strip_prefix": "tempfile-3.12.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tempfile-3.12.0.bazel"
+ }
+ },
+ "rules_rust_prost__tokio-1.39.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio/1.39.3/download"
+ ],
+ "strip_prefix": "tokio-1.39.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tokio-1.39.3.bazel"
+ }
+ },
+ "rules_rust_prost__tokio-macros-2.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-macros/2.4.0/download"
+ ],
+ "strip_prefix": "tokio-macros-2.4.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tokio-macros-2.4.0.bazel"
+ }
+ },
+ "rules_rust_prost__tokio-stream-0.1.15": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-stream/0.1.15/download"
+ ],
+ "strip_prefix": "tokio-stream-0.1.15",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tokio-stream-0.1.15.bazel"
+ }
+ },
+ "rules_rust_prost__tokio-util-0.7.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-util/0.7.11/download"
+ ],
+ "strip_prefix": "tokio-util-0.7.11",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tokio-util-0.7.11.bazel"
+ }
+ },
+ "rules_rust_prost__tonic-0.12.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tonic/0.12.1/download"
+ ],
+ "strip_prefix": "tonic-0.12.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tonic-0.12.1.bazel"
+ }
+ },
+ "rules_rust_prost__tonic-build-0.12.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "568392c5a2bd0020723e3f387891176aabafe36fd9fcd074ad309dfa0c8eb964",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tonic-build/0.12.1/download"
+ ],
+ "strip_prefix": "tonic-build-0.12.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tonic-build-0.12.1.bazel"
+ }
+ },
+ "rules_rust_prost__tower-0.4.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tower/0.4.13/download"
+ ],
+ "strip_prefix": "tower-0.4.13",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tower-0.4.13.bazel"
+ }
+ },
+ "rules_rust_prost__tower-layer-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tower-layer/0.3.3/download"
+ ],
+ "strip_prefix": "tower-layer-0.3.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tower-layer-0.3.3.bazel"
+ }
+ },
+ "rules_rust_prost__tower-service-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tower-service/0.3.3/download"
+ ],
+ "strip_prefix": "tower-service-0.3.3",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tower-service-0.3.3.bazel"
+ }
+ },
+ "rules_rust_prost__tracing-0.1.40": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tracing/0.1.40/download"
+ ],
+ "strip_prefix": "tracing-0.1.40",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tracing-0.1.40.bazel"
+ }
+ },
+ "rules_rust_prost__tracing-attributes-0.1.27": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tracing-attributes/0.1.27/download"
+ ],
+ "strip_prefix": "tracing-attributes-0.1.27",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tracing-attributes-0.1.27.bazel"
+ }
+ },
+ "rules_rust_prost__tracing-core-0.1.32": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tracing-core/0.1.32/download"
+ ],
+ "strip_prefix": "tracing-core-0.1.32",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.tracing-core-0.1.32.bazel"
+ }
+ },
+ "rules_rust_prost__try-lock-0.2.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/try-lock/0.2.5/download"
+ ],
+ "strip_prefix": "try-lock-0.2.5",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.try-lock-0.2.5.bazel"
+ }
+ },
+ "rules_rust_prost__unicode-ident-1.0.12": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-ident/1.0.12/download"
+ ],
+ "strip_prefix": "unicode-ident-1.0.12",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.unicode-ident-1.0.12.bazel"
+ }
+ },
+ "rules_rust_prost__want-0.3.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/want/0.3.1/download"
+ ],
+ "strip_prefix": "want-0.3.1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.want-0.3.1.bazel"
+ }
+ },
+ "rules_rust_prost__wasi-0.11.0-wasi-snapshot-preview1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasi/0.11.0+wasi-snapshot-preview1/download"
+ ],
+ "strip_prefix": "wasi-0.11.0+wasi-snapshot-preview1",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.wasi-0.11.0+wasi-snapshot-preview1.bazel"
+ }
+ },
+ "rules_rust_prost__windows-sys-0.52.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-sys/0.52.0/download"
+ ],
+ "strip_prefix": "windows-sys-0.52.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows-sys-0.52.0.bazel"
+ }
+ },
+ "rules_rust_prost__windows-sys-0.59.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-sys/0.59.0/download"
+ ],
+ "strip_prefix": "windows-sys-0.59.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows-sys-0.59.0.bazel"
+ }
+ },
+ "rules_rust_prost__windows-targets-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-targets/0.52.6/download"
+ ],
+ "strip_prefix": "windows-targets-0.52.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows-targets-0.52.6.bazel"
+ }
+ },
+ "rules_rust_prost__windows_aarch64_gnullvm-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_gnullvm/0.52.6/download"
+ ],
+ "strip_prefix": "windows_aarch64_gnullvm-0.52.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.52.6.bazel"
+ }
+ },
+ "rules_rust_prost__windows_aarch64_msvc-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_msvc/0.52.6/download"
+ ],
+ "strip_prefix": "windows_aarch64_msvc-0.52.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows_aarch64_msvc-0.52.6.bazel"
+ }
+ },
+ "rules_rust_prost__windows_i686_gnu-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_gnu/0.52.6/download"
+ ],
+ "strip_prefix": "windows_i686_gnu-0.52.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows_i686_gnu-0.52.6.bazel"
+ }
+ },
+ "rules_rust_prost__windows_i686_gnullvm-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_gnullvm/0.52.6/download"
+ ],
+ "strip_prefix": "windows_i686_gnullvm-0.52.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows_i686_gnullvm-0.52.6.bazel"
+ }
+ },
+ "rules_rust_prost__windows_i686_msvc-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_msvc/0.52.6/download"
+ ],
+ "strip_prefix": "windows_i686_msvc-0.52.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows_i686_msvc-0.52.6.bazel"
+ }
+ },
+ "rules_rust_prost__windows_x86_64_gnu-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnu/0.52.6/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnu-0.52.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows_x86_64_gnu-0.52.6.bazel"
+ }
+ },
+ "rules_rust_prost__windows_x86_64_gnullvm-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnullvm/0.52.6/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnullvm-0.52.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.52.6.bazel"
+ }
+ },
+ "rules_rust_prost__windows_x86_64_msvc-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_msvc/0.52.6/download"
+ ],
+ "strip_prefix": "windows_x86_64_msvc-0.52.6",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.windows_x86_64_msvc-0.52.6.bazel"
+ }
+ },
+ "rules_rust_prost__zerocopy-0.7.35": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/zerocopy/0.7.35/download"
+ ],
+ "strip_prefix": "zerocopy-0.7.35",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.zerocopy-0.7.35.bazel"
+ }
+ },
+ "rules_rust_prost__zerocopy-derive-0.7.35": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/zerocopy-derive/0.7.35/download"
+ ],
+ "strip_prefix": "zerocopy-derive-0.7.35",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.zerocopy-derive-0.7.35.bazel"
+ }
+ },
+ "rules_rust_prost__heck": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "integrity": "sha256-IwTgCYP4f/s4tVtES147YKiEtdMMD8p9gv4zRJu+Veo=",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/heck/heck-0.5.0.crate"
+ ],
+ "strip_prefix": "heck-0.5.0",
+ "build_file": "@@rules_rust+//proto/prost/private/3rdparty/crates:BUILD.heck-0.5.0.bazel"
+ }
+ },
+ "rules_rust_proto__autocfg-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/autocfg/1.1.0/download"
+ ],
+ "strip_prefix": "autocfg-1.1.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.autocfg-1.1.0.bazel"
+ }
+ },
+ "rules_rust_proto__base64-0.9.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/base64/0.9.3/download"
+ ],
+ "strip_prefix": "base64-0.9.3",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.base64-0.9.3.bazel"
+ }
+ },
+ "rules_rust_proto__bitflags-1.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/bitflags/1.3.2/download"
+ ],
+ "strip_prefix": "bitflags-1.3.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
+ }
+ },
+ "rules_rust_proto__byteorder-1.4.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/byteorder/1.4.3/download"
+ ],
+ "strip_prefix": "byteorder-1.4.3",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.byteorder-1.4.3.bazel"
+ }
+ },
+ "rules_rust_proto__bytes-0.4.12": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/bytes/0.4.12/download"
+ ],
+ "strip_prefix": "bytes-0.4.12",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.bytes-0.4.12.bazel"
+ }
+ },
+ "rules_rust_proto__cfg-if-0.1.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cfg-if/0.1.10/download"
+ ],
+ "strip_prefix": "cfg-if-0.1.10",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.cfg-if-0.1.10.bazel"
+ }
+ },
+ "rules_rust_proto__cfg-if-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cfg-if/1.0.0/download"
+ ],
+ "strip_prefix": "cfg-if-1.0.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
+ }
+ },
+ "rules_rust_proto__cloudabi-0.0.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cloudabi/0.0.3/download"
+ ],
+ "strip_prefix": "cloudabi-0.0.3",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.cloudabi-0.0.3.bazel"
+ }
+ },
+ "rules_rust_proto__crossbeam-deque-0.7.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-deque/0.7.4/download"
+ ],
+ "strip_prefix": "crossbeam-deque-0.7.4",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.crossbeam-deque-0.7.4.bazel"
+ }
+ },
+ "rules_rust_proto__crossbeam-epoch-0.8.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-epoch/0.8.2/download"
+ ],
+ "strip_prefix": "crossbeam-epoch-0.8.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.crossbeam-epoch-0.8.2.bazel"
+ }
+ },
+ "rules_rust_proto__crossbeam-queue-0.2.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-queue/0.2.3/download"
+ ],
+ "strip_prefix": "crossbeam-queue-0.2.3",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.crossbeam-queue-0.2.3.bazel"
+ }
+ },
+ "rules_rust_proto__crossbeam-utils-0.7.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-utils/0.7.2/download"
+ ],
+ "strip_prefix": "crossbeam-utils-0.7.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.crossbeam-utils-0.7.2.bazel"
+ }
+ },
+ "rules_rust_proto__fnv-1.0.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fnv/1.0.7/download"
+ ],
+ "strip_prefix": "fnv-1.0.7",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.fnv-1.0.7.bazel"
+ }
+ },
+ "rules_rust_proto__fuchsia-zircon-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fuchsia-zircon/0.3.3/download"
+ ],
+ "strip_prefix": "fuchsia-zircon-0.3.3",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.fuchsia-zircon-0.3.3.bazel"
+ }
+ },
+ "rules_rust_proto__fuchsia-zircon-sys-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fuchsia-zircon-sys/0.3.3/download"
+ ],
+ "strip_prefix": "fuchsia-zircon-sys-0.3.3",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.fuchsia-zircon-sys-0.3.3.bazel"
+ }
+ },
+ "rules_rust_proto__futures-0.1.31": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/futures/0.1.31/download"
+ ],
+ "strip_prefix": "futures-0.1.31",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.futures-0.1.31.bazel"
+ }
+ },
+ "rules_rust_proto__futures-cpupool-0.1.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/futures-cpupool/0.1.8/download"
+ ],
+ "strip_prefix": "futures-cpupool-0.1.8",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.futures-cpupool-0.1.8.bazel"
+ }
+ },
+ "rules_rust_proto__grpc-0.6.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2aaf1d741fe6f3413f1f9f71b99f5e4e26776d563475a8a53ce53a73a8534c1d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/grpc/0.6.2/download"
+ ],
+ "strip_prefix": "grpc-0.6.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.grpc-0.6.2.bazel"
+ }
+ },
+ "rules_rust_proto__grpc-compiler-0.6.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "907274ce8ee7b40a0d0b0db09022ea22846a47cfb1fc8ad2c983c70001b4ffb1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/grpc-compiler/0.6.2/download"
+ ],
+ "strip_prefix": "grpc-compiler-0.6.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.grpc-compiler-0.6.2.bazel"
+ }
+ },
+ "rules_rust_proto__hermit-abi-0.2.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hermit-abi/0.2.6/download"
+ ],
+ "strip_prefix": "hermit-abi-0.2.6",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.hermit-abi-0.2.6.bazel"
+ }
+ },
+ "rules_rust_proto__httpbis-0.7.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7689cfa896b2a71da4f16206af167542b75d242b6906313e53857972a92d5614",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/httpbis/0.7.0/download"
+ ],
+ "strip_prefix": "httpbis-0.7.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.httpbis-0.7.0.bazel"
+ }
+ },
+ "rules_rust_proto__iovec-0.1.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/iovec/0.1.4/download"
+ ],
+ "strip_prefix": "iovec-0.1.4",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.iovec-0.1.4.bazel"
+ }
+ },
+ "rules_rust_proto__kernel32-sys-0.2.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/kernel32-sys/0.2.2/download"
+ ],
+ "strip_prefix": "kernel32-sys-0.2.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.kernel32-sys-0.2.2.bazel"
+ }
+ },
+ "rules_rust_proto__lazy_static-1.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/lazy_static/1.4.0/download"
+ ],
+ "strip_prefix": "lazy_static-1.4.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.lazy_static-1.4.0.bazel"
+ }
+ },
+ "rules_rust_proto__libc-0.2.139": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/libc/0.2.139/download"
+ ],
+ "strip_prefix": "libc-0.2.139",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.libc-0.2.139.bazel"
+ }
+ },
+ "rules_rust_proto__lock_api-0.3.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/lock_api/0.3.4/download"
+ ],
+ "strip_prefix": "lock_api-0.3.4",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.lock_api-0.3.4.bazel"
+ }
+ },
+ "rules_rust_proto__log-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/log/0.3.9/download"
+ ],
+ "strip_prefix": "log-0.3.9",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.log-0.3.9.bazel"
+ }
+ },
+ "rules_rust_proto__log-0.4.17": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/log/0.4.17/download"
+ ],
+ "strip_prefix": "log-0.4.17",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.log-0.4.17.bazel"
+ }
+ },
+ "rules_rust_proto__maybe-uninit-2.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/maybe-uninit/2.0.0/download"
+ ],
+ "strip_prefix": "maybe-uninit-2.0.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.maybe-uninit-2.0.0.bazel"
+ }
+ },
+ "rules_rust_proto__memoffset-0.5.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/memoffset/0.5.6/download"
+ ],
+ "strip_prefix": "memoffset-0.5.6",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.memoffset-0.5.6.bazel"
+ }
+ },
+ "rules_rust_proto__mio-0.6.23": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/mio/0.6.23/download"
+ ],
+ "strip_prefix": "mio-0.6.23",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.mio-0.6.23.bazel"
+ }
+ },
+ "rules_rust_proto__mio-uds-0.6.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/mio-uds/0.6.8/download"
+ ],
+ "strip_prefix": "mio-uds-0.6.8",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.mio-uds-0.6.8.bazel"
+ }
+ },
+ "rules_rust_proto__miow-0.2.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/miow/0.2.2/download"
+ ],
+ "strip_prefix": "miow-0.2.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.miow-0.2.2.bazel"
+ }
+ },
+ "rules_rust_proto__net2-0.2.38": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/net2/0.2.38/download"
+ ],
+ "strip_prefix": "net2-0.2.38",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.net2-0.2.38.bazel"
+ }
+ },
+ "rules_rust_proto__num_cpus-1.15.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/num_cpus/1.15.0/download"
+ ],
+ "strip_prefix": "num_cpus-1.15.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.num_cpus-1.15.0.bazel"
+ }
+ },
+ "rules_rust_proto__parking_lot-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/parking_lot/0.9.0/download"
+ ],
+ "strip_prefix": "parking_lot-0.9.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.parking_lot-0.9.0.bazel"
+ }
+ },
+ "rules_rust_proto__parking_lot_core-0.6.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/parking_lot_core/0.6.3/download"
+ ],
+ "strip_prefix": "parking_lot_core-0.6.3",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.parking_lot_core-0.6.3.bazel"
+ }
+ },
+ "rules_rust_proto__protobuf-2.8.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "patch_args": [
+ "-p1"
+ ],
+ "patches": [
+ "@@rules_rust+//proto/protobuf/3rdparty/patches:protobuf-2.8.2.patch"
+ ],
+ "sha256": "70731852eec72c56d11226c8a5f96ad5058a3dab73647ca5f7ee351e464f2571",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/protobuf/2.8.2/download"
+ ],
+ "strip_prefix": "protobuf-2.8.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.protobuf-2.8.2.bazel"
+ }
+ },
+ "rules_rust_proto__protobuf-codegen-2.8.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3d74b9cbbf2ac9a7169c85a3714ec16c51ee9ec7cfd511549527e9a7df720795",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/protobuf-codegen/2.8.2/download"
+ ],
+ "strip_prefix": "protobuf-codegen-2.8.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.protobuf-codegen-2.8.2.bazel"
+ }
+ },
+ "rules_rust_proto__redox_syscall-0.1.57": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/redox_syscall/0.1.57/download"
+ ],
+ "strip_prefix": "redox_syscall-0.1.57",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.redox_syscall-0.1.57.bazel"
+ }
+ },
+ "rules_rust_proto__rustc_version-0.2.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustc_version/0.2.3/download"
+ ],
+ "strip_prefix": "rustc_version-0.2.3",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.rustc_version-0.2.3.bazel"
+ }
+ },
+ "rules_rust_proto__safemem-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/safemem/0.3.3/download"
+ ],
+ "strip_prefix": "safemem-0.3.3",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.safemem-0.3.3.bazel"
+ }
+ },
+ "rules_rust_proto__scoped-tls-0.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/scoped-tls/0.1.2/download"
+ ],
+ "strip_prefix": "scoped-tls-0.1.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.scoped-tls-0.1.2.bazel"
+ }
+ },
+ "rules_rust_proto__scopeguard-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/scopeguard/1.1.0/download"
+ ],
+ "strip_prefix": "scopeguard-1.1.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.scopeguard-1.1.0.bazel"
+ }
+ },
+ "rules_rust_proto__semver-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/semver/0.9.0/download"
+ ],
+ "strip_prefix": "semver-0.9.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.semver-0.9.0.bazel"
+ }
+ },
+ "rules_rust_proto__semver-parser-0.7.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/semver-parser/0.7.0/download"
+ ],
+ "strip_prefix": "semver-parser-0.7.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.semver-parser-0.7.0.bazel"
+ }
+ },
+ "rules_rust_proto__slab-0.3.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/slab/0.3.0/download"
+ ],
+ "strip_prefix": "slab-0.3.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.slab-0.3.0.bazel"
+ }
+ },
+ "rules_rust_proto__slab-0.4.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/slab/0.4.7/download"
+ ],
+ "strip_prefix": "slab-0.4.7",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.slab-0.4.7.bazel"
+ }
+ },
+ "rules_rust_proto__smallvec-0.6.14": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/smallvec/0.6.14/download"
+ ],
+ "strip_prefix": "smallvec-0.6.14",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.smallvec-0.6.14.bazel"
+ }
+ },
+ "rules_rust_proto__tls-api-0.1.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "049c03787a0595182357fbd487577947f4351b78ce20c3668f6d49f17feb13d1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tls-api/0.1.22/download"
+ ],
+ "strip_prefix": "tls-api-0.1.22",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tls-api-0.1.22.bazel"
+ }
+ },
+ "rules_rust_proto__tls-api-stub-0.1.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c9a0cc8c149724db9de7d73a0e1bc80b1a74f5394f08c6f301e11f9c35fa061e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tls-api-stub/0.1.22/download"
+ ],
+ "strip_prefix": "tls-api-stub-0.1.22",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tls-api-stub-0.1.22.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-0.1.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio/0.1.22/download"
+ ],
+ "strip_prefix": "tokio-0.1.22",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-0.1.22.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-codec-0.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-codec/0.1.2/download"
+ ],
+ "strip_prefix": "tokio-codec-0.1.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-codec-0.1.2.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-core-0.1.18": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-core/0.1.18/download"
+ ],
+ "strip_prefix": "tokio-core-0.1.18",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-core-0.1.18.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-current-thread-0.1.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-current-thread/0.1.7/download"
+ ],
+ "strip_prefix": "tokio-current-thread-0.1.7",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-current-thread-0.1.7.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-executor-0.1.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-executor/0.1.10/download"
+ ],
+ "strip_prefix": "tokio-executor-0.1.10",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-executor-0.1.10.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-fs-0.1.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-fs/0.1.7/download"
+ ],
+ "strip_prefix": "tokio-fs-0.1.7",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-fs-0.1.7.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-io-0.1.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-io/0.1.13/download"
+ ],
+ "strip_prefix": "tokio-io-0.1.13",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-io-0.1.13.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-reactor-0.1.12": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-reactor/0.1.12/download"
+ ],
+ "strip_prefix": "tokio-reactor-0.1.12",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-reactor-0.1.12.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-sync-0.1.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-sync/0.1.8/download"
+ ],
+ "strip_prefix": "tokio-sync-0.1.8",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-sync-0.1.8.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-tcp-0.1.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-tcp/0.1.4/download"
+ ],
+ "strip_prefix": "tokio-tcp-0.1.4",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-tcp-0.1.4.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-threadpool-0.1.18": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-threadpool/0.1.18/download"
+ ],
+ "strip_prefix": "tokio-threadpool-0.1.18",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-threadpool-0.1.18.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-timer-0.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-timer/0.1.2/download"
+ ],
+ "strip_prefix": "tokio-timer-0.1.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-timer-0.1.2.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-timer-0.2.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-timer/0.2.13/download"
+ ],
+ "strip_prefix": "tokio-timer-0.2.13",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-timer-0.2.13.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-tls-api-0.1.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "68d0e040d5b1f4cfca70ec4f371229886a5de5bb554d272a4a8da73004a7b2c9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-tls-api/0.1.22/download"
+ ],
+ "strip_prefix": "tokio-tls-api-0.1.22",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-tls-api-0.1.22.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-udp-0.1.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-udp/0.1.6/download"
+ ],
+ "strip_prefix": "tokio-udp-0.1.6",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-udp-0.1.6.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-uds-0.1.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "65ae5d255ce739e8537221ed2942e0445f4b3b813daebac1c0050ddaaa3587f9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-uds/0.1.7/download"
+ ],
+ "strip_prefix": "tokio-uds-0.1.7",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-uds-0.1.7.bazel"
+ }
+ },
+ "rules_rust_proto__tokio-uds-0.2.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tokio-uds/0.2.7/download"
+ ],
+ "strip_prefix": "tokio-uds-0.2.7",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.tokio-uds-0.2.7.bazel"
+ }
+ },
+ "rules_rust_proto__unix_socket-0.5.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unix_socket/0.5.0/download"
+ ],
+ "strip_prefix": "unix_socket-0.5.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.unix_socket-0.5.0.bazel"
+ }
+ },
+ "rules_rust_proto__void-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/void/1.0.2/download"
+ ],
+ "strip_prefix": "void-1.0.2",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.void-1.0.2.bazel"
+ }
+ },
+ "rules_rust_proto__winapi-0.2.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi/0.2.8/download"
+ ],
+ "strip_prefix": "winapi-0.2.8",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.winapi-0.2.8.bazel"
+ }
+ },
+ "rules_rust_proto__winapi-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi/0.3.9/download"
+ ],
+ "strip_prefix": "winapi-0.3.9",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
+ }
+ },
+ "rules_rust_proto__winapi-build-0.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-build/0.1.1/download"
+ ],
+ "strip_prefix": "winapi-build-0.1.1",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.winapi-build-0.1.1.bazel"
+ }
+ },
+ "rules_rust_proto__winapi-i686-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
+ ],
+ "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
+ }
+ },
+ "rules_rust_proto__winapi-x86_64-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
+ ],
+ "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
+ }
+ },
+ "rules_rust_proto__ws2_32-sys-0.2.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ws2_32-sys/0.2.1/download"
+ ],
+ "strip_prefix": "ws2_32-sys-0.2.1",
+ "build_file": "@@rules_rust+//proto/protobuf/3rdparty/crates:BUILD.ws2_32-sys-0.2.1.bazel"
}
},
"llvm-raw": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"urls": [
"https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.6/llvm-project-14.0.6.src.tar.xz"
@@ -11390,300 +6178,625 @@
"-p1"
],
"patches": [
- "@@rules_rust~//bindgen/3rdparty/patches:llvm-project.cxx17.patch",
- "@@rules_rust~//bindgen/3rdparty/patches:llvm-project.incompatible_disallow_empty_glob.patch"
+ "@@rules_rust+//bindgen/3rdparty/patches:llvm-project.cxx17.patch",
+ "@@rules_rust+//bindgen/3rdparty/patches:llvm-project.incompatible_disallow_empty_glob.patch"
]
}
},
- "cui__phf_codegen-0.11.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__bindgen-cli-0.70.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a",
+ "integrity": "sha256-Mz+eRtWNh1r7irkjwi27fmF4j1WtKPK12Yv5ENkL1ao=",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/phf_codegen/0.11.2/download"
+ "https://static.crates.io/crates/bindgen-cli/bindgen-cli-0.70.1.crate"
],
- "strip_prefix": "phf_codegen-0.11.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.phf_codegen-0.11.2.bazel"
+ "strip_prefix": "bindgen-cli-0.70.1",
+ "build_file": "@@rules_rust+//bindgen/3rdparty:BUILD.bindgen-cli.bazel"
}
},
- "cui__winapi-util-0.1.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__aho-corasick-1.1.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178",
+ "sha256": "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/winapi-util/0.1.5/download"
+ "https://static.crates.io/crates/aho-corasick/1.1.3/download"
],
- "strip_prefix": "winapi-util-0.1.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.winapi-util-0.1.5.bazel"
+ "strip_prefix": "aho-corasick-1.1.3",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.aho-corasick-1.1.3.bazel"
}
},
- "rules_rust_proto__tokio-current-thread-0.1.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__annotate-snippets-0.9.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e",
+ "sha256": "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/tokio-current-thread/0.1.7/download"
+ "https://static.crates.io/crates/annotate-snippets/0.9.2/download"
],
- "strip_prefix": "tokio-current-thread-0.1.7",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-current-thread-0.1.7.bazel"
+ "strip_prefix": "annotate-snippets-0.9.2",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.annotate-snippets-0.9.2.bazel"
}
},
- "cui__crossbeam-deque-0.8.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__anstream-0.6.15": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef",
+ "sha256": "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/crossbeam-deque/0.8.3/download"
+ "https://static.crates.io/crates/anstream/0.6.15/download"
],
- "strip_prefix": "crossbeam-deque-0.8.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.crossbeam-deque-0.8.3.bazel"
+ "strip_prefix": "anstream-0.6.15",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.anstream-0.6.15.bazel"
}
},
- "cui__android_system_properties-0.1.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__anstyle-1.0.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311",
+ "sha256": "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/android_system_properties/0.1.5/download"
+ "https://static.crates.io/crates/anstyle/1.0.8/download"
],
- "strip_prefix": "android_system_properties-0.1.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.android_system_properties-0.1.5.bazel"
+ "strip_prefix": "anstyle-1.0.8",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.anstyle-1.0.8.bazel"
}
},
- "cui__pest_meta-2.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__anstyle-parse-0.2.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0",
+ "sha256": "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/pest_meta/2.7.0/download"
+ "https://static.crates.io/crates/anstyle-parse/0.2.5/download"
],
- "strip_prefix": "pest_meta-2.7.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.pest_meta-2.7.0.bazel"
+ "strip_prefix": "anstyle-parse-0.2.5",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.anstyle-parse-0.2.5.bazel"
}
},
- "cui__anstyle-wincon-1.0.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__anstyle-query-1.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188",
+ "sha256": "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/anstyle-wincon/1.0.1/download"
+ "https://static.crates.io/crates/anstyle-query/1.1.1/download"
],
- "strip_prefix": "anstyle-wincon-1.0.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.anstyle-wincon-1.0.1.bazel"
+ "strip_prefix": "anstyle-query-1.1.1",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.anstyle-query-1.1.1.bazel"
}
},
- "rrra__anstyle-query-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__anstyle-wincon-3.0.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b",
+ "sha256": "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/anstyle-query/1.0.0/download"
+ "https://static.crates.io/crates/anstyle-wincon/3.0.4/download"
],
- "strip_prefix": "anstyle-query-1.0.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-query-1.0.0.bazel"
+ "strip_prefix": "anstyle-wincon-3.0.4",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.anstyle-wincon-3.0.4.bazel"
}
},
- "rrra__clap_derive-4.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__bindgen-0.70.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f",
+ "sha256": "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/clap_derive/4.3.2/download"
+ "https://static.crates.io/crates/bindgen/0.70.1/download"
],
- "strip_prefix": "clap_derive-4.3.2",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.clap_derive-4.3.2.bazel"
+ "strip_prefix": "bindgen-0.70.1",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.bindgen-0.70.1.bazel"
}
},
- "cui__gix-hash-0.13.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__bitflags-2.6.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "1884c7b41ea0875217c1be9ce91322f90bde433e91d374d0e1276073a51ccc60",
+ "sha256": "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/gix-hash/0.13.1/download"
+ "https://static.crates.io/crates/bitflags/2.6.0/download"
],
- "strip_prefix": "gix-hash-0.13.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-hash-0.13.1.bazel"
+ "strip_prefix": "bitflags-2.6.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.bitflags-2.6.0.bazel"
}
},
- "cui__maybe-async-0.2.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__cexpr-0.6.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305",
+ "sha256": "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/maybe-async/0.2.7/download"
+ "https://static.crates.io/crates/cexpr/0.6.0/download"
],
- "strip_prefix": "maybe-async-0.2.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.maybe-async-0.2.7.bazel"
+ "strip_prefix": "cexpr-0.6.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.cexpr-0.6.0.bazel"
}
},
- "cui__gix-filter-0.5.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__cfg-if-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "1be40d28cd41445bb6cd52c4d847d915900e5466f7433eaee6a9e0a3d1d88b08",
+ "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/gix-filter/0.5.0/download"
+ "https://static.crates.io/crates/cfg-if/1.0.0/download"
],
- "strip_prefix": "gix-filter-0.5.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-filter-0.5.0.bazel"
+ "strip_prefix": "cfg-if-1.0.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
}
},
- "rules_rust_wasm_bindgen__mime-0.3.17": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__clang-sys-1.8.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a",
+ "sha256": "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/mime/0.3.17/download"
+ "https://static.crates.io/crates/clang-sys/1.8.1/download"
],
- "strip_prefix": "mime-0.3.17",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.mime-0.3.17.bazel"
+ "strip_prefix": "clang-sys-1.8.1",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.clang-sys-1.8.1.bazel"
}
},
- "rrra__rustix-0.37.23": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__clap-4.5.17": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06",
+ "sha256": "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/rustix/0.37.23/download"
+ "https://static.crates.io/crates/clap/4.5.17/download"
],
- "strip_prefix": "rustix-0.37.23",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.rustix-0.37.23.bazel"
+ "strip_prefix": "clap-4.5.17",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.clap-4.5.17.bazel"
}
},
- "rules_rust_prost__hermit-abi-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__clap_builder-4.5.17": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286",
+ "sha256": "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/hermit-abi/0.3.1/download"
+ "https://static.crates.io/crates/clap_builder/4.5.17/download"
],
- "strip_prefix": "hermit-abi-0.3.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.hermit-abi-0.3.1.bazel"
+ "strip_prefix": "clap_builder-4.5.17",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.clap_builder-4.5.17.bazel"
}
},
- "cui__maplit-1.0.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__clap_complete-4.5.26": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d",
+ "sha256": "205d5ef6d485fa47606b98b0ddc4ead26eb850aaa86abfb562a94fb3280ecba0",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/maplit/1.0.2/download"
+ "https://static.crates.io/crates/clap_complete/4.5.26/download"
],
- "strip_prefix": "maplit-1.0.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.maplit-1.0.2.bazel"
+ "strip_prefix": "clap_complete-4.5.26",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.clap_complete-4.5.26.bazel"
}
},
- "rrra__syn-2.0.25": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__clap_derive-4.5.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2",
+ "sha256": "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/syn/2.0.25/download"
+ "https://static.crates.io/crates/clap_derive/4.5.13/download"
],
- "strip_prefix": "syn-2.0.25",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.syn-2.0.25.bazel"
+ "strip_prefix": "clap_derive-4.5.13",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.clap_derive-4.5.13.bazel"
}
},
- "cui__gix-worktree-0.26.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__clap_lex-0.7.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "9f5e32972801bd82d56609e6fc84efc358fa1f11f25c5e83b7807ee2280f14fe",
+ "sha256": "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/gix-worktree/0.26.0/download"
+ "https://static.crates.io/crates/clap_lex/0.7.2/download"
],
- "strip_prefix": "gix-worktree-0.26.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-worktree-0.26.0.bazel"
+ "strip_prefix": "clap_lex-0.7.2",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.clap_lex-0.7.2.bazel"
}
},
- "rules_rust_wasm_bindgen__semver-1.0.17": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__colorchoice-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed",
+ "sha256": "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/semver/1.0.17/download"
+ "https://static.crates.io/crates/colorchoice/1.0.2/download"
],
- "strip_prefix": "semver-1.0.17",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.semver-1.0.17.bazel"
+ "strip_prefix": "colorchoice-1.0.2",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.colorchoice-1.0.2.bazel"
}
},
- "cui__once_cell-1.18.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__either-1.13.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d",
+ "sha256": "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/once_cell/1.18.0/download"
+ "https://static.crates.io/crates/either/1.13.0/download"
],
- "strip_prefix": "once_cell-1.18.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.once_cell-1.18.0.bazel"
+ "strip_prefix": "either-1.13.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.either-1.13.0.bazel"
}
},
- "rules_rust_wasm_bindgen__wasmparser-0.80.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__env_logger-0.10.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b",
+ "sha256": "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/wasmparser/0.80.2/download"
+ "https://static.crates.io/crates/env_logger/0.10.2/download"
],
- "strip_prefix": "wasmparser-0.80.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasmparser-0.80.2.bazel"
+ "strip_prefix": "env_logger-0.10.2",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.env_logger-0.10.2.bazel"
}
},
- "cui__heck-0.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__glob-0.3.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8",
+ "sha256": "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b",
"type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/heck/0.4.1/download"
+ "https://static.crates.io/crates/glob/0.3.1/download"
],
- "strip_prefix": "heck-0.4.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.heck-0.4.1.bazel"
+ "strip_prefix": "glob-0.3.1",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.glob-0.3.1.bazel"
}
},
- "rules_rust_wasm_bindgen__winapi-x86_64-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_bindgen__heck-0.5.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/heck/0.5.0/download"
+ ],
+ "strip_prefix": "heck-0.5.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.heck-0.5.0.bazel"
+ }
+ },
+ "rules_rust_bindgen__hermit-abi-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hermit-abi/0.4.0/download"
+ ],
+ "strip_prefix": "hermit-abi-0.4.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.hermit-abi-0.4.0.bazel"
+ }
+ },
+ "rules_rust_bindgen__humantime-2.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/humantime/2.1.0/download"
+ ],
+ "strip_prefix": "humantime-2.1.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.humantime-2.1.0.bazel"
+ }
+ },
+ "rules_rust_bindgen__is-terminal-0.4.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/is-terminal/0.4.13/download"
+ ],
+ "strip_prefix": "is-terminal-0.4.13",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.is-terminal-0.4.13.bazel"
+ }
+ },
+ "rules_rust_bindgen__is_terminal_polyfill-1.70.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/is_terminal_polyfill/1.70.1/download"
+ ],
+ "strip_prefix": "is_terminal_polyfill-1.70.1",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.is_terminal_polyfill-1.70.1.bazel"
+ }
+ },
+ "rules_rust_bindgen__itertools-0.13.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/itertools/0.13.0/download"
+ ],
+ "strip_prefix": "itertools-0.13.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.itertools-0.13.0.bazel"
+ }
+ },
+ "rules_rust_bindgen__libc-0.2.158": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/libc/0.2.158/download"
+ ],
+ "strip_prefix": "libc-0.2.158",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.libc-0.2.158.bazel"
+ }
+ },
+ "rules_rust_bindgen__libloading-0.8.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/libloading/0.8.5/download"
+ ],
+ "strip_prefix": "libloading-0.8.5",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.libloading-0.8.5.bazel"
+ }
+ },
+ "rules_rust_bindgen__log-0.4.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/log/0.4.22/download"
+ ],
+ "strip_prefix": "log-0.4.22",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.log-0.4.22.bazel"
+ }
+ },
+ "rules_rust_bindgen__memchr-2.7.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/memchr/2.7.4/download"
+ ],
+ "strip_prefix": "memchr-2.7.4",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.memchr-2.7.4.bazel"
+ }
+ },
+ "rules_rust_bindgen__minimal-lexical-0.2.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/minimal-lexical/0.2.1/download"
+ ],
+ "strip_prefix": "minimal-lexical-0.2.1",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.minimal-lexical-0.2.1.bazel"
+ }
+ },
+ "rules_rust_bindgen__nom-7.1.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/nom/7.1.3/download"
+ ],
+ "strip_prefix": "nom-7.1.3",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.nom-7.1.3.bazel"
+ }
+ },
+ "rules_rust_bindgen__prettyplease-0.2.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/prettyplease/0.2.22/download"
+ ],
+ "strip_prefix": "prettyplease-0.2.22",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.prettyplease-0.2.22.bazel"
+ }
+ },
+ "rules_rust_bindgen__proc-macro2-1.0.86": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/proc-macro2/1.0.86/download"
+ ],
+ "strip_prefix": "proc-macro2-1.0.86",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.proc-macro2-1.0.86.bazel"
+ }
+ },
+ "rules_rust_bindgen__quote-1.0.37": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/quote/1.0.37/download"
+ ],
+ "strip_prefix": "quote-1.0.37",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.quote-1.0.37.bazel"
+ }
+ },
+ "rules_rust_bindgen__regex-1.10.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex/1.10.6/download"
+ ],
+ "strip_prefix": "regex-1.10.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.regex-1.10.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__regex-automata-0.4.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-automata/0.4.7/download"
+ ],
+ "strip_prefix": "regex-automata-0.4.7",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.regex-automata-0.4.7.bazel"
+ }
+ },
+ "rules_rust_bindgen__regex-syntax-0.8.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-syntax/0.8.4/download"
+ ],
+ "strip_prefix": "regex-syntax-0.8.4",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.regex-syntax-0.8.4.bazel"
+ }
+ },
+ "rules_rust_bindgen__rustc-hash-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustc-hash/1.1.0/download"
+ ],
+ "strip_prefix": "rustc-hash-1.1.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.rustc-hash-1.1.0.bazel"
+ }
+ },
+ "rules_rust_bindgen__shlex-1.3.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/shlex/1.3.0/download"
+ ],
+ "strip_prefix": "shlex-1.3.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.shlex-1.3.0.bazel"
+ }
+ },
+ "rules_rust_bindgen__strsim-0.11.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/strsim/0.11.1/download"
+ ],
+ "strip_prefix": "strsim-0.11.1",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.strsim-0.11.1.bazel"
+ }
+ },
+ "rules_rust_bindgen__syn-2.0.77": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/syn/2.0.77/download"
+ ],
+ "strip_prefix": "syn-2.0.77",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.syn-2.0.77.bazel"
+ }
+ },
+ "rules_rust_bindgen__termcolor-1.4.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/termcolor/1.4.1/download"
+ ],
+ "strip_prefix": "termcolor-1.4.1",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.termcolor-1.4.1.bazel"
+ }
+ },
+ "rules_rust_bindgen__unicode-ident-1.0.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-ident/1.0.13/download"
+ ],
+ "strip_prefix": "unicode-ident-1.0.13",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.unicode-ident-1.0.13.bazel"
+ }
+ },
+ "rules_rust_bindgen__unicode-width-0.1.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-width/0.1.13/download"
+ ],
+ "strip_prefix": "unicode-width-0.1.13",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.unicode-width-0.1.13.bazel"
+ }
+ },
+ "rules_rust_bindgen__utf8parse-0.2.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/utf8parse/0.2.2/download"
+ ],
+ "strip_prefix": "utf8parse-0.2.2",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.utf8parse-0.2.2.bazel"
+ }
+ },
+ "rules_rust_bindgen__winapi-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi/0.3.9/download"
+ ],
+ "strip_prefix": "winapi-0.3.9",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
+ }
+ },
+ "rules_rust_bindgen__winapi-i686-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
+ ],
+ "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
+ }
+ },
+ "rules_rust_bindgen__winapi-util-0.1.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-util/0.1.9/download"
+ ],
+ "strip_prefix": "winapi-util-0.1.9",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.winapi-util-0.1.9.bazel"
+ }
+ },
+ "rules_rust_bindgen__winapi-x86_64-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
"type": "tar.gz",
@@ -11691,12 +6804,2977 @@
"https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
],
"strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
}
},
+ "rules_rust_bindgen__windows-sys-0.52.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-sys/0.52.0/download"
+ ],
+ "strip_prefix": "windows-sys-0.52.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows-sys-0.52.0.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows-sys-0.59.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-sys/0.59.0/download"
+ ],
+ "strip_prefix": "windows-sys-0.59.0",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows-sys-0.59.0.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows-targets-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-targets/0.52.6/download"
+ ],
+ "strip_prefix": "windows-targets-0.52.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows-targets-0.52.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows_aarch64_gnullvm-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_gnullvm/0.52.6/download"
+ ],
+ "strip_prefix": "windows_aarch64_gnullvm-0.52.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.52.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows_aarch64_msvc-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_msvc/0.52.6/download"
+ ],
+ "strip_prefix": "windows_aarch64_msvc-0.52.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows_aarch64_msvc-0.52.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows_i686_gnu-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_gnu/0.52.6/download"
+ ],
+ "strip_prefix": "windows_i686_gnu-0.52.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows_i686_gnu-0.52.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows_i686_gnullvm-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_gnullvm/0.52.6/download"
+ ],
+ "strip_prefix": "windows_i686_gnullvm-0.52.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows_i686_gnullvm-0.52.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows_i686_msvc-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_msvc/0.52.6/download"
+ ],
+ "strip_prefix": "windows_i686_msvc-0.52.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows_i686_msvc-0.52.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows_x86_64_gnu-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnu/0.52.6/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnu-0.52.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows_x86_64_gnu-0.52.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows_x86_64_gnullvm-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnullvm/0.52.6/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnullvm-0.52.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.52.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__windows_x86_64_msvc-0.52.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_msvc/0.52.6/download"
+ ],
+ "strip_prefix": "windows_x86_64_msvc-0.52.6",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.windows_x86_64_msvc-0.52.6.bazel"
+ }
+ },
+ "rules_rust_bindgen__yansi-term-0.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/yansi-term/0.1.2/download"
+ ],
+ "strip_prefix": "yansi-term-0.1.2",
+ "build_file": "@@rules_rust+//bindgen/3rdparty/crates:BUILD.yansi-term-0.1.2.bazel"
+ }
+ },
+ "rrra__aho-corasick-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/aho-corasick/1.0.2/download"
+ ],
+ "strip_prefix": "aho-corasick-1.0.2",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.aho-corasick-1.0.2.bazel"
+ }
+ },
+ "rrra__anstream-0.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/anstream/0.3.2/download"
+ ],
+ "strip_prefix": "anstream-0.3.2",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstream-0.3.2.bazel"
+ }
+ },
+ "rrra__anstyle-1.0.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/anstyle/1.0.1/download"
+ ],
+ "strip_prefix": "anstyle-1.0.1",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-1.0.1.bazel"
+ }
+ },
+ "rrra__anstyle-parse-0.2.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/anstyle-parse/0.2.1/download"
+ ],
+ "strip_prefix": "anstyle-parse-0.2.1",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-parse-0.2.1.bazel"
+ }
+ },
+ "rrra__anstyle-query-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/anstyle-query/1.0.0/download"
+ ],
+ "strip_prefix": "anstyle-query-1.0.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-query-1.0.0.bazel"
+ }
+ },
+ "rrra__anstyle-wincon-1.0.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/anstyle-wincon/1.0.1/download"
+ ],
+ "strip_prefix": "anstyle-wincon-1.0.1",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-wincon-1.0.1.bazel"
+ }
+ },
+ "rrra__anyhow-1.0.71": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/anyhow/1.0.71/download"
+ ],
+ "strip_prefix": "anyhow-1.0.71",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anyhow-1.0.71.bazel"
+ }
+ },
+ "rrra__bitflags-1.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/bitflags/1.3.2/download"
+ ],
+ "strip_prefix": "bitflags-1.3.2",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
+ }
+ },
+ "rrra__cc-1.0.79": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cc/1.0.79/download"
+ ],
+ "strip_prefix": "cc-1.0.79",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.cc-1.0.79.bazel"
+ }
+ },
+ "rrra__clap-4.3.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/clap/4.3.11/download"
+ ],
+ "strip_prefix": "clap-4.3.11",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.clap-4.3.11.bazel"
+ }
+ },
+ "rrra__clap_builder-4.3.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/clap_builder/4.3.11/download"
+ ],
+ "strip_prefix": "clap_builder-4.3.11",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.clap_builder-4.3.11.bazel"
+ }
+ },
+ "rrra__clap_derive-4.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/clap_derive/4.3.2/download"
+ ],
+ "strip_prefix": "clap_derive-4.3.2",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.clap_derive-4.3.2.bazel"
+ }
+ },
+ "rrra__clap_lex-0.5.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/clap_lex/0.5.0/download"
+ ],
+ "strip_prefix": "clap_lex-0.5.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.clap_lex-0.5.0.bazel"
+ }
+ },
+ "rrra__colorchoice-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/colorchoice/1.0.0/download"
+ ],
+ "strip_prefix": "colorchoice-1.0.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.colorchoice-1.0.0.bazel"
+ }
+ },
+ "rrra__either-1.8.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/either/1.8.1/download"
+ ],
+ "strip_prefix": "either-1.8.1",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.either-1.8.1.bazel"
+ }
+ },
+ "rrra__env_logger-0.10.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/env_logger/0.10.0/download"
+ ],
+ "strip_prefix": "env_logger-0.10.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.env_logger-0.10.0.bazel"
+ }
+ },
+ "rrra__errno-0.3.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/errno/0.3.1/download"
+ ],
+ "strip_prefix": "errno-0.3.1",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.errno-0.3.1.bazel"
+ }
+ },
+ "rrra__errno-dragonfly-0.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/errno-dragonfly/0.1.2/download"
+ ],
+ "strip_prefix": "errno-dragonfly-0.1.2",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.errno-dragonfly-0.1.2.bazel"
+ }
+ },
+ "rrra__heck-0.4.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/heck/0.4.1/download"
+ ],
+ "strip_prefix": "heck-0.4.1",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.heck-0.4.1.bazel"
+ }
+ },
+ "rrra__hermit-abi-0.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hermit-abi/0.3.2/download"
+ ],
+ "strip_prefix": "hermit-abi-0.3.2",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.hermit-abi-0.3.2.bazel"
+ }
+ },
+ "rrra__humantime-2.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/humantime/2.1.0/download"
+ ],
+ "strip_prefix": "humantime-2.1.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.humantime-2.1.0.bazel"
+ }
+ },
+ "rrra__io-lifetimes-1.0.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/io-lifetimes/1.0.11/download"
+ ],
+ "strip_prefix": "io-lifetimes-1.0.11",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.io-lifetimes-1.0.11.bazel"
+ }
+ },
+ "rrra__is-terminal-0.4.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/is-terminal/0.4.7/download"
+ ],
+ "strip_prefix": "is-terminal-0.4.7",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.is-terminal-0.4.7.bazel"
+ }
+ },
+ "rrra__itertools-0.11.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/itertools/0.11.0/download"
+ ],
+ "strip_prefix": "itertools-0.11.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.itertools-0.11.0.bazel"
+ }
+ },
+ "rrra__itoa-1.0.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/itoa/1.0.8/download"
+ ],
+ "strip_prefix": "itoa-1.0.8",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.itoa-1.0.8.bazel"
+ }
+ },
+ "rrra__libc-0.2.147": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/libc/0.2.147/download"
+ ],
+ "strip_prefix": "libc-0.2.147",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.libc-0.2.147.bazel"
+ }
+ },
+ "rrra__linux-raw-sys-0.3.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/linux-raw-sys/0.3.8/download"
+ ],
+ "strip_prefix": "linux-raw-sys-0.3.8",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.linux-raw-sys-0.3.8.bazel"
+ }
+ },
+ "rrra__log-0.4.19": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/log/0.4.19/download"
+ ],
+ "strip_prefix": "log-0.4.19",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.log-0.4.19.bazel"
+ }
+ },
+ "rrra__memchr-2.5.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/memchr/2.5.0/download"
+ ],
+ "strip_prefix": "memchr-2.5.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.memchr-2.5.0.bazel"
+ }
+ },
+ "rrra__once_cell-1.18.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/once_cell/1.18.0/download"
+ ],
+ "strip_prefix": "once_cell-1.18.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.once_cell-1.18.0.bazel"
+ }
+ },
+ "rrra__proc-macro2-1.0.64": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/proc-macro2/1.0.64/download"
+ ],
+ "strip_prefix": "proc-macro2-1.0.64",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.proc-macro2-1.0.64.bazel"
+ }
+ },
+ "rrra__quote-1.0.29": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/quote/1.0.29/download"
+ ],
+ "strip_prefix": "quote-1.0.29",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.quote-1.0.29.bazel"
+ }
+ },
+ "rrra__regex-1.9.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex/1.9.1/download"
+ ],
+ "strip_prefix": "regex-1.9.1",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.regex-1.9.1.bazel"
+ }
+ },
+ "rrra__regex-automata-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-automata/0.3.3/download"
+ ],
+ "strip_prefix": "regex-automata-0.3.3",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.regex-automata-0.3.3.bazel"
+ }
+ },
+ "rrra__regex-syntax-0.7.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-syntax/0.7.4/download"
+ ],
+ "strip_prefix": "regex-syntax-0.7.4",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.regex-syntax-0.7.4.bazel"
+ }
+ },
+ "rrra__rustix-0.37.23": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustix/0.37.23/download"
+ ],
+ "strip_prefix": "rustix-0.37.23",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.rustix-0.37.23.bazel"
+ }
+ },
+ "rrra__ryu-1.0.14": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ryu/1.0.14/download"
+ ],
+ "strip_prefix": "ryu-1.0.14",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.ryu-1.0.14.bazel"
+ }
+ },
+ "rrra__serde-1.0.171": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde/1.0.171/download"
+ ],
+ "strip_prefix": "serde-1.0.171",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.serde-1.0.171.bazel"
+ }
+ },
+ "rrra__serde_derive-1.0.171": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde_derive/1.0.171/download"
+ ],
+ "strip_prefix": "serde_derive-1.0.171",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.serde_derive-1.0.171.bazel"
+ }
+ },
+ "rrra__serde_json-1.0.102": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde_json/1.0.102/download"
+ ],
+ "strip_prefix": "serde_json-1.0.102",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.serde_json-1.0.102.bazel"
+ }
+ },
+ "rrra__strsim-0.10.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/strsim/0.10.0/download"
+ ],
+ "strip_prefix": "strsim-0.10.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.strsim-0.10.0.bazel"
+ }
+ },
+ "rrra__syn-2.0.25": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/syn/2.0.25/download"
+ ],
+ "strip_prefix": "syn-2.0.25",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.syn-2.0.25.bazel"
+ }
+ },
+ "rrra__termcolor-1.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/termcolor/1.2.0/download"
+ ],
+ "strip_prefix": "termcolor-1.2.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.termcolor-1.2.0.bazel"
+ }
+ },
+ "rrra__unicode-ident-1.0.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-ident/1.0.10/download"
+ ],
+ "strip_prefix": "unicode-ident-1.0.10",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.unicode-ident-1.0.10.bazel"
+ }
+ },
+ "rrra__utf8parse-0.2.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/utf8parse/0.2.1/download"
+ ],
+ "strip_prefix": "utf8parse-0.2.1",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.utf8parse-0.2.1.bazel"
+ }
+ },
+ "rrra__winapi-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi/0.3.9/download"
+ ],
+ "strip_prefix": "winapi-0.3.9",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
+ }
+ },
+ "rrra__winapi-i686-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
+ ],
+ "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
+ }
+ },
+ "rrra__winapi-util-0.1.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-util/0.1.5/download"
+ ],
+ "strip_prefix": "winapi-util-0.1.5",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-util-0.1.5.bazel"
+ }
+ },
+ "rrra__winapi-x86_64-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
+ ],
+ "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
+ }
+ },
+ "rrra__windows-sys-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-sys/0.48.0/download"
+ ],
+ "strip_prefix": "windows-sys-0.48.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows-sys-0.48.0.bazel"
+ }
+ },
+ "rrra__windows-targets-0.48.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-targets/0.48.1/download"
+ ],
+ "strip_prefix": "windows-targets-0.48.1",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows-targets-0.48.1.bazel"
+ }
+ },
+ "rrra__windows_aarch64_gnullvm-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_gnullvm/0.48.0/download"
+ ],
+ "strip_prefix": "windows_aarch64_gnullvm-0.48.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.48.0.bazel"
+ }
+ },
+ "rrra__windows_aarch64_msvc-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_msvc/0.48.0/download"
+ ],
+ "strip_prefix": "windows_aarch64_msvc-0.48.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_aarch64_msvc-0.48.0.bazel"
+ }
+ },
+ "rrra__windows_i686_gnu-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_gnu/0.48.0/download"
+ ],
+ "strip_prefix": "windows_i686_gnu-0.48.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_i686_gnu-0.48.0.bazel"
+ }
+ },
+ "rrra__windows_i686_msvc-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_msvc/0.48.0/download"
+ ],
+ "strip_prefix": "windows_i686_msvc-0.48.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_i686_msvc-0.48.0.bazel"
+ }
+ },
+ "rrra__windows_x86_64_gnu-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnu/0.48.0/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnu-0.48.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_x86_64_gnu-0.48.0.bazel"
+ }
+ },
+ "rrra__windows_x86_64_gnullvm-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnullvm/0.48.0/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnullvm-0.48.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.48.0.bazel"
+ }
+ },
+ "rrra__windows_x86_64_msvc-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_msvc/0.48.0/download"
+ ],
+ "strip_prefix": "windows_x86_64_msvc-0.48.0",
+ "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_x86_64_msvc-0.48.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen_cli": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "08f61e21873f51e3059a8c7c3eef81ede7513d161cfc60751c7b2ffa6ed28270",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-cli/wasm-bindgen-cli-0.2.92.crate"
+ ],
+ "type": "tar.gz",
+ "strip_prefix": "wasm-bindgen-cli-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty:BUILD.wasm-bindgen-cli.bazel",
+ "patch_args": [
+ "-p1"
+ ],
+ "patches": [
+ "@@rules_rust+//wasm_bindgen/3rdparty/patches:resolver.patch"
+ ]
+ }
+ },
+ "rules_rust_wasm_bindgen__adler-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/adler/1.0.2/download"
+ ],
+ "strip_prefix": "adler-1.0.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.adler-1.0.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__aho-corasick-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/aho-corasick/1.0.2/download"
+ ],
+ "strip_prefix": "aho-corasick-1.0.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.aho-corasick-1.0.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__alloc-no-stdlib-2.0.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/alloc-no-stdlib/2.0.4/download"
+ ],
+ "strip_prefix": "alloc-no-stdlib-2.0.4",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.alloc-no-stdlib-2.0.4.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__alloc-stdlib-0.2.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/alloc-stdlib/0.2.2/download"
+ ],
+ "strip_prefix": "alloc-stdlib-0.2.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.alloc-stdlib-0.2.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__android-tzdata-0.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/android-tzdata/0.1.1/download"
+ ],
+ "strip_prefix": "android-tzdata-0.1.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.android-tzdata-0.1.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__android_system_properties-0.1.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/android_system_properties/0.1.5/download"
+ ],
+ "strip_prefix": "android_system_properties-0.1.5",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.android_system_properties-0.1.5.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__anyhow-1.0.71": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/anyhow/1.0.71/download"
+ ],
+ "strip_prefix": "anyhow-1.0.71",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.anyhow-1.0.71.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__ascii-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ascii/1.1.0/download"
+ ],
+ "strip_prefix": "ascii-1.1.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.ascii-1.1.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__assert_cmd-1.0.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/assert_cmd/1.0.8/download"
+ ],
+ "strip_prefix": "assert_cmd-1.0.8",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.assert_cmd-1.0.8.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__atty-0.2.14": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/atty/0.2.14/download"
+ ],
+ "strip_prefix": "atty-0.2.14",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.atty-0.2.14.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__autocfg-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/autocfg/1.1.0/download"
+ ],
+ "strip_prefix": "autocfg-1.1.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.autocfg-1.1.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__base64-0.13.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/base64/0.13.1/download"
+ ],
+ "strip_prefix": "base64-0.13.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.base64-0.13.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__base64-0.21.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/base64/0.21.5/download"
+ ],
+ "strip_prefix": "base64-0.21.5",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.base64-0.21.5.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__bitflags-1.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/bitflags/1.3.2/download"
+ ],
+ "strip_prefix": "bitflags-1.3.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.bitflags-1.3.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__brotli-decompressor-2.5.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/brotli-decompressor/2.5.1/download"
+ ],
+ "strip_prefix": "brotli-decompressor-2.5.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.brotli-decompressor-2.5.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__bstr-0.2.17": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/bstr/0.2.17/download"
+ ],
+ "strip_prefix": "bstr-0.2.17",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.bstr-0.2.17.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__buf_redux-0.8.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/buf_redux/0.8.4/download"
+ ],
+ "strip_prefix": "buf_redux-0.8.4",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.buf_redux-0.8.4.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__bumpalo-3.13.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/bumpalo/3.13.0/download"
+ ],
+ "strip_prefix": "bumpalo-3.13.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.bumpalo-3.13.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__cc-1.0.83": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cc/1.0.83/download"
+ ],
+ "strip_prefix": "cc-1.0.83",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.cc-1.0.83.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__cfg-if-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/cfg-if/1.0.0/download"
+ ],
+ "strip_prefix": "cfg-if-1.0.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__chrono-0.4.26": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/chrono/0.4.26/download"
+ ],
+ "strip_prefix": "chrono-0.4.26",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.chrono-0.4.26.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__chunked_transfer-1.4.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/chunked_transfer/1.4.1/download"
+ ],
+ "strip_prefix": "chunked_transfer-1.4.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.chunked_transfer-1.4.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__core-foundation-sys-0.8.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/core-foundation-sys/0.8.4/download"
+ ],
+ "strip_prefix": "core-foundation-sys-0.8.4",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.core-foundation-sys-0.8.4.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__crc32fast-1.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crc32fast/1.3.2/download"
+ ],
+ "strip_prefix": "crc32fast-1.3.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.crc32fast-1.3.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__crossbeam-channel-0.5.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-channel/0.5.8/download"
+ ],
+ "strip_prefix": "crossbeam-channel-0.5.8",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.crossbeam-channel-0.5.8.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__crossbeam-deque-0.8.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-deque/0.8.3/download"
+ ],
+ "strip_prefix": "crossbeam-deque-0.8.3",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.crossbeam-deque-0.8.3.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__crossbeam-epoch-0.9.15": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-epoch/0.9.15/download"
+ ],
+ "strip_prefix": "crossbeam-epoch-0.9.15",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.crossbeam-epoch-0.9.15.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__crossbeam-utils-0.8.16": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/crossbeam-utils/0.8.16/download"
+ ],
+ "strip_prefix": "crossbeam-utils-0.8.16",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.crossbeam-utils-0.8.16.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__diff-0.1.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/diff/0.1.13/download"
+ ],
+ "strip_prefix": "diff-0.1.13",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.diff-0.1.13.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__difference-2.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/difference/2.0.0/download"
+ ],
+ "strip_prefix": "difference-2.0.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.difference-2.0.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__difflib-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/difflib/0.4.0/download"
+ ],
+ "strip_prefix": "difflib-0.4.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.difflib-0.4.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__doc-comment-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/doc-comment/0.3.3/download"
+ ],
+ "strip_prefix": "doc-comment-0.3.3",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.doc-comment-0.3.3.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__docopt-1.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/docopt/1.1.1/download"
+ ],
+ "strip_prefix": "docopt-1.1.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.docopt-1.1.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__either-1.8.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/either/1.8.1/download"
+ ],
+ "strip_prefix": "either-1.8.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.either-1.8.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__env_logger-0.8.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/env_logger/0.8.4/download"
+ ],
+ "strip_prefix": "env_logger-0.8.4",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.env_logger-0.8.4.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__equivalent-1.0.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/equivalent/1.0.1/download"
+ ],
+ "strip_prefix": "equivalent-1.0.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.equivalent-1.0.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__errno-0.3.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/errno/0.3.1/download"
+ ],
+ "strip_prefix": "errno-0.3.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.errno-0.3.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__errno-dragonfly-0.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/errno-dragonfly/0.1.2/download"
+ ],
+ "strip_prefix": "errno-dragonfly-0.1.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.errno-dragonfly-0.1.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__fallible-iterator-0.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fallible-iterator/0.2.0/download"
+ ],
+ "strip_prefix": "fallible-iterator-0.2.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.fallible-iterator-0.2.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__fastrand-1.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/fastrand/1.9.0/download"
+ ],
+ "strip_prefix": "fastrand-1.9.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.fastrand-1.9.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__filetime-0.2.21": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/filetime/0.2.21/download"
+ ],
+ "strip_prefix": "filetime-0.2.21",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.filetime-0.2.21.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__flate2-1.0.28": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/flate2/1.0.28/download"
+ ],
+ "strip_prefix": "flate2-1.0.28",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.flate2-1.0.28.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__float-cmp-0.8.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/float-cmp/0.8.0/download"
+ ],
+ "strip_prefix": "float-cmp-0.8.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.float-cmp-0.8.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__form_urlencoded-1.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/form_urlencoded/1.2.0/download"
+ ],
+ "strip_prefix": "form_urlencoded-1.2.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.form_urlencoded-1.2.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__getrandom-0.2.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/getrandom/0.2.10/download"
+ ],
+ "strip_prefix": "getrandom-0.2.10",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.getrandom-0.2.10.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__gimli-0.26.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/gimli/0.26.2/download"
+ ],
+ "strip_prefix": "gimli-0.26.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.gimli-0.26.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__hashbrown-0.12.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hashbrown/0.12.3/download"
+ ],
+ "strip_prefix": "hashbrown-0.12.3",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.hashbrown-0.12.3.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__hashbrown-0.14.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hashbrown/0.14.0/download"
+ ],
+ "strip_prefix": "hashbrown-0.14.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.hashbrown-0.14.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__heck-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/heck/0.3.3/download"
+ ],
+ "strip_prefix": "heck-0.3.3",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.heck-0.3.3.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__hermit-abi-0.1.19": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hermit-abi/0.1.19/download"
+ ],
+ "strip_prefix": "hermit-abi-0.1.19",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.hermit-abi-0.1.19.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__hermit-abi-0.3.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/hermit-abi/0.3.2/download"
+ ],
+ "strip_prefix": "hermit-abi-0.3.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.hermit-abi-0.3.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__httparse-1.8.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/httparse/1.8.0/download"
+ ],
+ "strip_prefix": "httparse-1.8.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.httparse-1.8.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__httpdate-1.0.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/httpdate/1.0.2/download"
+ ],
+ "strip_prefix": "httpdate-1.0.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.httpdate-1.0.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__humantime-2.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/humantime/2.1.0/download"
+ ],
+ "strip_prefix": "humantime-2.1.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.humantime-2.1.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__iana-time-zone-0.1.57": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/iana-time-zone/0.1.57/download"
+ ],
+ "strip_prefix": "iana-time-zone-0.1.57",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.iana-time-zone-0.1.57.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__iana-time-zone-haiku-0.1.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/iana-time-zone-haiku/0.1.2/download"
+ ],
+ "strip_prefix": "iana-time-zone-haiku-0.1.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.iana-time-zone-haiku-0.1.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__id-arena-2.2.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/id-arena/2.2.1/download"
+ ],
+ "strip_prefix": "id-arena-2.2.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.id-arena-2.2.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__idna-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/idna/0.4.0/download"
+ ],
+ "strip_prefix": "idna-0.4.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.idna-0.4.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__indexmap-1.9.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/indexmap/1.9.3/download"
+ ],
+ "strip_prefix": "indexmap-1.9.3",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.indexmap-1.9.3.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__indexmap-2.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/indexmap/2.0.0/download"
+ ],
+ "strip_prefix": "indexmap-2.0.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.indexmap-2.0.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__instant-0.1.12": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/instant/0.1.12/download"
+ ],
+ "strip_prefix": "instant-0.1.12",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.instant-0.1.12.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__io-lifetimes-1.0.11": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/io-lifetimes/1.0.11/download"
+ ],
+ "strip_prefix": "io-lifetimes-1.0.11",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.io-lifetimes-1.0.11.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__itertools-0.10.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/itertools/0.10.5/download"
+ ],
+ "strip_prefix": "itertools-0.10.5",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.itertools-0.10.5.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__itoa-1.0.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/itoa/1.0.8/download"
+ ],
+ "strip_prefix": "itoa-1.0.8",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.itoa-1.0.8.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__js-sys-0.3.64": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/js-sys/0.3.64/download"
+ ],
+ "strip_prefix": "js-sys-0.3.64",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.js-sys-0.3.64.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__lazy_static-1.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/lazy_static/1.4.0/download"
+ ],
+ "strip_prefix": "lazy_static-1.4.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.lazy_static-1.4.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__leb128-0.2.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/leb128/0.2.5/download"
+ ],
+ "strip_prefix": "leb128-0.2.5",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.leb128-0.2.5.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__libc-0.2.150": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/libc/0.2.150/download"
+ ],
+ "strip_prefix": "libc-0.2.150",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.libc-0.2.150.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__linux-raw-sys-0.3.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/linux-raw-sys/0.3.8/download"
+ ],
+ "strip_prefix": "linux-raw-sys-0.3.8",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.linux-raw-sys-0.3.8.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__log-0.4.19": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/log/0.4.19/download"
+ ],
+ "strip_prefix": "log-0.4.19",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.log-0.4.19.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__memchr-2.5.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/memchr/2.5.0/download"
+ ],
+ "strip_prefix": "memchr-2.5.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.memchr-2.5.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__memoffset-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/memoffset/0.9.0/download"
+ ],
+ "strip_prefix": "memoffset-0.9.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.memoffset-0.9.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__mime-0.3.17": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/mime/0.3.17/download"
+ ],
+ "strip_prefix": "mime-0.3.17",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.mime-0.3.17.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__mime_guess-2.0.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/mime_guess/2.0.4/download"
+ ],
+ "strip_prefix": "mime_guess-2.0.4",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.mime_guess-2.0.4.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__miniz_oxide-0.7.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/miniz_oxide/0.7.1/download"
+ ],
+ "strip_prefix": "miniz_oxide-0.7.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.miniz_oxide-0.7.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__multipart-0.18.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/multipart/0.18.0/download"
+ ],
+ "strip_prefix": "multipart-0.18.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.multipart-0.18.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__normalize-line-endings-0.3.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/normalize-line-endings/0.3.0/download"
+ ],
+ "strip_prefix": "normalize-line-endings-0.3.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.normalize-line-endings-0.3.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__num-traits-0.2.15": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/num-traits/0.2.15/download"
+ ],
+ "strip_prefix": "num-traits-0.2.15",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.num-traits-0.2.15.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__num_cpus-1.16.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/num_cpus/1.16.0/download"
+ ],
+ "strip_prefix": "num_cpus-1.16.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.num_cpus-1.16.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__num_threads-0.1.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/num_threads/0.1.6/download"
+ ],
+ "strip_prefix": "num_threads-0.1.6",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.num_threads-0.1.6.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__once_cell-1.18.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/once_cell/1.18.0/download"
+ ],
+ "strip_prefix": "once_cell-1.18.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.once_cell-1.18.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__percent-encoding-2.3.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/percent-encoding/2.3.0/download"
+ ],
+ "strip_prefix": "percent-encoding-2.3.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.percent-encoding-2.3.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__ppv-lite86-0.2.17": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ppv-lite86/0.2.17/download"
+ ],
+ "strip_prefix": "ppv-lite86-0.2.17",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.ppv-lite86-0.2.17.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__predicates-1.0.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/predicates/1.0.8/download"
+ ],
+ "strip_prefix": "predicates-1.0.8",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.predicates-1.0.8.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__predicates-2.1.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/predicates/2.1.5/download"
+ ],
+ "strip_prefix": "predicates-2.1.5",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.predicates-2.1.5.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__predicates-core-1.0.6": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/predicates-core/1.0.6/download"
+ ],
+ "strip_prefix": "predicates-core-1.0.6",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.predicates-core-1.0.6.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__predicates-tree-1.0.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/predicates-tree/1.0.9/download"
+ ],
+ "strip_prefix": "predicates-tree-1.0.9",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.predicates-tree-1.0.9.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__proc-macro2-1.0.64": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/proc-macro2/1.0.64/download"
+ ],
+ "strip_prefix": "proc-macro2-1.0.64",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.proc-macro2-1.0.64.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__quick-error-1.2.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/quick-error/1.2.3/download"
+ ],
+ "strip_prefix": "quick-error-1.2.3",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.quick-error-1.2.3.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__quote-1.0.29": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/quote/1.0.29/download"
+ ],
+ "strip_prefix": "quote-1.0.29",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.quote-1.0.29.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rand-0.8.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rand/0.8.5/download"
+ ],
+ "strip_prefix": "rand-0.8.5",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rand-0.8.5.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rand_chacha-0.3.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rand_chacha/0.3.1/download"
+ ],
+ "strip_prefix": "rand_chacha-0.3.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rand_chacha-0.3.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rand_core-0.6.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rand_core/0.6.4/download"
+ ],
+ "strip_prefix": "rand_core-0.6.4",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rand_core-0.6.4.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rayon-1.7.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rayon/1.7.0/download"
+ ],
+ "strip_prefix": "rayon-1.7.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rayon-1.7.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rayon-core-1.11.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rayon-core/1.11.0/download"
+ ],
+ "strip_prefix": "rayon-core-1.11.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rayon-core-1.11.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__redox_syscall-0.2.16": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/redox_syscall/0.2.16/download"
+ ],
+ "strip_prefix": "redox_syscall-0.2.16",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.redox_syscall-0.2.16.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__redox_syscall-0.3.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/redox_syscall/0.3.5/download"
+ ],
+ "strip_prefix": "redox_syscall-0.3.5",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.redox_syscall-0.3.5.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__regex-1.9.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex/1.9.1/download"
+ ],
+ "strip_prefix": "regex-1.9.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.regex-1.9.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__regex-automata-0.1.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-automata/0.1.10/download"
+ ],
+ "strip_prefix": "regex-automata-0.1.10",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.regex-automata-0.1.10.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__regex-automata-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-automata/0.3.3/download"
+ ],
+ "strip_prefix": "regex-automata-0.3.3",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.regex-automata-0.3.3.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__regex-syntax-0.7.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/regex-syntax/0.7.4/download"
+ ],
+ "strip_prefix": "regex-syntax-0.7.4",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.regex-syntax-0.7.4.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__ring-0.17.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ring/0.17.5/download"
+ ],
+ "strip_prefix": "ring-0.17.5",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.ring-0.17.5.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rouille-3.6.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rouille/3.6.2/download"
+ ],
+ "strip_prefix": "rouille-3.6.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rouille-3.6.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rustc-demangle-0.1.23": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustc-demangle/0.1.23/download"
+ ],
+ "strip_prefix": "rustc-demangle-0.1.23",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rustc-demangle-0.1.23.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rustix-0.37.23": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustix/0.37.23/download"
+ ],
+ "strip_prefix": "rustix-0.37.23",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rustix-0.37.23.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rustls-0.21.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustls/0.21.8/download"
+ ],
+ "strip_prefix": "rustls-0.21.8",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rustls-0.21.8.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__rustls-webpki-0.101.7": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/rustls-webpki/0.101.7/download"
+ ],
+ "strip_prefix": "rustls-webpki-0.101.7",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.rustls-webpki-0.101.7.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__ryu-1.0.14": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ryu/1.0.14/download"
+ ],
+ "strip_prefix": "ryu-1.0.14",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.ryu-1.0.14.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__safemem-0.3.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/safemem/0.3.3/download"
+ ],
+ "strip_prefix": "safemem-0.3.3",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.safemem-0.3.3.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__scopeguard-1.1.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/scopeguard/1.1.0/download"
+ ],
+ "strip_prefix": "scopeguard-1.1.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.scopeguard-1.1.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__sct-0.7.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/sct/0.7.1/download"
+ ],
+ "strip_prefix": "sct-0.7.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.sct-0.7.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__semver-1.0.17": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/semver/1.0.17/download"
+ ],
+ "strip_prefix": "semver-1.0.17",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.semver-1.0.17.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__serde-1.0.171": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde/1.0.171/download"
+ ],
+ "strip_prefix": "serde-1.0.171",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.serde-1.0.171.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__serde_derive-1.0.171": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde_derive/1.0.171/download"
+ ],
+ "strip_prefix": "serde_derive-1.0.171",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.serde_derive-1.0.171.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__serde_json-1.0.102": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/serde_json/1.0.102/download"
+ ],
+ "strip_prefix": "serde_json-1.0.102",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.serde_json-1.0.102.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__sha1_smol-1.0.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/sha1_smol/1.0.0/download"
+ ],
+ "strip_prefix": "sha1_smol-1.0.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.sha1_smol-1.0.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__spin-0.9.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/spin/0.9.8/download"
+ ],
+ "strip_prefix": "spin-0.9.8",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.spin-0.9.8.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__stable_deref_trait-1.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/stable_deref_trait/1.2.0/download"
+ ],
+ "strip_prefix": "stable_deref_trait-1.2.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.stable_deref_trait-1.2.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__strsim-0.10.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/strsim/0.10.0/download"
+ ],
+ "strip_prefix": "strsim-0.10.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.strsim-0.10.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__syn-1.0.109": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/syn/1.0.109/download"
+ ],
+ "strip_prefix": "syn-1.0.109",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.syn-1.0.109.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__syn-2.0.25": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/syn/2.0.25/download"
+ ],
+ "strip_prefix": "syn-2.0.25",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.syn-2.0.25.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__tempfile-3.6.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tempfile/3.6.0/download"
+ ],
+ "strip_prefix": "tempfile-3.6.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.tempfile-3.6.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__termcolor-1.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/termcolor/1.2.0/download"
+ ],
+ "strip_prefix": "termcolor-1.2.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.termcolor-1.2.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__termtree-0.4.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/termtree/0.4.1/download"
+ ],
+ "strip_prefix": "termtree-0.4.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.termtree-0.4.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__threadpool-1.8.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/threadpool/1.8.1/download"
+ ],
+ "strip_prefix": "threadpool-1.8.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.threadpool-1.8.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__time-0.3.23": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/time/0.3.23/download"
+ ],
+ "strip_prefix": "time-0.3.23",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.time-0.3.23.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__time-core-0.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/time-core/0.1.1/download"
+ ],
+ "strip_prefix": "time-core-0.1.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.time-core-0.1.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__tiny_http-0.12.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tiny_http/0.12.0/download"
+ ],
+ "strip_prefix": "tiny_http-0.12.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.tiny_http-0.12.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__tinyvec-1.6.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tinyvec/1.6.0/download"
+ ],
+ "strip_prefix": "tinyvec-1.6.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.tinyvec-1.6.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__tinyvec_macros-0.1.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/tinyvec_macros/0.1.1/download"
+ ],
+ "strip_prefix": "tinyvec_macros-0.1.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.tinyvec_macros-0.1.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__twoway-0.1.8": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/twoway/0.1.8/download"
+ ],
+ "strip_prefix": "twoway-0.1.8",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.twoway-0.1.8.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__unicase-2.6.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicase/2.6.0/download"
+ ],
+ "strip_prefix": "unicase-2.6.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.unicase-2.6.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__unicode-bidi-0.3.13": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-bidi/0.3.13/download"
+ ],
+ "strip_prefix": "unicode-bidi-0.3.13",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.unicode-bidi-0.3.13.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__unicode-ident-1.0.10": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-ident/1.0.10/download"
+ ],
+ "strip_prefix": "unicode-ident-1.0.10",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.unicode-ident-1.0.10.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__unicode-normalization-0.1.22": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-normalization/0.1.22/download"
+ ],
+ "strip_prefix": "unicode-normalization-0.1.22",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.unicode-normalization-0.1.22.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__unicode-segmentation-1.10.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/unicode-segmentation/1.10.1/download"
+ ],
+ "strip_prefix": "unicode-segmentation-1.10.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.unicode-segmentation-1.10.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__untrusted-0.9.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/untrusted/0.9.0/download"
+ ],
+ "strip_prefix": "untrusted-0.9.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.untrusted-0.9.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__ureq-2.8.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/ureq/2.8.0/download"
+ ],
+ "strip_prefix": "ureq-2.8.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.ureq-2.8.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__url-2.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/url/2.4.0/download"
+ ],
+ "strip_prefix": "url-2.4.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.url-2.4.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__version_check-0.9.4": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/version_check/0.9.4/download"
+ ],
+ "strip_prefix": "version_check-0.9.4",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.version_check-0.9.4.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wait-timeout-0.2.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wait-timeout/0.2.0/download"
+ ],
+ "strip_prefix": "wait-timeout-0.2.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wait-timeout-0.2.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__walrus-0.20.3": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/walrus/0.20.3/download"
+ ],
+ "strip_prefix": "walrus-0.20.3",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.walrus-0.20.3.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__walrus-macro-0.19.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/walrus-macro/0.19.0/download"
+ ],
+ "strip_prefix": "walrus-macro-0.19.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.walrus-macro-0.19.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasi-0.11.0-wasi-snapshot-preview1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasi/0.11.0+wasi-snapshot-preview1/download"
+ ],
+ "strip_prefix": "wasi-0.11.0+wasi-snapshot-preview1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasi-0.11.0+wasi-snapshot-preview1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-backend-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-backend/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-backend-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-backend-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ca821da8c1ae6c87c5e94493939a206daa8587caff227c6032e0061a3d80817f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-cli-support/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-cli-support-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-cli-support-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-externref-xform-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "102582726b35a30d53157fbf8de3d0f0fed4c40c0c7951d69a034e9ef01da725",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-externref-xform/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-externref-xform-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-externref-xform-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-macro-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-macro/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-macro-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-macro-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-macro-support-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-macro-support/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-macro-support-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-macro-support-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-multi-value-xform-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "3498e4799f43523d780ceff498f04d882a8dbc9719c28020034822e5952f32a4",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-multi-value-xform/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-multi-value-xform-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-multi-value-xform-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-shared/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-shared-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-shared-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-threads-xform-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "2d5add359b7f7d09a55299a9d29be54414264f2b8cf84f8c8fda5be9269b5dd9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-threads-xform/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-threads-xform-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-threads-xform-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-wasm-conventions-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "8c04e3607b810e76768260db3a5f2e8beb477cb089ef8726da85c8eb9bd3b575",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-wasm-conventions/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-wasm-conventions-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-wasm-conventions-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-bindgen-wasm-interpreter-0.2.92": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "9ea966593c8243a33eb4d643254eb97a69de04e89462f46cf6b4f506aae89b3a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-bindgen-wasm-interpreter/0.2.92/download"
+ ],
+ "strip_prefix": "wasm-bindgen-wasm-interpreter-0.2.92",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-wasm-interpreter-0.2.92.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasm-encoder-0.29.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasm-encoder/0.29.0/download"
+ ],
+ "strip_prefix": "wasm-encoder-0.29.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasm-encoder-0.29.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasmparser-0.102.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasmparser/0.102.0/download"
+ ],
+ "strip_prefix": "wasmparser-0.102.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasmparser-0.102.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasmparser-0.108.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "76c956109dcb41436a39391139d9b6e2d0a5e0b158e1293ef352ec977e5e36c5",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasmparser/0.108.0/download"
+ ],
+ "strip_prefix": "wasmparser-0.108.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasmparser-0.108.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasmparser-0.80.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasmparser/0.80.2/download"
+ ],
+ "strip_prefix": "wasmparser-0.80.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasmparser-0.80.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__wasmprinter-0.2.60": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b76cb909fe3d9b0de58cee1f4072247e680ff5cc1558ccad2790a9de14a23993",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/wasmprinter/0.2.60/download"
+ ],
+ "strip_prefix": "wasmprinter-0.2.60",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.wasmprinter-0.2.60.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__webpki-roots-0.25.2": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/webpki-roots/0.25.2/download"
+ ],
+ "strip_prefix": "webpki-roots-0.25.2",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.webpki-roots-0.25.2.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__winapi-0.3.9": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi/0.3.9/download"
+ ],
+ "strip_prefix": "winapi-0.3.9",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__winapi-i686-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
+ ],
+ "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__winapi-util-0.1.5": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-util/0.1.5/download"
+ ],
+ "strip_prefix": "winapi-util-0.1.5",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.winapi-util-0.1.5.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__winapi-x86_64-pc-windows-gnu-0.4.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download"
+ ],
+ "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows/0.48.0/download"
+ ],
+ "strip_prefix": "windows-0.48.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows-0.48.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows-sys-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-sys/0.48.0/download"
+ ],
+ "strip_prefix": "windows-sys-0.48.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows-sys-0.48.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows-targets-0.48.1": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows-targets/0.48.1/download"
+ ],
+ "strip_prefix": "windows-targets-0.48.1",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows-targets-0.48.1.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows_aarch64_gnullvm-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_gnullvm/0.48.0/download"
+ ],
+ "strip_prefix": "windows_aarch64_gnullvm-0.48.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.48.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows_aarch64_msvc-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_aarch64_msvc/0.48.0/download"
+ ],
+ "strip_prefix": "windows_aarch64_msvc-0.48.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows_aarch64_msvc-0.48.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows_i686_gnu-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_gnu/0.48.0/download"
+ ],
+ "strip_prefix": "windows_i686_gnu-0.48.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows_i686_gnu-0.48.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows_i686_msvc-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_i686_msvc/0.48.0/download"
+ ],
+ "strip_prefix": "windows_i686_msvc-0.48.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows_i686_msvc-0.48.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows_x86_64_gnu-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnu/0.48.0/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnu-0.48.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows_x86_64_gnu-0.48.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows_x86_64_gnullvm-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_gnullvm/0.48.0/download"
+ ],
+ "strip_prefix": "windows_x86_64_gnullvm-0.48.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.48.0.bazel"
+ }
+ },
+ "rules_rust_wasm_bindgen__windows_x86_64_msvc-0.48.0": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+ "attributes": {
+ "sha256": "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a",
+ "type": "tar.gz",
+ "urls": [
+ "https://static.crates.io/crates/windows_x86_64_msvc/0.48.0/download"
+ ],
+ "strip_prefix": "windows_x86_64_msvc-0.48.0",
+ "build_file": "@@rules_rust+//wasm_bindgen/3rdparty/crates:BUILD.windows_x86_64_msvc-0.48.0.bazel"
+ }
+ },
+ "rules_rust_test_load_arbitrary_tool": {
+ "repoRuleId": "@@rules_rust+//test/load_arbitrary_tool:load_arbitrary_tool_test.bzl%_load_arbitrary_tool_test",
+ "attributes": {}
+ },
+ "generated_inputs_in_external_repo": {
+ "repoRuleId": "@@rules_rust+//test/generated_inputs:external_repo.bzl%_generated_inputs_in_external_repo",
+ "attributes": {}
+ },
"libc": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"build_file_content": "load(\"@rules_rust//rust:defs.bzl\", \"rust_library\")\n\nrust_library(\n name = \"libc\",\n srcs = glob([\"src/**/*.rs\"]),\n edition = \"2015\",\n rustc_flags = [\n # In most cases, warnings in 3rd party crates are not interesting as\n # they're out of the control of consumers. The flag here silences\n # warnings. For more details see:\n # https://doc.rust-lang.org/rustc/lints/levels.html\n \"--cap-lints=allow\",\n ],\n visibility = [\"//visibility:public\"],\n)\n",
"sha256": "1ac4c2ac6ed5a8fb9020c166bc63316205f1dc78d4b964ad31f4f21eb73f0c6d",
@@ -11707,2154 +9785,28 @@
]
}
},
- "rrra__either-1.8.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_rust_toolchain_test_target_json": {
+ "repoRuleId": "@@rules_rust+//test/unit/toolchain:toolchain_test_utils.bzl%rules_rust_toolchain_test_target_json_repository",
"attributes": {
- "sha256": "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/either/1.8.1/download"
- ],
- "strip_prefix": "either-1.8.1",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.either-1.8.1.bazel"
+ "target_json": "@@rules_rust+//test/unit/toolchain:toolchain-test-triple.json"
}
},
- "rules_rust_bindgen__minimal-lexical-0.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "com_google_googleapis": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a",
- "type": "tar.gz",
"urls": [
- "https://static.crates.io/crates/minimal-lexical/0.2.1/download"
+ "https://github.com/googleapis/googleapis/archive/18becb1d1426feb7399db144d7beeb3284f1ccb0.zip"
],
- "strip_prefix": "minimal-lexical-0.2.1",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.minimal-lexical-0.2.1.bazel"
+ "strip_prefix": "googleapis-18becb1d1426feb7399db144d7beeb3284f1ccb0",
+ "sha256": "b8c487191eb942361af905e40172644eab490190e717c3d09bf83e87f3994fff"
}
},
- "rrra__regex-automata-0.3.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
+ "rules_python": {
+ "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
- "sha256": "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-automata/0.3.3/download"
- ],
- "strip_prefix": "regex-automata-0.3.3",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.regex-automata-0.3.3.bazel"
- }
- },
- "cui__spdx-0.10.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "62bde1398b09b9f93fc2fc9b9da86e362693e999d3a54a8ac47a99a5a73f638b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/spdx/0.10.3/download"
- ],
- "strip_prefix": "spdx-0.10.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.spdx-0.10.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen__normalize-line-endings-0.3.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/normalize-line-endings/0.3.0/download"
- ],
- "strip_prefix": "normalize-line-endings-0.3.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.normalize-line-endings-0.3.0.bazel"
- }
- },
- "rules_rust_prost__h2-0.3.19": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/h2/0.3.19/download"
- ],
- "strip_prefix": "h2-0.3.19",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.h2-0.3.19.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasmparser-0.108.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "76c956109dcb41436a39391139d9b6e2d0a5e0b158e1293ef352ec977e5e36c5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasmparser/0.108.0/download"
- ],
- "strip_prefix": "wasmparser-0.108.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasmparser-0.108.0.bazel"
- }
- },
- "rules_rust_bindgen__colorchoice-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/colorchoice/1.0.0/download"
- ],
- "strip_prefix": "colorchoice-1.0.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.colorchoice-1.0.0.bazel"
- }
- },
- "rules_rust_proto__tokio-sync-0.1.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-sync/0.1.8/download"
- ],
- "strip_prefix": "tokio-sync-0.1.8",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-sync-0.1.8.bazel"
- }
- },
- "rules_rust_bindgen__nom-7.1.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/nom/7.1.3/download"
- ],
- "strip_prefix": "nom-7.1.3",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.nom-7.1.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen__hashbrown-0.12.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hashbrown/0.12.3/download"
- ],
- "strip_prefix": "hashbrown-0.12.3",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.hashbrown-0.12.3.bazel"
- }
- },
- "cui__clap-4.3.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/clap/4.3.11/download"
- ],
- "strip_prefix": "clap-4.3.11",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.clap-4.3.11.bazel"
- }
- },
- "rules_rust_bindgen__cexpr-0.6.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cexpr/0.6.0/download"
- ],
- "strip_prefix": "cexpr-0.6.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.cexpr-0.6.0.bazel"
- }
- },
- "cui__num-bigint-0.1.44": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num-bigint/0.1.44/download"
- ],
- "strip_prefix": "num-bigint-0.1.44",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.num-bigint-0.1.44.bazel"
- }
- },
- "cui__nu-ansi-term-0.46.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/nu-ansi-term/0.46.0/download"
- ],
- "strip_prefix": "nu-ansi-term-0.46.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.nu-ansi-term-0.46.0.bazel"
- }
- },
- "rules_rust_proto__winapi-i686-pc-windows-gnu-0.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download"
- ],
- "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel"
- }
- },
- "cui__lazy_static-1.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/lazy_static/1.4.0/download"
- ],
- "strip_prefix": "lazy_static-1.4.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.lazy_static-1.4.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__serde_derive-1.0.171": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde_derive/1.0.171/download"
- ],
- "strip_prefix": "serde_derive-1.0.171",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.serde_derive-1.0.171.bazel"
- }
- },
- "rules_rust_bindgen__anstyle-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anstyle/1.0.0/download"
- ],
- "strip_prefix": "anstyle-1.0.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.anstyle-1.0.0.bazel"
- }
- },
- "cui__gix-packetline-0.16.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "8a8384b1e964151aff0d5632dd9b191059d07dff358b96bd940f1b452600d7ab",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-packetline/0.16.7/download"
- ],
- "strip_prefix": "gix-packetline-0.16.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-packetline-0.16.7.bazel"
- }
- },
- "cui__time-core-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/time-core/0.1.2/download"
- ],
- "strip_prefix": "time-core-0.1.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.time-core-0.1.2.bazel"
- }
- },
- "cui__itertools-0.12.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/itertools/0.12.0/download"
- ],
- "strip_prefix": "itertools-0.12.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.itertools-0.12.0.bazel"
- }
- },
- "cui__time-macros-0.2.15": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/time-macros/0.2.15/download"
- ],
- "strip_prefix": "time-macros-0.2.15",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.time-macros-0.2.15.bazel"
- }
- },
- "rules_rust_prost__try-lock-0.2.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/try-lock/0.2.4/download"
- ],
- "strip_prefix": "try-lock-0.2.4",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.try-lock-0.2.4.bazel"
- }
- },
- "cui__tera-1.19.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tera/1.19.1/download"
- ],
- "strip_prefix": "tera-1.19.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.tera-1.19.1.bazel"
- }
- },
- "rules_rust_wasm_bindgen__tempfile-3.6.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tempfile/3.6.0/download"
- ],
- "strip_prefix": "tempfile-3.6.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.tempfile-3.6.0.bazel"
- }
- },
- "rules_rust_prost__axum-core-0.3.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/axum-core/0.3.4/download"
- ],
- "strip_prefix": "axum-core-0.3.4",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.axum-core-0.3.4.bazel"
- }
- },
- "cui__globset-0.4.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/globset/0.4.11/download"
- ],
- "strip_prefix": "globset-0.4.11",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.globset-0.4.11.bazel"
- }
- },
- "cui__colorchoice-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/colorchoice/1.0.0/download"
- ],
- "strip_prefix": "colorchoice-1.0.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.colorchoice-1.0.0.bazel"
- }
- },
- "rrra__windows-sys-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-sys/0.48.0/download"
- ],
- "strip_prefix": "windows-sys-0.48.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.windows-sys-0.48.0.bazel"
- }
- },
- "rules_rust_prost__libc-0.2.146": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/libc/0.2.146/download"
- ],
- "strip_prefix": "libc-0.2.146",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.libc-0.2.146.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-multi-value-xform-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d1e019acde479e2f090fb7f14a51fa0077ec3a7bb12a56e0e888a82be7b5bd3f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-multi-value-xform/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-multi-value-xform-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-multi-value-xform-0.2.91.bazel"
- }
- },
- "rules_rust_wasm_bindgen__itertools-0.10.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/itertools/0.10.5/download"
- ],
- "strip_prefix": "itertools-0.10.5",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.itertools-0.10.5.bazel"
- }
- },
- "cui__windows-sys-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-sys/0.48.0/download"
- ],
- "strip_prefix": "windows-sys-0.48.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows-sys-0.48.0.bazel"
- }
- },
- "rules_rust_proto__futures-0.1.31": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/futures/0.1.31/download"
- ],
- "strip_prefix": "futures-0.1.31",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.futures-0.1.31.bazel"
- }
- },
- "rules_rust_proto__crossbeam-deque-0.7.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-deque/0.7.4/download"
- ],
- "strip_prefix": "crossbeam-deque-0.7.4",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.crossbeam-deque-0.7.4.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rayon-1.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rayon/1.7.0/download"
- ],
- "strip_prefix": "rayon-1.7.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rayon-1.7.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__spin-0.9.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/spin/0.9.8/download"
- ],
- "strip_prefix": "spin-0.9.8",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.spin-0.9.8.bazel"
- }
- },
- "rules_rust_proto__winapi-0.2.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi/0.2.8/download"
- ],
- "strip_prefix": "winapi-0.2.8",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.winapi-0.2.8.bazel"
- }
- },
- "rules_rust_wasm_bindgen__num-traits-0.2.15": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/num-traits/0.2.15/download"
- ],
- "strip_prefix": "num-traits-0.2.15",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.num-traits-0.2.15.bazel"
- }
- },
- "rules_rust_prost__heck-0.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/heck/0.4.1/download"
- ],
- "strip_prefix": "heck-0.4.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.heck-0.4.1.bazel"
- }
- },
- "cui__rand_chacha-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rand_chacha/0.3.1/download"
- ],
- "strip_prefix": "rand_chacha-0.3.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rand_chacha-0.3.1.bazel"
- }
- },
- "rrra__anstream-0.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anstream/0.3.2/download"
- ],
- "strip_prefix": "anstream-0.3.2",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.anstream-0.3.2.bazel"
- }
- },
- "cui__cargo-lock-9.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e11c675378efb449ed3ce8de78d75d0d80542fc98487c26aba28eb3b82feac72",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cargo-lock/9.0.0/download"
- ],
- "strip_prefix": "cargo-lock-9.0.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.cargo-lock-9.0.0.bazel"
- }
- },
- "rules_rust_bindgen__winapi-util-0.1.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-util/0.1.5/download"
- ],
- "strip_prefix": "winapi-util-0.1.5",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.winapi-util-0.1.5.bazel"
- }
- },
- "rules_rust_wasm_bindgen__buf_redux-0.8.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/buf_redux/0.8.4/download"
- ],
- "strip_prefix": "buf_redux-0.8.4",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.buf_redux-0.8.4.bazel"
- }
- },
- "rules_rust_proto__tls-api-0.1.22": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "049c03787a0595182357fbd487577947f4351b78ce20c3668f6d49f17feb13d1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tls-api/0.1.22/download"
- ],
- "strip_prefix": "tls-api-0.1.22",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tls-api-0.1.22.bazel"
- }
- },
- "cui__faster-hex-0.8.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/faster-hex/0.8.1/download"
- ],
- "strip_prefix": "faster-hex-0.8.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.faster-hex-0.8.1.bazel"
- }
- },
- "rules_rust_prost__hashbrown-0.12.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hashbrown/0.12.3/download"
- ],
- "strip_prefix": "hashbrown-0.12.3",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.hashbrown-0.12.3.bazel"
- }
- },
- "cui__crossbeam-0.8.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam/0.8.2/download"
- ],
- "strip_prefix": "crossbeam-0.8.2",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.crossbeam-0.8.2.bazel"
- }
- },
- "rules_rust_prost__futures-channel-0.3.28": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/futures-channel/0.3.28/download"
- ],
- "strip_prefix": "futures-channel-0.3.28",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.futures-channel-0.3.28.bazel"
- }
- },
- "rules_rust_prost__scopeguard-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/scopeguard/1.1.0/download"
- ],
- "strip_prefix": "scopeguard-1.1.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.scopeguard-1.1.0.bazel"
- }
- },
- "rules_rust_prost__futures-util-0.3.28": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/futures-util/0.3.28/download"
- ],
- "strip_prefix": "futures-util-0.3.28",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.futures-util-0.3.28.bazel"
- }
- },
- "rules_rust_prost__serde-1.0.164": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde/1.0.164/download"
- ],
- "strip_prefix": "serde-1.0.164",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.serde-1.0.164.bazel"
- }
- },
- "cui__crossbeam-utils-0.8.16": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crossbeam-utils/0.8.16/download"
- ],
- "strip_prefix": "crossbeam-utils-0.8.16",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.crossbeam-utils-0.8.16.bazel"
- }
- },
- "cui__unic-segment-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unic-segment/0.9.0/download"
- ],
- "strip_prefix": "unic-segment-0.9.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unic-segment-0.9.0.bazel"
- }
- },
- "cui__regex-automata-0.4.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-automata/0.4.3/download"
- ],
- "strip_prefix": "regex-automata-0.4.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.regex-automata-0.4.3.bazel"
- }
- },
- "rules_rust_proto__miow-0.2.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/miow/0.2.2/download"
- ],
- "strip_prefix": "miow-0.2.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.miow-0.2.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__filetime-0.2.21": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/filetime/0.2.21/download"
- ],
- "strip_prefix": "filetime-0.2.21",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.filetime-0.2.21.bazel"
- }
- },
- "rules_rust_prost__windows-targets-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-targets/0.48.0/download"
- ],
- "strip_prefix": "windows-targets-0.48.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.windows-targets-0.48.0.bazel"
- }
- },
- "rules_rust_prost__petgraph-0.6.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/petgraph/0.6.3/download"
- ],
- "strip_prefix": "petgraph-0.6.3",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.petgraph-0.6.3.bazel"
- }
- },
- "cui__gix-revwalk-0.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e9870c6b1032f2084567710c3b2106ac603377f8d25766b8a6b7c33e6e3ca279",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-revwalk/0.8.0/download"
- ],
- "strip_prefix": "gix-revwalk-0.8.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-revwalk-0.8.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__windows-targets-0.48.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows-targets/0.48.1/download"
- ],
- "strip_prefix": "windows-targets-0.48.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.windows-targets-0.48.1.bazel"
- }
- },
- "rules_rust_prost__syn-1.0.109": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/syn/1.0.109/download"
- ],
- "strip_prefix": "syn-1.0.109",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.syn-1.0.109.bazel"
- }
- },
- "cui__percent-encoding-2.3.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/percent-encoding/2.3.0/download"
- ],
- "strip_prefix": "percent-encoding-2.3.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.percent-encoding-2.3.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__hashbrown-0.14.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hashbrown/0.14.0/download"
- ],
- "strip_prefix": "hashbrown-0.14.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.hashbrown-0.14.0.bazel"
- }
- },
- "cui__toml_datetime-0.6.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/toml_datetime/0.6.5/download"
- ],
- "strip_prefix": "toml_datetime-0.6.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.toml_datetime-0.6.5.bazel"
- }
- },
- "rules_rust_proto__log-0.4.17": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/log/0.4.17/download"
- ],
- "strip_prefix": "log-0.4.17",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.log-0.4.17.bazel"
- }
- },
- "cui__tinyvec-1.6.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tinyvec/1.6.0/download"
- ],
- "strip_prefix": "tinyvec-1.6.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.tinyvec-1.6.0.bazel"
- }
- },
- "cui__btoi-0.4.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/btoi/0.4.3/download"
- ],
- "strip_prefix": "btoi-0.4.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.btoi-0.4.3.bazel"
- }
- },
- "rules_rust_prost__ppv-lite86-0.2.17": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/ppv-lite86/0.2.17/download"
- ],
- "strip_prefix": "ppv-lite86-0.2.17",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.ppv-lite86-0.2.17.bazel"
- }
- },
- "rules_rust_prost__winapi-0.3.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi/0.3.9/download"
- ],
- "strip_prefix": "winapi-0.3.9",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.winapi-0.3.9.bazel"
- }
- },
- "rules_rust_wasm_bindgen__winapi-util-0.1.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/winapi-util/0.1.5/download"
- ],
- "strip_prefix": "winapi-util-0.1.5",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.winapi-util-0.1.5.bazel"
- }
- },
- "rules_rust_proto__maybe-uninit-2.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/maybe-uninit/2.0.0/download"
- ],
- "strip_prefix": "maybe-uninit-2.0.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.maybe-uninit-2.0.0.bazel"
- }
- },
- "rules_rust_proto__tokio-tcp-0.1.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-tcp/0.1.4/download"
- ],
- "strip_prefix": "tokio-tcp-0.1.4",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-tcp-0.1.4.bazel"
- }
- },
- "rules_rust_bindgen__yansi-term-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/yansi-term/0.1.2/download"
- ],
- "strip_prefix": "yansi-term-0.1.2",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.yansi-term-0.1.2.bazel"
- }
- },
- "cui__toml_edit-0.22.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/toml_edit/0.22.4/download"
- ],
- "strip_prefix": "toml_edit-0.22.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.toml_edit-0.22.4.bazel"
- }
- },
- "rules_rust_bindgen__windows_aarch64_msvc-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_msvc/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_msvc-0.48.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.windows_aarch64_msvc-0.48.0.bazel"
- }
- },
- "cui__block-buffer-0.10.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/block-buffer/0.10.4/download"
- ],
- "strip_prefix": "block-buffer-0.10.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.block-buffer-0.10.4.bazel"
- }
- },
- "cui__chrono-tz-build-0.2.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/chrono-tz-build/0.2.1/download"
- ],
- "strip_prefix": "chrono-tz-build-0.2.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.chrono-tz-build-0.2.1.bazel"
- }
- },
- "cui__gix-bitmap-0.2.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "0ccab4bc576844ddb51b78d81b4a42d73e6229660fa614dfc3d3999c874d1959",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-bitmap/0.2.7/download"
- ],
- "strip_prefix": "gix-bitmap-0.2.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-bitmap-0.2.7.bazel"
- }
- },
- "cui__gix-pathspec-0.3.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c3e26c9b47c51be73f98d38c84494bd5fb99334c5d6fda14ef5d036d50a9e5fd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-pathspec/0.3.0/download"
- ],
- "strip_prefix": "gix-pathspec-0.3.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-pathspec-0.3.0.bazel"
- }
- },
- "rrra__libc-0.2.147": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/libc/0.2.147/download"
- ],
- "strip_prefix": "libc-0.2.147",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.libc-0.2.147.bazel"
- }
- },
- "rules_rust_wasm_bindgen__base64-0.21.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/base64/0.21.5/download"
- ],
- "strip_prefix": "base64-0.21.5",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.base64-0.21.5.bazel"
- }
- },
- "cui__tracing-attributes-0.1.27": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tracing-attributes/0.1.27/download"
- ],
- "strip_prefix": "tracing-attributes-0.1.27",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.tracing-attributes-0.1.27.bazel"
- }
- },
- "rules_rust_test_load_arbitrary_tool": {
- "bzlFile": "@@rules_rust~//test/load_arbitrary_tool:load_arbitrary_tool_test.bzl",
- "ruleClassName": "_load_arbitrary_tool_test",
- "attributes": {}
- },
- "rules_rust_prost__tokio-1.28.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio/1.28.2/download"
- ],
- "strip_prefix": "tokio-1.28.2",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tokio-1.28.2.bazel"
- }
- },
- "rules_rust_proto__parking_lot_core-0.6.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/parking_lot_core/0.6.3/download"
- ],
- "strip_prefix": "parking_lot_core-0.6.3",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.parking_lot_core-0.6.3.bazel"
- }
- },
- "rules_rust_wasm_bindgen__chunked_transfer-1.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/chunked_transfer/1.4.1/download"
- ],
- "strip_prefix": "chunked_transfer-1.4.1",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.chunked_transfer-1.4.1.bazel"
- }
- },
- "cui__tinyvec_macros-0.1.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tinyvec_macros/0.1.1/download"
- ],
- "strip_prefix": "tinyvec_macros-0.1.1",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.tinyvec_macros-0.1.1.bazel"
- }
- },
- "rules_rust_proto__semver-parser-0.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/semver-parser/0.7.0/download"
- ],
- "strip_prefix": "semver-parser-0.7.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.semver-parser-0.7.0.bazel"
- }
- },
- "rrra__windows_i686_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_i686_gnu/0.48.0/download"
- ],
- "strip_prefix": "windows_i686_gnu-0.48.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.windows_i686_gnu-0.48.0.bazel"
- }
- },
- "rules_rust_proto__tokio-udp-0.1.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-udp/0.1.6/download"
- ],
- "strip_prefix": "tokio-udp-0.1.6",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-udp-0.1.6.bazel"
- }
- },
- "cui__unic-char-property-0.9.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unic-char-property/0.9.0/download"
- ],
- "strip_prefix": "unic-char-property-0.9.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unic-char-property-0.9.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__sha1_smol-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/sha1_smol/1.0.0/download"
- ],
- "strip_prefix": "sha1_smol-1.0.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.sha1_smol-1.0.0.bazel"
- }
- },
- "cui__siphasher-0.3.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/siphasher/0.3.10/download"
- ],
- "strip_prefix": "siphasher-0.3.10",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.siphasher-0.3.10.bazel"
- }
- },
- "cui__tracing-0.1.40": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tracing/0.1.40/download"
- ],
- "strip_prefix": "tracing-0.1.40",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.tracing-0.1.40.bazel"
- }
- },
- "rules_rust_wasm_bindgen__syn-2.0.25": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/syn/2.0.25/download"
- ],
- "strip_prefix": "syn-2.0.25",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.syn-2.0.25.bazel"
- }
- },
- "rules_rust_wasm_bindgen__version_check-0.9.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/version_check/0.9.4/download"
- ],
- "strip_prefix": "version_check-0.9.4",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.version_check-0.9.4.bazel"
- }
- },
- "rrra__is-terminal-0.4.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/is-terminal/0.4.7/download"
- ],
- "strip_prefix": "is-terminal-0.4.7",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.is-terminal-0.4.7.bazel"
- }
- },
- "rrra__errno-dragonfly-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno-dragonfly/0.1.2/download"
- ],
- "strip_prefix": "errno-dragonfly-0.1.2",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.errno-dragonfly-0.1.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__instant-0.1.12": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/instant/0.1.12/download"
- ],
- "strip_prefix": "instant-0.1.12",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.instant-0.1.12.bazel"
- }
- },
- "rules_rust_wasm_bindgen__regex-automata-0.1.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex-automata/0.1.10/download"
- ],
- "strip_prefix": "regex-automata-0.1.10",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.regex-automata-0.1.10.bazel"
- }
- },
- "rrra__hermit-abi-0.3.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hermit-abi/0.3.2/download"
- ],
- "strip_prefix": "hermit-abi-0.3.2",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.hermit-abi-0.3.2.bazel"
- }
- },
- "rules_rust_bindgen__strsim-0.10.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/strsim/0.10.0/download"
- ],
- "strip_prefix": "strsim-0.10.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.strsim-0.10.0.bazel"
- }
- },
- "cui__arrayvec-0.7.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/arrayvec/0.7.4/download"
- ],
- "strip_prefix": "arrayvec-0.7.4",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.arrayvec-0.7.4.bazel"
- }
- },
- "rules_rust_prost__errno-0.3.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/errno/0.3.1/download"
- ],
- "strip_prefix": "errno-0.3.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.errno-0.3.1.bazel"
- }
- },
- "rules_rust_proto__tokio-timer-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-timer/0.1.2/download"
- ],
- "strip_prefix": "tokio-timer-0.1.2",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-timer-0.1.2.bazel"
- }
- },
- "rules_rust_wasm_bindgen__js-sys-0.3.64": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/js-sys/0.3.64/download"
- ],
- "strip_prefix": "js-sys-0.3.64",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.js-sys-0.3.64.bazel"
- }
- },
- "rules_rust_wasm_bindgen__time-0.3.23": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/time/0.3.23/download"
- ],
- "strip_prefix": "time-0.3.23",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.time-0.3.23.bazel"
- }
- },
- "cui__gix-transport-0.37.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b9ec726e6a245e68ace59a34126a1d679de60360676612985e70b0d3b102fb4e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-transport/0.37.0/download"
- ],
- "strip_prefix": "gix-transport-0.37.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-transport-0.37.0.bazel"
- }
- },
- "rules_rust_proto__net2-0.2.38": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/net2/0.2.38/download"
- ],
- "strip_prefix": "net2-0.2.38",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.net2-0.2.38.bazel"
- }
- },
- "rules_rust_prost__pin-project-internal-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/pin-project-internal/1.1.0/download"
- ],
- "strip_prefix": "pin-project-internal-1.1.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.pin-project-internal-1.1.0.bazel"
- }
- },
- "cui__rustc-hash-1.1.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rustc-hash/1.1.0/download"
- ],
- "strip_prefix": "rustc-hash-1.1.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.rustc-hash-1.1.0.bazel"
- }
- },
- "cui__sharded-slab-0.1.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/sharded-slab/0.1.7/download"
- ],
- "strip_prefix": "sharded-slab-0.1.7",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.sharded-slab-0.1.7.bazel"
- }
- },
- "rrra__itoa-1.0.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/itoa/1.0.8/download"
- ],
- "strip_prefix": "itoa-1.0.8",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.itoa-1.0.8.bazel"
- }
- },
- "cui__form_urlencoded-1.2.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/form_urlencoded/1.2.0/download"
- ],
- "strip_prefix": "form_urlencoded-1.2.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.form_urlencoded-1.2.0.bazel"
- }
- },
- "cui__gix-commitgraph-0.21.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e75a975ee22cf0a002bfe9b5d5cb3d2a88e263a8a178cd7509133cff10f4df8a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-commitgraph/0.21.0/download"
- ],
- "strip_prefix": "gix-commitgraph-0.21.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-commitgraph-0.21.0.bazel"
- }
- },
- "rrra__serde_json-1.0.102": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde_json/1.0.102/download"
- ],
- "strip_prefix": "serde_json-1.0.102",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.serde_json-1.0.102.bazel"
- }
- },
- "rules_rust_prost__tonic-build-0.8.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tonic-build/0.8.4/download"
- ],
- "strip_prefix": "tonic-build-0.8.4",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.tonic-build-0.8.4.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rouille-3.6.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rouille/3.6.2/download"
- ],
- "strip_prefix": "rouille-3.6.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rouille-3.6.2.bazel"
- }
- },
- "cui__anyhow-1.0.75": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/anyhow/1.0.75/download"
- ],
- "strip_prefix": "anyhow-1.0.75",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.anyhow-1.0.75.bazel"
- }
- },
- "rules_rust_wasm_bindgen__url-2.4.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/url/2.4.0/download"
- ],
- "strip_prefix": "url-2.4.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.url-2.4.0.bazel"
- }
- },
- "cui__uluru-3.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "794a32261a1f5eb6a4462c81b59cec87b5c27d5deea7dd1ac8fc781c41d226db",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/uluru/3.0.0/download"
- ],
- "strip_prefix": "uluru-3.0.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.uluru-3.0.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__syn-1.0.109": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/syn/1.0.109/download"
- ],
- "strip_prefix": "syn-1.0.109",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.syn-1.0.109.bazel"
- }
- },
- "rules_rust_prost__socket2-0.4.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/socket2/0.4.9/download"
- ],
- "strip_prefix": "socket2-0.4.9",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.socket2-0.4.9.bazel"
- }
- },
- "rules_rust_prost__futures-sink-0.3.28": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/futures-sink/0.3.28/download"
- ],
- "strip_prefix": "futures-sink-0.3.28",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.futures-sink-0.3.28.bazel"
- }
- },
- "rules_rust_prost__unicode-ident-1.0.9": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-ident/1.0.9/download"
- ],
- "strip_prefix": "unicode-ident-1.0.9",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.unicode-ident-1.0.9.bazel"
- }
- },
- "cui__libc-0.2.149": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/libc/0.2.149/download"
- ],
- "strip_prefix": "libc-0.2.149",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.libc-0.2.149.bazel"
- }
- },
- "cui__unicode-linebreak-0.1.5": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-linebreak/0.1.5/download"
- ],
- "strip_prefix": "unicode-linebreak-0.1.5",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unicode-linebreak-0.1.5.bazel"
- }
- },
- "rules_rust_proto__unix_socket-0.5.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unix_socket/0.5.0/download"
- ],
- "strip_prefix": "unix_socket-0.5.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.unix_socket-0.5.0.bazel"
- }
- },
- "rrra__itertools-0.11.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/itertools/0.11.0/download"
- ],
- "strip_prefix": "itertools-0.11.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.itertools-0.11.0.bazel"
- }
- },
- "rules_rust_bindgen__regex-1.8.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/regex/1.8.4/download"
- ],
- "strip_prefix": "regex-1.8.4",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.regex-1.8.4.bazel"
- }
- },
- "cui__hashbrown-0.14.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/hashbrown/0.14.3/download"
- ],
- "strip_prefix": "hashbrown-0.14.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.hashbrown-0.14.3.bazel"
- }
- },
- "cui__crypto-common-0.1.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/crypto-common/0.1.6/download"
- ],
- "strip_prefix": "crypto-common-0.1.6",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.crypto-common-0.1.6.bazel"
- }
- },
- "rrra__windows_x86_64_gnu-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_gnu/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_gnu-0.48.0",
- "build_file": "@@rules_rust~//tools/rust_analyzer/3rdparty/crates:BUILD.windows_x86_64_gnu-0.48.0.bazel"
- }
- },
- "cui__byteyarn-0.2.3": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "a7534301c0ea17abb4db06d75efc7b4b0fa360fce8e175a4330d721c71c942ff",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/byteyarn/0.2.3/download"
- ],
- "strip_prefix": "byteyarn-0.2.3",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.byteyarn-0.2.3.bazel"
- }
- },
- "rules_rust_proto__tokio-executor-0.1.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-executor/0.1.10/download"
- ],
- "strip_prefix": "tokio-executor-0.1.10",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-executor-0.1.10.bazel"
- }
- },
- "rules_rust_proto__tokio-uds-0.1.7": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "65ae5d255ce739e8537221ed2942e0445f4b3b813daebac1c0050ddaaa3587f9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio-uds/0.1.7/download"
- ],
- "strip_prefix": "tokio-uds-0.1.7",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-uds-0.1.7.bazel"
- }
- },
- "rules_rust_prost__io-lifetimes-1.0.11": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/io-lifetimes/1.0.11/download"
- ],
- "strip_prefix": "io-lifetimes-1.0.11",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.io-lifetimes-1.0.11.bazel"
- }
- },
- "rules_rust_prost__itoa-1.0.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/itoa/1.0.6/download"
- ],
- "strip_prefix": "itoa-1.0.6",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.itoa-1.0.6.bazel"
- }
- },
- "rules_rust_wasm_bindgen__cfg-if-1.0.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/cfg-if/1.0.0/download"
- ],
- "strip_prefix": "cfg-if-1.0.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.cfg-if-1.0.0.bazel"
- }
- },
- "rules_rust_prost__windows_x86_64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_x86_64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_x86_64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.48.0.bazel"
- }
- },
- "cui__gix-credentials-0.20.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "46900b884cc5af6a6c141ee741607c0c651a4e1d33614b8d888a1ba81cc0bc8a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-credentials/0.20.0/download"
- ],
- "strip_prefix": "gix-credentials-0.20.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-credentials-0.20.0.bazel"
- }
- },
- "rules_rust_proto__tokio-0.1.22": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tokio/0.1.22/download"
- ],
- "strip_prefix": "tokio-0.1.22",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tokio-0.1.22.bazel"
- }
- },
- "rules_rust_proto__tls-api-stub-0.1.22": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c9a0cc8c149724db9de7d73a0e1bc80b1a74f5394f08c6f301e11f9c35fa061e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/tls-api-stub/0.1.22/download"
- ],
- "strip_prefix": "tls-api-stub-0.1.22",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.tls-api-stub-0.1.22.bazel"
- }
- },
- "rules_rust_prost__syn-2.0.18": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/syn/2.0.18/download"
- ],
- "strip_prefix": "syn-2.0.18",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.syn-2.0.18.bazel"
- }
- },
- "rules_rust_prost__linux-raw-sys-0.3.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/linux-raw-sys/0.3.8/download"
- ],
- "strip_prefix": "linux-raw-sys-0.3.8",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.linux-raw-sys-0.3.8.bazel"
- }
- },
- "cui__serde_derive-1.0.190": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde_derive/1.0.190/download"
- ],
- "strip_prefix": "serde_derive-1.0.190",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.serde_derive-1.0.190.bazel"
- }
- },
- "rules_rust_wasm_bindgen__serde-1.0.171": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/serde/1.0.171/download"
- ],
- "strip_prefix": "serde-1.0.171",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.serde-1.0.171.bazel"
- }
- },
- "rules_rust_proto__httpbis-0.7.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "7689cfa896b2a71da4f16206af167542b75d242b6906313e53857972a92d5614",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/httpbis/0.7.0/download"
- ],
- "strip_prefix": "httpbis-0.7.0",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.httpbis-0.7.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__wasm-bindgen-macro-0.2.91": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/wasm-bindgen-macro/0.2.91/download"
- ],
- "strip_prefix": "wasm-bindgen-macro-0.2.91",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.wasm-bindgen-macro-0.2.91.bazel"
- }
- },
- "cui__gix-revision-0.22.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c8c4b15cf2ab7a35f5bcb3ef146187c8d36df0177e171ca061913cbaaa890e89",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-revision/0.22.0/download"
- ],
- "strip_prefix": "gix-revision-0.22.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-revision-0.22.0.bazel"
- }
- },
- "cui__camino-1.1.6": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/camino/1.1.6/download"
- ],
- "strip_prefix": "camino-1.1.6",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.camino-1.1.6.bazel"
- }
- },
- "rules_rust_prost__signal-hook-registry-1.4.1": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/signal-hook-registry/1.4.1/download"
- ],
- "strip_prefix": "signal-hook-registry-1.4.1",
- "build_file": "@@rules_rust~//proto/prost/private/3rdparty/crates:BUILD.signal-hook-registry-1.4.1.bazel"
- }
- },
- "rules_rust_proto__mio-0.6.23": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/mio/0.6.23/download"
- ],
- "strip_prefix": "mio-0.6.23",
- "build_file": "@@rules_rust~//proto/protobuf/3rdparty/crates:BUILD.mio-0.6.23.bazel"
- }
- },
- "cui__gix-config-0.30.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "c171514b40487d3f677ae37efc0f45ac980e3169f23c27eb30a70b47fdf88ab5",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-config/0.30.0/download"
- ],
- "strip_prefix": "gix-config-0.30.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-config-0.30.0.bazel"
- }
- },
- "cui__unicode-ident-1.0.10": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/unicode-ident/1.0.10/download"
- ],
- "strip_prefix": "unicode-ident-1.0.10",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.unicode-ident-1.0.10.bazel"
- }
- },
- "rules_rust_wasm_bindgen__itoa-1.0.8": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/itoa/1.0.8/download"
- ],
- "strip_prefix": "itoa-1.0.8",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.itoa-1.0.8.bazel"
- }
- },
- "rules_rust_bindgen__libloading-0.7.4": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/libloading/0.7.4/download"
- ],
- "strip_prefix": "libloading-0.7.4",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.libloading-0.7.4.bazel"
- }
- },
- "rules_rust_bindgen__windows_aarch64_gnullvm-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows_aarch64_gnullvm/0.48.0/download"
- ],
- "strip_prefix": "windows_aarch64_gnullvm-0.48.0",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.48.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__alloc-stdlib-0.2.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/alloc-stdlib/0.2.2/download"
- ],
- "strip_prefix": "alloc-stdlib-0.2.2",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.alloc-stdlib-0.2.2.bazel"
- }
- },
- "rules_rust_bindgen__peeking_take_while-0.1.2": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/peeking_take_while/0.1.2/download"
- ],
- "strip_prefix": "peeking_take_while-0.1.2",
- "build_file": "@@rules_rust~//bindgen/3rdparty/crates:BUILD.peeking_take_while-0.1.2.bazel"
- }
- },
- "cui__gix-ignore-0.8.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "b048f443a1f6b02da4205c34d2e287e3fd45d75e8e2f06cfb216630ea9bff5e3",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/gix-ignore/0.8.0/download"
- ],
- "strip_prefix": "gix-ignore-0.8.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.gix-ignore-0.8.0.bazel"
- }
- },
- "rules_rust_wasm_bindgen__rayon-core-1.11.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/rayon-core/1.11.0/download"
- ],
- "strip_prefix": "rayon-core-1.11.0",
- "build_file": "@@rules_rust~//wasm_bindgen/3rdparty/crates:BUILD.rayon-core-1.11.0.bazel"
- }
- },
- "cui__windows-0.48.0": {
- "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
- "ruleClassName": "http_archive",
- "attributes": {
- "sha256": "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f",
- "type": "tar.gz",
- "urls": [
- "https://static.crates.io/crates/windows/0.48.0/download"
- ],
- "strip_prefix": "windows-0.48.0",
- "build_file": "@@rules_rust~//crate_universe/3rdparty/crates:BUILD.windows-0.48.0.bazel"
+ "sha256": "778aaeab3e6cfd56d681c89f5c10d7ad6bf8d2f1a72de9de55b23081b2d31618",
+ "strip_prefix": "rules_python-0.34.0",
+ "url": "https://github.com/bazelbuild/rules_python/releases/download/0.34.0/rules_python-0.34.0.tar.gz"
}
}
},
@@ -13862,50 +9814,52 @@
"explicitRootModuleDirectDeps": [
"rules_rust_tinyjson",
"cui",
- "cui__anyhow-1.0.75",
- "cui__camino-1.1.6",
- "cui__cargo-lock-9.0.0",
- "cui__cargo-platform-0.1.4",
+ "cui__anyhow-1.0.89",
+ "cui__camino-1.1.9",
+ "cui__cargo-lock-10.0.0",
+ "cui__cargo-platform-0.1.7",
"cui__cargo_metadata-0.18.1",
- "cui__cargo_toml-0.19.2",
- "cui__cfg-expr-0.15.5",
+ "cui__cargo_toml-0.20.5",
+ "cui__cfg-expr-0.17.0",
"cui__clap-4.3.11",
- "cui__crates-index-2.2.0",
+ "cui__crates-index-3.2.0",
"cui__hex-0.4.3",
- "cui__indoc-2.0.4",
- "cui__itertools-0.12.0",
- "cui__normpath-1.1.1",
- "cui__pathdiff-0.2.1",
- "cui__regex-1.10.2",
- "cui__semver-1.0.20",
- "cui__serde-1.0.190",
- "cui__serde_json-1.0.108",
- "cui__serde_starlark-0.1.14",
+ "cui__indoc-2.0.5",
+ "cui__itertools-0.13.0",
+ "cui__normpath-1.3.0",
+ "cui__once_cell-1.20.2",
+ "cui__pathdiff-0.2.2",
+ "cui__regex-1.11.0",
+ "cui__semver-1.0.23",
+ "cui__serde-1.0.210",
+ "cui__serde_json-1.0.129",
+ "cui__serde_starlark-0.1.16",
"cui__sha2-0.10.8",
- "cui__spdx-0.10.3",
- "cui__tempfile-3.8.1",
+ "cui__spdx-0.10.6",
+ "cui__tempfile-3.13.0",
"cui__tera-1.19.1",
- "cui__textwrap-0.16.0",
- "cui__toml-0.8.10",
+ "cui__textwrap-0.16.1",
+ "cui__toml-0.8.19",
"cui__tracing-0.1.40",
- "cui__tracing-subscriber-0.3.17",
+ "cui__tracing-subscriber-0.3.18",
+ "cui__url-2.5.2",
"cui__maplit-1.0.2",
- "cui__spectral-0.6.0",
"cargo_bazel.buildifier-darwin-amd64",
"cargo_bazel.buildifier-darwin-arm64",
"cargo_bazel.buildifier-linux-amd64",
"cargo_bazel.buildifier-linux-arm64",
+ "cargo_bazel.buildifier-linux-s390x",
"cargo_bazel.buildifier-windows-amd64.exe",
"rules_rust_prost__heck",
"rules_rust_prost",
- "rules_rust_prost__h2-0.3.19",
- "rules_rust_prost__prost-0.11.9",
- "rules_rust_prost__prost-types-0.11.9",
- "rules_rust_prost__protoc-gen-prost-0.2.2",
- "rules_rust_prost__protoc-gen-tonic-0.2.2",
- "rules_rust_prost__tokio-1.28.2",
- "rules_rust_prost__tokio-stream-0.1.14",
- "rules_rust_prost__tonic-0.9.2",
+ "rules_rust_prost__h2-0.4.6",
+ "rules_rust_prost__prost-0.13.1",
+ "rules_rust_prost__prost-types-0.13.1",
+ "rules_rust_prost__protoc-gen-prost-0.4.0",
+ "rules_rust_prost__protoc-gen-tonic-0.4.1",
+ "rules_rust_prost__tokio-1.39.3",
+ "rules_rust_prost__tokio-stream-0.1.15",
+ "rules_rust_prost__tonic-0.12.1",
"rules_rust_proto__grpc-0.6.2",
"rules_rust_proto__grpc-compiler-0.6.2",
"rules_rust_proto__log-0.4.17",
@@ -13914,12 +9868,12 @@
"rules_rust_proto__tls-api-0.1.22",
"rules_rust_proto__tls-api-stub-0.1.22",
"llvm-raw",
- "rules_rust_bindgen__bindgen-cli-0.69.1",
- "rules_rust_bindgen__bindgen-0.69.1",
- "rules_rust_bindgen__clang-sys-1.6.1",
- "rules_rust_bindgen__clap-4.3.3",
- "rules_rust_bindgen__clap_complete-4.3.1",
- "rules_rust_bindgen__env_logger-0.10.0",
+ "rules_rust_bindgen__bindgen-cli-0.70.1",
+ "rules_rust_bindgen__bindgen-0.70.1",
+ "rules_rust_bindgen__clang-sys-1.8.1",
+ "rules_rust_bindgen__clap-4.5.17",
+ "rules_rust_bindgen__clap_complete-4.5.26",
+ "rules_rust_bindgen__env_logger-0.10.2",
"rrra__anyhow-1.0.71",
"rrra__clap-4.3.11",
"rrra__env_logger-0.10.0",
@@ -13938,9 +9892,9 @@
"rules_rust_wasm_bindgen__serde_json-1.0.102",
"rules_rust_wasm_bindgen__ureq-2.8.0",
"rules_rust_wasm_bindgen__walrus-0.20.3",
- "rules_rust_wasm_bindgen__wasm-bindgen-0.2.91",
- "rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.91",
- "rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.91",
+ "rules_rust_wasm_bindgen__wasm-bindgen-0.2.92",
+ "rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.92",
+ "rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.92",
"rules_rust_wasm_bindgen__assert_cmd-1.0.8",
"rules_rust_wasm_bindgen__diff-0.1.13",
"rules_rust_wasm_bindgen__predicates-1.0.8",
@@ -13953,7 +9907,7 @@
"libc",
"rules_rust_toolchain_test_target_json",
"com_google_googleapis",
- "bazelci_rules"
+ "rules_python"
],
"explicitRootModuleDirectDevDeps": [],
"useAllRepos": "NO",
@@ -13961,399 +9915,419 @@
},
"recordedRepoMappingEntries": [
[
- "rules_rust~",
- "bazel_skylib",
- "bazel_skylib~"
+ "bazel_tools",
+ "rules_cc",
+ "rules_cc+"
],
[
- "rules_rust~",
+ "rules_cc+",
"bazel_tools",
"bazel_tools"
],
[
- "rules_rust~",
- "cui__anyhow-1.0.75",
- "rules_rust~~i~cui__anyhow-1.0.75"
+ "rules_rust+",
+ "bazel_skylib",
+ "bazel_skylib+"
],
[
- "rules_rust~",
- "cui__camino-1.1.6",
- "rules_rust~~i~cui__camino-1.1.6"
+ "rules_rust+",
+ "bazel_tools",
+ "bazel_tools"
],
[
- "rules_rust~",
- "cui__cargo-lock-9.0.0",
- "rules_rust~~i~cui__cargo-lock-9.0.0"
+ "rules_rust+",
+ "cui__anyhow-1.0.89",
+ "rules_rust++i+cui__anyhow-1.0.89"
],
[
- "rules_rust~",
- "cui__cargo-platform-0.1.4",
- "rules_rust~~i~cui__cargo-platform-0.1.4"
+ "rules_rust+",
+ "cui__camino-1.1.9",
+ "rules_rust++i+cui__camino-1.1.9"
],
[
- "rules_rust~",
+ "rules_rust+",
+ "cui__cargo-lock-10.0.0",
+ "rules_rust++i+cui__cargo-lock-10.0.0"
+ ],
+ [
+ "rules_rust+",
+ "cui__cargo-platform-0.1.7",
+ "rules_rust++i+cui__cargo-platform-0.1.7"
+ ],
+ [
+ "rules_rust+",
"cui__cargo_metadata-0.18.1",
- "rules_rust~~i~cui__cargo_metadata-0.18.1"
+ "rules_rust++i+cui__cargo_metadata-0.18.1"
],
[
- "rules_rust~",
- "cui__cargo_toml-0.19.2",
- "rules_rust~~i~cui__cargo_toml-0.19.2"
+ "rules_rust+",
+ "cui__cargo_toml-0.20.5",
+ "rules_rust++i+cui__cargo_toml-0.20.5"
],
[
- "rules_rust~",
- "cui__cfg-expr-0.15.5",
- "rules_rust~~i~cui__cfg-expr-0.15.5"
+ "rules_rust+",
+ "cui__cfg-expr-0.17.0",
+ "rules_rust++i+cui__cfg-expr-0.17.0"
],
[
- "rules_rust~",
+ "rules_rust+",
"cui__clap-4.3.11",
- "rules_rust~~i~cui__clap-4.3.11"
+ "rules_rust++i+cui__clap-4.3.11"
],
[
- "rules_rust~",
- "cui__crates-index-2.2.0",
- "rules_rust~~i~cui__crates-index-2.2.0"
+ "rules_rust+",
+ "cui__crates-index-3.2.0",
+ "rules_rust++i+cui__crates-index-3.2.0"
],
[
- "rules_rust~",
+ "rules_rust+",
"cui__hex-0.4.3",
- "rules_rust~~i~cui__hex-0.4.3"
+ "rules_rust++i+cui__hex-0.4.3"
],
[
- "rules_rust~",
- "cui__indoc-2.0.4",
- "rules_rust~~i~cui__indoc-2.0.4"
+ "rules_rust+",
+ "cui__indoc-2.0.5",
+ "rules_rust++i+cui__indoc-2.0.5"
],
[
- "rules_rust~",
- "cui__itertools-0.12.0",
- "rules_rust~~i~cui__itertools-0.12.0"
+ "rules_rust+",
+ "cui__itertools-0.13.0",
+ "rules_rust++i+cui__itertools-0.13.0"
],
[
- "rules_rust~",
+ "rules_rust+",
"cui__maplit-1.0.2",
- "rules_rust~~i~cui__maplit-1.0.2"
+ "rules_rust++i+cui__maplit-1.0.2"
],
[
- "rules_rust~",
- "cui__normpath-1.1.1",
- "rules_rust~~i~cui__normpath-1.1.1"
+ "rules_rust+",
+ "cui__normpath-1.3.0",
+ "rules_rust++i+cui__normpath-1.3.0"
],
[
- "rules_rust~",
- "cui__pathdiff-0.2.1",
- "rules_rust~~i~cui__pathdiff-0.2.1"
+ "rules_rust+",
+ "cui__once_cell-1.20.2",
+ "rules_rust++i+cui__once_cell-1.20.2"
],
[
- "rules_rust~",
- "cui__regex-1.10.2",
- "rules_rust~~i~cui__regex-1.10.2"
+ "rules_rust+",
+ "cui__pathdiff-0.2.2",
+ "rules_rust++i+cui__pathdiff-0.2.2"
],
[
- "rules_rust~",
- "cui__semver-1.0.20",
- "rules_rust~~i~cui__semver-1.0.20"
+ "rules_rust+",
+ "cui__regex-1.11.0",
+ "rules_rust++i+cui__regex-1.11.0"
],
[
- "rules_rust~",
- "cui__serde-1.0.190",
- "rules_rust~~i~cui__serde-1.0.190"
+ "rules_rust+",
+ "cui__semver-1.0.23",
+ "rules_rust++i+cui__semver-1.0.23"
],
[
- "rules_rust~",
- "cui__serde_json-1.0.108",
- "rules_rust~~i~cui__serde_json-1.0.108"
+ "rules_rust+",
+ "cui__serde-1.0.210",
+ "rules_rust++i+cui__serde-1.0.210"
],
[
- "rules_rust~",
- "cui__serde_starlark-0.1.14",
- "rules_rust~~i~cui__serde_starlark-0.1.14"
+ "rules_rust+",
+ "cui__serde_json-1.0.129",
+ "rules_rust++i+cui__serde_json-1.0.129"
],
[
- "rules_rust~",
+ "rules_rust+",
+ "cui__serde_starlark-0.1.16",
+ "rules_rust++i+cui__serde_starlark-0.1.16"
+ ],
+ [
+ "rules_rust+",
"cui__sha2-0.10.8",
- "rules_rust~~i~cui__sha2-0.10.8"
+ "rules_rust++i+cui__sha2-0.10.8"
],
[
- "rules_rust~",
- "cui__spdx-0.10.3",
- "rules_rust~~i~cui__spdx-0.10.3"
+ "rules_rust+",
+ "cui__spdx-0.10.6",
+ "rules_rust++i+cui__spdx-0.10.6"
],
[
- "rules_rust~",
- "cui__spectral-0.6.0",
- "rules_rust~~i~cui__spectral-0.6.0"
+ "rules_rust+",
+ "cui__tempfile-3.13.0",
+ "rules_rust++i+cui__tempfile-3.13.0"
],
[
- "rules_rust~",
- "cui__tempfile-3.8.1",
- "rules_rust~~i~cui__tempfile-3.8.1"
- ],
- [
- "rules_rust~",
+ "rules_rust+",
"cui__tera-1.19.1",
- "rules_rust~~i~cui__tera-1.19.1"
+ "rules_rust++i+cui__tera-1.19.1"
],
[
- "rules_rust~",
- "cui__textwrap-0.16.0",
- "rules_rust~~i~cui__textwrap-0.16.0"
+ "rules_rust+",
+ "cui__textwrap-0.16.1",
+ "rules_rust++i+cui__textwrap-0.16.1"
],
[
- "rules_rust~",
- "cui__toml-0.8.10",
- "rules_rust~~i~cui__toml-0.8.10"
+ "rules_rust+",
+ "cui__toml-0.8.19",
+ "rules_rust++i+cui__toml-0.8.19"
],
[
- "rules_rust~",
+ "rules_rust+",
"cui__tracing-0.1.40",
- "rules_rust~~i~cui__tracing-0.1.40"
+ "rules_rust++i+cui__tracing-0.1.40"
],
[
- "rules_rust~",
- "cui__tracing-subscriber-0.3.17",
- "rules_rust~~i~cui__tracing-subscriber-0.3.17"
+ "rules_rust+",
+ "cui__tracing-subscriber-0.3.18",
+ "rules_rust++i+cui__tracing-subscriber-0.3.18"
],
[
- "rules_rust~",
+ "rules_rust+",
+ "cui__url-2.5.2",
+ "rules_rust++i+cui__url-2.5.2"
+ ],
+ [
+ "rules_rust+",
"rrra__anyhow-1.0.71",
- "rules_rust~~i~rrra__anyhow-1.0.71"
+ "rules_rust++i+rrra__anyhow-1.0.71"
],
[
- "rules_rust~",
+ "rules_rust+",
"rrra__clap-4.3.11",
- "rules_rust~~i~rrra__clap-4.3.11"
+ "rules_rust++i+rrra__clap-4.3.11"
],
[
- "rules_rust~",
+ "rules_rust+",
"rrra__env_logger-0.10.0",
- "rules_rust~~i~rrra__env_logger-0.10.0"
+ "rules_rust++i+rrra__env_logger-0.10.0"
],
[
- "rules_rust~",
+ "rules_rust+",
"rrra__itertools-0.11.0",
- "rules_rust~~i~rrra__itertools-0.11.0"
+ "rules_rust++i+rrra__itertools-0.11.0"
],
[
- "rules_rust~",
+ "rules_rust+",
"rrra__log-0.4.19",
- "rules_rust~~i~rrra__log-0.4.19"
+ "rules_rust++i+rrra__log-0.4.19"
],
[
- "rules_rust~",
+ "rules_rust+",
"rrra__serde-1.0.171",
- "rules_rust~~i~rrra__serde-1.0.171"
+ "rules_rust++i+rrra__serde-1.0.171"
],
[
- "rules_rust~",
+ "rules_rust+",
"rrra__serde_json-1.0.102",
- "rules_rust~~i~rrra__serde_json-1.0.102"
+ "rules_rust++i+rrra__serde_json-1.0.102"
],
[
- "rules_rust~",
+ "rules_rust+",
+ "rules_cc",
+ "rules_cc+"
+ ],
+ [
+ "rules_rust+",
"rules_rust",
- "rules_rust~"
+ "rules_rust+"
],
[
- "rules_rust~",
- "rules_rust_bindgen__bindgen-0.69.1",
- "rules_rust~~i~rules_rust_bindgen__bindgen-0.69.1"
+ "rules_rust+",
+ "rules_rust_bindgen__bindgen-0.70.1",
+ "rules_rust++i+rules_rust_bindgen__bindgen-0.70.1"
],
[
- "rules_rust~",
- "rules_rust_bindgen__clang-sys-1.6.1",
- "rules_rust~~i~rules_rust_bindgen__clang-sys-1.6.1"
+ "rules_rust+",
+ "rules_rust_bindgen__clang-sys-1.8.1",
+ "rules_rust++i+rules_rust_bindgen__clang-sys-1.8.1"
],
[
- "rules_rust~",
- "rules_rust_bindgen__clap-4.3.3",
- "rules_rust~~i~rules_rust_bindgen__clap-4.3.3"
+ "rules_rust+",
+ "rules_rust_bindgen__clap-4.5.17",
+ "rules_rust++i+rules_rust_bindgen__clap-4.5.17"
],
[
- "rules_rust~",
- "rules_rust_bindgen__clap_complete-4.3.1",
- "rules_rust~~i~rules_rust_bindgen__clap_complete-4.3.1"
+ "rules_rust+",
+ "rules_rust_bindgen__clap_complete-4.5.26",
+ "rules_rust++i+rules_rust_bindgen__clap_complete-4.5.26"
],
[
- "rules_rust~",
- "rules_rust_bindgen__env_logger-0.10.0",
- "rules_rust~~i~rules_rust_bindgen__env_logger-0.10.0"
+ "rules_rust+",
+ "rules_rust_bindgen__env_logger-0.10.2",
+ "rules_rust++i+rules_rust_bindgen__env_logger-0.10.2"
],
[
- "rules_rust~",
- "rules_rust_prost__h2-0.3.19",
- "rules_rust~~i~rules_rust_prost__h2-0.3.19"
+ "rules_rust+",
+ "rules_rust_prost__h2-0.4.6",
+ "rules_rust++i+rules_rust_prost__h2-0.4.6"
],
[
- "rules_rust~",
- "rules_rust_prost__prost-0.11.9",
- "rules_rust~~i~rules_rust_prost__prost-0.11.9"
+ "rules_rust+",
+ "rules_rust_prost__prost-0.13.1",
+ "rules_rust++i+rules_rust_prost__prost-0.13.1"
],
[
- "rules_rust~",
- "rules_rust_prost__prost-types-0.11.9",
- "rules_rust~~i~rules_rust_prost__prost-types-0.11.9"
+ "rules_rust+",
+ "rules_rust_prost__prost-types-0.13.1",
+ "rules_rust++i+rules_rust_prost__prost-types-0.13.1"
],
[
- "rules_rust~",
- "rules_rust_prost__protoc-gen-prost-0.2.2",
- "rules_rust~~i~rules_rust_prost__protoc-gen-prost-0.2.2"
+ "rules_rust+",
+ "rules_rust_prost__protoc-gen-prost-0.4.0",
+ "rules_rust++i+rules_rust_prost__protoc-gen-prost-0.4.0"
],
[
- "rules_rust~",
- "rules_rust_prost__protoc-gen-tonic-0.2.2",
- "rules_rust~~i~rules_rust_prost__protoc-gen-tonic-0.2.2"
+ "rules_rust+",
+ "rules_rust_prost__protoc-gen-tonic-0.4.1",
+ "rules_rust++i+rules_rust_prost__protoc-gen-tonic-0.4.1"
],
[
- "rules_rust~",
- "rules_rust_prost__tokio-1.28.2",
- "rules_rust~~i~rules_rust_prost__tokio-1.28.2"
+ "rules_rust+",
+ "rules_rust_prost__tokio-1.39.3",
+ "rules_rust++i+rules_rust_prost__tokio-1.39.3"
],
[
- "rules_rust~",
- "rules_rust_prost__tokio-stream-0.1.14",
- "rules_rust~~i~rules_rust_prost__tokio-stream-0.1.14"
+ "rules_rust+",
+ "rules_rust_prost__tokio-stream-0.1.15",
+ "rules_rust++i+rules_rust_prost__tokio-stream-0.1.15"
],
[
- "rules_rust~",
- "rules_rust_prost__tonic-0.9.2",
- "rules_rust~~i~rules_rust_prost__tonic-0.9.2"
+ "rules_rust+",
+ "rules_rust_prost__tonic-0.12.1",
+ "rules_rust++i+rules_rust_prost__tonic-0.12.1"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_proto__grpc-0.6.2",
- "rules_rust~~i~rules_rust_proto__grpc-0.6.2"
+ "rules_rust++i+rules_rust_proto__grpc-0.6.2"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_proto__grpc-compiler-0.6.2",
- "rules_rust~~i~rules_rust_proto__grpc-compiler-0.6.2"
+ "rules_rust++i+rules_rust_proto__grpc-compiler-0.6.2"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_proto__log-0.4.17",
- "rules_rust~~i~rules_rust_proto__log-0.4.17"
+ "rules_rust++i+rules_rust_proto__log-0.4.17"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_proto__protobuf-2.8.2",
- "rules_rust~~i~rules_rust_proto__protobuf-2.8.2"
+ "rules_rust++i+rules_rust_proto__protobuf-2.8.2"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_proto__protobuf-codegen-2.8.2",
- "rules_rust~~i~rules_rust_proto__protobuf-codegen-2.8.2"
+ "rules_rust++i+rules_rust_proto__protobuf-codegen-2.8.2"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_proto__tls-api-0.1.22",
- "rules_rust~~i~rules_rust_proto__tls-api-0.1.22"
+ "rules_rust++i+rules_rust_proto__tls-api-0.1.22"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_proto__tls-api-stub-0.1.22",
- "rules_rust~~i~rules_rust_proto__tls-api-stub-0.1.22"
+ "rules_rust++i+rules_rust_proto__tls-api-stub-0.1.22"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__anyhow-1.0.71",
- "rules_rust~~i~rules_rust_wasm_bindgen__anyhow-1.0.71"
+ "rules_rust++i+rules_rust_wasm_bindgen__anyhow-1.0.71"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__assert_cmd-1.0.8",
- "rules_rust~~i~rules_rust_wasm_bindgen__assert_cmd-1.0.8"
+ "rules_rust++i+rules_rust_wasm_bindgen__assert_cmd-1.0.8"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__diff-0.1.13",
- "rules_rust~~i~rules_rust_wasm_bindgen__diff-0.1.13"
+ "rules_rust++i+rules_rust_wasm_bindgen__diff-0.1.13"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__docopt-1.1.1",
- "rules_rust~~i~rules_rust_wasm_bindgen__docopt-1.1.1"
+ "rules_rust++i+rules_rust_wasm_bindgen__docopt-1.1.1"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__env_logger-0.8.4",
- "rules_rust~~i~rules_rust_wasm_bindgen__env_logger-0.8.4"
+ "rules_rust++i+rules_rust_wasm_bindgen__env_logger-0.8.4"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__log-0.4.19",
- "rules_rust~~i~rules_rust_wasm_bindgen__log-0.4.19"
+ "rules_rust++i+rules_rust_wasm_bindgen__log-0.4.19"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__predicates-1.0.8",
- "rules_rust~~i~rules_rust_wasm_bindgen__predicates-1.0.8"
+ "rules_rust++i+rules_rust_wasm_bindgen__predicates-1.0.8"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__rayon-1.7.0",
- "rules_rust~~i~rules_rust_wasm_bindgen__rayon-1.7.0"
+ "rules_rust++i+rules_rust_wasm_bindgen__rayon-1.7.0"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__rouille-3.6.2",
- "rules_rust~~i~rules_rust_wasm_bindgen__rouille-3.6.2"
+ "rules_rust++i+rules_rust_wasm_bindgen__rouille-3.6.2"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__serde-1.0.171",
- "rules_rust~~i~rules_rust_wasm_bindgen__serde-1.0.171"
+ "rules_rust++i+rules_rust_wasm_bindgen__serde-1.0.171"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__serde_derive-1.0.171",
- "rules_rust~~i~rules_rust_wasm_bindgen__serde_derive-1.0.171"
+ "rules_rust++i+rules_rust_wasm_bindgen__serde_derive-1.0.171"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__serde_json-1.0.102",
- "rules_rust~~i~rules_rust_wasm_bindgen__serde_json-1.0.102"
+ "rules_rust++i+rules_rust_wasm_bindgen__serde_json-1.0.102"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__tempfile-3.6.0",
- "rules_rust~~i~rules_rust_wasm_bindgen__tempfile-3.6.0"
+ "rules_rust++i+rules_rust_wasm_bindgen__tempfile-3.6.0"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__ureq-2.8.0",
- "rules_rust~~i~rules_rust_wasm_bindgen__ureq-2.8.0"
+ "rules_rust++i+rules_rust_wasm_bindgen__ureq-2.8.0"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__walrus-0.20.3",
- "rules_rust~~i~rules_rust_wasm_bindgen__walrus-0.20.3"
+ "rules_rust++i+rules_rust_wasm_bindgen__walrus-0.20.3"
],
[
- "rules_rust~",
- "rules_rust_wasm_bindgen__wasm-bindgen-0.2.91",
- "rules_rust~~i~rules_rust_wasm_bindgen__wasm-bindgen-0.2.91"
+ "rules_rust+",
+ "rules_rust_wasm_bindgen__wasm-bindgen-0.2.92",
+ "rules_rust++i+rules_rust_wasm_bindgen__wasm-bindgen-0.2.92"
],
[
- "rules_rust~",
- "rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.91",
- "rules_rust~~i~rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.91"
+ "rules_rust+",
+ "rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.92",
+ "rules_rust++i+rules_rust_wasm_bindgen__wasm-bindgen-cli-support-0.2.92"
],
[
- "rules_rust~",
- "rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.91",
- "rules_rust~~i~rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.91"
+ "rules_rust+",
+ "rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.92",
+ "rules_rust++i+rules_rust_wasm_bindgen__wasm-bindgen-shared-0.2.92"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__wasmparser-0.102.0",
- "rules_rust~~i~rules_rust_wasm_bindgen__wasmparser-0.102.0"
+ "rules_rust++i+rules_rust_wasm_bindgen__wasmparser-0.102.0"
],
[
- "rules_rust~",
+ "rules_rust+",
"rules_rust_wasm_bindgen__wasmprinter-0.2.60",
- "rules_rust~~i~rules_rust_wasm_bindgen__wasmprinter-0.2.60"
+ "rules_rust++i+rules_rust_wasm_bindgen__wasmprinter-0.2.60"
]
]
}
diff --git a/bazel_placeholder/Cargo.lock b/bazel_placeholder/Cargo.lock
index 7f40048..ff22e4d 100644
--- a/bazel_placeholder/Cargo.lock
+++ b/bazel_placeholder/Cargo.lock
@@ -192,11 +192,25 @@
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
+ "const-oid",
"crypto-common",
"subtle",
]
[[package]]
+name = "ecdsa"
+version = "0.16.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
+dependencies = [
+ "der",
+ "digest",
+ "elliptic-curve",
+ "rfc6979",
+ "signature",
+]
+
+[[package]]
name = "ed25519"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -394,8 +408,10 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
dependencies = [
+ "ecdsa",
"elliptic-curve",
"primeorder",
+ "sha2",
]
[[package]]
@@ -464,9 +480,9 @@
[[package]]
name = "proc-macro2"
-version = "1.0.81"
+version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
@@ -511,6 +527,16 @@
]
[[package]]
+name = "rfc6979"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
+dependencies = [
+ "hmac",
+ "subtle",
+]
+
+[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -560,6 +586,10 @@
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "digest",
+ "rand_core",
+]
[[package]]
name = "spin"
@@ -594,9 +624,9 @@
[[package]]
name = "syn"
-version = "2.0.59"
+version = "2.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a"
+checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
dependencies = [
"proc-macro2",
"quote",
@@ -605,18 +635,18 @@
[[package]]
name = "thiserror"
-version = "1.0.58"
+version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.58"
+version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36"
dependencies = [
"proc-macro2",
"quote",
diff --git a/bazel_placeholder/Cargo.toml b/bazel_placeholder/Cargo.toml
index 3344be6..25ae03a 100644
--- a/bazel_placeholder/Cargo.toml
+++ b/bazel_placeholder/Cargo.toml
@@ -9,7 +9,7 @@
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
strum = { version = "0.25.0", default-features = false }
strum_macros = { version = "0.25.3", default-features = false }
-thiserror = "1.0.51"
+thiserror = { version = "2.0.4", default-features = false }
nom = { version = "7.1.3", default-features = false }
tinyvec = { version = "1.6.0", features = ["rustc_1_55"] }
cfg-if = "1.0.0"
@@ -25,9 +25,9 @@
ed25519-dalek = { version = "2.1.0", features = ["rand_core"], default-features = false }
hkdf = "0.12.3"
hmac = "0.12.1"
-p256 = { version = "0.13.2", features = ["ecdh"], default-features = false }
+p256 = { version = "0.13.2", features = ["ecdh", "ecdsa"], default-features = false }
x25519-dalek = { version = "2.0.0", default-features = false }
subtle = { version = "2.5.0", default-features = false }
sec1 = "0.7.3"
sha2 = { version = "0.10.8", default-features = false }
-itertools = "0.13.0"
\ No newline at end of file
+itertools = "0.13.0"
diff --git a/betosync/Cargo.lock b/betosync/Cargo.lock
new file mode 100644
index 0000000..0d0915d
--- /dev/null
+++ b/betosync/Cargo.lock
@@ -0,0 +1,1523 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+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 = "anstream"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+dependencies = [
+ "anstyle",
+ "once_cell",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.97"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+
+[[package]]
+name = "arbitrary"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
+dependencies = [
+ "derive_arbitrary",
+]
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+
+[[package]]
+name = "bstr"
+version = "1.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "build_scripts"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "cmd_runner",
+ "env_logger 0.11.7",
+ "log",
+ "serde",
+ "serde_json",
+ "xshell",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+
+[[package]]
+name = "bytes"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+
+[[package]]
+name = "cc"
+version = "1.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
+dependencies = [
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[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.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-link",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+[[package]]
+name = "cmd_runner"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "clap",
+ "file-header",
+ "globset",
+ "log",
+ "owo-colors",
+ "serde",
+ "serde_json",
+ "shell-escape",
+ "xshell",
+]
+
+[[package]]
+name = "colorchoice"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "crdt"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "derive-where",
+ "derive_fuzztest",
+ "distributed_time",
+ "log",
+ "serde",
+ "serde_json",
+ "submerge_internal_proto",
+ "thiserror",
+]
+
+[[package]]
+name = "crdt_fuzz"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "crdt",
+ "derive_fuzztest",
+ "distributed_time",
+ "libfuzzer-sys",
+]
+
+[[package]]
+name = "crossbeam"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+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.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "derive-where"
+version = "1.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "derive_arbitrary"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "derive_fuzztest"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98a03c381eaef21fd453d46a2c9be9f7527e588f113fdc63c3c4a11d7da8b473"
+dependencies = [
+ "arbitrary",
+ "derive_fuzztest_macro",
+ "libfuzzer-sys",
+ "quickcheck",
+]
+
+[[package]]
+name = "derive_fuzztest_macro"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "001301b8ebda15d520d9916f226711c0fa30be0bc8f055dd2379523bde1b4a32"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "distributed_time"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "derive-where",
+ "derive_fuzztest",
+ "num-traits",
+ "serde",
+ "submerge_internal_proto",
+ "thiserror",
+]
+
+[[package]]
+name = "distributed_time_fuzz"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "derive_fuzztest",
+ "distributed_time",
+ "libfuzzer-sys",
+]
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "env_filter"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "jiff",
+ "log",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "errno"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "file-header"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f572687d306c51b578ca07b5b2b10ad334a486cb11673d0f88437f22d68eed06"
+dependencies = [
+ "crossbeam",
+ "lazy_static",
+ "license",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.13.3+wasi-0.2.2",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "globset"
+version = "0.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "handle_map"
+version = "0.1.0"
+dependencies = [
+ "lazy_static",
+ "lock_adapter",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "home"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+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.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "jiff"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e"
+dependencies = [
+ "jiff-static",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.171"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75"
+dependencies = [
+ "arbitrary",
+ "cc",
+]
+
+[[package]]
+name = "license"
+version = "3.5.0+3.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef560a8a3b3ac5fef31a9cfb8e4464cd1caa36c6bf1c1896b5d5a237e6486ee1"
+dependencies = [
+ "reword",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9"
+
+[[package]]
+name = "lock_adapter"
+version = "0.1.0"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[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 = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad"
+
+[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "portable-atomic"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
+name = "pourover"
+version = "0.1.0"
+dependencies = [
+ "jni",
+ "pourover_macro",
+]
+
+[[package]]
+name = "pourover_macro"
+version = "0.1.0"
+dependencies = [
+ "nom",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "protobuf"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4"
+dependencies = [
+ "once_cell",
+ "protobuf-support",
+ "thiserror",
+]
+
+[[package]]
+name = "protobuf-codegen"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace"
+dependencies = [
+ "anyhow",
+ "once_cell",
+ "protobuf",
+ "protobuf-parse",
+ "regex",
+ "tempfile",
+ "thiserror",
+]
+
+[[package]]
+name = "protobuf-parse"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "log",
+ "protobuf",
+ "protobuf-support",
+ "tempfile",
+ "thiserror",
+ "which",
+]
+
+[[package]]
+name = "protobuf-support"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "quickcheck"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
+dependencies = [
+ "env_logger 0.8.4",
+ "log",
+ "rand",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.15",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "reword"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe272098dce9ed76b479995953f748d1851261390b08f8a0ff619c885a1f0765"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.9.2",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+
+[[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.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.140"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "shell-escape"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "smallvec"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "submerge"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "bincode",
+ "crdt",
+ "derive-where",
+ "derive_fuzztest",
+ "distributed_time",
+ "hex",
+ "log",
+ "protobuf",
+ "serde",
+ "serde_json",
+ "submerge_internal_proto",
+ "thiserror",
+]
+
+[[package]]
+name = "submerge-fuzz"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "arrayvec",
+ "crdt",
+ "derive_fuzztest",
+ "distributed_time",
+ "env_logger 0.11.7",
+ "itertools",
+ "libfuzzer-sys",
+ "log",
+ "serde",
+ "submerge",
+ "thiserror",
+]
+
+[[package]]
+name = "submerge_internal_proto"
+version = "0.1.0"
+dependencies = [
+ "protobuf",
+ "protobuf-codegen",
+ "thiserror",
+]
+
+[[package]]
+name = "submerge_java"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "arbitrary",
+ "crdt",
+ "derive-where",
+ "distributed_time",
+ "handle_map",
+ "jni",
+ "log",
+ "parking_lot",
+ "pourover",
+ "protobuf",
+ "submerge",
+ "submerge_internal_proto",
+ "thiserror",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "getrandom 0.3.1",
+ "once_cell",
+ "rustix 1.0.2",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+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 = "wasi"
+version = "0.13.3+wasi-0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[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 0.38.44",
+]
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.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.6",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
+
+[[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.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[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.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[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.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[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.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[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.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[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.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[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.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[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.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[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.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "xshell"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d"
+dependencies = [
+ "xshell-macros",
+]
+
+[[package]]
+name = "xshell-macros"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547"
diff --git a/betosync/Cargo.toml b/betosync/Cargo.toml
new file mode 100644
index 0000000..b01a027
--- /dev/null
+++ b/betosync/Cargo.toml
@@ -0,0 +1,98 @@
+[workspace]
+members = [
+ "submerge/crdt",
+ "submerge/crdt/fuzz",
+ "submerge/distributed_time",
+ "submerge/distributed_time/fuzz",
+ "submerge/submerge",
+ "submerge/submerge/fuzz",
+ "submerge/submerge_java",
+ "submerge/submerge_internal_proto",
+ "build_scripts",
+]
+default-members = ["build_scripts"]
+
+resolver = "2"
+
+[workspace.lints.rust]
+unsafe_code = "deny"
+missing_docs = "deny"
+trivial_casts = "deny"
+trivial_numeric_casts = "deny"
+unused_extern_crates = "deny"
+unused_import_braces = "deny"
+unused_results = "deny"
+unexpected_cfgs = { level = "warn", check-cfg = ["cfg(fuzzing)", "cfg(rust_analyzer)"] }
+unsafe_op_in_unsafe_fn = "forbid"
+
+[workspace.lints.clippy]
+cast_possible_truncation = "warn"
+cast_possible_wrap = "warn"
+cast_precision_loss = "warn"
+cast_sign_loss = "warn"
+indexing_slicing = "deny"
+missing_safety_doc = "deny" # This should really be "forbid", but some macros are not compatible
+multiple_unsafe_ops_per_block = "deny"
+panic = "deny"
+undocumented_unsafe_blocks = "deny" # This should really be "forbid", but some macros are not compatible
+unnecessary_safety_comment = "deny"
+unnecessary_safety_doc = "deny"
+unwrap_used = "deny"
+
+[workspace.package]
+version = "0.1.0"
+edition = "2021"
+publish = false
+license = "Apache-2.0"
+
+[workspace.dependencies]
+# Dependencies from this repo
+cmd_runner = { path = "../common/cmd_runner" }
+crdt = { path = "submerge/crdt" }
+distributed_time = { path = "submerge/distributed_time" }
+handle_map = { path = "../common/handle_map" }
+pourover = { path = "../common/pourover" }
+submerge = { path = "submerge/submerge" }
+submerge_internal_proto = { path = "submerge/submerge_internal_proto" }
+
+# Dependencies from crates.io
+anyhow = "1.0.86"
+arbitrary = { version = "1.3.2", features = ["derive"] }
+arrayvec = "0.7.4"
+bincode = "1.3.3"
+clap = { version = "4.5.13", features = ["derive"] }
+derive_fuzztest = "0.1.3"
+derive-where = "1.2.7"
+env_logger = "0.11.7"
+hex = "0.4.3"
+itertools = "0.13.0"
+jni = "0.21.1"
+lazy_static = "1.5.0"
+libfuzzer-sys = "0.4"
+log = "0.4.21"
+num-traits = "0.2.17"
+protobuf = "3.7.2"
+protobuf-codegen = "3.7.2"
+serde = { version = "1.0.193", features = ["serde_derive"] }
+serde_json = "1.0.117"
+thiserror = "1.0.61"
+xshell = "0.2.6"
+
+[profile.test]
+# speed up test execution
+opt-level = 3
+
+[profile.bench]
+# Use LTO to allow cross-crate inlining.
+# fat vs thin: thin compiles a lot faster, and doesn't seem any slower.
+lto = "thin"
+
+# build profile optimized for size
+[profile.release-min-size]
+inherits = "release"
+panic = "abort"
+codegen-units = 1
+lto = true
+# z optimizes for size
+opt-level = "z"
+strip = true
diff --git a/betosync/README.md b/betosync/README.md
new file mode 100644
index 0000000..9957343
--- /dev/null
+++ b/betosync/README.md
@@ -0,0 +1,7 @@
+# Betosync
+
+Betosync enables data syncing between multiple devices that may be connected with different network topologies or disconnected temporarily. It ensures eventual consistency of the data while preferring availability when disconnected or partitioned.
+
+## Components
+
+* [Submerge](submerge/README.md): A data-consistency library for sharing data between multiple devices that ensures causal consistency while being always available using state-based CRDTs.
diff --git a/betosync/build_scripts/Cargo.toml b/betosync/build_scripts/Cargo.toml
new file mode 100644
index 0000000..b227446
--- /dev/null
+++ b/betosync/build_scripts/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "build_scripts"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lints]
+workspace = true
+
+[dependencies]
+# Dependencies within this repo
+cmd_runner.workspace = true
+
+# Dependencies from crates.io
+anyhow.workspace = true
+clap.workspace = true
+env_logger.workspace = true
+log.workspace = true
+serde.workspace = true
+serde_json.workspace = true
+xshell.workspace = true
+
+[[bin]]
+name = "build_scripts"
+doc = false
diff --git a/betosync/build_scripts/src/coverage.rs b/betosync/build_scripts/src/coverage.rs
new file mode 100644
index 0000000..329e11c
--- /dev/null
+++ b/betosync/build_scripts/src/coverage.rs
@@ -0,0 +1,14 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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/betosync/build_scripts/src/license.rs b/betosync/build_scripts/src/license.rs
new file mode 100644
index 0000000..6c1923c
--- /dev/null
+++ b/betosync/build_scripts/src/license.rs
@@ -0,0 +1,64 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use cmd_runner::license_checker::LicenseChecker;
+
+pub const LICENSE_CHECKER: LicenseChecker = LicenseChecker {
+ // These will be checked against the absolute path of each file.
+ ignore: &[
+ "**/android/build/**",
+ "**/target/**",
+ "**/*.inc.rs",
+ "**/.idea/**",
+ "**/*.toml",
+ "**/*.md",
+ "**/*.lock",
+ "**/*.json",
+ "**/*.rsp",
+ "**/*.patch",
+ "**/*.dockerignore",
+ "**/*.apk",
+ "**/gradle/*",
+ "**/.gradle/*",
+ "**/.git*",
+ "**/.DS_Store",
+ "**/fuzz/corpus/**",
+ "**/.*.swp",
+ "**/*.vim",
+ "**/*.properties",
+ "**/third_party/**",
+ "**/*.png",
+ "**/*.ico",
+ "**/.editorconfig",
+ "**/*.class",
+ "**/fuzz/artifacts/**",
+ "**/MODULE.bazel",
+ "**/WORKSPACE",
+ "**/.bazelrc",
+ "**/submerge_java/lib/build/**",
+ "**/*.wasm",
+ "**/submerge_web_demo/node_modules/**",
+ "**/submerge_web_demo/wasm/pkg/**",
+ ],
+};
+
+#[cfg(test)]
+mod tests {
+ use super::LICENSE_CHECKER;
+
+ #[test]
+ fn new_ignore_is_likely_buggy() {
+ LICENSE_CHECKER.check_new_ignore_is_likely_buggy()
+ }
+}
diff --git a/betosync/build_scripts/src/main.rs b/betosync/build_scripts/src/main.rs
new file mode 100644
index 0000000..eb316b2
--- /dev/null
+++ b/betosync/build_scripts/src/main.rs
@@ -0,0 +1,176 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Main build script for the betosync project, used for both local development and in CI.
+//!
+//! Usage:
+//! ```sh
+//! cargo run -p build_scripts -- run-default-checks
+//! ```
+
+#![allow(missing_docs)]
+
+use clap::Parser as _;
+use cmd_runner::{
+ cargo_workspace::{CargoOptions, CargoWorkspaceSubcommand, FormatterOptions},
+ fuzzers::run_workspace_fuzz_targets,
+ license_checker::LicenseSubcommand,
+};
+use env_logger::Env;
+use license::LICENSE_CHECKER;
+use log::info;
+use std::{env, path};
+use xshell::{cmd, Shell};
+
+mod license;
+
+fn main() -> anyhow::Result<()> {
+ env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
+ let cli: Cli = Cli::parse();
+
+ let build_script_dir = path::PathBuf::from(
+ env::var("CARGO_MANIFEST_DIR").expect("Must be run via Cargo to establish root directory"),
+ );
+ let root_dir = build_script_dir
+ .parent()
+ .expect("Parent dir from build_script crate should exist");
+
+ match cli.subcommand {
+ Subcommand::RunDefaultChecks(ref check_options) => {
+ run_default_checks(root_dir, check_options)?;
+ print!(concat!(
+ "Congratulations, the default checks passed. Since you like quality, here are\n",
+ "some more checks you may like:\n",
+ " cargo run -p build_scripts -- run-rust-fuzzers\n",
+ ));
+ }
+ Subcommand::VerifyCi { ref check_options } => verify_ci(root_dir, check_options)?,
+ Subcommand::RunAllChecks { ref check_options } => run_all_checks(root_dir, check_options)?,
+ Subcommand::CleanEverything => clean_everything(root_dir)?,
+ Subcommand::CargoWorkspace(subcommand) => {
+ let sh = xshell::Shell::new()?;
+ sh.change_dir(root_dir);
+ subcommand.run(&sh, "betosync", ["--features=enable-required-features"])?
+ }
+ Subcommand::RunRustFuzzers => run_workspace_fuzz_targets(root_dir)?,
+ Subcommand::License(license_subcommand) => {
+ license_subcommand.run(&LICENSE_CHECKER, root_dir)?
+ }
+ }
+
+ Ok(())
+}
+
+fn check_java(sh: &Shell) -> anyhow::Result<()> {
+ let _guard = sh.push_dir("submerge/submerge_java");
+ cmd!(sh, "./gradlew :lib:test --rerun-tasks").run()?;
+ Ok(())
+}
+
+#[allow(clippy::if_same_then_else)]
+pub fn verify_ci(root: &path::Path, check_options: &crate::CheckOptions) -> anyhow::Result<()> {
+ if cfg!(target_os = "linux") {
+ run_default_checks(root, check_options)?;
+ } else if cfg!(target_os = "macos") {
+ license::LICENSE_CHECKER.check(root)?;
+ let sh = xshell::Shell::new()?;
+ sh.change_dir(root);
+ check_options.cargo_options.check_workspace(
+ &sh,
+ "betosync",
+ ["--features=enable-required-features"],
+ )?;
+ } else if cfg!(windows) {
+ license::LICENSE_CHECKER.check(root)?;
+ let sh = xshell::Shell::new()?;
+ sh.change_dir(root);
+ check_options.cargo_options.check_workspace(
+ &sh,
+ "betosync",
+ ["--features=enable-required-features"],
+ )?;
+ } else {
+ anyhow::bail!("Unsupported OS for running CI!")
+ }
+ Ok(())
+}
+
+/// Runs checks to ensure lints are passing and all targets are building
+pub fn run_all_checks(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> {
+ run_default_checks(root, check_options)?;
+ run_workspace_fuzz_targets(root)?;
+ Ok(())
+}
+
+/// Runs default checks that are suitable for verifying a local change.
+pub fn run_default_checks(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> {
+ license::LICENSE_CHECKER.check(root)?;
+ let sh = xshell::Shell::new()?;
+ sh.change_dir(root);
+ check_options.cargo_options.check_workspace(
+ &sh,
+ "betosync",
+ ["--features=enable-required-features"],
+ )?;
+ check_java(&sh)?;
+ Ok(())
+}
+
+pub fn clean_everything(root: &path::Path) -> anyhow::Result<()> {
+ let sh = Shell::new()?;
+ sh.change_dir(root);
+ cmd!(sh, "cargo clean").run()?;
+ sh.change_dir(root.join("submerge/submerge_java"));
+ cmd!(sh, "./gradlew :tests --rerun-tasks").run()?;
+ Ok(())
+}
+
+#[derive(clap::Parser)]
+struct Cli {
+ #[clap(subcommand)]
+ subcommand: Subcommand,
+}
+
+#[derive(clap::Subcommand, Debug, Clone)]
+enum Subcommand {
+ /// Runs all the checks that CI runs
+ VerifyCi {
+ #[command(flatten)]
+ check_options: CheckOptions,
+ },
+ /// Runs all available checks, including ones that are not normally run on CI
+ RunAllChecks {
+ #[command(flatten)]
+ check_options: CheckOptions,
+ },
+ /// Runs the default set of checks suitable for verifying local changes.
+ RunDefaultChecks(CheckOptions),
+ /// Cleans the main workspace and all sub projects - useful if upgrading rust compiler version
+ /// and need dependencies to be compiled with the same version
+ CleanEverything,
+ #[command(flatten)]
+ CargoWorkspace(CargoWorkspaceSubcommand),
+ /// Build and run pure Rust fuzzers for 10000 runs
+ RunRustFuzzers,
+ #[command(flatten)]
+ License(LicenseSubcommand),
+}
+
+#[derive(clap::Args, Debug, Clone, Default)]
+pub struct CheckOptions {
+ #[command(flatten)]
+ formatter_options: FormatterOptions,
+ #[command(flatten)]
+ cargo_options: CargoOptions,
+}
diff --git a/betosync/clippy.toml b/betosync/clippy.toml
new file mode 100644
index 0000000..4da81ba
--- /dev/null
+++ b/betosync/clippy.toml
@@ -0,0 +1 @@
+allow-unwrap-in-tests = true
\ No newline at end of file
diff --git a/betosync/deny.toml b/betosync/deny.toml
new file mode 100644
index 0000000..e8b7ecb
--- /dev/null
+++ b/betosync/deny.toml
@@ -0,0 +1,174 @@
+# This template contains all of the possible sections and their default values
+
+# Note that all fields that take a lint level have these possible values:
+# * deny - An error will be produced and the check will fail
+# * warn - A warning will be produced, but the check will not fail
+# * allow - No warning or error will be produced, though in some cases a note
+# will be
+
+# The values provided in this template are the default values that will be used
+# when any section or field is not specified in your own configuration
+
+# This section is considered when running `cargo deny check advisories`
+# More documentation for the advisories section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
+[advisories]
+# The path where the advisory database is cloned/fetched into
+db-path = "~/.cargo/advisory-db"
+# The url(s) of the advisory databases to use
+db-urls = ["https://github.com/rustsec/advisory-db"]
+# The lint level for crates that have been yanked from their source registry
+yanked = "warn"
+# A list of advisory IDs to ignore. Note that ignored advisories will still
+# output a note when they are encountered.
+ignore = [
+ # comment explaining why we have to ignore it
+ # "RUSTSEC-FOO",
+]
+# Threshold for security vulnerabilities, any vulnerability with a CVSS score
+# lower than the range specified will be ignored. Note that ignored advisories
+# will still output a note when they are encountered.
+# * None - CVSS Score 0.0
+# * Low - CVSS Score 0.1 - 3.9
+# * Medium - CVSS Score 4.0 - 6.9
+# * High - CVSS Score 7.0 - 8.9
+# * Critical - CVSS Score 9.0 - 10.0
+#severity-threshold =
+
+# If this is true, then cargo deny will use the git executable to fetch advisory database.
+# If this is false, then it uses a built-in git library.
+# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
+# See Git Authentication for more information about setting up git authentication.
+#git-fetch-with-cli = true
+
+# This section is considered when running `cargo deny check licenses`
+# More documentation for the licenses section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
+[licenses]
+# List of explicitly allowed licenses
+# See https://spdx.org/licenses/ for list of possible licenses
+# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
+allow = [
+ "MIT",
+ "Apache-2.0",
+ # "Apache-2.0 WITH LLVM-exception",
+ # "BSD-3-Clause",
+ # "BSD-2-Clause",
+ # "ISC",
+ "Unicode-DFS-2016",
+ # "OpenSSL",
+ "Unlicense",
+ # "CC0-1.0"
+ "Unicode-3.0",
+ "NCSA",
+]
+# The confidence threshold for detecting a license from license text.
+# The higher the value, the more closely the license text must be to the
+# canonical license text of a valid SPDX license file.
+# [possible values: any between 0.0 and 1.0].
+confidence-threshold = 0.8
+# Allow 1 or more licenses on a per-crate basis, so that particular licenses
+# aren't accepted for every possible crate as with the normal allow list
+exceptions = [
+ # Each entry is the crate and version constraint, and its specific allow
+ # list
+
+]
+
+# Some crates don't have (easily) machine readable licensing information,
+# adding a clarification entry for it allows you to manually specify the
+# licensing information
+#[[licenses.clarify]]
+# The name of the crate the clarification applies to
+#name = "ring"
+# The optional version constraint for the crate
+#version = "*"
+# The SPDX expression for the license requirements of the crate
+#expression = "MIT AND ISC AND OpenSSL"
+# One or more files in the crate's source used as the "source of truth" for
+# the license expression. If the contents match, the clarification will be used
+# when running the license check, otherwise the clarification will be ignored
+# and the crate will be checked normally, which may produce warnings or errors
+# depending on the rest of your configuration
+#license-files = [
+# Each entry is a crate relative path, and the (opaque) hash of its contents
+#{ path = "LICENSE", hash = 0xbd0eed23 }
+#]
+
+[[licenses.clarify]]
+name = "ring"
+version = "*"
+expression = "MIT AND ISC AND OpenSSL"
+license-files = [
+ # Each entry is a crate relative path, and the (opaque) hash of its contents
+ { path = "LICENSE", hash = 0xbd0eed23 }
+]
+
+[licenses.private]
+# If true, ignores workspace crates that aren't published, or are only
+# published to private registries.
+# To see how to mark a crate as unpublished (to the official registry),
+# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
+ignore = false
+# One or more private registries that you might publish crates to, if a crate
+# is only published to private registries, and ignore is true, the crate will
+# not have its license(s) checked
+registries = [
+ #"https://sekretz.com/registry
+]
+
+# This section is considered when running `cargo deny check bans`.
+# More documentation about the 'bans' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
+[bans]
+# Lint level for when multiple versions of the same crate are detected
+multiple-versions = "allow"
+# Lint level for when a crate version requirement is `*`
+wildcards = "allow"
+# The graph highlighting used when creating dotgraphs for crates
+# with multiple versions
+# * lowest-version - The path to the lowest versioned duplicate is highlighted
+# * simplest-path - The path to the version with the fewest edges is highlighted
+# * all - Both lowest-version and simplest-path are used
+highlight = "all"
+# List of crates that are allowed. Use with care!
+allow = [
+ #{ name = "ansi_term", version = "=0.11.0" },
+]
+# List of crates to deny
+deny = [
+ # Each entry the name of a crate and a version range. If version is
+ # not specified, all versions will be matched.
+ #{ name = "ansi_term", version = "=0.11.0" },
+ #
+ # Wrapper crates can optionally be specified to allow the crate when it
+ # is a direct dependency of the otherwise banned crate
+ #{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
+]
+# Certain crates/versions that will be skipped when doing duplicate detection.
+skip = [
+ #{ name = "ansi_term", version = "=0.11.0" },
+]
+# Similarly to `skip` allows you to skip certain crates during duplicate
+# detection. Unlike skip, it also includes the entire tree of transitive
+# dependencies starting at the specified crate, up to a certain depth, which is
+# by default infinite
+skip-tree = [
+ #{ name = "ansi_term", version = "=0.11.0", depth = 20 },
+]
+
+# This section is considered when running `cargo deny check sources`.
+# More documentation about the 'sources' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
+[sources]
+# Lint level for what to happen when a crate from a crate registry that is not
+# in the allow list is encountered
+unknown-registry = "warn"
+# Lint level for what to happen when a crate from a git repository that is not
+# in the allow list is encountered
+unknown-git = "warn"
+# List of URLs for allowed crate registries. Defaults to the crates.io index
+# if not specified. If it is specified but empty, no registries are allowed.
+allow-registry = ["https://github.com/rust-lang/crates.io-index"]
+# List of URLs for allowed Git repositories
+allow-git = []
diff --git a/betosync/rust-toolchain.toml b/betosync/rust-toolchain.toml
new file mode 100644
index 0000000..0193dee
--- /dev/null
+++ b/betosync/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "1.83.0"
diff --git a/betosync/submerge/README.md b/betosync/submerge/README.md
new file mode 100644
index 0000000..d669c04
--- /dev/null
+++ b/betosync/submerge/README.md
@@ -0,0 +1,41 @@
+# Submerge
+
+A data-consistency library for sharing data between multiple devices that ensures causal consistency while being always available using state-based CRDTs.
+
+## Structure
+
+```
+submerge/
+├── distributed_time
+│ A low level library implementing vector clocks and hybrid logical clocks
+│ for time tracking.
+├── crdt
+│ Implementation of state-based CRDTs, including Map, Set, Register, and
+│ VectorData. These data types ensure eventual consistency and defines a
+│ conflict resolution mechanism even when there are concurrent
+│ modifications.
+├── submerge
+│ Main API surface implementing the data model, with interfaces to
+│ integrate with network and storage. This layer is responsible for
+│ serializing and deserializing the CRDTs, and also keeps track of the
+│ versions at the document level.
+├── submerge_java
+│ Java bindings for submerge, including both the `submerge_java` crate
+│ that exposes the JNI functions, and the Java side library
+│ `com.google.android.submerge` that exposes an idiomatic Java API.
+└── submerge_internal_proto
+ An internal crate to support proto serialization and deserialization.
+```
+
+## Why is it called submerge?
+
+1. it is designed for ~~sinking~~ syncing
+2. its core operation is the `merge` function
+
+## Why use submerge?
+
+When syncing data between multiple devices that can get connected and disconnected, and where the network topology can change, it is often difficult to ensure that all of the devices converge to the same consistent view of the data. Naive approaches like wall-clock-based last-writer-wins may not reflect the programmer's or user's intention and may result in data loss.
+
+Submerge builds upon the research done on CRDT and eventually convergent data types and utilizes state-based CRDTs to achieve the eventual consistency guarantees. It uses Hybrid Logical Clocks for time-tracking to ensure that causality is prioritized over wall-clock time (which are often out-of-sync between one another).
+
+Submerge combines the theoretical foundations with generative fuzz-based testing to ensure that the asserted invariants are being upheld by running through millions of generated test cases.
diff --git a/betosync/submerge/crdt/Cargo.toml b/betosync/submerge/crdt/Cargo.toml
new file mode 100644
index 0000000..a020573
--- /dev/null
+++ b/betosync/submerge/crdt/Cargo.toml
@@ -0,0 +1,36 @@
+[package]
+name = "crdt"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+publish.workspace = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+submerge_internal_proto = { workspace = true, optional = true }
+
+arbitrary.workspace = true
+derive-where.workspace = true
+distributed_time.workspace = true
+log.workspace = true
+serde.workspace = true
+thiserror.workspace = true
+
+[features]
+default = []
+checker = []
+testing = []
+proto = ["dep:submerge_internal_proto", "distributed_time/proto"]
+enable-required-features = ["checker"]
+
+[dev-dependencies]
+derive_fuzztest.workspace = true
+serde_json.workspace = true
+
+[lints]
+workspace = true
+
+[[example]]
+name = "simulation_integer_max"
+required-features = ["checker"]
diff --git a/betosync/submerge/crdt/examples/simulation_integer_max/main.rs b/betosync/submerge/crdt/examples/simulation_integer_max/main.rs
new file mode 100644
index 0000000..d83dfeb
--- /dev/null
+++ b/betosync/submerge/crdt/examples/simulation_integer_max/main.rs
@@ -0,0 +1,61 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+#![allow(missing_docs)]
+
+use arbitrary::Arbitrary;
+use crdt::{
+ checker::{
+ simulation::{Operation, Simulation, SimulationContext},
+ test_fakes::TriState,
+ },
+ delta::{AsDeltaMut, AsDeltaRef, DeltaMut},
+ CrdtState,
+};
+
+/// Our trivial example CRDT.
+#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Arbitrary)]
+pub struct IntegerMaxCrdt(u8);
+
+/// Merges by taking the max of the two values.
+impl CrdtState for IntegerMaxCrdt {
+ fn merge(a: &Self, b: &Self) -> Self {
+ a.max(b).clone()
+ }
+}
+
+/// Define the operations that can be performed on the CRDT.
+#[derive(Debug, Clone, Arbitrary)]
+pub enum IntegerMaxOp {
+ /// Only operation supported in this simulation is setting the integer value
+ SetInteger { value: u8 },
+ // Operations outside of the CRDT, like setting the wall clock can also be defined here.
+}
+
+impl Operation<IntegerMaxCrdt, TriState> for IntegerMaxOp {
+ fn apply(self, mut state: DeltaMut<IntegerMaxCrdt>, _ctx: &SimulationContext<TriState>) {
+ match self {
+ IntegerMaxOp::SetInteger { value } => {
+ if value > state.as_ref().merge().0 {
+ state.delta_mut().0 = value;
+ }
+ }
+ }
+ }
+}
+
+#[derive_fuzztest::fuzztest]
+fn simulator(sim: Simulation<IntegerMaxCrdt, IntegerMaxOp>) {
+ let _ = sim.run();
+}
diff --git a/betosync/submerge/crdt/fuzz/.gitignore b/betosync/submerge/crdt/fuzz/.gitignore
new file mode 100644
index 0000000..1a45eee
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/.gitignore
@@ -0,0 +1,4 @@
+target
+corpus
+artifacts
+coverage
diff --git a/betosync/submerge/crdt/fuzz/Cargo.toml b/betosync/submerge/crdt/fuzz/Cargo.toml
new file mode 100644
index 0000000..c908e61
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/Cargo.toml
@@ -0,0 +1,73 @@
+[package]
+name = "crdt_fuzz"
+version.workspace = true
+publish = false
+edition.workspace = true
+license.workspace = true
+
+[package.metadata]
+cargo-fuzz = true
+
+[dependencies]
+arbitrary.workspace = true
+crdt = { workspace = true, features = ["checker"] }
+derive_fuzztest.workspace = true
+distributed_time.workspace = true
+
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
+
+[lints]
+workspace = true
+
+[[bin]]
+name = "default_bottom_state"
+doc = false
+[[bin]]
+name = "delta_lww_crdt_container"
+doc = false
+[[bin]]
+name = "delta_lww_map"
+doc = false
+[[bin]]
+name = "delta_register"
+doc = false
+[[bin]]
+name = "delta_set"
+doc = false
+[[bin]]
+name = "delta_vector_data"
+doc = false
+[[bin]]
+name = "merge_lww_crdt_container"
+doc = false
+[[bin]]
+name = "merge_lww_map"
+doc = false
+[[bin]]
+name = "merge_register"
+doc = false
+[[bin]]
+name = "merge_set"
+doc = false
+[[bin]]
+name = "merge_typed_crdt"
+doc = false
+[[bin]]
+name = "merge_vector_data"
+doc = false
+[[bin]]
+name = "simulator_everything"
+doc = false
+[[bin]]
+name = "simulator_lww_map"
+doc = false
+[[bin]]
+name = "simulator_register"
+doc = false
+[[bin]]
+name = "simulator_set"
+doc = false
+[[bin]]
+name = "simulator_vector_data"
+doc = false
diff --git a/betosync/submerge/crdt/fuzz/src/bin/default_bottom_state.rs b/betosync/submerge/crdt/fuzz/src/bin/default_bottom_state.rs
new file mode 100644
index 0000000..86788d9
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/default_bottom_state.rs
@@ -0,0 +1,62 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that the `Default` implementation of the CRDTs all return bottom states.
+//!
+//! A _bottom state_ of a CRDT refers to a state that, when merged with any other valid state `S`,
+//! will result in `S`. It's called "bottom state" because in a Hasse diagram of CRDT states this
+//! state will show up in the bottom.
+
+#![cfg_attr(fuzzing, no_main)]
+
+use crdt::{
+ checker::{test_crdt_union::TestCrdtUnion, test_fakes::TriState, utils::DeterministicHasher},
+ lww_crdt_container::LwwCrdtContainer,
+ lww_map::LwwMap,
+ register::Register,
+ set::Set,
+ vector_data::VectorData,
+ CrdtState,
+};
+use distributed_time::{fake_timestamp::FakeTotalTimestamp, vector_clock::VectorClock};
+
+#[derive(arbitrary::Arbitrary, Clone, Debug)]
+enum ArbitraryCrdt {
+ Set(Set<TriState, DeterministicHasher>),
+ Register(Register<TriState, VectorClock<u8>>),
+ VectorData(VectorData<TriState, TriState>),
+ Map(LwwMap<TriState, TestCrdtUnion, FakeTotalTimestamp<u8>, u8>),
+ Container(LwwCrdtContainer<TestCrdtUnion, FakeTotalTimestamp<u8>, u8>),
+}
+
+#[derive_fuzztest::fuzztest]
+fn default_bottom_state(crdt: ArbitraryCrdt) {
+ match crdt {
+ ArbitraryCrdt::Set(original) => {
+ assert_eq!(original, CrdtState::merge(&original, &Default::default()));
+ }
+ ArbitraryCrdt::Register(original) => {
+ assert_eq!(original, CrdtState::merge(&original, &Default::default()));
+ }
+ ArbitraryCrdt::VectorData(original) => {
+ assert_eq!(original, CrdtState::merge(&original, &Default::default()));
+ }
+ ArbitraryCrdt::Map(original) => {
+ assert_eq!(original, CrdtState::merge(&original, &Default::default()));
+ }
+ ArbitraryCrdt::Container(original) => {
+ assert_eq!(original, CrdtState::merge(&original, &Default::default()));
+ }
+ }
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/delta_lww_crdt_container.rs b/betosync/submerge/crdt/fuzz/src/bin/delta_lww_crdt_container.rs
new file mode 100644
index 0000000..edd1a24
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/delta_lww_crdt_container.rs
@@ -0,0 +1,88 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that delta operations on a container behaves the same as a non-delta container.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use arbitrary::Arbitrary;
+use crdt::{
+ checker::{test_crdt_union::TestCrdtUnion, test_fakes::FakeContext},
+ delta::DeltaOwned,
+ lww_crdt_container::{LwwCrdtContainer, LwwCrdtContainerRead, LwwCrdtContainerWrite},
+};
+use distributed_time::{fake_timestamp::FakeTotalTimestamp, DistributedClock};
+
+#[derive(Arbitrary, Clone, Debug)]
+pub enum Op {
+ Set {
+ ctx: FakeContext<()>,
+ value: TestCrdtUnion,
+ },
+ Remove {
+ ctx: FakeContext<()>,
+ },
+}
+
+#[derive_fuzztest::fuzztest]
+fn delta_lww_crdt_container(
+ original: LwwCrdtContainer<TestCrdtUnion, FakeTotalTimestamp<()>, ()>,
+ ops: Vec<Op>,
+) {
+ let mut expected = original.clone();
+ let mut delta = DeltaOwned::new(&original);
+
+ for op in ops {
+ match op {
+ Op::Set { mut ctx, value } => {
+ // The ctx version must be at least as new as the subtree_version, since it is
+ // supposed to be monotonic. Updating here to make sure the arbitrarily generated
+ // update context is new enough.
+ ctx.version
+ .least_upper_bound_in_place(expected.subtree_version());
+ let expected_result = expected.set_value(&ctx, value.clone());
+ let delta_result = delta.set_value(&ctx, value);
+
+ assert_eq!(expected_result, delta_result);
+ }
+ Op::Remove { mut ctx } => {
+ // The ctx version must be at least as new as the subtree_version, since it is
+ // supposed to be monotonic. Updating here to make sure the arbitrarily generated
+ // update context is new enough.
+ ctx.version
+ .least_upper_bound_in_place(expected.subtree_version());
+ let expected_result = expected.remove_value(&ctx);
+ let delta_result = delta.remove_value(&ctx);
+
+ assert_eq!(expected_result, delta_result);
+ }
+ }
+ }
+
+ assert_eq!(
+ expected.get_value().map(|d| d.merge()),
+ delta.get_value().map(|d| d.merge())
+ );
+ let ctx = FakeContext::new_with_version((), 0_u8, expected.subtree_version().clone());
+ assert_eq!(
+ expected
+ .get_value_mut(&ctx)
+ .map(|result| result.map(|d| d.merge())),
+ delta
+ .get_value_mut(&ctx)
+ .map(|result| result.map(|d| d.merge())),
+ );
+ assert_eq!(expected, delta.merge());
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/delta_lww_map.rs b/betosync/submerge/crdt/fuzz/src/bin/delta_lww_map.rs
new file mode 100644
index 0000000..37f1c4d
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/delta_lww_map.rs
@@ -0,0 +1,92 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that delta operations on a map behaves the same as a non-delta map.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use arbitrary::Arbitrary;
+use crdt::{
+ checker::{
+ test_crdt_union::TestCrdtUnion,
+ test_fakes::{FakeContext, TriState},
+ },
+ delta::DeltaOwned,
+ lww_map::{LwwMap, LwwMapRead, LwwMapWrite},
+};
+use distributed_time::{fake_timestamp::FakeTotalTimestamp, DistributedClock};
+
+#[derive(Arbitrary, Clone, Debug)]
+pub enum Op {
+ Set {
+ key: TriState,
+ ctx: FakeContext<()>,
+ value: TestCrdtUnion,
+ },
+ Remove {
+ key: TriState,
+ ctx: FakeContext<()>,
+ },
+}
+
+#[derive_fuzztest::fuzztest]
+fn delta_lww_map(
+ original: LwwMap<TriState, TestCrdtUnion, FakeTotalTimestamp<()>, ()>,
+ ops: Vec<Op>,
+) {
+ let mut expected = original.clone();
+ let mut delta = DeltaOwned::new(&original);
+
+ for op in ops {
+ match op {
+ Op::Set {
+ key,
+ mut ctx,
+ value,
+ } => {
+ // The ctx version must be at least as new as the subtree_version, since it is
+ // supposed to be monotonic. Updating here to make sure the arbitrarily generated
+ // update context is new enough.
+ ctx.version
+ .least_upper_bound_in_place(&expected.subtree_version_least_upper_bound());
+ let expected_result = expected.set_value(&ctx, key, value.clone());
+ let delta_result = delta.set_value(&ctx, key, value);
+ assert_eq!(expected_result, delta_result);
+ }
+ Op::Remove { key, mut ctx } => {
+ // The ctx version must be at least as new as the subtree_version, since it is
+ // supposed to be monotonic. Updating here to make sure the arbitrarily generated
+ // update context is new enough.
+ ctx.version
+ .least_upper_bound_in_place(&expected.subtree_version_least_upper_bound());
+ let expected_result = expected.remove_value(&ctx, key);
+ let delta_result = delta.remove_value(&ctx, key);
+ assert_eq!(expected_result, delta_result);
+ }
+ }
+ }
+
+ assert_eq!(
+ expected
+ .keys()
+ .map(|k| (k, expected.get_value(k).map(|f| f.merge())))
+ .collect::<Vec<_>>(),
+ delta
+ .keys()
+ .map(|k| (k, delta.get_value(k).map(|f| f.merge())))
+ .collect::<Vec<_>>()
+ );
+ assert_eq!(expected, delta.merge());
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/delta_register.rs b/betosync/submerge/crdt/fuzz/src/bin/delta_register.rs
new file mode 100644
index 0000000..1c688af
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/delta_register.rs
@@ -0,0 +1,47 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that delta operations on a register behaves the same as a non-delta register.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use crdt::{
+ checker::{
+ simulation::{Operation, SimulationContext},
+ test_fakes::TriState,
+ },
+ delta::{AsDeltaMut, DeltaOwned},
+ register::{checker::RegOp, Register, RegisterRead},
+};
+use distributed_time::vector_clock::VectorClock;
+
+#[derive_fuzztest::fuzztest]
+fn delta_register(
+ original: Register<TriState, VectorClock<u8>>,
+ ops: Vec<RegOp<u8, TriState>>,
+ ctx: SimulationContext<u8>,
+) {
+ let mut expected = original.clone();
+ let mut delta = DeltaOwned::new(&original);
+
+ for op in ops {
+ op.clone().apply(expected.as_mut(), &ctx);
+ op.apply(delta.as_mut(), &ctx);
+ }
+
+ assert_eq!(expected.get_all(), delta.get_all());
+ assert_eq!(expected.get(), delta.get());
+ assert_eq!(expected, delta.merge());
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/delta_set.rs b/betosync/submerge/crdt/fuzz/src/bin/delta_set.rs
new file mode 100644
index 0000000..984e3c7
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/delta_set.rs
@@ -0,0 +1,46 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that delta operations on a set behaves the same as a non-delta set.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use crdt::{
+ checker::{
+ simulation::{Operation, SimulationContext},
+ test_fakes::TriState,
+ utils::DeterministicHasher,
+ },
+ delta::{AsDeltaMut, DeltaOwned},
+ set::{checker::SetOp, Set, SetRead},
+};
+
+#[derive_fuzztest::fuzztest]
+fn delta_set(
+ original: Set<TriState, DeterministicHasher>,
+ ops: Vec<SetOp<TriState>>,
+ ctx: SimulationContext<TriState>,
+) {
+ let mut expected = original.clone();
+ let mut delta = DeltaOwned::new(&original);
+
+ for op in ops {
+ op.clone().apply(delta.as_mut(), &ctx);
+ op.apply(expected.as_mut(), &ctx);
+ }
+
+ assert_eq!(expected.entries(), delta.entries());
+ assert_eq!(expected, delta.merge());
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/delta_vector_data.rs b/betosync/submerge/crdt/fuzz/src/bin/delta_vector_data.rs
new file mode 100644
index 0000000..8ec0c7a
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/delta_vector_data.rs
@@ -0,0 +1,45 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that delta operations on a vector data behaves the same as a non-delta vector data.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use crdt::{
+ checker::{
+ simulation::{Operation, SimulationContext},
+ test_fakes::TriState,
+ },
+ delta::{AsDeltaMut, DeltaOwned},
+ vector_data::{checker::VecDataOp, VectorData, VectorDataRead},
+};
+
+#[derive_fuzztest::fuzztest]
+fn delta_vector_data(
+ original: VectorData<u8, TriState>,
+ ops: Vec<VecDataOp<u8, TriState>>,
+ ctx: SimulationContext<u8>,
+) {
+ let mut expected = original.clone();
+ let mut delta = DeltaOwned::new(&original);
+
+ for op in ops {
+ op.clone().apply(expected.as_mut(), &ctx);
+ op.apply(delta.as_mut(), &ctx);
+ }
+
+ assert_eq!(expected.entries(), delta.entries());
+ assert_eq!(expected, delta.merge());
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/merge_lww_crdt_container.rs b/betosync/submerge/crdt/fuzz/src/bin/merge_lww_crdt_container.rs
new file mode 100644
index 0000000..5259746
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/merge_lww_crdt_container.rs
@@ -0,0 +1,32 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that the merge invariants are obeyed by a LWW CRDT container.
+//!
+//! See [`MergeInvariantTest`]
+
+#![cfg_attr(fuzzing, no_main)]
+
+use crdt::{
+ checker::{test_crdt_union::TestCrdtUnion, MergeInvariantTest},
+ lww_crdt_container::LwwCrdtContainer,
+};
+use distributed_time::fake_timestamp::FakeTotalTimestamp;
+
+#[derive_fuzztest::fuzztest]
+fn merge_lww_crdt_container(
+ test: MergeInvariantTest<LwwCrdtContainer<TestCrdtUnion, FakeTotalTimestamp<()>, ()>>,
+) {
+ test.test();
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/merge_lww_map.rs b/betosync/submerge/crdt/fuzz/src/bin/merge_lww_map.rs
new file mode 100644
index 0000000..2a18029
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/merge_lww_map.rs
@@ -0,0 +1,32 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that the merge invariants are obeyed by a LWW map.
+//!
+//! See [`MergeInvariantTest`]
+
+#![cfg_attr(fuzzing, no_main)]
+
+use crdt::{
+ checker::{test_crdt_union::TestCrdtUnion, test_fakes::TriState, MergeInvariantTest},
+ lww_map::LwwMap,
+};
+use distributed_time::fake_timestamp::FakeTotalTimestamp;
+
+#[derive_fuzztest::fuzztest]
+fn merge_lwwmap(
+ test: MergeInvariantTest<LwwMap<TriState, TestCrdtUnion, FakeTotalTimestamp<()>, ()>>,
+) {
+ test.test();
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/merge_register.rs b/betosync/submerge/crdt/fuzz/src/bin/merge_register.rs
new file mode 100644
index 0000000..aecdb30
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/merge_register.rs
@@ -0,0 +1,30 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that the merge invariants are obeyed by a register.
+//!
+//! See [`MergeInvariantTest`]
+
+#![cfg_attr(fuzzing, no_main)]
+
+use crdt::{
+ checker::{test_fakes::TriState, MergeInvariantTest},
+ register::Register,
+};
+use distributed_time::vector_clock::VectorClock;
+
+#[derive_fuzztest::fuzztest]
+fn merge_register(test: MergeInvariantTest<Register<TriState, VectorClock<u8>>>) {
+ test.test();
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/merge_set.rs b/betosync/submerge/crdt/fuzz/src/bin/merge_set.rs
new file mode 100644
index 0000000..413d5c3
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/merge_set.rs
@@ -0,0 +1,29 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that the merge invariants are obeyed by a set.
+//!
+//! See [`MergeInvariantTest`]
+
+#![cfg_attr(fuzzing, no_main)]
+
+use crdt::{
+ checker::{test_fakes::TriState, utils::DeterministicHasher, MergeInvariantTest},
+ set::Set,
+};
+
+#[derive_fuzztest::fuzztest]
+fn merge_set(test: MergeInvariantTest<Set<TriState, DeterministicHasher>>) {
+ test.test()
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/merge_typed_crdt.rs b/betosync/submerge/crdt/fuzz/src/bin/merge_typed_crdt.rs
new file mode 100644
index 0000000..f425182
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/merge_typed_crdt.rs
@@ -0,0 +1,26 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that the merge invariants are obeyed by a TypedCrdt.
+//!
+//! See [`MergeInvariantTest`]
+
+#![cfg_attr(fuzzing, no_main)]
+
+use crdt::{checker::MergeInvariantTest, typed_crdt::checker::CrdtUnionMergeResult};
+
+#[derive_fuzztest::fuzztest]
+fn merge_typed_crdt(test: MergeInvariantTest<CrdtUnionMergeResult>) {
+ test.test();
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/merge_vector_data.rs b/betosync/submerge/crdt/fuzz/src/bin/merge_vector_data.rs
new file mode 100644
index 0000000..8f0c713
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/merge_vector_data.rs
@@ -0,0 +1,29 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that the merge invariants are obeyed by a vector data.
+//!
+//! See [`MergeInvariantTest`]
+
+#![cfg_attr(fuzzing, no_main)]
+
+use crdt::{
+ checker::{test_fakes::TriState, MergeInvariantTest},
+ vector_data::VectorData,
+};
+
+#[derive_fuzztest::fuzztest]
+fn merge_vectordata(test: MergeInvariantTest<VectorData<TriState, TriState>>) {
+ test.test();
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/simulator_everything.rs b/betosync/submerge/crdt/fuzz/src/bin/simulator_everything.rs
new file mode 100644
index 0000000..6dff50e
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/simulator_everything.rs
@@ -0,0 +1,149 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Simulator that runs through all possible operations in a nested CRDT tree implemented by this
+//! crate.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use std::collections::BTreeMap;
+
+use arbitrary::Arbitrary;
+use crdt::{
+ checker::{
+ simulation::{Operation, Simulation, SimulationContext},
+ test_fakes::TriState,
+ utils::DeterministicHasher,
+ },
+ delta::DeltaMut,
+ lww_map::{LwwMap, LwwMapRead, LwwMapWrite},
+ register::{checker::RegOp, Register},
+ set::{checker::SetOp, Set},
+ typed_crdt::{PlainTypes, TypedCrdt},
+ vector_data::{checker::VecDataOp, VectorData},
+};
+use distributed_time::hybrid_logical_clock::HybridLogicalTimestamp;
+
+type Node = TriState;
+
+#[derive(Debug, Clone, Arbitrary)]
+pub enum CrdtType {
+ Map,
+ Register,
+ Set,
+ VectorData,
+ None,
+}
+
+/// Map operation that may be applied to the root or to one of the child if its type is a Map.
+#[derive(Debug, Clone, Arbitrary)]
+pub enum MapOperation {
+ SetType {
+ node: Node,
+ key: String,
+ value: CrdtType,
+ },
+ ChildOperation {
+ node: Node,
+ key: String,
+ op: ChildOperation,
+ },
+ ApplyChanges {
+ node: Node,
+ plain_type: BTreeMap<String, PlainTypes<Node, TriState, DeterministicHasher>>,
+ },
+}
+
+#[derive(Debug, Clone, Arbitrary)]
+pub enum ChildOperation {
+ Map(Box<MapOperation>),
+ RegisterOp(RegOp<Node, TriState>),
+ SetOp(SetOp<TriState>),
+ VectorDataOp(VecDataOp<Node, TriState>),
+}
+
+type TestLwwMap = LwwMap<
+ String,
+ TypedCrdt<TriState, Node, DeterministicHasher>,
+ HybridLogicalTimestamp<Node>,
+ Node,
+>;
+
+impl Operation<TestLwwMap, Node> for MapOperation {
+ fn apply(self, mut map: DeltaMut<TestLwwMap>, sim_ctx: &SimulationContext<Node>) {
+ match self {
+ MapOperation::SetType { node, key, value } => {
+ let ctx = sim_ctx.context(&node);
+ match value {
+ CrdtType::Map => {
+ let _ = map.get_child_or_default::<TestLwwMap>(&ctx, key);
+ }
+ CrdtType::Register => {
+ let _ = map.get_child_or_default::<Register<_, _>>(&ctx, key);
+ }
+ CrdtType::None => {
+ let _ = map.remove_value(&ctx, key);
+ }
+ CrdtType::Set => {
+ let _ = map.get_child_or_default::<Set<_, _>>(&ctx, key);
+ }
+ CrdtType::VectorData => {
+ let _ = map.get_child_or_default::<VectorData<_, _>>(&ctx, key);
+ }
+ }
+ }
+ MapOperation::ChildOperation { node, key, op } => {
+ let ctx = sim_ctx.context(&node);
+ match op {
+ ChildOperation::Map(map_op) => {
+ if let Ok(Some(child_map)) =
+ map.get_child_mut::<LwwMap<_, _, _, _>>(&ctx, key)
+ {
+ map_op.apply(child_map, sim_ctx);
+ }
+ }
+ ChildOperation::RegisterOp(reg_op) => {
+ if let Ok(Some(reg)) = map.get_child_mut::<Register<_, _>>(&ctx, key) {
+ reg_op.apply(reg, sim_ctx);
+ }
+ }
+ ChildOperation::SetOp(set_op) => {
+ if let Ok(Some(set)) = map.get_child_mut::<Set<_, _>>(&ctx, key) {
+ set_op.apply(set, sim_ctx);
+ }
+ }
+ ChildOperation::VectorDataOp(vec_data_op) => {
+ if let Ok(Some(vd)) = map.get_child_mut::<VectorData<_, _>>(&ctx, key) {
+ vec_data_op.apply(vd, sim_ctx);
+ }
+ }
+ }
+ }
+ MapOperation::ApplyChanges { node, plain_type } => {
+ if map
+ .apply_changes(&sim_ctx.context(&node), plain_type.clone())
+ .is_ok()
+ {
+ assert_eq!(plain_type, map.to_plain());
+ }
+ }
+ }
+ }
+}
+
+#[derive_fuzztest::fuzztest]
+fn simulator_lwwmap(sim: Simulation<TestLwwMap, MapOperation>) {
+ let _ = sim.run();
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/simulator_lww_map.rs b/betosync/submerge/crdt/fuzz/src/bin/simulator_lww_map.rs
new file mode 100644
index 0000000..b837a18
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/simulator_lww_map.rs
@@ -0,0 +1,107 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Simulator that runs through all possible operations in an LWW Map.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs, clippy::unwrap_used)]
+
+use arbitrary::Arbitrary;
+use crdt::{
+ checker::{
+ simulation::{Operation, Simulation, SimulationContext},
+ test_crdt_union::{TestCrdtUnion, TestCrdtUnionMut, TestVariant1, TestVariant2},
+ test_fakes::TriState,
+ },
+ delta::DeltaMut,
+ lww_crdt_container::CrdtUnion,
+ lww_map::{LwwMap, LwwMapRead, LwwMapWrite},
+};
+use distributed_time::hybrid_logical_clock::HybridLogicalTimestamp;
+
+type Node = TriState;
+
+#[derive(Debug, Clone, Arbitrary)]
+pub enum MapOperation {
+ SetValue {
+ node: Node,
+ key: TriState,
+ value: TestCrdtUnion,
+ },
+ RemoveValue {
+ node: Node,
+ key: TriState,
+ },
+ SetChildValue {
+ key: TriState,
+ value: u8,
+ node: Node,
+ },
+}
+
+type State = LwwMap<TriState, TestCrdtUnion, HybridLogicalTimestamp<Node>, Node>;
+
+impl Operation<State, TriState> for MapOperation {
+ fn apply(self, mut map: DeltaMut<State>, context: &SimulationContext<TriState>) {
+ match self {
+ Self::SetValue { node, key, value } => {
+ if map
+ .set_value(&context.context(&node), key, value.clone())
+ .is_ok()
+ {
+ assert_eq!(
+ <TestCrdtUnion as CrdtUnion<Node>>::create_ref(None, Some(&value)),
+ map.get_value(&key)
+ )
+ }
+ }
+ Self::RemoveValue { node, key } => {
+ if map.remove_value(&context.context(&node), key).is_ok() {
+ assert_eq!(None, map.get_value(&key));
+ }
+ }
+ Self::SetChildValue { key, value, node } => {
+ match map.get_value_mut(&context.context(&node), key) {
+ Ok(Some(TestCrdtUnionMut::Type1(mut child1))) => {
+ child1.set_value(value);
+ assert_eq!(
+ value,
+ map.get_child::<TestVariant1>(&key).unwrap().get_value()
+ );
+ assert_eq!(None, map.get_child::<TestVariant2>(&key));
+ }
+ Ok(Some(TestCrdtUnionMut::Type2(mut child2))) => {
+ child2.set_value(value);
+ assert_eq!(
+ value,
+ map.get_child::<TestVariant2>(&key).unwrap().get_value()
+ );
+ assert_eq!(None, map.get_child::<TestVariant1>(&key));
+ }
+ Ok(None) => {
+ assert_eq!(None, map.get_value(&key));
+ }
+ Err(_) => {
+ // Ignore timestamp overflows.
+ }
+ }
+ }
+ }
+ }
+}
+
+#[derive_fuzztest::fuzztest]
+fn simulator_lwwmap(sim: Simulation<State, MapOperation>) {
+ let _ = sim.run();
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/simulator_register.rs b/betosync/submerge/crdt/fuzz/src/bin/simulator_register.rs
new file mode 100644
index 0000000..9b81fbf
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/simulator_register.rs
@@ -0,0 +1,34 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Simulator that runs through all possible operations in a register.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use distributed_time::vector_clock::VectorClock;
+
+use crdt::{
+ checker::{simulation::Simulation, test_fakes::TriState},
+ register::{checker::RegOp, Register},
+};
+
+type Node = TriState;
+
+#[derive_fuzztest::fuzztest]
+fn simulator_register(
+ sim: Simulation<Register<TriState, VectorClock<Node>>, RegOp<Node, TriState>>,
+) {
+ let _ = sim.run();
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/simulator_set.rs b/betosync/submerge/crdt/fuzz/src/bin/simulator_set.rs
new file mode 100644
index 0000000..def7c64
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/simulator_set.rs
@@ -0,0 +1,28 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Simulator that runs through all possible operations in a set.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use crdt::{
+ checker::{simulation::Simulation, test_fakes::TriState, utils::DeterministicHasher},
+ set::{checker::SetOp, Set},
+};
+
+#[derive_fuzztest::fuzztest]
+fn simulator_set(sim: Simulation<Set<TriState, DeterministicHasher>, SetOp<TriState>>) {
+ let _ = sim.run();
+}
diff --git a/betosync/submerge/crdt/fuzz/src/bin/simulator_vector_data.rs b/betosync/submerge/crdt/fuzz/src/bin/simulator_vector_data.rs
new file mode 100644
index 0000000..93d5d45
--- /dev/null
+++ b/betosync/submerge/crdt/fuzz/src/bin/simulator_vector_data.rs
@@ -0,0 +1,30 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Simulator that runs through all possible operations in a vector data.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use crdt::{
+ checker::{simulation::Simulation, test_fakes::TriState},
+ vector_data::{checker::VecDataOp, VectorData},
+};
+
+type Node = TriState;
+
+#[derive_fuzztest::fuzztest]
+fn simulator_vectordata(sim: Simulation<VectorData<Node, TriState>, VecDataOp<Node, TriState>>) {
+ let _ = sim.run();
+}
diff --git a/betosync/submerge/crdt/src/checker/eventual_delivery.rs b/betosync/submerge/crdt/src/checker/eventual_delivery.rs
new file mode 100644
index 0000000..403bee2
--- /dev/null
+++ b/betosync/submerge/crdt/src/checker/eventual_delivery.rs
@@ -0,0 +1,94 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use arbitrary::Arbitrary;
+
+/// Simulation of an eventual delivery system that may arbitrarily reorder or duplicate the given
+/// messages
+#[derive(Debug, Clone, Arbitrary)]
+pub struct EventualDeliveryScenario {
+ pub message_scrambling_guide: Vec<usize>,
+}
+
+impl EventualDeliveryScenario {
+ /// Returns the scrambled list of input, or `None` if the scrambling guide contained in this
+ /// struct does not have enough information to satisfy eventual delivery. The returned list, if
+ /// returned, is guaranteed to contain each message given in `sent_messages` at least once, to
+ /// simulate eventual delivery.
+ ///
+ /// This consumes the eventual delivery scenario so that the input data is not reused for
+ /// scrambling other messages. If needed, the caller can create multiple delivery scenarios for
+ /// different packets that needs to be scrambled independently.
+ pub fn scramble<T: Clone>(mut self, sent_messages: &[T]) -> Option<Vec<T>> {
+ if sent_messages.is_empty() {
+ return Some(vec![]);
+ }
+ let mut output = sent_messages.to_vec();
+ let len = sent_messages.len();
+ // Shuffle with Fisher-Yates algorithm
+ for i in 1..len {
+ let rand_index = sample_from(&mut self.message_scrambling_guide, len - i)?;
+ output.swap(len - i, rand_index)
+ }
+
+ // For the remaining elements, copy them as duplicate messages
+ while let (Some(copy_from), Some(new_index)) = (
+ sample_from(&mut self.message_scrambling_guide, sent_messages.len()),
+ sample_from(&mut self.message_scrambling_guide, output.len()),
+ ) {
+ output.insert(new_index, sent_messages[copy_from].clone());
+ }
+
+ Some(output)
+ }
+}
+
+/// Given an `input` that is uniformly random between `[0, usize::MAX]`, return an input between
+/// `[0, max)` that is not biased. Returns `None` if it is not possible to clamp without introducing
+/// bias.
+fn clamp_uniform_sample(input: usize, max: usize) -> Option<usize> {
+ let max_admissible = usize::MAX - (usize::MAX % max);
+ if input <= max_admissible {
+ Some(input % max)
+ } else {
+ None
+ }
+}
+
+/// Sample from the given source (pop items from the end of the vec), and clamping them within the
+/// range `[0, max)`. It will retry until the source runs out of elements, or until a maximum of 256
+/// retries is reached.
+fn sample_from(source: &mut Vec<usize>, max: usize) -> Option<usize> {
+ for _ in 0..256 {
+ if let Some(output) = clamp_uniform_sample(source.pop()?, max) {
+ return Some(output);
+ }
+ }
+ panic!("Failed to sample from source without bias")
+}
+
+#[cfg(test)]
+mod tests {
+ use super::EventualDeliveryScenario;
+
+ #[derive_fuzztest::proptest]
+ fn scramble_len(input: Vec<u8>, scenario: EventualDeliveryScenario) {
+ if let Some(scrambled) = scenario.scramble(&input) {
+ assert!(scrambled.len() >= input.len());
+ for i in input {
+ assert!(scrambled.contains(&i));
+ }
+ }
+ }
+}
diff --git a/betosync/submerge/crdt/src/checker/mod.rs b/betosync/submerge/crdt/src/checker/mod.rs
new file mode 100644
index 0000000..06a1b3f
--- /dev/null
+++ b/betosync/submerge/crdt/src/checker/mod.rs
@@ -0,0 +1,159 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Module for testing and checking the invariants of a CRDT implementation.
+//!
+//! There are two main types of tests provided in this module:
+//! * [`MergeInvariantTest`] tests the invariants required for state-based CRDTs directly on
+//! arbitrary instances of the given `CrdtState`.
+//! * [`Simulation`][simulation::Simulation] tests the CRDT implementations by defining valid
+//! operations on a CRDT implementation, arbitrarily shuffling the resulting update messages, and
+//! check to ensure that the implementations converge.
+//!
+//! Note that both of these tests only test for invariants that apply to all CRDT implementations,
+//! namely eventual consistency. Unit tests are still required to ensure that the resulting state
+//! matches the user intent.
+//!
+//! _Requires the `checker` feature_.
+
+use std::fmt::Debug;
+
+use crate::CrdtState;
+use arbitrary::{Arbitrary, Unstructured};
+
+pub(crate) mod eventual_delivery;
+pub mod simulation;
+pub mod test_crdt_union;
+pub mod test_fakes;
+pub mod utils;
+
+/// Test case for the invariant properties required for `merge` functions of state-based CRDTs.
+///
+/// This type implements `Arbitrary` and can be used in property-testing and fuzzing in order to
+/// verify these invariants automatically (see the `derive-fuzztest` crate).
+///
+/// This test checks against collections of state that are valid, according to
+/// [`CrdtState::is_valid_collection`], which by default is any instance allowed by the type system.
+/// If there are additional conditions for convergence, (e.g. if your CRDT relies on globally unique
+/// timestamps), you should implement `is_valid_collection`. The helper functions
+/// [`is_uniq`][crate::checker::utils::IterExt::is_uniq] and
+/// [`iter_pairs`][crate::checker::utils::IterExt::iter_pairs_unordered] may be useful.
+///
+/// # Example
+///
+/// This example implements a last-writer-wins register that requires globally unique timestamps.
+///
+/// ```
+/// use crdt::CrdtState;
+/// use crdt::checker::MergeInvariantTest;
+///
+/// #[derive(Debug, Clone, PartialEq, Eq)]
+/// pub struct LwwValue { timestamp: u64, value: String }
+///
+/// impl CrdtState for LwwValue {
+/// fn merge(a: &Self, b: &Self) -> Self {
+/// std::cmp::max_by_key(a, b, |v| v.timestamp).clone()
+/// }
+///
+/// #[cfg(any(test, feature = "checker"))]
+/// fn is_valid_collection<'a>(collection: impl IntoIterator<Item = &'a Self>) -> bool
+/// where
+/// Self: 'a,
+/// {
+/// use crdt::checker::utils::IterExt as _;
+///
+/// collection.into_iter().map(|s| &s.timestamp).is_uniq()
+/// }
+/// }
+///
+/// #[derive_fuzztest::fuzztest]
+/// fn invariant_test(test: MergeInvariantTest<LwwValue>) {
+/// test.test()
+/// }
+/// ```
+#[derive(Clone, Debug)]
+pub enum MergeInvariantTest<S: CrdtState + Eq + Debug> {
+ /// Test associativity `merge(merge(a, b), c) == merge(a, merge(b, c))`
+ Associativity(S, S, S),
+ /// Test commutativity `merge(a, b) == merge(b, a)`
+ Commutativity(S, S),
+ /// Test idempotence `merge(a, a) == a`
+ Idempotence(S),
+}
+
+impl<S: CrdtState + Eq + Debug> MergeInvariantTest<S> {
+ /// Run the test, panicking if the invariant is violated.
+ pub fn test(self) {
+ match self {
+ Self::Associativity(s1, s2, s3) => {
+ assert_eq!(
+ S::merge(&S::merge(&s1, &s2), &s3),
+ S::merge(&s1, &S::merge(&s2, &s3)),
+ "Associativity violated:\ns1={s1:#?}\ns2={s2:#?}\ns3={s3:#?}",
+ )
+ }
+ Self::Commutativity(s1, s2) => {
+ assert_eq!(
+ S::merge(&s1, &s2),
+ S::merge(&s2, &s1),
+ "Commutativity violated:\ns1={s1:#?}\ns2={s2:#?}"
+ )
+ }
+ Self::Idempotence(s1) => {
+ assert_eq!(S::merge(&s1, &s1), s1, "Idempotence violated:\ns1={s1:#?}")
+ }
+ }
+ }
+}
+
+impl<'a, S> Arbitrary<'a> for MergeInvariantTest<S>
+where
+ S: CrdtState + Eq + Debug + Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match u.int_in_range(0..=2)? {
+ 0 => {
+ let s1 = S::arbitrary(u)?;
+ let s2 = S::arbitrary(u)?;
+ let s3 = S::arbitrary(u)?;
+ if S::is_valid_collection([&s1, &s2, &s3]) {
+ Ok(MergeInvariantTest::Associativity(s1, s2, s3))
+ } else {
+ Err(arbitrary::Error::IncorrectFormat)
+ }
+ }
+ 1 => {
+ let s1 = S::arbitrary(u)?;
+ let s2 = S::arbitrary(u)?;
+ if S::is_valid_collection([&s1, &s2]) {
+ Ok(MergeInvariantTest::Commutativity(s1, s2))
+ } else {
+ Err(arbitrary::Error::IncorrectFormat)
+ }
+ }
+ 2 => {
+ let s = S::arbitrary(u)?;
+ if S::is_valid_collection([&s]) {
+ Ok(MergeInvariantTest::Idempotence(s))
+ } else {
+ Err(arbitrary::Error::IncorrectFormat)
+ }
+ }
+ _ => Err(arbitrary::Error::IncorrectFormat),
+ }
+ }
+}
+
+#[cfg(test)]
+pub mod tests;
diff --git a/betosync/submerge/crdt/src/checker/simulation.rs b/betosync/submerge/crdt/src/checker/simulation.rs
new file mode 100644
index 0000000..001e255
--- /dev/null
+++ b/betosync/submerge/crdt/src/checker/simulation.rs
@@ -0,0 +1,244 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests for CRDT convergence by simulating the operations, shuffling the resulting update messages
+//! and verifying that the merge results are consistent.
+//!
+//! See [`Simulation`].
+
+use crate::{
+ delta::{AsDeltaMut, DeltaMut, DeltaOwned},
+ CrdtState,
+};
+use arbitrary::Arbitrary;
+use derive_where::derive_where;
+use distributed_time::fake_timestamp::FakeTimestamp;
+use std::{collections::BTreeMap, fmt::Debug};
+
+use super::{
+ eventual_delivery::EventualDeliveryScenario,
+ test_fakes::{FakeContext, TriState},
+};
+
+/// An operation that can be tested in a [`Simulation`].
+///
+/// Typically an operation is an enum which represents all possible operations for the state `S`
+/// under test. These operations may include environmental changes like clock value updates. An
+/// operation should implement [`Arbitrary`] so that an arbitrary [`Simulation`] can be created
+/// during fuzzing or property testing.
+pub trait Operation<S, N: Ord + Eq> {
+ /// Applies this operation to the given `state`.
+ ///
+ /// The operation should mutate the given `state`. If `state` contains a non-empty delta
+ /// component when this call returns, the simulation will record the delta as the update
+ /// message.
+ ///
+ /// Implementations can also add `assert!` or `panic!` to check for invariants that should be
+ /// kept after an operation is applied.
+ ///
+ /// # Params
+ /// * `state` - the current state that the operation should be applied to.
+ /// * `context` - the context information for this execution.
+ fn apply(self, state: DeltaMut<S>, context: &SimulationContext<N>);
+}
+
+/// A simulation test for a CRDT implementation `S`.
+///
+/// The simulation starts at an arbitrary `init_state: S`, applies an arbitrary list of operations
+/// `OP` and gathers the update messages generated during [`Operation::apply`]. It then shuffles the
+/// gathered messages according to eventual delivery rules (i.e. messages can be reordered and
+/// duplicated, but not lost). The merge result from the shuffled update messages is then compared
+/// against the merge result from unshuffled ones, and makes sure that they are equal (i.e.
+/// satisfies eventual consistency).
+///
+/// ## Params
+/// * `S`: The CRDT state to test against.
+/// * `OP`: The operations to be applied onto `S`, typically an enum.
+/// * `N`: The Node ID. The default is [`TriState`], which means the system has at most 3 nodes.
+///
+/// # Example
+/// ```
+/// use arbitrary::Arbitrary;
+/// use crdt::{
+/// checker::{
+/// simulation::{Operation, Simulation, SimulationContext},
+/// test_fakes::TriState,
+/// },
+/// delta::{AsDeltaMut, AsDeltaRef, DeltaMut},
+/// CrdtState,
+/// };
+///
+/// /// Our trivial example CRDT.
+/// #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Arbitrary)]
+/// pub struct IntegerMaxCrdt(u8);
+///
+/// /// Merges by taking the max of the two values.
+/// impl CrdtState for IntegerMaxCrdt {
+/// fn merge(a: &Self, b: &Self) -> Self {
+/// a.max(b).clone()
+/// }
+/// }
+///
+/// /// Define the operations that can be performed on the CRDT.
+/// #[derive(Debug, Clone, Arbitrary)]
+/// pub enum IntegerMaxOp {
+/// /// Only operation supported in this simulation is setting the integer value
+/// SetInteger { value: u8 },
+/// // Operations outside of the CRDT, like setting the wall clock can also be defined here.
+/// }
+///
+/// impl Operation<IntegerMaxCrdt, TriState> for IntegerMaxOp {
+/// fn apply(self, mut state: DeltaMut<IntegerMaxCrdt>, _ctx: &SimulationContext<TriState>) {
+/// match self {
+/// IntegerMaxOp::SetInteger { value } => {
+/// if value > state.as_ref().merge().0 {
+/// state.delta_mut().0 = value;
+/// }
+/// }
+/// }
+/// }
+/// }
+///
+/// #[derive_fuzztest::fuzztest]
+/// fn simulator(sim: Simulation<IntegerMaxCrdt, IntegerMaxOp>) {
+/// let _ = sim.run();
+/// }
+/// ```
+#[derive(Debug, Clone, Arbitrary)]
+pub struct Simulation<S, OP, N = TriState>
+where
+ S: CrdtState,
+ OP: Operation<S, N> + Debug,
+ N: Ord,
+{
+ init_state: S,
+ operations: Vec<SimulationOperation<N, OP>>,
+ delivery_scenario: EventualDeliveryScenario,
+}
+
+impl<S, OP, N> Simulation<S, OP, N>
+where
+ S: CrdtState + Clone + Eq + Debug + Default,
+ OP: Operation<S, N> + Debug + Clone,
+ N: Ord,
+{
+ /// Drains the operations from `self.operations` and gathers the list of resulting messages.
+ fn sent_messages(&mut self) -> (S, Vec<S>) {
+ let mut context = SimulationContext::<N>::default();
+ let mut update_messages = Vec::with_capacity(self.operations.len());
+ update_messages.push(self.init_state.clone());
+ let mut current_state = self.init_state.clone();
+ for op in self.operations.drain(..) {
+ match op {
+ SimulationOperation::RegularOp(op) => {
+ let mut delta = DeltaOwned::new(¤t_state);
+ op.apply(delta.as_mut(), &context);
+ let delta_component = delta.into_delta();
+ current_state = CrdtState::merge(¤t_state, &delta_component);
+ update_messages.push(delta_component);
+ }
+ SimulationOperation::ContextOp(op) => {
+ op.apply(&mut context);
+ }
+ }
+ }
+ (current_state, update_messages)
+ }
+
+ /// Run the simulation test and assert that the results converge after arbitrarily shuffling
+ /// the update messages.
+ ///
+ /// # Returns
+ /// Returns the two resulting states (which are equal according to `Eq`), or `None` if a valid
+ /// shuffling cannot be generated (and the test should be discarded). The returned states can be
+ /// used to verify additional invariants specific to the CRDT implementation, for example if
+ /// there are states that should not be reachable.
+ ///
+ /// # Panics
+ /// Panics if the resulting states from the shuffled and unshuffled operations are not equal.
+ pub fn run(mut self) -> Option<(S, S)> {
+ let (final_state, sent_messages) = self.sent_messages();
+ let delivered_messages = self.delivery_scenario.scramble(&sent_messages)?;
+ let state1 = sent_messages
+ .into_iter()
+ .fold(self.init_state.clone(), |a, b| CrdtState::merge(&a, &b));
+ let state2 = delivered_messages
+ .into_iter()
+ .fold(self.init_state.clone(), |a, b| CrdtState::merge(&a, &b));
+
+ assert_eq!(state1, state2);
+ assert_eq!(state1, final_state);
+ Some((state1, state2))
+ }
+}
+
+#[derive(Clone, Debug, Arbitrary)]
+enum SimulationOperation<N: Ord, OP> {
+ RegularOp(OP),
+ ContextOp(SimulationContextOperation<N>),
+}
+
+/// Stores contextual information for all the nodes in a simulation.
+///
+/// Currently this only stores the wall-clock timestamps for each node.
+#[derive(Debug, Clone, Arbitrary)]
+#[derive_where(Default)]
+pub struct SimulationContext<N: Ord + Eq> {
+ timestamps: BTreeMap<N, FakeTimestamp>,
+}
+
+impl<N> SimulationContext<N>
+where
+ N: Ord + Eq + Clone,
+{
+ /// Get the [`UpdateContext`][crate::UpdateContext] for the given node in this simulation.
+ pub fn context(&self, node: &N) -> FakeContext<N, u8> {
+ FakeContext::new(
+ node.clone(),
+ self.timestamps.get(node).cloned().unwrap_or_default().0,
+ )
+ }
+}
+
+/// A mutation operation on the [`SimulationContext`].
+#[derive(Debug, Clone, Arbitrary)]
+#[allow(missing_docs)]
+enum SimulationContextOperation<N: Ord + Eq> {
+ Increment { node: N },
+ Decrement { node: N },
+ SetValue { node: N, timestamp: FakeTimestamp },
+}
+
+impl<N: Ord + Eq> SimulationContextOperation<N> {
+ /// Applies the operation on the given simulation context.
+ fn apply(self, state: &mut SimulationContext<N>) {
+ match self {
+ SimulationContextOperation::Increment { node } => {
+ let timestamp = state.timestamps.entry(node).or_default();
+ if let Some(incremented) = timestamp.0.checked_add(1) {
+ *timestamp = FakeTimestamp(incremented);
+ }
+ }
+ SimulationContextOperation::Decrement { node } => {
+ let timestamp = state.timestamps.entry(node).or_default();
+ if let Some(decremented) = timestamp.0.checked_sub(1) {
+ *timestamp = FakeTimestamp(decremented);
+ }
+ }
+ SimulationContextOperation::SetValue { node, timestamp } => {
+ let _ = state.timestamps.insert(node, timestamp);
+ }
+ }
+ }
+}
diff --git a/betosync/submerge/crdt/src/checker/test_crdt_union.rs b/betosync/submerge/crdt/src/checker/test_crdt_union.rs
new file mode 100644
index 0000000..b4cbce6
--- /dev/null
+++ b/betosync/submerge/crdt/src/checker/test_crdt_union.rs
@@ -0,0 +1,301 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test implementation of [`CrdtUnion`].
+//!
+//! See [`TestCrdtUnion`].
+
+use std::{convert::Infallible, fmt::Debug};
+
+use crate::{
+ delta::{AsDeltaMut, AsDeltaRef, DeltaMut, DeltaRef},
+ lww_crdt_container::{CrdtUnion, IncompatibleType, TryAsChild},
+ ApplyChanges, CrdtState, HasPlainRepresentation, ToPlain, UpdateContext,
+};
+use arbitrary::Arbitrary;
+use distributed_time::vector_clock::VectorClock;
+
+/// A test implementation of [`CrdtUnion`] with two variants, which merges by simply taking the max
+/// if values are of the same enum variant, or fails if values are of different enum variants.
+#[derive(Debug, Clone, Arbitrary, PartialEq, Eq)]
+#[allow(missing_docs)]
+pub enum TestCrdtUnion {
+ Type1(TestVariant1),
+ Type2(TestVariant2),
+}
+
+impl From<TestVariant1> for TestCrdtUnion {
+ fn from(value: TestVariant1) -> Self {
+ Self::Type1(value)
+ }
+}
+
+impl From<TestVariant2> for TestCrdtUnion {
+ fn from(value: TestVariant2) -> Self {
+ Self::Type2(value)
+ }
+}
+
+/// A enum analogous to [`TestCrdtUnion`], but contains [`DeltaRef`] of each variant.
+#[derive(Debug, PartialEq, Eq)]
+#[allow(missing_docs)]
+pub enum TestCrdtUnionRef<'d> {
+ Type1(DeltaRef<'d, TestVariant1>),
+ Type2(DeltaRef<'d, TestVariant2>),
+}
+
+impl TestCrdtUnionRef<'_> {
+ /// Combines the base part and the delta part and return the resulting merged component.
+ ///
+ /// See [`CrdtState::merge`] for details on the merge operation.
+ pub fn merge(&self) -> TestCrdtUnion {
+ match self {
+ Self::Type1(r) => TestCrdtUnion::Type1(r.merge()),
+ Self::Type2(r) => TestCrdtUnion::Type2(r.merge()),
+ }
+ }
+}
+
+/// A enum analogous to [`TestCrdtUnion`], but contains [`DeltaMut`] of each variant.
+#[derive(Debug)]
+#[allow(missing_docs)]
+pub enum TestCrdtUnionMut<'d> {
+ Type1(DeltaMut<'d, TestVariant1>),
+ Type2(DeltaMut<'d, TestVariant2>),
+}
+
+impl TestCrdtUnionMut<'_> {
+ /// Combines the base part and the delta part and return the resulting merged component.
+ ///
+ /// See [`CrdtState::merge`] for details on the merge operation.
+ pub fn merge(&self) -> TestCrdtUnion {
+ match self {
+ Self::Type1(r) => TestCrdtUnion::Type1(r.as_ref().merge()),
+ Self::Type2(r) => TestCrdtUnion::Type2(r.as_ref().merge()),
+ }
+ }
+}
+
+/// Test variant used in [`TestCrdtUnion`].
+#[derive(Default, Debug, Clone, Copy, Arbitrary, PartialEq, Eq, PartialOrd, Ord)]
+pub struct TestVariant1(pub u8);
+
+impl CrdtState for TestVariant1 {
+ fn merge(a: &Self, b: &Self) -> Self {
+ Self(a.0.max(b.0))
+ }
+}
+
+impl DeltaMut<'_, TestVariant1> {
+ /// Sets the value of this.
+ pub fn set_value(&mut self, value: u8) {
+ self.delta.0 = value
+ }
+}
+
+impl DeltaRef<'_, TestVariant1> {
+ /// Gets the value.
+ pub fn get_value(&self) -> u8 {
+ self.delta.or(self.base).map(|v| v.0).unwrap_or_default()
+ }
+}
+
+/// Test variant used in [`TestCrdtUnion`].
+#[derive(Default, Debug, Clone, Copy, Arbitrary, PartialEq, Eq, PartialOrd, Ord)]
+pub struct TestVariant2(pub u8);
+
+impl CrdtState for TestVariant2 {
+ fn merge(a: &Self, b: &Self) -> Self {
+ Self(a.0.max(b.0))
+ }
+}
+
+impl DeltaMut<'_, TestVariant2> {
+ /// Sets the value of this.
+ pub fn set_value(&mut self, value: u8) {
+ self.delta.0 = value
+ }
+}
+
+impl DeltaRef<'_, TestVariant2> {
+ /// Gets the value.
+ pub fn get_value(&self) -> u8 {
+ self.delta.or(self.base).map(|v| v.0).unwrap_or_default()
+ }
+}
+
+impl<N: Ord> TryAsChild<TestVariant1, N> for TestCrdtUnion {
+ fn try_as_child_ref(this: Self::DeltaRef<'_>) -> Option<DeltaRef<'_, TestVariant1>> {
+ match this {
+ TestCrdtUnionRef::Type1(v) => Some(v),
+ TestCrdtUnionRef::Type2(_) => None,
+ }
+ }
+
+ fn try_as_child_mut(this: Self::DeltaMut<'_>) -> Option<DeltaMut<'_, TestVariant1>> {
+ match this {
+ TestCrdtUnionMut::Type1(v) => Some(v),
+ TestCrdtUnionMut::Type2(_) => None,
+ }
+ }
+}
+
+impl<N: Ord> TryAsChild<TestVariant2, N> for TestCrdtUnion {
+ fn try_as_child_ref(this: Self::DeltaRef<'_>) -> Option<DeltaRef<'_, TestVariant2>> {
+ match this {
+ TestCrdtUnionRef::Type1(_) => None,
+ TestCrdtUnionRef::Type2(v) => Some(v),
+ }
+ }
+
+ fn try_as_child_mut(this: Self::DeltaMut<'_>) -> Option<DeltaMut<'_, TestVariant2>> {
+ match this {
+ TestCrdtUnionMut::Type1(_) => None,
+ TestCrdtUnionMut::Type2(v) => Some(v),
+ }
+ }
+}
+
+impl<N: Ord> CrdtUnion<N> for TestCrdtUnion {
+ type DeltaRef<'d> = TestCrdtUnionRef<'d>;
+ type DeltaMut<'d> = TestCrdtUnionMut<'d>;
+
+ fn try_merge(a: &Self, b: &Self) -> Result<Self, IncompatibleType> {
+ match (a, b) {
+ (TestCrdtUnion::Type1(state_a), TestCrdtUnion::Type1(state_b)) => {
+ Ok(TestCrdtUnion::Type1(*state_a.max(state_b)))
+ }
+ (TestCrdtUnion::Type2(state_a), TestCrdtUnion::Type2(state_b)) => {
+ Ok(TestCrdtUnion::Type2(*state_a.max(state_b)))
+ }
+ _ => Err(IncompatibleType),
+ }
+ }
+
+ #[allow(clippy::panic)]
+ fn create_ref<'d>(
+ base: Option<&'d Self>,
+ delta: Option<&'d Self>,
+ ) -> Option<Self::DeltaRef<'d>> {
+ match (base, delta) {
+ (None, None) => None,
+ (None, Some(Self::Type1(t1))) => Some(TestCrdtUnionRef::Type1(DeltaRef {
+ base: None,
+ delta: Some(t1),
+ })),
+ (None, Some(Self::Type2(t2))) => Some(TestCrdtUnionRef::Type2(DeltaRef {
+ base: None,
+ delta: Some(t2),
+ })),
+ (Some(Self::Type1(t1)), None) => Some(TestCrdtUnionRef::Type1(DeltaRef {
+ base: Some(t1),
+ delta: None,
+ })),
+ (Some(Self::Type2(t2)), None) => Some(TestCrdtUnionRef::Type2(DeltaRef {
+ base: Some(t2),
+ delta: None,
+ })),
+ (Some(Self::Type1(base)), Some(Self::Type1(delta))) => {
+ Some(TestCrdtUnionRef::Type1(DeltaRef {
+ base: Some(base),
+ delta: Some(delta),
+ }))
+ }
+ (Some(Self::Type2(base)), Some(Self::Type2(delta))) => {
+ Some(TestCrdtUnionRef::Type2(DeltaRef {
+ base: Some(base),
+ delta: Some(delta),
+ }))
+ }
+ _ => panic!("Incompatible type"),
+ }
+ }
+
+ #[allow(clippy::panic)]
+ fn create_mut<'d>(base: Option<&'d Self>, delta: &'d mut Self) -> Option<Self::DeltaMut<'d>> {
+ match (base, delta) {
+ (None, Self::Type1(t1)) => Some(TestCrdtUnionMut::Type1(DeltaMut {
+ base: None,
+ delta: t1,
+ })),
+ (None, Self::Type2(t2)) => Some(TestCrdtUnionMut::Type2(DeltaMut {
+ base: None,
+ delta: t2,
+ })),
+ (Some(Self::Type1(base)), Self::Type1(delta)) => {
+ Some(TestCrdtUnionMut::Type1(DeltaMut {
+ base: Some(base),
+ delta,
+ }))
+ }
+ (Some(Self::Type2(base)), Self::Type2(delta)) => {
+ Some(TestCrdtUnionMut::Type2(DeltaMut {
+ base: Some(base),
+ delta,
+ }))
+ }
+ _ => panic!("Incompatible type"),
+ }
+ }
+
+ fn create_matching_default(example: &Self) -> Self {
+ match example {
+ TestCrdtUnion::Type1(_) => TestCrdtUnion::Type1(Default::default()),
+ TestCrdtUnion::Type2(_) => TestCrdtUnion::Type2(Default::default()),
+ }
+ }
+
+ fn calculate_delta(&self, _base_version: &VectorClock<N>) -> Self {
+ self.clone()
+ }
+}
+
+impl<N: Ord> HasPlainRepresentation<N> for TestCrdtUnion {
+ type Plain = TestCrdtUnion;
+ type Error = Infallible;
+
+ fn init_from_plain(
+ _ctx: &impl UpdateContext<N>,
+ plain: Self::Plain,
+ ) -> Result<Self, Self::Error> {
+ Ok(plain)
+ }
+}
+
+impl ToPlain for TestCrdtUnionRef<'_> {
+ type Plain = TestCrdtUnion;
+
+ fn to_plain(&self) -> TestCrdtUnion {
+ self.merge()
+ }
+}
+
+impl<N> ApplyChanges<N> for TestCrdtUnionMut<'_> {
+ type Plain = TestCrdtUnion;
+
+ type Error = Infallible;
+
+ fn apply_changes(
+ &mut self,
+ _ctx: &impl UpdateContext<N>,
+ plain: Self::Plain,
+ ) -> Result<bool, Self::Error> {
+ match (self, plain) {
+ (TestCrdtUnionMut::Type1(r1), TestCrdtUnion::Type1(v1)) => *r1.delta_mut() = v1,
+ (TestCrdtUnionMut::Type2(r2), TestCrdtUnion::Type2(v2)) => *r2.delta_mut() = v2,
+ _ => unreachable!(),
+ }
+ Ok(false)
+ }
+}
diff --git a/betosync/submerge/crdt/src/checker/test_fakes.rs b/betosync/submerge/crdt/src/checker/test_fakes.rs
new file mode 100644
index 0000000..7634920
--- /dev/null
+++ b/betosync/submerge/crdt/src/checker/test_fakes.rs
@@ -0,0 +1,97 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A collection of test fakes that can be used in testing.
+//!
+//! Our CRDT implementations in this crate uses generic with relaxed trait bounds as much as
+//! possible to enable substituting in these fakes for testing.
+
+use arbitrary::Arbitrary;
+use distributed_time::{fake_timestamp::FakeTimestamp, vector_clock::VectorClock};
+use serde::{Deserialize, Serialize};
+
+use crate::UpdateContext;
+
+/// An enum with 3 states for testing. This is intended to be used as generic arguments to reduce
+/// the state space while performing property-based tests.
+#[derive(
+ Debug,
+ Default,
+ Clone,
+ Copy,
+ Hash,
+ PartialEq,
+ Eq,
+ PartialOrd,
+ Ord,
+ arbitrary::Arbitrary,
+ Serialize,
+ Deserialize,
+)]
+#[allow(missing_docs)]
+pub enum TriState {
+ #[default]
+ A,
+ B,
+ C,
+}
+
+/// An [`UpdateContext`] implemented used for testing using [`FakeTimestamp`].
+#[derive(Debug, Clone, Arbitrary)]
+pub struct FakeContext<N: Ord, T: Into<u64> + TryFrom<u64> + Clone + Ord = u8> {
+ /// The updater's node ID.
+ pub updater: N,
+ /// The fake timestamp provider.
+ pub timestamp_provider: FakeTimestamp<T>,
+ /// The version of the document.
+ ///
+ /// See [`UpdateContext::version`].
+ pub version: VectorClock<N>,
+}
+
+impl<N: Ord, U: Into<u64> + TryFrom<u64> + Clone + Ord> FakeContext<N, U> {
+ /// Create a new fake context with the given updater and timestamp.
+ pub fn new(updater: N, timestamp: U) -> Self {
+ Self {
+ updater,
+ timestamp_provider: FakeTimestamp(timestamp),
+ version: VectorClock::default(),
+ }
+ }
+
+ /// Create a new fake context with the given updater, timestamp, and version.
+ pub fn new_with_version(updater: N, timestamp: U, version: VectorClock<N>) -> Self {
+ Self {
+ updater,
+ timestamp_provider: FakeTimestamp(timestamp),
+ version,
+ }
+ }
+}
+
+impl<N: Ord, U: Into<u64> + TryFrom<u64> + Clone + Ord> UpdateContext<N> for FakeContext<N, U> {
+ type WallClock = FakeTimestamp<U>;
+
+ fn updater(&self) -> &N {
+ &self.updater
+ }
+
+ fn timestamp_provider(&self) -> &Self::WallClock {
+ &self.timestamp_provider
+ }
+
+ fn version(&self) -> &VectorClock<N> {
+ &self.version
+ }
+}
diff --git a/betosync/submerge/crdt/src/checker/tests/integer_max_merge.rs b/betosync/submerge/crdt/src/checker/tests/integer_max_merge.rs
new file mode 100644
index 0000000..a780f2d
--- /dev/null
+++ b/betosync/submerge/crdt/src/checker/tests/integer_max_merge.rs
@@ -0,0 +1,30 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use crate::{checker::MergeInvariantTest, CrdtState};
+use arbitrary::Arbitrary;
+
+#[derive(Debug, Clone, PartialEq, Eq, Arbitrary)]
+struct Counter(u8);
+
+impl CrdtState for Counter {
+ fn merge(a: &Self, b: &Self) -> Self {
+ Counter(a.0.max(b.0))
+ }
+}
+
+#[derive_fuzztest::proptest]
+fn merge_invariant_test(input: MergeInvariantTest<Counter>) {
+ input.test()
+}
diff --git a/betosync/submerge/crdt/src/checker/tests/mod.rs b/betosync/submerge/crdt/src/checker/tests/mod.rs
new file mode 100644
index 0000000..ef41c8d
--- /dev/null
+++ b/betosync/submerge/crdt/src/checker/tests/mod.rs
@@ -0,0 +1,18 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test cases for crdt checker
+
+mod integer_max_merge;
+mod set_union_merge;
diff --git a/betosync/submerge/crdt/src/checker/tests/set_union_merge.rs b/betosync/submerge/crdt/src/checker/tests/set_union_merge.rs
new file mode 100644
index 0000000..9461e7c
--- /dev/null
+++ b/betosync/submerge/crdt/src/checker/tests/set_union_merge.rs
@@ -0,0 +1,42 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::collections::HashSet;
+
+use crate::{
+ checker::{utils::DeterministicHasher, MergeInvariantTest},
+ CrdtState,
+};
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct SetUnion(HashSet<u8, DeterministicHasher>);
+
+impl<'a> arbitrary::Arbitrary<'a> for SetUnion {
+ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(SetUnion(
+ u.arbitrary_iter()?.collect::<Result<HashSet<_, _>, _>>()?,
+ ))
+ }
+}
+
+impl CrdtState for SetUnion {
+ fn merge(a: &Self, b: &Self) -> Self {
+ SetUnion(a.0.union(&b.0).cloned().collect())
+ }
+}
+
+#[derive_fuzztest::proptest]
+fn merge_invariant_test(input: MergeInvariantTest<SetUnion>) {
+ input.test()
+}
diff --git a/betosync/submerge/crdt/src/checker/utils.rs b/betosync/submerge/crdt/src/checker/utils.rs
new file mode 100644
index 0000000..6936a36
--- /dev/null
+++ b/betosync/submerge/crdt/src/checker/utils.rs
@@ -0,0 +1,99 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Helper functions for implementing the CRDT checker.
+
+use std::{
+ collections::BTreeSet,
+ hash::{BuildHasherDefault, DefaultHasher},
+};
+
+/// Extension functions on an iterator.
+pub trait IterExt<T>: Iterator<Item = T> + Sized {
+ /// Whether all elements in the iterator are unique, as determined by `Ord`.
+ fn is_uniq(&mut self) -> bool
+ where
+ T: Ord,
+ {
+ let mut set = BTreeSet::new();
+ self.all(|t| set.insert(t))
+ }
+
+ /// Iterate through every pair of elements `(a, b)` in the iterator where `a` is not `b`.
+ ///
+ /// The returned pair are treated as unordered, which means that the returned iterator will
+ /// contain `(a, b)` but not `(b, a)` since they are considered equivalent. This function
+ /// requires `T: Copy`, which in many cases can be satisfied by iterating on shared references.
+ fn iter_pairs_unordered(&mut self) -> impl Iterator<Item = (T, T)>
+ where
+ T: Copy,
+ {
+ let values = self.collect::<Vec<_>>();
+ let len = values.len();
+ let indices = (0..len).flat_map(move |i| ((i + 1)..len).map(move |j| (i, j)));
+ indices.map(move |(i, j)| (values[i], values[j]))
+ }
+}
+
+impl<T, I: Iterator<Item = T>> IterExt<T> for I {}
+
+/// A [`BuildHasher`][std::hash::BuildHasher] implementation that is deterministic.
+///
+/// This can be used with [`HashSet`][std::collections::HashSet],
+/// [`HashMap`][std::collections::HashMap], or other types that that takes a `BuildHasher` as the
+/// type parameter, to replace the default [`RandomState`][std::collections::hash_map::RandomState]
+/// that has randomness built-in for HashDos prevention.
+///
+/// This is especially useful in instrumentation-guided fuzzing where non-determinism will make
+/// fuzzing inefficient.
+pub type DeterministicHasher = BuildHasherDefault<DefaultHasher>;
+
+#[cfg(test)]
+mod tests {
+ use std::hash::BuildHasher;
+
+ use super::{DeterministicHasher, IterExt};
+
+ #[test]
+ fn test_iter_pairs() {
+ let arr = [1, 2, 3, 4];
+ let pairs: Vec<_> = arr.iter().iter_pairs_unordered().collect();
+ assert_eq!(
+ vec![(&1, &2), (&1, &3), (&1, &4), (&2, &3), (&2, &4), (&3, &4),],
+ pairs
+ );
+ }
+
+ #[test]
+ fn test_iter_pairs_empty() {
+ let pairs: Vec<_> = [0_u8; 0].iter().iter_pairs_unordered().collect();
+ assert_eq!(Vec::<(&u8, &u8)>::new(), pairs);
+ }
+
+ #[test]
+ fn test_is_uniq() {
+ assert!([1, 2, 3, 4, 5].into_iter().is_uniq());
+ assert!(![1, 5, 3, 5, 2].into_iter().is_uniq());
+ }
+
+ #[test]
+ fn test_deterministic_hasher() {
+ let build_hasher1 = DeterministicHasher::default();
+ let build_hasher2 = DeterministicHasher::default();
+ assert_eq!(
+ build_hasher1.hash_one(b"hello"),
+ build_hasher2.hash_one(b"hello")
+ );
+ }
+}
diff --git a/betosync/submerge/crdt/src/delta.rs b/betosync/submerge/crdt/src/delta.rs
new file mode 100644
index 0000000..e93db92
--- /dev/null
+++ b/betosync/submerge/crdt/src/delta.rs
@@ -0,0 +1,249 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Representation for delta CRDT types.
+//!
+//! A delta CRDT type consists of two components:
+//! 1. the `base`, which is the read only part that this delta CRDT is building on top of, and
+//! 2. the `delta`, which is a mutable part that contains the changes within this delta.
+//!
+//! For example, a delta CRDT can be created during a transaction, in which the delta component
+//! keeps track of the mutations made within the transaction. This allows the resulting update
+//! message generated for the network interface to only contain the latest changes.
+//!
+//! This module offers three containers for delta CRDTs:
+//! * [`DeltaOwned`] owns its own delta component, and has a shared reference to the base component.
+//! * [`DeltaMut`] has a mutable reference to the delta component, and has a shared reference to the
+//! base component.
+//! * [`DeltaRef`] has shared references to both the delta and base components.
+//!
+//! [`CrdtState`] implementations and [`DeltaOwned`] can be converted into [`DeltaMut`] using
+//! [`as_mut`][AsDeltaMut::as_mut], and can be converted into [`DeltaRef`] using
+//! [`as_ref`][AsDeltaRef::as_ref].
+
+use crate::CrdtState;
+
+/// A read-only reference to a base and delta components to a CRDT.
+///
+/// This contains a reference to the `base` component, which is the read-only component representing
+/// the state when this delta was created, and the `delta` component, where the mutations since the
+/// creation of the CRDT are stored.
+#[derive(Debug, PartialEq, Eq)]
+pub struct DeltaRef<'d, T> {
+ /// The base component, which can be `None` if there is no base.
+ pub base: Option<&'d T>,
+ /// The delta component.
+ ///
+ /// This can be `None` if delta component doesn't exist, for example when trying to get an
+ /// element from a map that only exist in the base component.
+ pub delta: Option<&'d T>,
+}
+
+impl<T> DeltaRef<'_, T> {
+ /// Combines the base part and the delta part and return the resulting merged component.
+ ///
+ /// See [`CrdtState::merge`] for details on the merge operation.
+ pub fn merge(&self) -> T
+ where
+ T: CrdtState + Clone + Default,
+ {
+ match (self.base, self.delta) {
+ (None, None) => T::default(),
+ (None, Some(delta)) => delta.clone(),
+ (Some(base), None) => base.clone(),
+ (Some(base), Some(delta)) => T::merge(base, delta),
+ }
+ }
+}
+
+impl<C> AsDeltaRef<C> for DeltaRef<'_, C> {
+ fn base(&self) -> Option<&C> {
+ self.base
+ }
+
+ fn delta(&self) -> Option<&C> {
+ self.delta
+ }
+}
+
+/// A mutable reference to a delta-CRDT.
+///
+/// Contains a reference to the `base` component, which is the read-only component representing the
+/// state when this delta was created, and the `delta` component, where the mutations since the
+/// creation of the CRDT are stored.
+///
+/// Semantically this delta type behaves as if it is `CrdtState::merge(base, delta)`.
+#[derive(Debug)]
+pub struct DeltaMut<'d, T> {
+ /// The base component, which can be `None` if there is no base.
+ pub base: Option<&'d T>,
+ /// The delta component.
+ ///
+ /// Unlike [`DeltaRef`], this is not an `Option`. Instead, if the value doesn't exist, the
+ /// parent container or collection should insert the default value such that any modifications
+ /// to the subtree can be tracked.
+ pub delta: &'d mut T,
+}
+
+impl<C> AsDeltaRef<C> for DeltaMut<'_, C> {
+ fn base(&self) -> Option<&C> {
+ self.base
+ }
+
+ fn delta(&self) -> Option<&C> {
+ Some(self.delta)
+ }
+}
+
+impl<C> AsDeltaMut<C> for DeltaMut<'_, C> {
+ fn delta_mut(&mut self) -> &mut C {
+ self.delta
+ }
+
+ fn as_mut(&mut self) -> DeltaMut<'_, C> {
+ DeltaMut {
+ base: self.base,
+ delta: self.delta,
+ }
+ }
+}
+
+/// A CRDT structure where the delta-component is owned.
+///
+/// Contains a reference to the `base` component, which is the read-only component representing the
+/// state when this delta was created, and the `delta` component, where the mutations since the
+/// creation of the CRDT are stored.
+#[derive(Debug)]
+pub struct DeltaOwned<'b, T> {
+ /// The base component.
+ ///
+ /// Unlike [`DeltaMut`] and [`DeltaRef`], this is not an `Option` because a `DeltaOwned` without
+ /// a base is the same as just `T`.
+ pub base: &'b T,
+ /// The delta component.
+ pub delta: T,
+}
+
+impl<'b, T> DeltaOwned<'b, T> {
+ /// Creates a new owned delta from the given base.
+ ///
+ /// The delta component is initially empty, so the contents are semantically the same as the
+ /// given `base`.
+ pub fn new(base: &'b T) -> Self
+ where
+ T: Default,
+ {
+ Self {
+ base,
+ delta: Default::default(),
+ }
+ }
+
+ /// Merges the base and the delta components of this.
+ ///
+ /// See [`CrdtState::merge`] for details on the merge operation.
+ pub fn merge(&self) -> T
+ where
+ T: CrdtState,
+ {
+ T::merge(self.base, &self.delta)
+ }
+
+ /// Converts this into its delta components, discarding the reference to the
+ /// base component.
+ pub fn into_delta(self) -> T {
+ self.delta
+ }
+}
+
+impl<C: CrdtState> AsDeltaRef<C> for DeltaOwned<'_, C> {
+ fn base(&self) -> Option<&C> {
+ Some(self.base)
+ }
+
+ fn delta(&self) -> Option<&C> {
+ Some(&self.delta)
+ }
+}
+
+impl<C: CrdtState> AsDeltaMut<C> for DeltaOwned<'_, C> {
+ fn delta_mut(&mut self) -> &mut C {
+ &mut self.delta
+ }
+
+ fn as_mut(&mut self) -> DeltaMut<'_, C> {
+ DeltaMut {
+ base: Some(self.base),
+ delta: &mut self.delta,
+ }
+ }
+}
+
+/// Something that can behave like a read-only reference to a delta CRDT.
+pub trait AsDeltaRef<C> {
+ /// The base component. This is `None` if there is no base, for example if the given type is
+ /// just a CRDT component.
+ fn base(&self) -> Option<&C>;
+
+ /// The delta component. This is `None` if there is no delta, for example when a value is read
+ /// from a map in which the entry only exists in the base component.
+ fn delta(&self) -> Option<&C>;
+
+ /// Returns a [`DeltaRef`] from this.
+ fn as_ref(&self) -> DeltaRef<'_, C> {
+ DeltaRef {
+ base: self.base(),
+ delta: self.delta(),
+ }
+ }
+}
+
+/// Something that can behave like a mutable reference to a delta CRDT.
+pub trait AsDeltaMut<C>: AsDeltaRef<C> {
+ /// The delta component.
+ ///
+ /// Unlike [`AsDeltaRef::delta`], this cannot be `None`. If the value does not exist yet it
+ /// should be initialized with `Default::default()`.
+ fn delta_mut(&mut self) -> &mut C;
+
+ /// Returns a [`DeltaMut`] from this.
+ fn as_mut(&mut self) -> DeltaMut<'_, C>;
+}
+
+/// A blanket implementation for any [`CrdtState`] to be treated as a delta, which has no base
+/// component and the CRDT's state is treated as the delta.
+impl<C: CrdtState> AsDeltaRef<C> for C {
+ fn base(&self) -> Option<&C> {
+ None
+ }
+
+ fn delta(&self) -> Option<&C> {
+ Some(self)
+ }
+}
+
+/// A blanket implementation for any [`CrdtState`] to be treated as a delta, which has no base
+/// component and the CRDT's state is treated as the delta.
+impl<C: CrdtState> AsDeltaMut<C> for C {
+ fn delta_mut(&mut self) -> &mut C {
+ self
+ }
+
+ fn as_mut(&mut self) -> DeltaMut<'_, C> {
+ DeltaMut {
+ base: None,
+ delta: self,
+ }
+ }
+}
diff --git a/betosync/submerge/crdt/src/lib.rs b/betosync/submerge/crdt/src/lib.rs
new file mode 100644
index 0000000..df9f693
--- /dev/null
+++ b/betosync/submerge/crdt/src/lib.rs
@@ -0,0 +1,198 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Implementation of state-based CRDTs.
+//!
+//! See documentation for [`CrdtState`] and the individual modules for more.
+
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+
+use distributed_time::{
+ vector_clock::VectorClock, DistributedClock, TimestampOverflow, WallTimestampProvider,
+};
+use lww_crdt_container::CrdtUnion;
+
+pub mod lww_crdt_container;
+pub mod lww_map;
+pub mod register;
+pub mod set;
+pub mod typed_crdt;
+pub mod vector_data;
+
+#[cfg(any(test, feature = "checker"))]
+#[allow(clippy::indexing_slicing, clippy::panic)]
+pub mod checker;
+pub mod delta;
+mod utils;
+
+/// A CRDT state that is mergeable with instance. The [`merge`][CrdtState::merge] operation must be:
+///
+/// * associative: `merge(merge(a, b), c) == merge(a, merge(b, c))`, and
+/// * commutative: `merge(a, b) == merge(b, a)`, and
+/// * idempotent: `merge(a, a) == a`
+///
+/// These properties can be verified using
+/// [`MergeInvariantTest`][crate::checker::MergeInvariantTest]. Any implementations of `CrdtState`
+/// should have a corresponding `MergeInvariantTest` to verify its correctness.
+///
+/// Violating any of these properties is a logic error. The behavior resulting from a logic error is
+/// not specified, but `unsafe` users of this trait MUST NOT rely on the correctness of these
+/// methods for memory safety (since this is not an `unsafe trait`).
+///
+/// See also:
+/// * [A comprehensive study of Convergent and Commutative Replicated Data
+/// Types](https://inria.hal.science/inria-00555588/document)
+pub trait CrdtState {
+ /// Merges two CRDT states of the same type. See the trait documentation for details.
+ fn merge(a: &Self, b: &Self) -> Self;
+
+ /// Return whether a given collection of CRDTs are valid.
+ ///
+ /// The default implementation returns true, meaning that any instances accepted by the type
+ /// system can be merged with any other instance. If there are additional requirements, like if
+ /// associated timestamps need to be globally unique, implementations can override this to
+ /// return false in cases where different instances conflict with each other (see
+ /// [`is_uniq`][crate::checker::utils::IterExt::is_uniq] and
+ /// [`iter_pairs`][crate::checker::utils::IterExt::iter_pairs_unordered] for helper functions
+ /// that can help implement this).
+ ///
+ /// Implementations must make sure these invalid combinations cannot be generated from mutations
+ /// on the same ancestor state. [`Simulation`][crate::checker::simulation::Simulation] can be
+ /// used to verify this.
+ ///
+ /// Requires the feature _`checker`_.
+ #[cfg(any(test, feature = "checker"))]
+ fn is_valid_collection<'a>(_collection: impl IntoIterator<Item = &'a Self>) -> bool
+ where
+ Self: 'a,
+ {
+ true
+ }
+}
+
+/// Represents a CRDT that has a plain representation. A plain representation is the data in the
+/// CRDT without the metadata (e.g. the timestamps used for Last-Writer-Wins).
+///
+/// A plain representation can be passed to the application for display and for modification. A
+/// modified version of the plain representation can be applied to the original CRDT snapshot (using
+/// `apply_changes`) to create a new, updated CRDT.
+///
+/// ## Performance warning
+/// Because the plain representation has to recalculate the diff and "replay" the operations
+/// performed, its performance can be much slower compared to the dedicated methods, especially on
+/// large collections. This API is best used for prototyping, and should be avoided in production.
+pub trait HasPlainRepresentation<N: Ord>: CrdtUnion<N>
+where
+ for<'d> Self::DeltaRef<'d>: ToPlain<Plain = Self::Plain>,
+ for<'d> Self::DeltaMut<'d>: ApplyChanges<N, Plain = Self::Plain, Error = Self::Error>,
+{
+ /// The type for the plain representation of this CRDT.
+ type Plain;
+ /// Error type returned when applying changes or initializing the CRDT from a plain
+ /// representation.
+ type Error;
+
+ /// Initializes a CRDT from the plain representation. If the CRDT implements [`Default`], the
+ /// result should be the same as `Crdt::default().apply_changes(id, ctx)`.
+ fn init_from_plain(
+ ctx: &impl UpdateContext<N>,
+ plain: Self::Plain,
+ ) -> Result<Self, Self::Error>;
+}
+
+/// A type that has a plain, non-CRDT representation.
+///
+/// This plain representation can be thought of as the CRDT type without the metadata for
+/// consistency. For example, for a [`set::Set<E>`], the plain type is simply
+/// [`HashSet<E>`][std::collections::HashSet].
+///
+/// See [`HasPlainRepresentation`].
+pub trait ToPlain {
+ /// The plain representation that this type can convert into.
+ type Plain;
+
+ /// Convert this CRDT into its plain representation.
+ fn to_plain(&self) -> Self::Plain;
+}
+
+/// Implemented by mutable delta CRDTs, this trait indicates that a type can apply changes from a
+/// given plain representation.
+///
+/// See [`HasPlainRepresentation`].
+pub trait ApplyChanges<N> {
+ /// The plain representation that can be applied by this type.
+ type Plain;
+ /// The error that may be returned if applying a change failed.
+ type Error;
+
+ /// Apply the changes in the plain representation into this CRDT. This assumes that all changes
+ /// in the plain representation were made by the calling node, updating the associated CRDT
+ /// metadata in the process.
+ ///
+ /// After applying the changes, [`to_plain`][ToPlain::to_plain] should return the same value as
+ /// `plain`.
+ ///
+ /// # Returns
+ ///
+ /// `true` if applying the change results in an update in the CRDT state, or false if Returns
+ /// true if applying the change results in an update in the CRDT state, or false if `plain` is
+ /// the same as [`to_plain`][ToPlain::to_plain] to begin with. This can be used to avoid sending
+ /// unnecessary update messages over the network, or writing changes to disk unnecessarily.
+ fn apply_changes(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ plain: Self::Plain,
+ ) -> Result<bool, Self::Error>;
+}
+
+/// An update context for CRDTs. Many CRDT mutations require a context such as the updater's node ID
+/// or the current wall clock time.
+pub trait UpdateContext<N> {
+ /// The provider for wall-clock timestamps.
+ type WallClock: WallTimestampProvider;
+
+ /// Get the updater associated with this update context, i.e. the node this code is running on.
+ fn updater(&self) -> &N;
+ /// Get the timestamp provider from this context.
+ fn timestamp_provider(&self) -> &Self::WallClock;
+
+ /// The current version of the document this CRDT is a part of.
+ ///
+ /// The CRDT implementations assume that this context is
+ /// monotonically increasing, meaning that the version may be copied into the CRDT tree, and any
+ /// later operations' context must return a version newer than or equal to the copied version
+ /// value.
+ fn version(&self) -> &VectorClock<N>
+ where
+ N: Ord;
+
+ /// The next version of the document after the pending changes are committed.
+ fn next_version(&self) -> Result<VectorClock<N>, TimestampOverflow>
+ where
+ N: Ord + Clone,
+ {
+ self.version().increment(self.updater())
+ }
+}
+
+/// A type supporting content equality check besides the standard PartialEq check. This is useful if
+/// a type contains both payload and metadata, in which case PartialEq compares both, while
+/// ContentEq only compares the payload.
+///
+/// This corresponds to [equivalence relations](https://en.wikipedia.org/wiki/Equivalence_relation).
+/// Implementations must make sure that the relation is symmetric, transitive, and reflexive.
+pub trait ContentEq<Rhs: ?Sized = Self> {
+ /// Perform a content equality check.
+ fn content_eq(&self, other: &Rhs) -> bool;
+}
diff --git a/betosync/submerge/crdt/src/lww_crdt_container.rs b/betosync/submerge/crdt/src/lww_crdt_container.rs
new file mode 100644
index 0000000..8d9d8f0
--- /dev/null
+++ b/betosync/submerge/crdt/src/lww_crdt_container.rs
@@ -0,0 +1,995 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A last-writer-wins container that is useful for holding a union of different CRDTs.
+//!
+//! See the docs for [`LwwCrdtContainer`].
+
+use std::{cmp::Ordering, fmt::Debug};
+
+use crate::{
+ delta::{AsDeltaMut, AsDeltaRef, DeltaMut, DeltaRef},
+ ApplyChanges, ContentEq, CrdtState, HasPlainRepresentation, ToPlain, UpdateContext,
+};
+use arbitrary::Arbitrary;
+use derive_where::derive_where;
+use distributed_time::{
+ vector_clock::VectorClock, DistributedClock, TimestampOverflow, TotalTimestamp,
+};
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+
+/// Error returned when a [`CrdtUnion`] cannot be merged.
+#[derive(Debug)]
+pub struct IncompatibleType;
+
+/// A [`CrdtUnion`] is a tagged union of [`CrdtState`]s, which can be implemented by a Rust `enum`
+/// or a `dyn` type.
+///
+/// This type should implement [`TryAsChild`] and `From<Child>` for each of the variants it can
+/// contain. The implementation of [`try_merge`][CrdtUnion::try_merge] delegate to the child's
+/// [`CrdtState::merge`], or return [`IncompatibleType`] if the two given unions are not of the same
+/// type. The merge must follow the same properties (Associative, Commutative, Idempotent) as
+/// defined in [`CrdtState`].
+///
+/// This is primarily designed for use with [`LwwCrdtContainer`], which delegates the merging to the
+/// child value iff the timestamps are equal.
+pub trait CrdtUnion<N: Ord>: Sized {
+ /// The read-only reference for the delta type of this CRDT union. This should be a union (enum
+ /// or dyn-type) that has the same variants, such that for each variant `Variant(T)` in the
+ /// union, there is a corresponding `Variant(DeltaRef<T>)` in the ref type.
+ type DeltaRef<'d>
+ where
+ Self: 'd;
+
+ /// The mutable reference for the delta type of this CRDT union. This should be a union (enum
+ /// or dyn-type) that has the same variants, such that for each variant `Variant(T)` in the
+ /// union, there is a corresponding `Variant(DeltaMut<T>)` in the mut type.
+ type DeltaMut<'d>
+ where
+ Self: 'd;
+
+ /// Merges two CRDT unions.
+ ///
+ /// If `a` and `b` are of the same variant, it should return
+ /// `Self::from(CrdtState::merge(a.as_child(), b.as_child()))`. Otherwise, it should return
+ /// `Err(IncompatibleType)`.
+ fn try_merge(a: &Self, b: &Self) -> Result<Self, IncompatibleType>;
+
+ /// Create a new read-only delta reference from the given `base` and `delta` components.
+ fn create_ref<'d>(
+ base: Option<&'d Self>,
+ delta: Option<&'d Self>,
+ ) -> Option<Self::DeltaRef<'d>>;
+
+ /// Create a new mutable delta reference from the given `base` and `delta` components.
+ fn create_mut<'d>(base: Option<&'d Self>, delta: &'d mut Self) -> Option<Self::DeltaMut<'d>>;
+
+ /// Create a default (i.e. bottom state) for this union that has the same variant as `example`.
+ ///
+ /// For example, if `example` is `VariantN(TypeN { .. })`, then this should return
+ /// `VariantN(TypeN::default())`.
+ fn create_matching_default(example: &Self) -> Self;
+
+ /// Calculates the delta needed to update from the given `base_version`.
+ ///
+ /// The `base_version` must be a version vector from a replica of the same document. The
+ /// returned document can be applied using [`CrdtState::merge`] by supplying a base
+ /// that is at least as new as `base_version`.
+ ///
+ /// Note that the version is not tracked in this crate. Users of this crate must track the
+ /// versioning information externally.
+ fn calculate_delta(&self, base_version: &VectorClock<N>) -> Self;
+}
+
+/// A trait for fallibly converting a [`CrdtUnion`] into its child type.
+///
+/// Usage of this type is similar to [`AsRef`] and [`AsMut`], with the additional requirement that
+/// the container is a [`CrdtUnion`] and the contained child must be a [`CrdtState`]. See the
+/// documentation for [`CrdtUnion`] for more.
+pub trait TryAsChild<C, N: Ord>: From<C> + CrdtUnion<N> {
+ /// Converts this union into a child CRDT state reference.
+ fn try_as_child_ref(this: Self::DeltaRef<'_>) -> Option<DeltaRef<'_, C>>;
+ /// Converts this union into a child CRDT state mutable reference.
+ fn try_as_child_mut(this: Self::DeltaMut<'_>) -> Option<DeltaMut<'_, C>>;
+}
+
+/// A error returned by [`LwwCrdtContainerWrite::apply_changes`].
+#[derive(Debug, PartialEq, Eq, Error)]
+pub enum LwwCrdtContainerApplyError<E> {
+ /// Merge failed because the types are incompatible.
+ IncompatibleType,
+ /// Merge failed because the timestamp overflowed the maximum value.
+ TimestampOverflow,
+ /// An error was returned by the child CRDT's `apply_changes`.
+ ChildError(#[from] E),
+}
+
+/// A last-writer-wins container that contains a [`CrdtUnion`] as its value.
+///
+/// This container consists of two layers:
+/// * `LwwCrdtContainer` contains a `V: CrdtUnion`. Modification of this layer is done through
+/// [`set_value`][LwwCrdtContainerWrite::set_value], which follows last-writer-wins semantics.
+/// * `V` in turn can switch between multiple types `Child: CrdtState` where `V: TryAsChild<Child>`.
+/// Modification of this layer is done through
+/// [`get_child_mut`][LwwCrdtContainerWrite::get_child_mut], which mutates without updating the
+/// timestamp, and merges according to the semantics of the `Child` type.
+///
+/// The result is that you can have dynamically typed CRDTs at runtime (via enum or dyn types),
+/// where the type information (i.e. the schema) merges by last writer wins, and the data merges
+/// according to the schema defined type.
+///
+/// The merge operation is defined as follows:
+/// * If the timestamps are not equal, the value associated with the larger timestamp is taken. The
+/// value associated with the smaller timestamp is discarded, and NOT merged even if it is the
+/// same type is the newer value.
+/// * If the timestamps are equal, the two values are merged using the
+/// [`try_merge`][CrdtUnion::try_merge] function, and is expected to succeed because `try_merge`
+/// requires the implementation to delegate to the child variant, and the API surface of this
+/// container does not allow changing the variant of the [`CrdtUnion`] without updating the
+/// timestamp.
+///
+/// # Param
+/// * `V` — The [`CrdtUnion`] type contained in this container.
+/// * `T` — A total ordered timestamp use for last-writer-wins comparison. This timestamp must be
+/// monotonically increasing, totally ordered, and globally unique. Typical usage should consider
+/// using [`CompoundTimestamp`][distributed_time::compound_timestamp::CompoundTimestamp].
+///
+/// # Implementations
+/// * See [`LwwCrdtContainerRead`] for methods on read-only references of [`LwwCrdtContainer`].
+/// * See [`LwwCrdtContainerWrite`] for methods on mutable references of [`LwwCrdtContainer`].
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Arbitrary)]
+#[derive_where(Default)]
+pub struct LwwCrdtContainer<V, T, N: Ord> {
+ value: Option<LwwCrdtContainerInner<V, T>>,
+ subtree_version: VectorClock<N>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Arbitrary)]
+#[derive_where(Default; T)]
+struct LwwCrdtContainerInner<V, T> {
+ value: Option<V>,
+ timestamp: T,
+}
+
+impl<V, T, N: Ord> LwwCrdtContainer<V, T, N> {
+ fn timestamp(&self) -> Option<&T> {
+ self.value.as_ref().map(|inner| &inner.timestamp)
+ }
+
+ fn value(&self) -> Option<&V> {
+ self.value.as_ref().and_then(|inner| inner.value.as_ref())
+ }
+
+ fn value_mut(&mut self) -> Option<&mut V> {
+ self.value.as_mut().and_then(|inner| inner.value.as_mut())
+ }
+
+ /// The version of the entire subtree under this node.
+ ///
+ /// When a _subtree version updating operation_ is performed, this version is updated to
+ /// [`UpdateContext::next_version`]. It represents the version of the document when changes were
+ /// made to any nodes in the subtree, but may be updated conservatively even when the contents
+ /// are unchanged. In the current implementation, this is conservatively updated whenever a
+ /// mutable reference to the underlying value is returned.
+ ///
+ /// This version is used in [`LwwCrdtContainer::calculate_delta`] to selectively traverse down
+ /// the tree and find all of the nodes that has updates not contained in a given version.
+ pub fn subtree_version(&self) -> &VectorClock<N> {
+ &self.subtree_version
+ }
+
+ /// Calculates the delta needed to update from the given `base_version`.
+ ///
+ /// The `base_version` must be a version vector from a replica of the same document. The
+ /// returned document can be applied using [`CrdtState::merge`] by supplying a base
+ /// that is at least as new as `base_version`.
+ ///
+ /// Note that the version is not tracked in this crate. Users of this crate must track the
+ /// versioning information externally.
+ pub fn calculate_delta(&self, base_version: &VectorClock<N>) -> Self
+ where
+ V: CrdtUnion<N>,
+ N: Ord + Clone,
+ T: Clone,
+ {
+ if base_version >= &self.subtree_version {
+ // No new updates needed because the base is new enough. Just return the default which
+ // will not result in any changes when merged.
+ return Self::default();
+ }
+ // Otherwise, the base is older or concurrent, calculate the delta
+ match &self.value {
+ Some(inner) => match &inner.value {
+ Some(value) => Self {
+ value: Some(LwwCrdtContainerInner {
+ value: Some(value.calculate_delta(base_version)),
+ timestamp: inner.timestamp.clone(),
+ }),
+ subtree_version: self.subtree_version.clone(),
+ },
+ None => Self {
+ value: Some(LwwCrdtContainerInner {
+ value: None,
+ timestamp: inner.timestamp.clone(),
+ }),
+ subtree_version: self.subtree_version.clone(),
+ },
+ },
+ None => Self {
+ value: None,
+ subtree_version: self.subtree_version.clone(),
+ },
+ }
+ }
+}
+
+/// Read-only operations for a CRDT container.
+///
+/// ## See also
+/// * See [`LwwCrdtContainer`] for a description of the CRDT container type.
+/// * See [`LwwCrdtContainerWrite`] for mutating operations on CRDT containers.
+pub trait LwwCrdtContainerRead<V, T, N: Ord>: AsDeltaRef<LwwCrdtContainer<V, T, N>>
+where
+ V: 'static,
+ T: 'static,
+ N: 'static,
+{
+ /// Get the timestamp associated with the value in this container.
+ fn timestamp(&self) -> Option<&T>
+ where
+ T: Ord,
+ {
+ let delta_timestamp = self.delta().and_then(|delta| delta.timestamp());
+ let base_timestamp = self.base().and_then(|base| base.timestamp());
+ delta_timestamp.max(base_timestamp)
+ }
+
+ /// Get the value in this container as a read-only reference.
+ fn get_value(&self) -> Option<V::DeltaRef<'_>>
+ where
+ T: Ord,
+ V: CrdtUnion<N>,
+ {
+ self.as_ref().into_value()
+ }
+
+ /// Get a reference to the child from this container, or `None` if the contained CRDT value
+ /// cannot be converted to `Child`.
+ ///
+ /// A "child" is the type contained in one of the variants in the [`CrdtUnion`] (`V`).
+ fn get_child<Child>(&self) -> Option<DeltaRef<'_, Child>>
+ where
+ V: TryAsChild<Child, N>,
+ T: Ord,
+ {
+ self.get_value().and_then(V::try_as_child_ref)
+ }
+
+ /// Copies the data without the associated metadata and returns the plain type.
+ ///
+ /// See also: [`crate::HasPlainRepresentation`].
+ fn to_plain(&self) -> Option<V::Plain>
+ where
+ V: CrdtUnion<N> + HasPlainRepresentation<N> + Clone,
+ T: TotalTimestamp<NodeId = N> + Clone,
+ // Bounds implied by V: HasPlainRepresentation below
+ for<'d> V::DeltaRef<'d>: ToPlain<Plain = V::Plain>,
+ for<'d> V::DeltaMut<'d>: ApplyChanges<N, Plain = V::Plain, Error = V::Error>,
+ {
+ self.get_value().map(|v| v.to_plain())
+ }
+}
+
+impl<'d, V, T, N: Ord> DeltaRef<'d, LwwCrdtContainer<V, T, N>> {
+ /// Convert this into a read-only reference to the value in this container.
+ ///
+ /// Returns `None` if the value exists in the base but not in the delta.
+ pub fn into_value(self) -> Option<V::DeltaRef<'d>>
+ where
+ T: Ord,
+ V: CrdtUnion<N>,
+ {
+ match (self.base, self.delta) {
+ (None, None) => None,
+ (None, Some(delta)) => V::create_ref(None, delta.value()),
+ (Some(base), None) => V::create_ref(base.value(), None),
+ (Some(base), Some(delta)) => match delta.timestamp().cmp(&base.timestamp()) {
+ Ordering::Less => V::create_ref(base.value(), None),
+ Ordering::Equal => V::create_ref(base.value(), delta.value()),
+ Ordering::Greater => V::create_ref(None, delta.value()),
+ },
+ }
+ }
+
+ /// Convert this into a read-only reference to the child.
+ ///
+ /// A "child" is the type contained in one of the variants in the [`CrdtUnion`] (`V`).
+ ///
+ /// Returns `None` if the contained value cannot be converted into the `Child` type.
+ pub fn into_child<Child>(self) -> Option<DeltaRef<'d, Child>>
+ where
+ V: TryAsChild<Child, N>,
+ T: Ord,
+ {
+ self.into_value().and_then(V::try_as_child_ref)
+ }
+}
+
+/// Implementation for [`LwwCrdtContainerWrite::set_value`] and
+/// [`LwwCrdtContainerWrite::remove_value`]. I wish traits can have private functions.
+fn set_value_impl<V, T, N, W>(
+ this: &mut W,
+ ctx: &impl UpdateContext<N>,
+ value: Option<V>,
+) -> Result<(), TimestampOverflow>
+where
+ V: 'static,
+ T: TotalTimestamp<NodeId = N> + 'static,
+ N: Ord + Clone + 'static,
+ W: LwwCrdtContainerWrite<V, T, N> + ?Sized,
+{
+ let timestamp = T::increment_or_new(this.timestamp(), ctx.updater(), ctx.timestamp_provider())?;
+ let delta_mut = this.delta_mut();
+ let next_version = ctx.next_version()?;
+ // Violation of this assertion is a fault in the update context. It means that version we
+ // get from `ctx` is not monotonically increasing. Perhaps `ctx` is not the context
+ // associated with this document?
+ debug_assert!(next_version >= delta_mut.subtree_version);
+ *delta_mut = LwwCrdtContainer {
+ value: Some(LwwCrdtContainerInner { value, timestamp }),
+ subtree_version: next_version,
+ };
+ Ok(())
+}
+
+/// Mutating operations for a CRDT container.
+///
+/// ## See also
+/// * See [`LwwCrdtContainer`] for a description of the CRDT container type.
+/// * See [`LwwCrdtContainerRead`] for read-only operations on CRDT containers.
+pub trait LwwCrdtContainerWrite<V, T, N>:
+ LwwCrdtContainerRead<V, T, N> + AsDeltaMut<LwwCrdtContainer<V, T, N>>
+where
+ V: 'static,
+ T: 'static,
+ N: Ord + Clone + 'static,
+{
+ /// Set this CRDT container to a new value. This updates the associated timestamp, and will
+ /// always override other values with smaller timestamps without invoking
+ /// [`V::try_merge`][CrdtUnion::try_merge]. If merging the child state is
+ /// desired, use [`get_child_mut`][Self::get_child_mut] instead.
+ fn set_value(&mut self, ctx: &impl UpdateContext<N>, value: V) -> Result<(), TimestampOverflow>
+ where
+ T: TotalTimestamp<NodeId = N>,
+ {
+ set_value_impl(self, ctx, Some(value))
+ }
+
+ /// Set the value in this container to `None`, updating the associated timestamp in the
+ /// process.
+ fn remove_value(&mut self, ctx: &impl UpdateContext<N>) -> Result<(), TimestampOverflow>
+ where
+ T: TotalTimestamp<NodeId = N>,
+ {
+ set_value_impl(self, ctx, None)
+ }
+
+ /// Get a mutable reference to the underlying union value, or `None` if the contained CRDT value
+ /// does not exist.
+ ///
+ /// This operation does not increment the timestamp associated with the value. Any mutations
+ /// made to the returned value will be merged according to the child's merge function.
+ ///
+ /// This is a _subtree version updating operation_ (See
+ /// [`subtree_version`](LwwCrdtContainer::subtree_version)).
+ fn get_value_mut(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ ) -> Result<Option<V::DeltaMut<'_>>, TimestampOverflow>
+ where
+ V: CrdtUnion<N>,
+ T: Ord + Clone,
+ {
+ self.as_mut().into_value_mut(ctx)
+ }
+
+ /// Get a mutable reference to the child CRDT from this container, or `None` if the contained
+ /// CRDT value cannot be converted to `Child`.
+ ///
+ /// This operation does not increment the timestamp associated with the value. Any mutations
+ /// made to the returned value will be merged according to the child's merge function.
+ ///
+ /// This is a _subtree version updating operation_ (See
+ /// [`subtree_version`](LwwCrdtContainer::subtree_version)).
+ fn get_child_mut<Child>(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ ) -> Result<Option<DeltaMut<'_, Child>>, TimestampOverflow>
+ where
+ V: TryAsChild<Child, N>,
+ T: Ord + Clone,
+ {
+ self.as_mut().into_child_mut(ctx)
+ }
+
+ /// Get the child CRDT from this container, or set it to `Child::default()`. Returns a mutable
+ /// reference to the existing or newly added value, or `None` if the existing CRDT value cannot
+ /// be converted to `Child`.
+ ///
+ /// This operation increments the timestamp associated with the value only if it was initially
+ /// empty as was initialized with the default. Any subsequent changes made to the returned value
+ /// does not increment the timestamp and will be merged according to the child's merge function.
+ ///
+ /// This is a _subtree version updating operation_ (See
+ /// [`subtree_version`](LwwCrdtContainer::subtree_version)).
+ fn get_child_or_default<Child>(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ ) -> Result<Option<DeltaMut<'_, Child>>, TimestampOverflow>
+ where
+ Child: Default,
+ V: TryAsChild<Child, N>,
+ T: TotalTimestamp<NodeId = N> + Clone,
+ {
+ self.as_mut().into_child_or_default(ctx)
+ }
+
+ /// Apply the changes in the plain representation into this CRDT. This assumes that all changes
+ /// in the plain representation were made by the calling node, updating the associated CRDT
+ /// metadata in the process.
+ ///
+ /// After applying the changes, [`to_plain`][ToPlain::to_plain] should return the same value as
+ /// `plain`.
+ ///
+ /// # Returns
+ ///
+ /// `true` if applying the change results in an update in the CRDT state, or false if Returns
+ /// true if applying the change results in an update in the CRDT state, or false if `plain` is
+ /// the same as [`to_plain`][ToPlain::to_plain] to begin with. This can be used to avoid sending
+ /// unnecessary update messages over the network, or writing changes to disk unnecessarily.
+ fn apply_changes(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ plain: Option<V::Plain>,
+ ) -> Result<bool, LwwCrdtContainerApplyError<V::Error>>
+ where
+ V: CrdtUnion<N> + HasPlainRepresentation<N> + Clone,
+ for<'d> V::DeltaRef<'d>: ToPlain<Plain = V::Plain>,
+ for<'d> V::DeltaMut<'d>: ApplyChanges<N, Plain = V::Plain, Error = V::Error>,
+ T: TotalTimestamp<NodeId = N> + Clone,
+ {
+ let value = self
+ .get_value_mut(ctx)
+ .map_err(|_| LwwCrdtContainerApplyError::TimestampOverflow)?;
+ let new_value = match (value, plain) {
+ (None, None) => return Ok(false),
+ (Some(_), None) => None,
+ (None, Some(v)) => Some(V::init_from_plain(ctx, v)?),
+ (Some(mut crdt), Some(plain)) => return Ok(crdt.apply_changes(ctx, plain)?),
+ };
+ set_value_impl(self, ctx, new_value)
+ .map_err(|_| LwwCrdtContainerApplyError::TimestampOverflow)?;
+ Ok(true)
+ }
+}
+
+impl<'d, V, T, N: Ord> DeltaMut<'d, LwwCrdtContainer<V, T, N>> {
+ /// Convert this into a mutable reference to the value in this container.
+ ///
+ /// If the value exists in the base but not in the delta, the value in the delta is initialized
+ /// to the default value, so the mutations in the returned value are tracked.
+ ///
+ /// This is a _subtree version updating operation_ (See
+ /// [`subtree_version`](LwwCrdtContainer::subtree_version)).
+ pub fn into_value_mut(
+ self,
+ ctx: &impl UpdateContext<N>,
+ ) -> Result<Option<V::DeltaMut<'d>>, TimestampOverflow>
+ where
+ V: CrdtUnion<N>,
+ T: Clone + Ord,
+ N: Clone,
+ {
+ let next_version = ctx.next_version()?;
+ // Violation of this assertion is a fault in the update context. It means that version we
+ // get from `ctx` is not monotonically increasing. Perhaps `ctx` is not the context
+ // associated with this document?
+ debug_assert!(next_version >= self.delta.subtree_version,);
+ self.delta.subtree_version = next_version;
+ let base_timestamp = self.base.and_then(|b| b.timestamp());
+ match self.delta.timestamp().cmp(&base_timestamp) {
+ Ordering::Less => {
+ // Assign a value to delta if base exists, so it modifications to the value can be
+ // recorded. This is hoisted up from the match expression below to satisfy the
+ // borrow checker.
+ let base_value = self.base.and_then(|b| b.value());
+ self.delta.value = Some(LwwCrdtContainerInner {
+ value: base_value.map(V::create_matching_default),
+ timestamp: base_timestamp
+ .expect("Must not be None since base_timestamp > delta_timestamp")
+ .clone(),
+ });
+ if let Some(delta_value) = self.delta.value_mut() {
+ Ok(V::create_mut(base_value, delta_value))
+ } else {
+ Ok(None)
+ }
+ }
+ Ordering::Equal => {
+ let base = self.base.and_then(|b| b.value());
+ let delta = self.delta.value_mut();
+ match (base, delta) {
+ (None, None) => Ok(None),
+ (Some(_), None) => unreachable!(),
+ (_, Some(delta_value)) => Ok(V::create_mut(base, delta_value)),
+ }
+ }
+ Ordering::Greater => Ok(self.delta.value_mut().and_then(|v| V::create_mut(None, v))),
+ }
+ }
+
+ /// Convert this into a mutable reference to the child.
+ ///
+ /// A "child" is the type contained in one of the variants in the [`CrdtUnion`] (`V`).
+ ///
+ /// Returns `Ok(None)` if the contained value cannot be converted into the `Child` type.
+ ///
+ /// This is a _subtree version updating operation_ (See
+ /// [`subtree_version`](LwwCrdtContainer::subtree_version)).
+ pub fn into_child_mut<Child>(
+ self,
+ ctx: &impl UpdateContext<N>,
+ ) -> Result<Option<DeltaMut<'d, Child>>, TimestampOverflow>
+ where
+ V: TryAsChild<Child, N>,
+ T: Clone + Ord,
+ N: Clone,
+ {
+ Ok(self.into_value_mut(ctx)?.and_then(V::try_as_child_mut))
+ }
+
+ /// Convert this into a mutable reference to the child, initializing it to default if needed.
+ ///
+ /// A "child" is the type contained in one of the variants in the [`CrdtUnion`] (`V`).
+ ///
+ /// If the current value in the container is none, it will be initialized to `Child::default()`,
+ /// and a reference to the newly initialized value will be returned. If there is an existing
+ /// child in this container, but it is not convertible to `Child`, then `None` is returned.
+ ///
+ /// This is a _subtree version updating operation_ (See
+ /// [`subtree_version`](LwwCrdtContainer::subtree_version)).
+ pub fn into_child_or_default<Child>(
+ mut self,
+ ctx: &impl UpdateContext<N>,
+ ) -> Result<Option<DeltaMut<'d, Child>>, TimestampOverflow>
+ where
+ Child: Default,
+ V: TryAsChild<Child, N> + 'static,
+ T: TotalTimestamp<NodeId = N> + Clone + 'static,
+ N: Clone + 'static,
+ {
+ if self.get_value().is_none() {
+ let _ = self.set_value(ctx, Child::default().into()).ok();
+ }
+ self.into_child_mut(ctx)
+ }
+}
+
+impl<V, T, N> CrdtState for LwwCrdtContainer<V, T, N>
+where
+ V: CrdtUnion<N> + Clone,
+ T: TotalTimestamp + Clone,
+ N: Ord + Clone,
+{
+ fn merge(a: &Self, b: &Self) -> Self {
+ fn merge_inner<V, T, N: Ord>(
+ a: &LwwCrdtContainerInner<V, T>,
+ b: &LwwCrdtContainerInner<V, T>,
+ ) -> LwwCrdtContainerInner<V, T>
+ where
+ V: CrdtUnion<N> + Clone,
+ T: TotalTimestamp + Clone,
+ {
+ match a.timestamp.cmp(&b.timestamp) {
+ Ordering::Equal => {
+ match (&a.value, &b.value) {
+ (Some(value_a), Some(value_b)) => LwwCrdtContainerInner {
+ value: Some(
+ CrdtUnion::try_merge(value_a, value_b)
+ // This container does not allow updating the value without also
+ // updating the timestamp outside of mutating a child CRDT, and
+ // `try_merge` is required to delegate to the child CRDT's
+ // `merge` which is infallible.
+ .expect("Values with the same timestamp should be mergeable."),
+ ),
+ timestamp: a.timestamp.clone(),
+ },
+ (Some(_), None) => a.clone(),
+ (None, Some(_)) => b.clone(),
+ (None, None) => a.clone(),
+ }
+ }
+ Ordering::Less => b.clone(),
+ Ordering::Greater => a.clone(),
+ }
+ }
+
+ LwwCrdtContainer {
+ value: match (&a.value, &b.value) {
+ (Some(inner_a), Some(inner_b)) => Some(merge_inner(inner_a, inner_b)),
+ (Some(inner), None) | (None, Some(inner)) => Some(inner.clone()),
+ (None, None) => None,
+ },
+ subtree_version: VectorClock::least_upper_bound(&a.subtree_version, &b.subtree_version),
+ }
+ }
+
+ #[cfg(any(test, feature = "checker"))]
+ fn is_valid_collection<'a>(collection: impl IntoIterator<Item = &'a Self>) -> bool
+ where
+ Self: 'a,
+ {
+ use crate::checker::utils::IterExt as _;
+
+ collection
+ .into_iter()
+ .map(|s| s.timestamp())
+ .by_ref()
+ .is_uniq()
+ }
+}
+
+impl<V, T, N> ContentEq for LwwCrdtContainer<V, T, N>
+where
+ V: CrdtUnion<N> + ContentEq,
+ T: TotalTimestamp,
+ N: Ord,
+{
+ fn content_eq(&self, other: &Self) -> bool {
+ match (&self.value, &other.value) {
+ (Some(self_inner), Some(other_inner)) => {
+ if self_inner.timestamp != other_inner.timestamp {
+ return false;
+ }
+ match (&self_inner.value, &other_inner.value) {
+ (Some(self_crdt), Some(other_crdt)) => self_crdt.content_eq(other_crdt),
+ (None, None) => true,
+ _ => false,
+ }
+ }
+ (None, None) => true,
+ _ => false,
+ }
+ }
+}
+
+impl<V, T, N, C> LwwCrdtContainerRead<V, T, N> for C
+where
+ V: 'static,
+ T: 'static,
+ N: Ord + 'static,
+ C: AsDeltaRef<LwwCrdtContainer<V, T, N>>,
+{
+}
+impl<V, T, N, C> LwwCrdtContainerWrite<V, T, N> for C
+where
+ V: 'static,
+ T: 'static,
+ N: Ord + Clone + 'static,
+ C: AsDeltaMut<LwwCrdtContainer<V, T, N>>,
+{
+}
+
+#[cfg(feature = "proto")]
+mod proto {
+ use distributed_time::{
+ hybrid_logical_clock::HybridLogicalTimestamp, vector_clock::VectorClock,
+ };
+ use submerge_internal_proto::{FromProto, FromProtoError, NodeMapping, ToProto};
+
+ use crate::typed_crdt::{proto::TypedCrdtFromProtoError, TypedCrdt};
+
+ use super::{LwwCrdtContainer, LwwCrdtContainerInner};
+
+ impl FromProto
+ for LwwCrdtContainer<TypedCrdt<Vec<u8>, String>, HybridLogicalTimestamp<String>, String>
+ {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeContainer;
+
+ fn from_proto(proto: &Self::Proto, node_ids: &[String]) -> Result<Self, FromProtoError> {
+ let value = match proto
+ .value
+ .as_ref()
+ .map(|v| TypedCrdt::from_proto(v, node_ids))
+ {
+ Some(Ok(v)) => Some(v),
+ Some(Err(TypedCrdtFromProtoError::FromProtoError(e))) => return Err(e),
+ Some(Err(TypedCrdtFromProtoError::UnrecognizedType)) => {
+ // For forwards compatibility, ignore any unknown type and treat as if this node
+ // is empty.
+ return Ok(Self::default());
+ }
+ None => None,
+ };
+ Ok(Self {
+ value: match proto.timestamp.as_ref() {
+ Some(t) => Some(LwwCrdtContainerInner {
+ value,
+ timestamp: HybridLogicalTimestamp::from_proto(
+ t,
+ proto.updater.ok_or(FromProtoError::MissingRequiredField)?,
+ node_ids,
+ )?,
+ }),
+ None => None,
+ },
+ subtree_version: VectorClock::from_proto(&proto.subtree_version, node_ids)?,
+ })
+ }
+ }
+
+ impl ToProto
+ for LwwCrdtContainer<TypedCrdt<Vec<u8>, String>, HybridLogicalTimestamp<String>, String>
+ {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeContainer;
+
+ fn to_proto(&self, node_ids: &mut NodeMapping<String>) -> Self::Proto {
+ let hlc = self.timestamp();
+ let (hlc_proto, updater) = hlc.map(|t| t.to_proto(node_ids)).unzip();
+ submerge_internal_proto::protos::submerge::SubmergeContainer {
+ updater,
+ timestamp: hlc_proto.into(),
+ subtree_version: Some(self.subtree_version().to_proto(node_ids)).into(),
+ value: self.value().map(|v| v.to_proto(node_ids)),
+ ..Default::default()
+ }
+ }
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn container_roundtrip(
+ container: LwwCrdtContainer<
+ TypedCrdt<Vec<u8>, String>,
+ HybridLogicalTimestamp<String>,
+ String,
+ >,
+ ) {
+ let mut node_ids = NodeMapping::default();
+ assert_eq!(
+ LwwCrdtContainer::from_proto(&container.to_proto(&mut node_ids), &node_ids.into_vec())
+ .unwrap(),
+ container,
+ );
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use distributed_time::{
+ hybrid_logical_clock::HybridLogicalTimestamp, vector_clock::VectorClock,
+ };
+ use serde_json::json;
+
+ use crate::{
+ checker::test_fakes::FakeContext,
+ lww_crdt_container::{LwwCrdtContainerRead, LwwCrdtContainerWrite},
+ register::{Register, RegisterRead, RegisterWrite},
+ set::Set,
+ typed_crdt::{TypedCrdt, TypedCrdtRef},
+ vector_data::VectorData,
+ CrdtState,
+ };
+
+ use super::LwwCrdtContainer;
+
+ type TestContainer =
+ LwwCrdtContainer<TypedCrdt<&'static str, u8>, HybridLogicalTimestamp<u8>, u8>;
+
+ #[test]
+ fn test_set_value() {
+ let mut reg = Register::<_, _>::default();
+ let ctx = FakeContext::new(0, 0_u8);
+ reg.set(&ctx, "1234").unwrap();
+ let mut container = TestContainer::default();
+ container.set_value(&ctx, reg.into()).unwrap();
+
+ assert_eq!(
+ &"1234",
+ container
+ .get_child_mut::<Register<_, _>>(&ctx)
+ .unwrap()
+ .unwrap()
+ .get()
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn test_get_as_wrong_type() {
+ let mut reg = Register::<_, VectorClock<u8>>::default();
+ let ctx = FakeContext::new(0, 0_u8);
+ reg.set(&ctx, "1234").unwrap();
+ let mut container = TestContainer::default();
+ container.set_value(&ctx, reg.into()).unwrap();
+
+ assert!(container
+ .get_child_mut::<Set<&'static str>>(&ctx)
+ .unwrap()
+ .is_none());
+ }
+
+ #[test]
+ fn test_merge() {
+ let mut container1 = TestContainer::default();
+ container1
+ .set_value(&FakeContext::new(0, 0_u8), Register::default().into())
+ .unwrap();
+ let mut container2: LwwCrdtContainer<
+ TypedCrdt<&'static str, u8>,
+ HybridLogicalTimestamp<u8>,
+ u8,
+ > = LwwCrdtContainer::default();
+ container2
+ .set_value(&FakeContext::new(1, 2_u8), VectorData::default().into())
+ .unwrap();
+
+ let merged = CrdtState::merge(&container1, &container2);
+ // container2 wins because its timestamp is larger
+ assert!(matches!(
+ merged.get_value(),
+ Some(TypedCrdtRef::VectorData(_))
+ ));
+ }
+
+ #[test]
+ fn test_merge_causal() {
+ let mut container1 = TestContainer::default();
+ container1
+ .set_value(&FakeContext::new(0, 2_u8), Register::default().into())
+ .unwrap();
+
+ let mut ctx2 = FakeContext::new(1, 0_u8);
+ ctx2.version = VectorClock::new([(0, 1)]);
+ let mut container2 = container1.clone();
+ container2
+ .set_value(&ctx2, VectorData::default().into())
+ .unwrap();
+
+ let merged = CrdtState::merge(&container1, &container2);
+ // d2 wins even though its timestamp is smaller, because it has observed d1 (d2 is cloned
+ // from d1)
+ assert!(matches!(
+ merged.get_value(),
+ Some(TypedCrdtRef::VectorData(_))
+ ));
+ }
+
+ #[test]
+ fn test_get_mut() {
+ let mut orig_reg = Register::<_, _>::default();
+ let ctx = FakeContext::new(0, 0_u8);
+ orig_reg.set(&ctx, "1234").unwrap();
+ let mut container1 = TestContainer::default();
+ container1.set_value(&ctx, orig_reg.into()).unwrap();
+
+ let mut container2 = container1.clone();
+
+ let _ = container2
+ .get_child_mut::<Register<_, _>>(&ctx)
+ .unwrap()
+ .unwrap();
+ // get_child_mut alone should not mutate the container
+ assert_eq!(container1, container2);
+
+ // container2 set reg = "5678"
+ let mut child_reg2 = container2
+ .get_child_mut::<Register<_, _>>(&ctx)
+ .unwrap()
+ .unwrap();
+ child_reg2.set(&FakeContext::new(1, 12_u8), "5678").unwrap();
+
+ // container1 set reg = "abcd"
+ let mut child_reg1 = container1
+ .get_child_mut::<Register<_, _>>(&ctx)
+ .unwrap()
+ .unwrap();
+ child_reg1.set(&FakeContext::new(0, 10_u8), "abcd").unwrap();
+
+ let merged = CrdtState::merge(&container1, &container2);
+ let merged_child_reg = merged.get_child::<Register<_, _>>().unwrap();
+ // Merge should be done by Register rules, which is to keep all conflicting values.
+ // "5678" comes first because it is newer (timestamp value of 12)
+ assert_eq!(merged_child_reg.get_all(), vec![&"5678", &"abcd"]);
+ }
+
+ #[test]
+ fn json_encode() {
+ let mut container =
+ LwwCrdtContainer::<TypedCrdt<String, u16>, HybridLogicalTimestamp<u16>, u16>::default();
+ let _ = container
+ .get_child_or_default::<Register<String, VectorClock<u16>>>(&FakeContext::new(0, 0_u8));
+ let json_value = serde_json::to_value(container).unwrap();
+ let expected_json = json! ({
+ "value": {
+ "value": {
+ "Register": [],
+ },
+ "timestamp": {
+ "time": {
+ "logical_time": 0,
+ "causality": 0,
+ },
+ "updater": 0
+ },
+ },
+ "subtree_version": {
+ "0": 1
+ },
+ });
+
+ assert_eq!(expected_json, json_value);
+ }
+
+ #[test]
+ fn json_decode() {
+ let json = json! ({
+ "value": {
+ "value": {
+ "Register": [
+ {
+ "value": "#0",
+ "timestamp": {
+ "distributed_clock": {},
+ "hybrid_logical_timestamp": {
+ "logical_time": 1,
+ "causality": 0,
+ },
+ }
+ },
+ {
+ "value": "#1",
+ "timestamp": {
+ "distributed_clock": {},
+ "hybrid_logical_timestamp": {
+ "logical_time": 2,
+ "causality": 0,
+ },
+ }
+ }
+ ],
+ },
+ "timestamp": {
+ "time": {
+ "logical_time": 1,
+ "causality": 1,
+ },
+ "updater": 0
+ },
+ },
+ "subtree_version": {
+ "0": 1
+ },
+ });
+ let container = serde_json::from_value::<
+ LwwCrdtContainer<TypedCrdt<String, u16>, HybridLogicalTimestamp<u16>, u16>,
+ >(json)
+ .unwrap();
+ assert_eq!(
+ container.get_child::<Register<_, _>>().unwrap().get_all(),
+ vec!["#1", "#0"]
+ );
+ }
+}
diff --git a/betosync/submerge/crdt/src/lww_map.rs b/betosync/submerge/crdt/src/lww_map.rs
new file mode 100644
index 0000000..a48d14f
--- /dev/null
+++ b/betosync/submerge/crdt/src/lww_map.rs
@@ -0,0 +1,547 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A last-writer-wins map implementation that can contain dynamically typed values, where type info
+//! modifications are merged by last-writer-wins semantics.
+//!
+//! See the docs in [`LwwMap`] for details.
+
+use std::{collections::BTreeMap, fmt::Debug};
+
+use crate::{
+ delta::{AsDeltaMut, AsDeltaRef, DeltaMut, DeltaRef},
+ lww_crdt_container::{LwwCrdtContainerRead, LwwCrdtContainerWrite, TryAsChild},
+ utils::{zip_btree_map, Either, ZipItem},
+ ApplyChanges, ContentEq, CrdtState, HasPlainRepresentation, ToPlain, UpdateContext,
+};
+use arbitrary::Arbitrary;
+use derive_where::derive_where;
+use distributed_time::{vector_clock::VectorClock, TimestampOverflow, TotalTimestamp};
+use serde::{Deserialize, Serialize};
+
+use crate::lww_crdt_container::{CrdtUnion, LwwCrdtContainer, LwwCrdtContainerApplyError};
+
+/// A last-writer-wins map implementation that can contain dynamically typed values.
+///
+/// The values in this map can be dynamically typed CRDTs:
+/// - The value type `V` contains the type information. The type can be modified using
+/// [`set_value`][LwwMapWrite::set_value] or [`remove_value`][LwwMapWrite::remove_value], and is
+/// merged using last-writer-wins.
+/// - The value type `V` can switch between multiple child types by implementing `V: TryAsChild<D>`.
+/// Child data values modified using [`get_child_mut`][LwwMapWrite::get_child_mut] are merged
+/// using the child's `merge` function.
+///
+/// # Implementations
+/// * See [`LwwMapRead`] for methods on read-only references of [`LwwMap`].
+/// * See [`LwwMapWrite`] for methods on mutable references of [`LwwMap`].
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Arbitrary)]
+#[derive_where(Default)]
+#[serde(transparent)]
+pub struct LwwMap<K, V, T, N>
+where
+ K: Ord,
+ N: Ord,
+{
+ /// An "insert-only" map containing the values and their type information. Items must not be
+ /// directly removed from or modified this map, but instead go through the APIs in
+ /// [`LwwCrdtContainer`].
+ elements: BTreeMap<K, LwwCrdtContainer<V, T, N>>,
+}
+
+impl<K, V, T, N> LwwMap<K, V, T, N>
+where
+ K: Ord,
+ N: Ord,
+{
+ /// Calculates the delta needed to update from the given `base_version`.
+ ///
+ /// The `base_version` must be a version vector from a replica of the same document. The
+ /// returned document can be applied using [`CrdtState::merge`] by supplying a base
+ /// that is at least as new as `base_version`.
+ ///
+ /// Note that the version is not tracked in this crate. Users of this crate must track the
+ /// versioning information externally.
+ pub fn calculate_delta(&self, base_version: &VectorClock<N>) -> Self
+ where
+ V: CrdtUnion<N>,
+ K: Clone,
+ N: Clone,
+ T: Clone,
+ {
+ LwwMap {
+ elements: self
+ .elements
+ .iter()
+ .map(|(k, v)| (k.clone(), v.calculate_delta(base_version)))
+ .collect(),
+ }
+ }
+}
+
+/// Read-only operations for this Map CRDT.
+///
+/// ## See also
+/// * See [`LwwMap`] for a description of the CRDT map type.
+/// * See [`LwwMapWrite`] for mutating operations on CRDT maps.
+pub trait LwwMapRead<K, V, T, N>: AsDeltaRef<LwwMap<K, V, T, N>>
+where
+ K: Ord + 'static,
+ V: 'static,
+ T: 'static,
+ N: Ord + 'static,
+{
+ /// Get a reference to the container associated with `key` from this map.
+ fn get_container(&self, key: &K) -> DeltaRef<'_, LwwCrdtContainer<V, T, N>> {
+ let delta_container = self.delta().and_then(|delta| delta.elements.get(key));
+ let base_container = self.base().and_then(|b| b.elements.get(key));
+ DeltaRef {
+ base: base_container,
+ delta: delta_container,
+ }
+ }
+
+ /// Get a reference to the child CRDT at the given key, or `None` if the value does not exist or
+ /// cannot be converted to type `Child`.
+ fn get_child<Child>(&self, key: &K) -> Option<DeltaRef<'_, Child>>
+ where
+ V: TryAsChild<Child, N> + Clone,
+ T: TotalTimestamp + Clone,
+ {
+ self.get_container(key).into_child()
+ }
+
+ /// Get a reference to the value at the given `key`.
+ fn get_value(&self, key: &K) -> Option<V::DeltaRef<'_>>
+ where
+ V: CrdtUnion<N> + Clone,
+ T: TotalTimestamp + Clone,
+ {
+ self.get_container(key).into_value()
+ }
+
+ /// Get an iterator though all the keys in this map.
+ fn keys(&self) -> impl Iterator<Item = &K> {
+ let iter_opt = match (self.base(), self.delta()) {
+ (None, None) => None,
+ (None, Some(v)) | (Some(v), None) => Some(Either::Left(v.elements.keys())),
+ (Some(base), Some(delta)) => {
+ let iter = zip_btree_map(&base.elements, &delta.elements).map(|(key, _)| key);
+ Some(Either::Right(iter))
+ }
+ };
+ iter_opt.into_iter().flat_map(|e| e.into_iter())
+ }
+
+ /// Copies the data without the associated metadata and returns the plain type.
+ ///
+ /// See also: [`crate::HasPlainRepresentation`].
+ fn to_plain(&self) -> BTreeMap<K, V::Plain>
+ where
+ K: Clone,
+ V: CrdtUnion<N> + HasPlainRepresentation<N> + Clone,
+ for<'d> V::DeltaRef<'d>: ToPlain<Plain = V::Plain>,
+ for<'d> V::DeltaMut<'d>: ApplyChanges<N, Plain = V::Plain, Error = V::Error>,
+ T: TotalTimestamp<NodeId = N> + Clone,
+ {
+ self.keys()
+ .filter_map(|k| {
+ let container = self.get_container(k);
+ Some((k.clone(), container.to_plain()?))
+ })
+ .collect()
+ }
+
+ /// Returns the combined least upper bound of all the subtree_version of all of the items in
+ /// this map.
+ #[cfg(feature = "checker")]
+ fn subtree_version_least_upper_bound(&self) -> VectorClock<N>
+ where
+ N: Clone,
+ {
+ use distributed_time::DistributedClock;
+
+ let mut version = VectorClock::default();
+ for key in self.keys() {
+ let container = self.get_container(key);
+ if let Some(delta) = container.delta() {
+ version.least_upper_bound_in_place(delta.subtree_version());
+ }
+ if let Some(base) = container.base() {
+ version.least_upper_bound_in_place(base.subtree_version());
+ }
+ }
+ version
+ }
+}
+
+/// Mutating operations for this Map CRDT.
+///
+/// ## See also
+/// * See [`LwwMap`] for a description of the CRDT map type.
+/// * See [`LwwMapRead`] for read-only operations on CRDT maps.
+pub trait LwwMapWrite<K, V, T, N>: LwwMapRead<K, V, T, N> + AsDeltaMut<LwwMap<K, V, T, N>>
+where
+ K: Ord + 'static,
+ V: 'static,
+ T: 'static,
+ N: Ord + Clone + 'static,
+{
+ /// Get a mutable reference to the container associated with `key` from this map.
+ fn get_container_mut(&mut self, key: K) -> DeltaMut<'_, LwwCrdtContainer<V, T, N>> {
+ let self_mut = self.as_mut();
+ let base = self_mut.base.and_then(|base| base.elements.get(&key));
+ let delta = self_mut.delta.elements.entry(key).or_default();
+ DeltaMut { base, delta }
+ }
+
+ /// Get a mutable reference to the value at the given `key`.
+ ///
+ /// This is a _subtree version updating operation_ (See
+ /// [`subtree_version`](LwwCrdtContainer::subtree_version)).
+ fn get_value_mut(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ key: K,
+ ) -> Result<Option<V::DeltaMut<'_>>, TimestampOverflow>
+ where
+ V: CrdtUnion<N>,
+ T: Clone + Ord,
+ {
+ self.get_container_mut(key).into_value_mut(ctx)
+ }
+
+ /// Get a mutable reference to the child CRDT at the given key, or `None` if the value does not
+ /// exist or cannot be converted to type `Child`.
+ ///
+ /// This is a _subtree version updating operation_ (See
+ /// [`subtree_version`](LwwCrdtContainer::subtree_version)).
+ fn get_child_mut<Child>(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ key: K,
+ ) -> Result<Option<DeltaMut<'_, Child>>, TimestampOverflow>
+ where
+ Child: Default,
+ V: TryAsChild<Child, N> + Clone,
+ T: TotalTimestamp + Clone,
+ {
+ self.get_container_mut(key).into_child_mut(ctx)
+ }
+
+ /// Get a mutable reference to the child CRDT at the given key, or set the value to
+ /// `D::default()` if it does not already exist.
+ ///
+ /// This is a _subtree version updating operation_ (See
+ /// [`subtree_version`](LwwCrdtContainer::subtree_version)).
+ // TODO: This is probably a bad way to do schema management, should consider removing it.
+ fn get_child_or_default<Child>(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ key: K,
+ ) -> Result<Option<DeltaMut<'_, Child>>, TimestampOverflow>
+ where
+ Child: Default,
+ V: TryAsChild<Child, N> + Clone,
+ T: TotalTimestamp<NodeId = N> + Clone,
+ {
+ self.get_container_mut(key).into_child_or_default(ctx)
+ }
+
+ /// Set the value associated with `key` in this map.
+ ///
+ /// Setting the value will increment the timestamp associated with this value, and will
+ /// overwrite other concurrent changes made via [`LwwMapWrite::get_child_mut`].
+ fn set_value(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ key: K,
+ value: V,
+ ) -> Result<(), TimestampOverflow>
+ where
+ V: CrdtUnion<N> + Clone,
+ T: TotalTimestamp<NodeId = N> + Clone,
+ {
+ self.get_container_mut(key).set_value(ctx, value)
+ }
+
+ /// Remove the child CRDT at the given key from this map.
+ fn remove_value(&mut self, ctx: &impl UpdateContext<N>, key: K) -> Result<(), TimestampOverflow>
+ where
+ V: CrdtUnion<N> + Clone,
+ T: TotalTimestamp<NodeId = N> + Clone,
+ {
+ self.get_container_mut(key).remove_value(ctx)
+ }
+
+ /// Apply the changes in the plain representation into this CRDT. This assumes that all changes
+ /// in the plain representation were made by the calling node, updating the associated CRDT
+ /// metadata in the process.
+ ///
+ /// After applying the changes, [`to_plain`][LwwMapRead::to_plain] should return the same value
+ /// as `plain`.
+ ///
+ /// # Returns
+ ///
+ /// `true` if applying the change results in an update in the CRDT state, or false if Returns
+ /// true if applying the change results in an update in the CRDT state, or false if `plain` is
+ /// the same as [`to_plain`][LwwMapRead::to_plain] to begin with. This can be used to avoid
+ /// sending unnecessary update messages over the network, or writing changes to disk
+ /// unnecessarily.
+ fn apply_changes(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ plain: BTreeMap<K, V::Plain>,
+ ) -> Result<bool, LwwCrdtContainerApplyError<V::Error>>
+ where
+ K: Ord + Clone,
+ V: CrdtUnion<N> + HasPlainRepresentation<N> + Clone,
+ for<'d> V::DeltaRef<'d>: ToPlain<Plain = V::Plain>,
+ for<'d> V::DeltaMut<'d>: ApplyChanges<N, Plain = V::Plain, Error = V::Error>,
+ T: TotalTimestamp<NodeId = N> + Clone,
+ {
+ let mut changed = false;
+ let keys: Vec<_> = self.keys().cloned().collect();
+ for k in keys {
+ if !plain.contains_key(&k) {
+ changed = self.get_container_mut(k).apply_changes(ctx, None)? || changed;
+ }
+ }
+ for (k, v) in plain {
+ changed = self.get_container_mut(k).apply_changes(ctx, Some(v))? || changed;
+ }
+ Ok(changed)
+ }
+}
+
+impl<'d, K, V, T, N> DeltaMut<'d, LwwMap<K, V, T, N>>
+where
+ K: Ord,
+ N: Ord + Clone,
+{
+ /// Converts this into a mutable reference to the container associated with `key` from this map.
+ ///
+ /// Same as [`LwwMapWrite::get_container_mut`], but consumes self to allow the lifetime to be
+ /// propagated to the returned value.
+ pub fn into_container_mut(self, key: K) -> DeltaMut<'d, LwwCrdtContainer<V, T, N>> {
+ let base = self.base.and_then(|base| base.elements.get(&key));
+ let delta = self.delta.elements.entry(key).or_default();
+ DeltaMut { base, delta }
+ }
+}
+
+impl<K, V, T, N> CrdtState for LwwMap<K, V, T, N>
+where
+ K: Ord + Clone,
+ V: CrdtUnion<N> + Clone,
+ T: TotalTimestamp + Clone,
+ N: Ord + Clone,
+{
+ fn merge(a: &Self, b: &Self) -> Self {
+ Self {
+ elements: zip_btree_map(&a.elements, &b.elements)
+ .map(|(key, item)| {
+ (
+ key.clone(),
+ match item {
+ ZipItem::Left(value) | ZipItem::Right(value) => value.clone(),
+ ZipItem::Both(value_a, value_b) => CrdtState::merge(value_a, value_b),
+ },
+ )
+ })
+ .collect(),
+ }
+ }
+
+ #[cfg(any(test, feature = "checker"))]
+ fn is_valid_collection<'a>(collection: impl IntoIterator<Item = &'a Self>) -> bool
+ where
+ Self: 'a,
+ {
+ LwwCrdtContainer::is_valid_collection(
+ collection.into_iter().flat_map(|s| s.elements.values()),
+ )
+ }
+}
+
+impl<K, V, T, N> ContentEq for LwwMap<K, V, T, N>
+where
+ K: Ord,
+ V: CrdtUnion<N> + ContentEq,
+ T: TotalTimestamp,
+ N: Ord + Clone,
+{
+ fn content_eq(&self, other: &Self) -> bool {
+ if self.elements.len() != other.elements.len() {
+ return false;
+ }
+ self.elements
+ .iter()
+ .all(|(key, self_container)| match other.elements.get(key) {
+ Some(other_container) => self_container.content_eq(other_container),
+ None => false,
+ })
+ }
+}
+
+impl<K, V, T, N, C> LwwMapRead<K, V, T, N> for C
+where
+ K: Ord + 'static,
+ V: 'static,
+ T: 'static,
+ N: Ord + 'static,
+ C: AsDeltaRef<LwwMap<K, V, T, N>>,
+{
+}
+impl<K, V, T, N, C> LwwMapWrite<K, V, T, N> for C
+where
+ K: Ord + 'static,
+ V: 'static,
+ T: 'static,
+ N: Ord + Clone + 'static,
+ C: AsDeltaMut<LwwMap<K, V, T, N>>,
+{
+}
+
+#[cfg(feature = "proto")]
+mod proto {
+ use distributed_time::hybrid_logical_clock::HybridLogicalTimestamp;
+ use submerge_internal_proto::{FromProto, FromProtoError, NodeMapping, ToProto};
+
+ use crate::{lww_crdt_container::LwwCrdtContainer, typed_crdt::TypedCrdt};
+
+ use super::LwwMap;
+
+ impl FromProto
+ for LwwMap<String, TypedCrdt<Vec<u8>, String>, HybridLogicalTimestamp<String>, String>
+ {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeMap;
+
+ fn from_proto(proto: &Self::Proto, node_ids: &[String]) -> Result<Self, FromProtoError> {
+ Ok(Self {
+ elements: proto
+ .elements
+ .iter()
+ .map(|(key, container)| {
+ Ok((
+ key.clone(),
+ LwwCrdtContainer::from_proto(container, node_ids)?,
+ ))
+ })
+ .collect::<Result<_, _>>()?,
+ })
+ }
+ }
+
+ impl ToProto
+ for LwwMap<String, TypedCrdt<Vec<u8>, String>, HybridLogicalTimestamp<String>, String>
+ {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeMap;
+
+ fn to_proto(&self, node_ids: &mut NodeMapping<String>) -> Self::Proto {
+ submerge_internal_proto::protos::submerge::SubmergeMap {
+ elements: self
+ .elements
+ .iter()
+ .map(|(node, container)| (node.clone(), container.to_proto(node_ids)))
+ .collect(),
+ ..Default::default()
+ }
+ }
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn map_roundtrip(
+ map: LwwMap<String, TypedCrdt<Vec<u8>, String>, HybridLogicalTimestamp<String>, String>,
+ ) {
+ let mut node_ids = NodeMapping::default();
+ assert_eq!(
+ LwwMap::from_proto(&map.to_proto(&mut node_ids), &node_ids.into_vec()).unwrap(),
+ map
+ );
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::BTreeMap;
+
+ use crate::{
+ checker::test_fakes::{FakeContext, TriState},
+ lww_map::{LwwMapRead, LwwMapWrite},
+ register::{RegisterRead, RegisterWrite},
+ };
+ use distributed_time::{
+ hybrid_logical_clock::HybridLogicalTimestamp, vector_clock::VectorClock,
+ };
+
+ use crate::{
+ lww_crdt_container::LwwCrdtContainerApplyError,
+ register::Register,
+ typed_crdt::{PlainTypes, TypedCrdt, TypedError},
+ vector_data::VectorDataError,
+ };
+
+ use super::LwwMap;
+
+ #[test]
+ fn test_set_value() {
+ let mut map: LwwMap<TriState, TypedCrdt<&'static str, u8>, HybridLogicalTimestamp<u8>, u8> =
+ LwwMap::default();
+ let mut reg = Register::<&'static str, VectorClock<u8>>::default();
+ reg.set(&FakeContext::new(1, 1_u8), "1234").unwrap();
+ map.set_value(&FakeContext::new(1, 1_u8), TriState::A, reg.into())
+ .unwrap();
+
+ assert_eq!(
+ &"1234",
+ map.get_child::<Register::<&'static str, VectorClock<u8>>>(&TriState::A)
+ .unwrap()
+ .get()
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn test_apply_changes() {
+ let mut map: LwwMap<TriState, TypedCrdt<TriState, u8>, HybridLogicalTimestamp<u8>, u8> =
+ LwwMap::default();
+ let plain = BTreeMap::from_iter([(
+ TriState::A,
+ PlainTypes::VectorData(BTreeMap::from_iter([(1, TriState::A)])),
+ )]);
+ let _ = map
+ .apply_changes(&FakeContext::new(1, 1_u8), plain.clone())
+ .unwrap();
+
+ assert_eq!(map.to_plain(), plain);
+ }
+
+ #[test]
+ fn test_apply_changes_vectordata_mismatch() {
+ let mut map: LwwMap<TriState, TypedCrdt<TriState, u8>, HybridLogicalTimestamp<u8>, u8> =
+ LwwMap::default();
+ let plain = BTreeMap::from_iter([(
+ TriState::A,
+ PlainTypes::VectorData(BTreeMap::from_iter([(2, TriState::A)])),
+ )]);
+ assert_eq!(
+ LwwCrdtContainerApplyError::ChildError(TypedError::VectorData(
+ VectorDataError::MismatchedNode
+ )),
+ map.apply_changes(&FakeContext::new(1, 1_u8), plain.clone())
+ .unwrap_err()
+ );
+ }
+}
diff --git a/betosync/submerge/crdt/src/register.rs b/betosync/submerge/crdt/src/register.rs
new file mode 100644
index 0000000..183eb0b
--- /dev/null
+++ b/betosync/submerge/crdt/src/register.rs
@@ -0,0 +1,560 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A register can store any serializable type and updates atomically.
+//!
+//! Each register value is associated with timestamps such that clients can either get the last
+//! written value, or get a list of all concurrent values ordered by wall-clock timestamps.
+
+use std::{cmp::Ordering, collections::BTreeSet, fmt::Debug};
+
+use crate::{
+ delta::{AsDeltaMut, AsDeltaRef},
+ utils::{merge_descending_iters, Either},
+ ContentEq, CrdtState, UpdateContext,
+};
+use arbitrary::{Arbitrary, Unstructured};
+use derive_where::derive_where;
+use distributed_time::{
+ compound_timestamp::CompoundTimestamp, DistributedClock, NonSemanticOrd, TimestampOverflow,
+ TotalTimestamp,
+};
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Default, Clone, Arbitrary, Serialize, Deserialize)]
+#[derive_where(PartialEq, Eq, PartialOrd, Ord; T)]
+struct RegEntry<V, T> {
+ #[derive_where(skip)]
+ value: V,
+ timestamp: T,
+}
+
+/// A register can store any serializable type and updates atomically without merging.
+///
+/// Each register value is associated with timestamps such that clients can either get the last
+/// written value (using [`get`][RegisterRead::get]), or get a list of all concurrent values ordered
+/// by a [`CompoundTimestamp`] (using [`get_all`][RegisterRead::get_all]).
+///
+/// # Params
+/// - `V`: The value type
+/// - `D`: The logical component of the timestamp, which provides the causality order for values in
+/// this register.
+/// - `W`: Provides the current wall clock time, used to tie-break concurrent events.
+///
+/// # Implementations
+/// * See [`RegisterRead`] for methods on read-only references of [`Register`].
+/// * See [`RegisterWrite`] for methods on mutable references of [`Register`].
+#[derive(Debug, Serialize, Deserialize)]
+#[derive_where(Default)]
+#[derive_where(PartialEq, Eq, Clone; V, CompoundTimestamp<D>)]
+#[serde(bound(
+ serialize = "V: Serialize, D: Serialize",
+ deserialize = "V: Deserialize<'de>, D: Deserialize<'de> + NonSemanticOrd"
+))]
+#[serde(transparent)]
+pub struct Register<V, D>
+where
+ D: DistributedClock,
+{
+ elements: BTreeSet<RegEntry<V, CompoundTimestamp<D>>>,
+}
+
+impl<'a, V, D> Arbitrary<'a> for Register<V, D>
+where
+ V: Arbitrary<'a>,
+ D: DistributedClock + NonSemanticOrd + Default + Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ let values: BTreeSet<RegEntry<V, CompoundTimestamp<D>>> = u.arbitrary()?;
+ let mut clock = D::default();
+ Ok(Self {
+ elements: values
+ .into_iter()
+ .rev()
+ // Iterate through the elements in descending order of the timestamp. If the entry
+ // is less than `clock` (the least-upper-bound of all the entries we've seen so
+ // far), that entry is superseded and should not have made it into the register
+ // (`merge` would have filtered those out)
+ .filter(move |entry| {
+ let order = entry.timestamp.distributed_clock.partial_cmp(&clock);
+ clock.least_upper_bound_in_place(&entry.timestamp.distributed_clock);
+ order != Some(Ordering::Less)
+ })
+ .collect(),
+ })
+ }
+}
+
+impl<V, D> Register<V, D>
+where
+ V: Clone,
+ D: DistributedClock + NonSemanticOrd + Default + Clone,
+{
+ /// Create a new register with the given value.
+ pub fn new(ctx: &impl UpdateContext<D::NodeId>, value: V) -> Result<Self, TimestampOverflow> {
+ let timestamp =
+ CompoundTimestamp::new_with_context(ctx.updater(), ctx.timestamp_provider());
+ Ok(Self {
+ elements: BTreeSet::from_iter([RegEntry { timestamp, value }]),
+ })
+ }
+
+ fn merge_elements(
+ a: &BTreeSet<RegEntry<V, CompoundTimestamp<D>>>,
+ b: &BTreeSet<RegEntry<V, CompoundTimestamp<D>>>,
+ ) -> BTreeSet<RegEntry<V, CompoundTimestamp<D>>> {
+ let mut clock = D::default();
+ let merged_iter =
+ merge_descending_iters(a.iter().rev(), b.iter().rev()).filter(move |item| {
+ let order = item.timestamp.distributed_clock.partial_cmp(&clock);
+ clock.least_upper_bound_in_place(&item.timestamp.distributed_clock);
+ order != Some(Ordering::Less)
+ });
+ merged_iter.cloned().collect()
+ }
+}
+
+fn iter_descending<V, D, R>(
+ register: &R,
+) -> impl Iterator<Item = &RegEntry<V, CompoundTimestamp<D>>>
+where
+ V: 'static,
+ D: DistributedClock + NonSemanticOrd + Default + 'static,
+ R: RegisterRead<V, D> + ?Sized,
+{
+ let iter_opt = match (register.base(), register.delta()) {
+ (None, None) => None,
+ (None, Some(reg)) | (Some(reg), None) => Some(Either::Left(reg.elements.iter().rev())),
+ (Some(base), Some(delta)) => {
+ let mut version_vector = D::default();
+ Some(Either::Right(
+ merge_descending_iters(base.elements.iter().rev(), delta.elements.iter().rev())
+ .filter(move |value| {
+ // filter out values that have been superseded
+ let comparison = value
+ .timestamp
+ .distributed_clock
+ .partial_cmp(&version_vector);
+ version_vector
+ .least_upper_bound_in_place(&value.timestamp.distributed_clock);
+ comparison != Some(std::cmp::Ordering::Less)
+ }),
+ ))
+ }
+ };
+ iter_opt.into_iter().flat_map(|e| e.into_iter())
+}
+
+/// Read-only operations for this Register CRDT.
+///
+/// ## See also
+/// * See [`Register`] for a description of this register type.
+/// * See [`RegisterWrite`] for mutating operations on this register type.
+pub trait RegisterRead<V, D>: AsDeltaRef<Register<V, D>>
+where
+ D: DistributedClock,
+{
+ /// Returns a vec containing the values associated with all concurrent changes, ordered from
+ /// newest to oldest. The returned vec may be empty if the register have just been initialized
+ /// with `default` and have never been written to.
+ fn get_all(&self) -> Vec<&V>
+ where
+ V: 'static,
+ D: DistributedClock + NonSemanticOrd + Default + 'static,
+ {
+ iter_descending(self)
+ .map(|RegEntry { value, .. }| value)
+ .collect()
+ }
+
+ /// Get the latest value from the register according to the timestamps.
+ fn get(&self) -> Option<&V>
+ where
+ V: 'static,
+ D: DistributedClock + NonSemanticOrd + Default + 'static,
+ {
+ iter_descending(self)
+ .map(|RegEntry { value, .. }| value)
+ .next()
+ }
+
+ /// Copies the data without the associated metadata and returns the plain type.
+ ///
+ /// See also: [`crate::HasPlainRepresentation`].
+ fn to_plain(&self) -> Vec<V>
+ where
+ V: Clone + 'static,
+ D: DistributedClock + NonSemanticOrd + Default + 'static,
+ {
+ self.get_all().into_iter().cloned().collect()
+ }
+}
+
+/// Mutating operations for this Register CRDT.
+///
+/// ## See also
+/// * See [`Register`] for a description of this register type.
+/// * See [`RegisterRead`] for read-only operations of this register type.
+pub trait RegisterWrite<V, D>: RegisterRead<V, D> + AsDeltaMut<Register<V, D>>
+where
+ D: DistributedClock + Eq,
+{
+ /// Set the value of this register.
+ ///
+ /// This sets the register value as a single unit. When merged, it either takes this value `V`,
+ /// or another value `V` from another `set` call, never combining the results from different
+ /// `V`s.
+ ///
+ /// This overrides all values currently in the register, implicitly resolving all "conflicts"
+ /// from concurrent modifications that happened before.
+ fn set(
+ &mut self,
+ ctx: &impl UpdateContext<D::NodeId>,
+ value: V,
+ ) -> Result<(), TimestampOverflow>
+ where
+ V: 'static,
+ D: NonSemanticOrd + Default + Clone + 'static,
+ {
+ // Merge all of the timestamps to form the basis of our new timestamp, since we have
+ // observed those values.
+ let timestamp = match CompoundTimestamp::least_upper_bound(
+ iter_descending(self).map(|entry| &entry.timestamp),
+ ) {
+ Some(t) => t.increment(ctx.updater(), ctx.timestamp_provider())?,
+ None => CompoundTimestamp::new_with_context(ctx.updater(), ctx.timestamp_provider()),
+ };
+ *self.delta_mut() = Register {
+ elements: BTreeSet::from_iter([RegEntry { timestamp, value }]),
+ };
+ Ok(())
+ }
+
+ /// Apply the changes in the plain representation into this CRDT. This assumes that all changes
+ /// in the plain representation were made by the calling node, updating the associated CRDT
+ /// metadata in the process.
+ ///
+ /// After applying the changes, [`to_plain`][RegisterRead::to_plain] should return the same
+ /// value as `plain`.
+ ///
+ /// # Returns
+ ///
+ /// `true` if applying the change results in an update in the CRDT state, or false if Returns
+ /// true if applying the change results in an update in the CRDT state, or false if `plain` is
+ /// the same as [`to_plain`][RegisterRead::to_plain] to begin with. This can be used to avoid
+ /// sending unnecessary update messages over the network, or writing changes to disk
+ /// unnecessarily.
+ fn apply_changes(
+ &mut self,
+ ctx: &impl UpdateContext<D::NodeId>,
+ mut plain: Vec<V>,
+ ) -> Result<bool, RegisterError>
+ where
+ V: PartialEq + 'static,
+ D: Default + NonSemanticOrd + Clone + 'static,
+ {
+ if iter_descending(self)
+ .map(|RegEntry { value, .. }| value)
+ .ne(plain.as_slice())
+ {
+ if plain.len() != 1 {
+ // When changing the value, only one value is allowed. Since timestamp data is
+ // tagged per-node, allowing multiple values to be written may violate the global
+ // uniqueness of the timestamps.
+ return Err(RegisterError::UnexpectedLength);
+ }
+ self.set(ctx, plain.remove(0))?;
+ Ok(true)
+ } else {
+ // If the values did not change, no need to check for the length, just keep all existing
+ // concurrent values.
+ Ok(false)
+ }
+ }
+}
+
+impl<V, D> CrdtState for Register<V, D>
+where
+ V: Clone,
+ D: DistributedClock + NonSemanticOrd + Clone + Default,
+{
+ fn merge(a: &Self, b: &Self) -> Self {
+ Self {
+ elements: Self::merge_elements(&a.elements, &b.elements),
+ }
+ }
+
+ #[cfg(any(test, feature = "checker"))]
+ fn is_valid_collection<'a>(collection: impl IntoIterator<Item = &'a Self>) -> bool
+ where
+ Self: 'a,
+ {
+ use crate::checker::utils::IterExt;
+
+ let mut timestamps = collection
+ .into_iter()
+ .flat_map(|s| s.elements.iter().map(|v| &v.timestamp));
+ let is_comparable = timestamps
+ .iter_pairs_unordered()
+ .all(|(v1, v2)| CompoundTimestamp::is_comparable(v1, v2));
+ is_comparable
+ }
+}
+
+impl<V, D> ContentEq for Register<V, D>
+where
+ V: PartialEq,
+ D: DistributedClock + PartialEq,
+ CompoundTimestamp<D>: PartialEq,
+{
+ fn content_eq(&self, other: &Self) -> bool {
+ self == other
+ }
+}
+
+impl<T: AsDeltaRef<Register<V, D>>, V, D: DistributedClock> RegisterRead<V, D> for T {}
+impl<T: AsDeltaMut<Register<V, D>>, V, D: DistributedClock> RegisterWrite<V, D> for T {}
+
+/// Error in [`RegisterWrite::apply_changes`].
+#[derive(Debug, PartialEq, Eq)]
+pub enum RegisterError {
+ /// The list passed into `apply_changes` must have length 1.
+ UnexpectedLength,
+ /// The operation cannot be performed because the associated timestamp will overflow.
+ TimestampOverflow,
+}
+
+impl From<TimestampOverflow> for RegisterError {
+ fn from(_: TimestampOverflow) -> Self {
+ RegisterError::TimestampOverflow
+ }
+}
+
+/// Checker to help implement invariant tests over arbitrary operations on a register.
+///
+/// Requires the feature _`checker`_.
+#[cfg(any(test, feature = "checker"))]
+pub mod checker {
+ use std::fmt::Debug;
+
+ use arbitrary::Arbitrary;
+ use distributed_time::vector_clock::VectorClock;
+
+ use crate::{
+ checker::simulation::{Operation, SimulationContext},
+ delta::DeltaMut,
+ register::{Register, RegisterRead, RegisterWrite},
+ };
+
+ /// Mutation operations on a [`RegisterWrite`].
+ ///
+ /// Can be used with [`Arbitrary`] to generate arbitrary operations to be applied on the
+ /// register.
+ #[derive(Debug, Clone, Arbitrary)]
+ pub enum RegOp<N, V> {
+ /// Represents the [`RegisterWrite::set`] operation.
+ Set {
+ /// The ID of the node setting this value.
+ node_id: N,
+ /// The value to be set.
+ value: V,
+ },
+ /// Represents the [`RegisterWrite::apply_changes`] operation.
+ ApplyChanges {
+ /// The ID of the node applying the changes.
+ node_id: N,
+ /// The list of values to be set.
+ plain: Vec<V>,
+ },
+ }
+
+ impl<N, V> Operation<Register<V, VectorClock<N>>, N> for RegOp<N, V>
+ where
+ N: Ord + Clone + 'static,
+ V: Clone + Eq + Debug + 'static,
+ {
+ fn apply(
+ self,
+ mut state: DeltaMut<Register<V, VectorClock<N>>>,
+ ctx: &SimulationContext<N>,
+ ) {
+ match self {
+ RegOp::Set { node_id, value } => {
+ let result = state.set(&ctx.context(&node_id), value.clone());
+ if result.is_ok() {
+ assert_eq!(Some(&value), state.get());
+ assert_eq!(vec![&value], state.get_all());
+ }
+ }
+ RegOp::ApplyChanges { node_id, plain } => {
+ let ctx = ctx.context(&node_id);
+ if state.apply_changes(&ctx, plain.clone()).is_ok() {
+ assert_eq!(plain, state.to_plain());
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(feature = "proto")]
+mod proto {
+ use distributed_time::{compound_timestamp::CompoundTimestamp, vector_clock::VectorClock};
+ use submerge_internal_proto::{FromProto, FromProtoError, NodeMapping, ToProto};
+
+ use super::{RegEntry, Register};
+
+ impl FromProto for Register<Vec<u8>, VectorClock<String>> {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeRegister;
+
+ fn from_proto(proto: &Self::Proto, node_ids: &[String]) -> Result<Self, FromProtoError> {
+ Ok(Self {
+ elements: proto
+ .elements
+ .iter()
+ .map(|elem| {
+ Ok(RegEntry {
+ value: elem
+ .value
+ .clone()
+ .ok_or(FromProtoError::MissingRequiredField)?,
+ timestamp: CompoundTimestamp::from_proto(
+ &elem.hlc,
+ &elem.vector_clock,
+ node_ids,
+ )?,
+ })
+ })
+ .collect::<Result<_, _>>()?,
+ })
+ }
+ }
+
+ impl ToProto for Register<Vec<u8>, VectorClock<String>> {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeRegister;
+
+ fn to_proto(&self, node_ids: &mut NodeMapping<String>) -> Self::Proto {
+ submerge_internal_proto::protos::submerge::SubmergeRegister {
+ elements: self
+ .elements
+ .iter()
+ .map(|reg_entry| {
+ let (hlc, vector_clock) = reg_entry.timestamp.to_proto(node_ids);
+ submerge_internal_proto::protos::submerge::submerge_register::RegisterElement {
+ value: Some(reg_entry.value.clone()),
+ vector_clock: Some(vector_clock).into(),
+ hlc: Some(hlc).into(),
+ ..Default::default()
+ }
+ })
+ .collect(),
+ ..Default::default()
+ }
+ }
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn register_roundtrip(register: Register<Vec<u8>, VectorClock<String>>) {
+ let mut node_ids = NodeMapping::default();
+ assert_eq!(
+ Register::from_proto(®ister.to_proto(&mut node_ids), &node_ids.into_vec()).unwrap(),
+ register
+ );
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Register;
+ use crate::{
+ checker::test_fakes::FakeContext,
+ register::{RegisterRead, RegisterWrite},
+ CrdtState,
+ };
+ use distributed_time::{vector_clock::VectorClock, TimestampOverflow};
+ use serde_json::json;
+
+ type TestRegister = Register<u8, VectorClock<u8>>;
+
+ #[test]
+ fn test_set() {
+ let r: TestRegister = Register::new(&FakeContext::new(0, 0_u8), 1).unwrap();
+
+ assert_eq!(vec![&1], r.get_all());
+ assert_eq!(Some(&1), r.get());
+ }
+
+ #[test]
+ fn test_set_concurrent() -> Result<(), TimestampOverflow> {
+ let r1: TestRegister = Register::new(&FakeContext::new(0, 2_u8), 1)?;
+
+ let r2: TestRegister = Register::new(&FakeContext::new(1, 0_u8), 2)?;
+
+ let r3: TestRegister = Register::new(&FakeContext::new(2, 2_u8), 3)?;
+
+ let r_merged = CrdtState::merge(&r1, &r2);
+ let r_merged = CrdtState::merge(&r_merged, &r3);
+
+ // Ordered from oldest to newest according to timestamp, tie-broken by node ID
+ assert_eq!(vec![&3, &1, &2], r_merged.get_all());
+ assert_eq!(Some(&3), r_merged.get());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_overwrite_value() -> Result<(), TimestampOverflow> {
+ let mut r1: TestRegister = Register::new(&FakeContext::new(0, 2_u8), 1)?;
+ r1.set(&FakeContext::new(1, 0_u8), 2)?;
+
+ let r2: TestRegister = Register::new(&FakeContext::new(0, 2_u8), 1)?;
+
+ // Merging r2 doesn't change r1 because r1 has already observed that change.
+ let r_merged = CrdtState::merge(&r1, &r2);
+
+ assert_eq!(vec![&2], r_merged.get_all());
+ assert_eq!(Some(&2), r_merged.get());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_merge_same() {
+ let r1: TestRegister = Register::new(&FakeContext::new(0, 0_u8), 1).unwrap();
+
+ let r_merged = CrdtState::merge(&r1, &r1);
+
+ assert_eq!(vec![&1], r_merged.get_all());
+ assert_eq!(Some(&1), r_merged.get());
+ }
+
+ #[test]
+ fn json_decode() {
+ let json = json! ([
+ {
+ "value": "What is your #0 favorite emoji?",
+ "timestamp": {
+ "distributed_clock": {},
+ "hybrid_logical_timestamp": {
+ "logical_time": 1,
+ "causality": 0,
+ },
+ }
+ }
+ ]);
+ let register = serde_json::from_value::<Register<String, VectorClock<u8>>>(json).unwrap();
+ assert_eq!(register.get_all(), vec!["What is your #0 favorite emoji?"])
+ }
+}
diff --git a/betosync/submerge/crdt/src/set.rs b/betosync/submerge/crdt/src/set.rs
new file mode 100644
index 0000000..c490af2
--- /dev/null
+++ b/betosync/submerge/crdt/src/set.rs
@@ -0,0 +1,604 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A set CRDT that resolves concurrent modifications by comparing the generation number (a.k.a. the
+//! causal length).
+//!
+//! See [`Set`].
+
+use std::{
+ collections::{hash_map::RandomState, HashMap, HashSet},
+ fmt::Debug,
+ hash::{BuildHasher, Hash},
+};
+
+use crate::{
+ delta::{AsDeltaMut, AsDeltaRef},
+ utils::{zip_hash_map, ZipItem},
+ ContentEq, CrdtState,
+};
+use arbitrary::{Arbitrary, Unstructured};
+use derive_where::derive_where;
+use serde::{Deserialize, Serialize};
+
+/// The metadata associated with an element in the set. This is an alternative encoding to the
+/// "causal-length" described in the paper. The paper uses odd numbers to represent `removed =
+/// false` and even numbers to represent `removed = true`, whereas this uses a separate boolean to
+/// be more explicit.
+///
+/// Translation from this struct to causal-length can be done by `causal-length = generation * 2 +
+/// if removed { 1 } else { 0 } - 1`.
+#[derive(Debug, Clone, Copy, Arbitrary, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
+struct ElementState {
+ /// The generation number of this element. During merge, the element with higher generation
+ /// wins.
+ generation: u32,
+ /// Whether the element is removed. The element itself needs to be kept as CRDT metadata (a.k.a.
+ /// tombstone) to ensure convergence. During merge, `removed = true` wins for elements with the
+ /// same generation number.
+ removed: bool,
+}
+
+impl Default for ElementState {
+ fn default() -> Self {
+ Self {
+ generation: 0,
+ removed: true,
+ }
+ }
+}
+
+/// A Causal Length Set, which is a set CRDT that resolves concurrent modifications by comparing
+/// the generation number (a.k.a. the causal length).
+///
+/// The generation number is incremented when an entry goes from removed state to added state. That
+/// means the more a node has observed an entry being (re-)added, the more likely its modification
+/// will win during a merge. Adding a value that already exists or removing a value that is not in
+/// the set are no-ops.
+///
+/// Reference: [Causal-Length Set](https://dl.acm.org/doi/abs/10.1145/3380787.3393678)
+///
+/// Values of this set must implement [`Eq`]. In addition to the reflexive, symmetric, and
+/// transitive properties required by `Eq`, the value should also make sure that any non-trivial
+/// fields (i.e. fields sent over the wire) participate in the `Eq` comparison. Convergence of
+/// fields not participating in the `Eq` comparison is not guaranteed.
+///
+/// Garbage collection is not supported, meaning removed elements (a.k.a. "tombstones") remain in
+/// the set as metadata.
+///
+/// # Implementations
+/// * See [`SetRead`] for methods on read-only references of [`Set`].
+/// * See [`SetWrite`] for methods on mutable references of [`Set`].
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive_where(Default; S)]
+#[derive_where(PartialEq, Eq; E: Eq + Hash, S: BuildHasher)]
+#[serde(transparent)]
+pub struct Set<E, S = RandomState> {
+ #[serde(bound(
+ serialize = "E: Serialize",
+ deserialize = "E: Deserialize<'de> + Eq + Hash, S: BuildHasher + Default"
+ ))]
+ elements: HashMap<E, ElementState, S>,
+}
+
+// Manual implementation of Arbitrary that always have no base, and is not bound on `S: Arbitrary`.
+impl<'a, E, S> Arbitrary<'a> for Set<E, S>
+where
+ E: Hash + Eq + Arbitrary<'a>,
+ S: BuildHasher + Default,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self {
+ elements: u.arbitrary_iter()?.collect::<Result<_, _>>()?,
+ })
+ }
+}
+
+/// Cannot add new element to the set because the generation number space is exhausted.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct GenerationExhausted;
+
+/// A private helper function, intended for use by [`SetRead`] to get the element state taking
+/// both the base and the delta into account.
+fn get_entry<E, S>(
+ base: Option<&Set<E, S>>,
+ delta: Option<&Set<E, S>>,
+ value: &E,
+) -> Option<ElementState>
+where
+ E: Hash + Eq,
+ S: BuildHasher,
+{
+ let original = base.and_then(|b| b.elements.get(value));
+ let delta = delta.and_then(|delta| delta.elements.get(value));
+ match (original, delta) {
+ (None, None) => None,
+ (Some(s), None) | (None, Some(s)) => Some(*s),
+ (Some(o), Some(d)) => Some(*o.max(d)),
+ }
+}
+
+fn iter_entries<'a, E, S>(
+ base: Option<&'a Set<E, S>>,
+ delta: Option<&'a Set<E, S>>,
+) -> impl Iterator<Item = (&'a E, &'a ElementState)>
+where
+ S: 'static,
+ E: Hash + Eq,
+ S: BuildHasher + Default,
+{
+ enum HeterogeneousIter<I1, I2, I3> {
+ Empty,
+ Iter1(I1),
+ Iter2(I2),
+ Iter3(I3),
+ }
+ impl<I1, I2, I3> Iterator for HeterogeneousIter<I1, I2, I3>
+ where
+ I1: Iterator,
+ I2: Iterator<Item = I1::Item>,
+ I3: Iterator<Item = I1::Item>,
+ {
+ type Item = I1::Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self {
+ HeterogeneousIter::Empty => None,
+ HeterogeneousIter::Iter1(i1) => i1.next(),
+ HeterogeneousIter::Iter2(i2) => i2.next(),
+ HeterogeneousIter::Iter3(i3) => i3.next(),
+ }
+ }
+ }
+
+ match (base, delta) {
+ (None, None) => HeterogeneousIter::Empty,
+ (None, Some(delta)) => HeterogeneousIter::Iter1(delta.elements.iter()),
+ (Some(base), None) => HeterogeneousIter::Iter2(base.elements.iter()),
+ (Some(base), Some(delta)) => {
+ HeterogeneousIter::Iter3(zip_hash_map(&base.elements, &delta.elements).map(
+ |(key, item)| -> (&E, &ElementState) {
+ match item {
+ ZipItem::Left(elem) | ZipItem::Right(elem) => (key, elem),
+ ZipItem::Both(left, right) => (key, left.max(right)),
+ }
+ },
+ ))
+ }
+ }
+}
+
+/// Read-only operations for this Set CRDT.
+///
+/// ## See also
+/// * See [`Set`] for a description of this set type.
+/// * See [`SetWrite`] for mutating operations on this set type.
+pub trait SetRead<E, S>: AsDeltaRef<Set<E, S>> {
+ /// Get the set of entries currently in the map.
+ fn entries(&self) -> HashSet<&E>
+ where
+ S: 'static,
+ E: Hash + Eq,
+ S: BuildHasher + Default,
+ {
+ iter_entries(self.base(), self.delta())
+ .filter(|(_, element_state)| !element_state.removed)
+ .map(|(element, _)| element)
+ .collect()
+ }
+
+ /// Returns `true` if the set contains `value`.
+ fn contains(&self, value: &E) -> bool
+ where
+ E: Hash + Eq,
+ S: BuildHasher,
+ {
+ get_entry(self.base(), self.delta(), value)
+ .map(|state| !state.removed)
+ .unwrap_or(false)
+ }
+
+ /// Copies the data without the associated metadata and returns the plain type.
+ ///
+ /// See also: [`crate::HasPlainRepresentation`].
+ fn to_plain(&self) -> HashSet<E, S>
+ where
+ E: Hash + Eq + Clone,
+ S: BuildHasher + Default + 'static,
+ {
+ self.entries().into_iter().cloned().collect()
+ }
+}
+
+/// Mutating operations for this Set CRDT.
+///
+/// ## See also
+/// * See [`Set`] for a description of this set type.
+/// * See [`SetRead`] for read-only operations of this set type.
+pub trait SetWrite<E, S>: SetRead<E, S> + AsDeltaMut<Set<E, S>> {
+ /// Add an element into the set.
+ ///
+ /// # Returns
+ /// True if the set has changed. False if the value is already in the set.
+ fn add(&mut self, value: E) -> Result<bool, GenerationExhausted>
+ where
+ E: Hash + Eq,
+ S: BuildHasher,
+ {
+ let generation = match get_entry(self.base(), self.delta(), &value) {
+ Some(entry) => {
+ if entry.removed {
+ entry.generation.checked_add(1).ok_or(GenerationExhausted)?
+ } else {
+ return Ok(false);
+ }
+ }
+ None => 1,
+ };
+ let _ = self.delta_mut().elements.insert(
+ value,
+ ElementState {
+ generation,
+ removed: false,
+ },
+ );
+ Ok(true)
+ }
+
+ /// Removes an element from the set.
+ ///
+ /// Note that the element will be kept as metadata, but will be marked as removed such that it
+ /// won't show up in [`SetRead::entries`].
+ ///
+ /// # Returns
+ /// True if the value is removed. False if the value was not in the set to begin with.
+ fn remove(&mut self, value: E) -> bool
+ where
+ E: Hash + Eq,
+ S: BuildHasher,
+ {
+ if let Some(v) = get_entry(self.base(), self.delta(), &value) {
+ if !v.removed {
+ let _ = self.delta_mut().elements.insert(
+ value,
+ ElementState {
+ generation: v.generation,
+ removed: true,
+ },
+ );
+ return true;
+ }
+ }
+ false
+ }
+
+ /// Adds the given `value` to the set, and bumps the generation number by `increment`.
+ ///
+ /// Intended for testing only, so tests can artificially create a set with a large generation
+ /// number, and test the behavior when the generation number is exhausted. Unlike
+ /// [`add`][SetWrite::add], this will increment the generation number even if `value` is already
+ /// in the set.
+ #[cfg(any(test, feature = "testing"))]
+ fn bump_generation_number_for_testing(
+ &mut self,
+ value: E,
+ increment: u32,
+ ) -> Result<(), GenerationExhausted>
+ where
+ E: Hash + Eq,
+ S: BuildHasher,
+ {
+ let generation = match get_entry(self.base(), self.delta(), &value) {
+ Some(entry) => entry
+ .generation
+ .checked_add(increment)
+ .ok_or(GenerationExhausted)?,
+ None => increment,
+ };
+ let _ = self.delta_mut().elements.insert(
+ value,
+ ElementState {
+ generation,
+ removed: false,
+ },
+ );
+ Ok(())
+ }
+
+ /// Apply the changes in the plain representation into this CRDT. This assumes that all changes
+ /// in the plain representation were made by the calling node, updating the associated CRDT
+ /// metadata in the process.
+ ///
+ /// After applying the changes, [`to_plain`][SetRead::to_plain] should return the same
+ /// value as `plain`.
+ ///
+ /// # Returns
+ ///
+ /// `true` if applying the change results in an update in the CRDT state, or false if Returns
+ /// true if applying the change results in an update in the CRDT state, or false if `plain` is
+ /// the same as [`to_plain`][SetRead::to_plain] to begin with. This can be used to avoid
+ /// sending unnecessary update messages over the network, or writing changes to disk
+ /// unnecessarily.
+ fn apply_changes(&mut self, plain: HashSet<E, S>) -> Result<bool, GenerationExhausted>
+ where
+ E: Hash + Eq + Clone,
+ S: BuildHasher + Default + 'static,
+ {
+ let mut changed = false;
+ let to_be_removed: Vec<_> = self
+ .entries()
+ .into_iter()
+ .filter(|e| !plain.contains(e))
+ .cloned()
+ .collect();
+ for e in to_be_removed {
+ let _ = self.remove(e);
+ changed = true;
+ }
+ for e in plain {
+ changed = self.add(e)? || changed;
+ }
+ Ok(changed)
+ }
+}
+
+impl<E, S> CrdtState for Set<E, S>
+where
+ E: Hash + Eq + Clone,
+ S: BuildHasher + Default,
+{
+ fn merge(a: &Self, b: &Self) -> Self {
+ Self {
+ elements: {
+ zip_hash_map(&a.elements, &b.elements)
+ .map(|(key, item)| match item {
+ ZipItem::Left(v) | ZipItem::Right(v) => (key.clone(), *v),
+ ZipItem::Both(l, r) => (key.clone(), *l.max(r)),
+ })
+ .collect()
+ },
+ }
+ }
+}
+
+impl<E, S> ContentEq for Set<E, S>
+where
+ E: Hash + Eq,
+ S: BuildHasher,
+{
+ fn content_eq(&self, other: &Self) -> bool {
+ self == other
+ }
+}
+
+impl<E, S, T> SetRead<E, S> for T where T: AsDeltaRef<Set<E, S>> {}
+impl<E, S, T> SetWrite<E, S> for T where T: AsDeltaMut<Set<E, S>> {}
+
+/// Checker to help implement invariant tests over arbitrary operations on a set.
+///
+/// Requires the feature _`checker`_.
+#[cfg(any(test, feature = "checker"))]
+pub mod checker {
+ use std::{collections::HashSet, fmt::Debug, hash::Hash};
+
+ use arbitrary::Arbitrary;
+
+ use crate::{
+ checker::{
+ simulation::{Operation, SimulationContext},
+ test_fakes::TriState,
+ utils::DeterministicHasher,
+ },
+ delta::DeltaMut,
+ set::SetRead,
+ };
+
+ use super::{Set, SetWrite};
+
+ /// Mutation operations on a [`SetWrite`].
+ ///
+ /// Can be used with [`Arbitrary`] to generate arbitrary operations to be applied on the set.
+ #[derive(Debug, Clone, Arbitrary)]
+ pub enum SetOp<E: Eq + Hash> {
+ /// Represents the [`SetWrite::add`] operation.
+ Add {
+ /// The value passed to [`SetWrite::add`].
+ value: E,
+ },
+ /// Represents the [`SetWrite::remove`] operation.
+ Remove {
+ /// The value passed to [`SetWrite::remove`].
+ value: E,
+ },
+ /// Represents the [`SetWrite::apply_changes`] operation.
+ ApplyChanges {
+ /// The plain representation of the set passed to [`SetWrite::apply_changes`].
+ value_set: HashSet<E, DeterministicHasher>,
+ },
+ }
+
+ impl<V> Operation<Set<V, DeterministicHasher>, TriState> for SetOp<V>
+ where
+ V: Clone + Eq + Hash + Debug,
+ {
+ fn apply(
+ self,
+ mut state: DeltaMut<Set<V, DeterministicHasher>>,
+ _: &SimulationContext<TriState>,
+ ) {
+ match self {
+ SetOp::Add { value } => {
+ if state.add(value.clone()).is_ok() {
+ assert!(state.contains(&value));
+ }
+ }
+ SetOp::Remove { value } => {
+ let _ = state.remove(value.clone());
+ assert!(!state.contains(&value));
+ }
+ SetOp::ApplyChanges { value_set } => {
+ if state.apply_changes(value_set.clone()).is_ok() {
+ assert_eq!(HashSet::from_iter(&value_set), state.entries())
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(feature = "proto")]
+mod proto {
+ use submerge_internal_proto::{FromProto, FromProtoError, NodeMapping, ToProto};
+
+ use super::{ElementState, Set};
+
+ impl FromProto for Set<Vec<u8>> {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeSet;
+
+ fn from_proto(proto: &Self::Proto, _node_ids: &[String]) -> Result<Self, FromProtoError> {
+ Ok(Self {
+ elements: proto
+ .elements
+ .iter()
+ .map(|elem| {
+ (
+ elem.value.clone(),
+ ElementState {
+ generation: elem.generation,
+ removed: elem.removed,
+ },
+ )
+ })
+ .collect(),
+ })
+ }
+ }
+
+ impl ToProto for Set<Vec<u8>> {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeSet;
+
+ fn to_proto(&self, _node_ids: &mut NodeMapping<String>) -> Self::Proto {
+ submerge_internal_proto::protos::submerge::SubmergeSet {
+ elements: self
+ .elements
+ .iter()
+ .map(|(value, set_element)| {
+ submerge_internal_proto::protos::submerge::submerge_set::SetElement {
+ value: value.clone(),
+ generation: set_element.generation,
+ removed: set_element.removed,
+ ..Default::default()
+ }
+ })
+ .collect(),
+ ..Default::default()
+ }
+ }
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn set_roundtrip(set: Set<Vec<u8>>) {
+ let mut node_ids = NodeMapping::default();
+ assert_eq!(
+ Set::from_proto(&set.to_proto(&mut node_ids), &node_ids.into_vec()).unwrap(),
+ set
+ );
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::HashSet;
+
+ use crate::{
+ checker::{test_fakes::TriState, utils::DeterministicHasher},
+ set::GenerationExhausted,
+ CrdtState,
+ };
+
+ use super::{Set, SetRead, SetWrite};
+
+ type TestSet = Set<u8, DeterministicHasher>;
+
+ #[test]
+ fn test_add() {
+ let mut set: TestSet = Set::default();
+ let _ = set.add(1).unwrap();
+ let _ = set.add(2).unwrap();
+ let _ = set.add(3).unwrap();
+
+ assert_eq!(HashSet::from_iter(&[1, 2, 3]), set.entries());
+ }
+
+ #[test]
+ fn test_remove() {
+ let mut set: TestSet = Set::default();
+ let _ = set.add(1).unwrap();
+ let _ = set.add(2).unwrap();
+ let _ = set.remove(3);
+ let _ = set.remove(1);
+
+ assert_eq!(HashSet::from_iter(&[2]), set.entries());
+ }
+
+ #[test]
+ fn test_generation_exhausted() {
+ let mut set = Set::<TriState>::default();
+ set.bump_generation_number_for_testing(TriState::A, u32::MAX)
+ .unwrap();
+ let _ = set.add(TriState::A).unwrap(); // This is a no-op since 1 is already in the set
+ let _ = set.add(TriState::B).unwrap();
+ let _ = set.remove(TriState::A);
+ assert_eq!(GenerationExhausted, set.add(TriState::A).unwrap_err());
+ let _ = set.add(TriState::C).unwrap(); // Adding other values should still work
+
+ assert_eq!(
+ HashSet::from_iter(&[TriState::B, TriState::C]),
+ set.entries()
+ );
+ }
+
+ #[test]
+ fn test_merge() {
+ let mut set1: TestSet = Set::default();
+ let _ = set1.add(1).unwrap();
+ let _ = set1.add(2).unwrap();
+ let _ = set1.add(3).unwrap();
+
+ let mut set2: TestSet = Set::default();
+ let _ = set2.add(2).unwrap();
+ let _ = set2.add(3).unwrap();
+ let _ = set2.add(4).unwrap();
+
+ let merged = CrdtState::merge(&set1, &set2);
+
+ assert_eq!(HashSet::from_iter(&[1, 2, 3, 4]), merged.entries());
+ }
+
+ #[test]
+ fn test_remove_remote() {
+ let mut set1: TestSet = Set::default();
+ let _ = set1.add(1).unwrap();
+ let _ = set1.add(2).unwrap();
+ let _ = set1.add(3).unwrap();
+
+ let set2: TestSet = Set::default();
+
+ let mut set2 = CrdtState::merge(&set1, &set2);
+ let _ = set2.remove(2);
+
+ assert_eq!(HashSet::from_iter(&[1, 3]), set2.entries());
+ }
+}
diff --git a/betosync/submerge/crdt/src/typed_crdt.rs b/betosync/submerge/crdt/src/typed_crdt.rs
new file mode 100644
index 0000000..83163c9
--- /dev/null
+++ b/betosync/submerge/crdt/src/typed_crdt.rs
@@ -0,0 +1,723 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A specific typed CRDT used to implement the data model defined by this crate.
+//!
+//! See the docs for [`TypedCrdt`] for details.
+
+use std::{
+ collections::{BTreeMap, HashSet},
+ fmt::Debug,
+ hash::{BuildHasher, Hash, RandomState},
+};
+
+use crate::{
+ delta::{DeltaMut, DeltaRef},
+ lww_crdt_container::TryAsChild,
+ lww_map::{LwwMapRead, LwwMapWrite},
+ register::{RegisterRead, RegisterWrite},
+ set::{SetRead, SetWrite},
+ vector_data::{VectorDataRead, VectorDataWrite},
+ ApplyChanges, ContentEq, CrdtState, HasPlainRepresentation, ToPlain, UpdateContext,
+};
+use arbitrary::{Arbitrary, Unstructured};
+use derive_where::derive_where;
+use distributed_time::{hybrid_logical_clock::HybridLogicalTimestamp, vector_clock::VectorClock};
+use serde::{Deserialize, Serialize};
+
+use crate::{
+ lww_crdt_container::{CrdtUnion, IncompatibleType, LwwCrdtContainerApplyError},
+ lww_map::LwwMap,
+ register::{Register, RegisterError},
+ set::{GenerationExhausted, Set},
+ vector_data::{VectorData, VectorDataError},
+};
+
+/// An enum of the CRDT types supported by this crate.
+///
+/// In this data model, the main structural component is the `Map`, which has a string key and a
+/// recursive `TypedCrdt` as the value, creating a tree-like structure. The values contained in this
+/// data model must be of type `V`; if dynamic typing is desired, users of this type must define
+/// their own enum or `dyn` type to hold the type information and switch between different
+/// underlying types.
+#[derive(Debug, Serialize, Deserialize)]
+#[derive_where(PartialEq, Eq; V, N)]
+#[derive_where(Clone; S)]
+#[serde(bound(
+ serialize = "V: Serialize, N: Serialize",
+ deserialize = "V: Deserialize<'de>, N: Deserialize<'de>"
+))]
+pub enum TypedCrdt<V, N, S = RandomState>
+where
+ V: Eq + Hash + Clone + 'static,
+ N: Ord + Clone + 'static,
+ S: BuildHasher + Default + 'static,
+{
+ /// Map is the main structural component of this data model. It recursively contains `TypedCrdt`
+ /// as values, creating a tree-like structure. See [`LwwMap`][crate::lww_map::LwwMap].
+ Map(LwwMap<String, TypedCrdt<V, N, S>, HybridLogicalTimestamp<N>, N>),
+ /// Register contains a single atomically updated value. See
+ /// [`Register`][crate::register::Register].
+ Register(Register<V, VectorClock<N>>),
+ /// A no-duplicate, unordered set of values. See [`Set`][crate::set::Set].
+ Set(Set<V, S>),
+ /// A vector of data where each node can only modify its own area. See
+ /// [`VectorData`][crate::vector_data::VectorData].
+ VectorData(VectorData<N, V>),
+}
+
+// Manual implementation of `Arbitrary` so it doesn't bound on `S: Arbitrary`.
+impl<'a, V, N, S> Arbitrary<'a> for TypedCrdt<V, N, S>
+where
+ V: Eq + Hash + Clone + Arbitrary<'a> + 'static,
+ N: Ord + Clone + Arbitrary<'a> + 'static,
+ S: BuildHasher + Default + 'static,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ match u.int_in_range(0..=3)? {
+ 0 => Ok(Self::Map(u.arbitrary()?)),
+ 1 => Ok(Self::Register(u.arbitrary()?)),
+ 2 => Ok(Self::Set(u.arbitrary()?)),
+ 3 => Ok(Self::VectorData(u.arbitrary()?)),
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// An enum type that has the same variants as [`TypedCrdt`] but contains [`DeltaRef`] as the values
+/// contained.
+#[derive(Debug)]
+pub enum TypedCrdtRef<'d, V, N, S>
+where
+ V: Eq + Hash + Clone + 'static,
+ N: Ord + Clone + 'static,
+ S: BuildHasher + Default + 'static,
+{
+ /// Map is the main structural component of this data model. It recursively contains `TypedCrdt`
+ /// as values, creating a tree-like structure. See [`LwwMap`].
+ #[allow(clippy::type_complexity)]
+ Map(DeltaRef<'d, LwwMap<String, TypedCrdt<V, N, S>, HybridLogicalTimestamp<N>, N>>),
+ /// Register contains a single atomically updated value. See [`Register`].
+ Register(DeltaRef<'d, Register<V, VectorClock<N>>>),
+ /// A no-duplicate, unordered set of values. See [`Set`].
+ Set(DeltaRef<'d, Set<V, S>>),
+ /// A vector of data where each node can only modify its own area. See [`VectorData`].
+ VectorData(DeltaRef<'d, VectorData<N, V>>),
+}
+
+/// An enum type that has the same variants as [`TypedCrdt`] but contains [`DeltaMut`] as the values
+/// contained.
+#[derive(Debug)]
+pub enum TypedCrdtMut<'d, V, N, S = RandomState>
+where
+ V: Eq + Hash + Clone + 'static,
+ N: Ord + Clone + 'static,
+ S: BuildHasher + Default + 'static,
+{
+ /// Map is the main structural component of this data model. It recursively contains `TypedCrdt`
+ /// as values, creating a tree-like structure. See [`LwwMap`].
+ #[allow(clippy::type_complexity)]
+ Map(DeltaMut<'d, LwwMap<String, TypedCrdt<V, N, S>, HybridLogicalTimestamp<N>, N>>),
+ /// Register contains a single atomically updated value. See [`Register`].
+ Register(DeltaMut<'d, Register<V, VectorClock<N>>>),
+ /// A no-duplicate, unordered set of values. See [`Set`].
+ Set(DeltaMut<'d, Set<V, S>>),
+ /// A vector of data where each node can only modify its own area. See [`VectorData`].
+ VectorData(DeltaMut<'d, VectorData<N, V>>),
+}
+
+impl<N, V, S> ToPlain for TypedCrdtRef<'_, V, N, S>
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default + Clone,
+{
+ type Plain = PlainTypes<N, V, S>;
+
+ fn to_plain(&self) -> PlainTypes<N, V, S> {
+ match self {
+ TypedCrdtRef::Map(m) => PlainTypes::Map(m.to_plain()),
+ TypedCrdtRef::Register(v) => PlainTypes::Register(v.to_plain()),
+ TypedCrdtRef::Set(v) => PlainTypes::Set(v.to_plain()),
+ TypedCrdtRef::VectorData(v) => PlainTypes::VectorData(v.to_plain()),
+ }
+ }
+}
+
+impl<N, V, S> ApplyChanges<N> for TypedCrdtMut<'_, V, N, S>
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default + Clone,
+{
+ type Plain = PlainTypes<N, V, S>;
+ type Error = TypedError;
+
+ fn apply_changes(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ plain: Self::Plain,
+ ) -> Result<bool, Self::Error> {
+ Ok(match (self, plain) {
+ (TypedCrdtMut::Map(map_crdt), PlainTypes::Map(plain_map)) => {
+ map_crdt.apply_changes(ctx, plain_map)?
+ }
+ (TypedCrdtMut::Register(reg_crdt), PlainTypes::Register(plain_reg)) => {
+ reg_crdt.apply_changes(ctx, plain_reg)?
+ }
+ (TypedCrdtMut::Set(set_crdt), PlainTypes::Set(plain_set)) => {
+ set_crdt.apply_changes(plain_set)?
+ }
+ (TypedCrdtMut::VectorData(vd_crdt), PlainTypes::VectorData(plain_vd)) => {
+ vd_crdt.apply_changes(ctx, plain_vd)?
+ }
+ _ => return Err(TypedError::IncompatibleType),
+ })
+ }
+}
+
+macro_rules! impl_try_as_child {
+ (impl<V, N, S> TryAsChild<$ty:ty, N> for TypedCrdt<V, N, S>::$variant:ident) => {
+ impl<V, N, S> TryAsChild<$ty, N> for TypedCrdt<V, N, S>
+ where
+ V: Eq + Hash + Clone + 'static,
+ N: Ord + Clone + 'static,
+ S: BuildHasher + Default + Clone + 'static,
+ {
+ fn try_as_child_ref(
+ this: Self::DeltaRef<'_>,
+ ) -> Option<$crate::delta::DeltaRef<'_, $ty>> {
+ match this {
+ TypedCrdtRef::$variant(v) => Some(v),
+ _ => None,
+ }
+ }
+
+ fn try_as_child_mut(
+ this: Self::DeltaMut<'_>,
+ ) -> Option<$crate::delta::DeltaMut<'_, $ty>> {
+ match this {
+ TypedCrdtMut::$variant(v) => Some(v),
+ _ => None,
+ }
+ }
+ }
+ };
+}
+
+impl_try_as_child! {
+ impl<V, N, S> TryAsChild<
+ LwwMap<String, TypedCrdt<V, N, S>, HybridLogicalTimestamp<N>, N>, N
+ > for TypedCrdt<V, N, S>::Map
+}
+impl_try_as_child! {
+ impl<V, N, S> TryAsChild<Register<V, VectorClock<N>>, N> for TypedCrdt<V, N, S>::Register
+}
+impl_try_as_child! { impl<V, N, S> TryAsChild<Set<V, S>, N> for TypedCrdt<V, N, S>::Set }
+impl_try_as_child! { impl<V, N, S> TryAsChild<VectorData<N, V>, N> for TypedCrdt<V, N, S>::VectorData }
+
+impl<V, N, S> From<LwwMap<String, TypedCrdt<V, N, S>, HybridLogicalTimestamp<N>, N>>
+ for TypedCrdt<V, N, S>
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default,
+{
+ fn from(value: LwwMap<String, TypedCrdt<V, N, S>, HybridLogicalTimestamp<N>, N>) -> Self {
+ Self::Map(value)
+ }
+}
+
+impl<V, N, S> From<Register<V, VectorClock<N>>> for TypedCrdt<V, N, S>
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default,
+{
+ fn from(value: Register<V, VectorClock<N>>) -> Self {
+ Self::Register(value)
+ }
+}
+
+impl<V, N, S> From<Set<V, S>> for TypedCrdt<V, N, S>
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default,
+{
+ fn from(value: Set<V, S>) -> Self {
+ Self::Set(value)
+ }
+}
+
+impl<V, N, S> From<VectorData<N, V>> for TypedCrdt<V, N, S>
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default,
+{
+ fn from(value: VectorData<N, V>) -> Self {
+ Self::VectorData(value)
+ }
+}
+
+impl<V, N, S> CrdtUnion<N> for TypedCrdt<V, N, S>
+where
+ V: Eq + Hash + Clone + 'static,
+ N: Ord + Clone + 'static,
+ S: BuildHasher + Default + Clone + 'static,
+{
+ type DeltaRef<'d> = TypedCrdtRef<'d, V, N, S>;
+ type DeltaMut<'d> = TypedCrdtMut<'d, V, N, S>;
+
+ fn try_merge(a: &Self, b: &Self) -> Result<Self, IncompatibleType> {
+ match (a, b) {
+ (TypedCrdt::Map(map_a), TypedCrdt::Map(map_b)) => {
+ Ok(TypedCrdt::Map(CrdtState::merge(map_a, map_b)))
+ }
+ (TypedCrdt::Register(reg_a), TypedCrdt::Register(reg_b)) => {
+ Ok(TypedCrdt::Register(CrdtState::merge(reg_a, reg_b)))
+ }
+ (TypedCrdt::Set(set_a), TypedCrdt::Set(set_b)) => {
+ Ok(TypedCrdt::Set(CrdtState::merge(set_a, set_b)))
+ }
+ (TypedCrdt::VectorData(vec_a), TypedCrdt::VectorData(vec_b)) => {
+ Ok(TypedCrdt::VectorData(CrdtState::merge(vec_a, vec_b)))
+ }
+ _ => Err(IncompatibleType),
+ }
+ }
+
+ fn create_ref<'d>(
+ base: Option<&'d Self>,
+ delta: Option<&'d Self>,
+ ) -> Option<Self::DeltaRef<'d>> {
+ match (base, delta) {
+ (None, None) => None,
+ (Some(Self::Map(_)) | None, Some(Self::Map(_)) | None) => {
+ let base = match base {
+ Some(Self::Map(base)) => Some(base),
+ _ => None,
+ };
+ let delta = match delta {
+ Some(Self::Map(delta)) => Some(delta),
+ _ => None,
+ };
+ Some(TypedCrdtRef::Map(DeltaRef { base, delta }))
+ }
+ (Some(Self::Set(_)) | None, Some(Self::Set(_)) | None) => {
+ let base = match base {
+ Some(Self::Set(base)) => Some(base),
+ _ => None,
+ };
+ let delta = match delta {
+ Some(Self::Set(delta)) => Some(delta),
+ _ => None,
+ };
+ Some(TypedCrdtRef::Set(DeltaRef { base, delta }))
+ }
+ (Some(Self::Register(_)) | None, Some(Self::Register(_)) | None) => {
+ let base = match base {
+ Some(Self::Register(base)) => Some(base),
+ _ => None,
+ };
+ let delta = match delta {
+ Some(Self::Register(delta)) => Some(delta),
+ _ => None,
+ };
+ Some(TypedCrdtRef::Register(DeltaRef { base, delta }))
+ }
+ (Some(Self::VectorData(_)) | None, Some(Self::VectorData(_)) | None) => {
+ let base = match base {
+ Some(Self::VectorData(base)) => Some(base),
+ _ => None,
+ };
+ let delta = match delta {
+ Some(Self::VectorData(delta)) => Some(delta),
+ _ => None,
+ };
+ Some(TypedCrdtRef::VectorData(DeltaRef { base, delta }))
+ }
+ _ => {
+ debug_assert!(false, "Incompatible type!");
+ None
+ }
+ }
+ }
+
+ fn create_mut<'d>(base: Option<&'d Self>, delta: &'d mut Self) -> Option<Self::DeltaMut<'d>> {
+ match (base, delta) {
+ (Some(Self::Map(_)) | None, Self::Map(delta)) => {
+ let base = match base {
+ Some(Self::Map(value)) => Some(value),
+ _ => None,
+ };
+ Some(TypedCrdtMut::Map(DeltaMut { base, delta }))
+ }
+ (Some(Self::Register(_)) | None, Self::Register(delta)) => {
+ let base = match base {
+ Some(Self::Register(value)) => Some(value),
+ _ => None,
+ };
+ Some(TypedCrdtMut::Register(DeltaMut { base, delta }))
+ }
+ (Some(Self::Set(_)) | None, Self::Set(delta)) => {
+ let base = match base {
+ Some(Self::Set(value)) => Some(value),
+ _ => None,
+ };
+ Some(TypedCrdtMut::Set(DeltaMut { base, delta }))
+ }
+ (Some(Self::VectorData(_)) | None, Self::VectorData(delta)) => {
+ let base = match base {
+ Some(Self::VectorData(value)) => Some(value),
+ _ => None,
+ };
+ Some(TypedCrdtMut::VectorData(DeltaMut { base, delta }))
+ }
+ _ => {
+ debug_assert!(false, "Incompatible type!");
+ None
+ }
+ }
+ }
+
+ fn create_matching_default(example: &Self) -> Self {
+ match example {
+ TypedCrdt::Map(_) => TypedCrdt::Map(Default::default()),
+ TypedCrdt::Register(_) => TypedCrdt::Register(Default::default()),
+ TypedCrdt::Set(_) => TypedCrdt::Set(Default::default()),
+ TypedCrdt::VectorData(_) => TypedCrdt::VectorData(Default::default()),
+ }
+ }
+
+ fn calculate_delta(&self, base_version: &VectorClock<N>) -> Self {
+ match self {
+ TypedCrdt::Map(map) => TypedCrdt::Map(map.calculate_delta(base_version)),
+ TypedCrdt::Register(_) => self.clone(),
+ TypedCrdt::Set(_) => self.clone(),
+ TypedCrdt::VectorData(_) => self.clone(),
+ }
+ }
+}
+
+impl<V, N, S> ContentEq for TypedCrdt<V, N, S>
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default + Clone,
+{
+ fn content_eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (Self::Map(a), Self::Map(b)) => a.content_eq(b),
+ (Self::Register(a), Self::Register(b)) => a.content_eq(b),
+ (Self::Set(a), Self::Set(b)) => a.content_eq(b),
+ (Self::VectorData(a), Self::VectorData(b)) => a.content_eq(b),
+ _ => false,
+ }
+ }
+}
+
+impl<V, N, S> HasPlainRepresentation<N> for TypedCrdt<V, N, S>
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default + Clone,
+{
+ type Plain = PlainTypes<N, V, S>;
+ type Error = TypedError;
+
+ fn init_from_plain(
+ ctx: &impl UpdateContext<N>,
+ plain: Self::Plain,
+ ) -> Result<Self, Self::Error> {
+ Ok(match plain {
+ PlainTypes::Map(v) => {
+ let mut map = LwwMap::default();
+ let _ = map.apply_changes(ctx, v)?;
+ Self::Map(map)
+ }
+ PlainTypes::Register(v) => {
+ let mut crdt = Register::default();
+ let _ = crdt.apply_changes(ctx, v)?;
+ Self::Register(crdt)
+ }
+ PlainTypes::Set(v) => {
+ let mut crdt = Set::default();
+ let _ = crdt.apply_changes(v)?;
+ Self::Set(crdt)
+ }
+ PlainTypes::VectorData(v) => {
+ let mut crdt = VectorData::default();
+ let _ = crdt.apply_changes(ctx, v)?;
+ Self::VectorData(crdt)
+ }
+ })
+ }
+}
+
+/// Error returned by [`TypedCrdtMut::apply_changes`]. In addition to the general `IncompatibleType`
+/// error, this encodes [`HasPlainRepresentation::Error`] for each child enum variant.
+#[derive(Debug, PartialEq, Eq)]
+pub enum TypedError {
+ /// Error while merging a child map. This is boxed to prevent infinite-sized type.
+ Map(Box<LwwCrdtContainerApplyError<TypedError>>),
+ /// Error while merging a child register.
+ Register(RegisterError),
+ /// Error while merging a child set.
+ Set(GenerationExhausted),
+ /// Error while merging a child vector data.
+ VectorData(VectorDataError),
+ /// Data cannot be merged because the children values are not of the same type.
+ IncompatibleType,
+}
+
+impl From<LwwCrdtContainerApplyError<TypedError>> for TypedError {
+ fn from(value: LwwCrdtContainerApplyError<TypedError>) -> Self {
+ Self::Map(Box::new(value))
+ }
+}
+
+impl From<RegisterError> for TypedError {
+ fn from(value: RegisterError) -> Self {
+ Self::Register(value)
+ }
+}
+
+impl From<GenerationExhausted> for TypedError {
+ fn from(value: GenerationExhausted) -> Self {
+ Self::Set(value)
+ }
+}
+
+impl From<VectorDataError> for TypedError {
+ fn from(value: VectorDataError) -> Self {
+ Self::VectorData(value)
+ }
+}
+
+/// The plain types corresponding to a [`TypedCrdt`]. This encodes [`HasPlainRepresentation::Plain`]
+/// for each child enum variant.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive_where(PartialEq, Eq; N, V)]
+
+pub enum PlainTypes<N: Ord, V: Eq + Hash, S: BuildHasher + Default> {
+ /// The plain type for `Map`. This should be the same as the return type of
+ /// [`LwwMapRead::to_plain`].
+ Map(BTreeMap<String, PlainTypes<N, V, S>>),
+ /// The plain type for `Register`. This should be the same as the return type of
+ /// [`RegisterRead::to_plain`].
+ Register(Vec<V>),
+ /// The plain type for `Set`. This should be the same as the return type of
+ /// [`SetRead::to_plain`].
+ Set(HashSet<V, S>),
+ /// The plain type for `VectorData`. This should be the same as the return type of
+ /// [`VectorDataRead::to_plain`].
+ VectorData(BTreeMap<N, V>),
+}
+
+// Manually implement `Arbitrary` so it doesn't bound on `S: Arbitrary`.
+impl<'a, N, V, S> Arbitrary<'a> for PlainTypes<N, V, S>
+where
+ N: Ord + Arbitrary<'a>,
+ V: Eq + Hash + Arbitrary<'a>,
+ S: BuildHasher + Default,
+{
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(match u.int_in_range(0..=3)? {
+ 0 => Self::Map(u.arbitrary()?),
+ 1 => Self::Register(u.arbitrary()?),
+ 2 => Self::Set(u.arbitrary()?),
+ 3 => Self::VectorData(u.arbitrary()?),
+ _ => unreachable!(),
+ })
+ }
+}
+
+/// Checker to validate the convergence of [`TypedCrdt`].
+///
+/// This is used in fuzz tests to make sure that [`TypedCrdt`] converges if [`TypedCrdt::try_merge`]
+/// returns success.
+///
+/// Requires the feature _`checker`_.
+#[cfg(any(test, feature = "checker"))]
+pub mod checker {
+ use arbitrary::Arbitrary;
+
+ use crate::{
+ checker::{test_fakes::TriState, utils::DeterministicHasher},
+ lww_crdt_container::CrdtUnion,
+ lww_map::LwwMap,
+ register::Register,
+ set::Set,
+ vector_data::VectorData,
+ CrdtState,
+ };
+
+ use super::TypedCrdt;
+
+ /// A wrapper around [`TypedCrdt`] that makes it implement [`CrdtState`] (instead of just
+ /// [`CrdtUnion`]).
+ ///
+ /// If merging results in an error, the wrapped value becomes `None`, and merging `None` with
+ /// anything results in `None`.
+ #[derive(Debug, Clone, PartialEq, Eq, Arbitrary)]
+ pub struct CrdtUnionMergeResult(Option<TypedCrdt<TriState, u8, DeterministicHasher>>);
+
+ impl CrdtState for CrdtUnionMergeResult {
+ fn merge(a: &Self, b: &Self) -> Self {
+ match (&a.0, &b.0) {
+ (Some(v_a), Some(v_b)) => match CrdtUnion::try_merge(v_a, v_b) {
+ Ok(v) => CrdtUnionMergeResult(Some(v)),
+ Err(_) => CrdtUnionMergeResult(None),
+ },
+ _ => CrdtUnionMergeResult(None),
+ }
+ }
+
+ fn is_valid_collection<'a>(collection: impl IntoIterator<Item = &'a Self>) -> bool
+ where
+ Self: 'a,
+ {
+ // Delegate to child types to check for convergence requirements
+ let mut maps = Vec::new();
+ let mut vector_datas = Vec::new();
+ let mut registers = Vec::new();
+ let mut sets = Vec::new();
+ for item in collection {
+ match &item.0 {
+ Some(TypedCrdt::Map(map)) => maps.push(map),
+ Some(TypedCrdt::VectorData(v)) => vector_datas.push(v),
+ Some(TypedCrdt::Register(reg)) => registers.push(reg),
+ Some(TypedCrdt::Set(set)) => sets.push(set),
+ None => {}
+ }
+ }
+ LwwMap::is_valid_collection(maps)
+ && VectorData::is_valid_collection(vector_datas)
+ && Register::is_valid_collection(registers)
+ && Set::is_valid_collection(sets)
+ }
+ }
+}
+
+#[cfg(feature = "proto")]
+pub(crate) mod proto {
+ use submerge_internal_proto::{
+ protos::submerge as submerge_proto, FromProto, FromProtoError, NodeMapping, ToProto,
+ };
+ use thiserror::Error;
+
+ use crate::{lww_map::LwwMap, register::Register, set::Set, vector_data::VectorData};
+
+ use super::TypedCrdt;
+
+ #[derive(Debug, Error)]
+ pub(crate) enum TypedCrdtFromProtoError {
+ #[error(transparent)]
+ FromProtoError(#[from] FromProtoError),
+ #[error("Unrecognized type")]
+ UnrecognizedType,
+ }
+
+ impl TypedCrdt<Vec<u8>, String> {
+ /// Construct a typed CRDT from the given `proto`.
+ pub(crate) fn from_proto(
+ proto: &submerge_proto::submerge_container::Value,
+ node_ids: &[String],
+ ) -> Result<Self, TypedCrdtFromProtoError> {
+ Ok(match proto {
+ submerge_proto::submerge_container::Value::Map(submerge_map) => {
+ TypedCrdt::Map(LwwMap::from_proto(submerge_map, node_ids)?)
+ }
+ submerge_proto::submerge_container::Value::Set(submerge_set) => {
+ TypedCrdt::Set(Set::from_proto(submerge_set, node_ids)?)
+ }
+ submerge_proto::submerge_container::Value::Register(submerge_register) => {
+ TypedCrdt::Register(Register::from_proto(submerge_register, node_ids)?)
+ }
+ submerge_proto::submerge_container::Value::VectorData(submerge_vector_data) => {
+ TypedCrdt::VectorData(VectorData::from_proto(submerge_vector_data, node_ids)?)
+ }
+ _ => Err(TypedCrdtFromProtoError::UnrecognizedType)?,
+ })
+ }
+ }
+
+ impl ToProto for TypedCrdt<Vec<u8>, String> {
+ type Proto = submerge_proto::submerge_container::Value;
+
+ fn to_proto(&self, node_ids: &mut NodeMapping<String>) -> Self::Proto {
+ match self {
+ TypedCrdt::Map(lww_map) => {
+ submerge_proto::submerge_container::Value::Map(lww_map.to_proto(node_ids))
+ }
+ TypedCrdt::Register(register) => {
+ submerge_proto::submerge_container::Value::Register(register.to_proto(node_ids))
+ }
+ TypedCrdt::Set(set) => {
+ submerge_proto::submerge_container::Value::Set(set.to_proto(node_ids))
+ }
+ TypedCrdt::VectorData(vector_data) => {
+ submerge_proto::submerge_container::Value::VectorData(
+ vector_data.to_proto(node_ids),
+ )
+ }
+ }
+ }
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn typed_crdt_roundtrip(typed_crdt: TypedCrdt<Vec<u8>, String>) {
+ let mut node_ids = NodeMapping::default();
+ assert_eq!(
+ TypedCrdt::from_proto(&typed_crdt.to_proto(&mut node_ids), &node_ids.into_vec())
+ .unwrap(),
+ typed_crdt
+ );
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use serde_json::json;
+
+ use crate::register::RegisterRead;
+
+ use super::TypedCrdt;
+
+ #[test]
+ fn json_decode() {
+ let json = json!({
+ "Register": [
+ {
+ "value": "What is your #0 favorite emoji?",
+ "timestamp": {
+ "distributed_clock": {},
+ "hybrid_logical_timestamp": {
+ "logical_time": 1,
+ "causality": 0
+ }
+ }
+ }
+ ],
+ });
+ let typed_crdt = serde_json::from_value::<TypedCrdt<String, u8>>(json).unwrap();
+ let TypedCrdt::Register(register) = typed_crdt else {
+ unreachable!()
+ };
+ assert_eq!(register.get_all(), vec!["What is your #0 favorite emoji?"]);
+ }
+}
diff --git a/betosync/submerge/crdt/src/utils.rs b/betosync/submerge/crdt/src/utils.rs
new file mode 100644
index 0000000..50875f8
--- /dev/null
+++ b/betosync/submerge/crdt/src/utils.rs
@@ -0,0 +1,184 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::{
+ borrow::Borrow,
+ collections::{BTreeMap, BTreeSet, HashMap, HashSet},
+ hash::{BuildHasher, Hash},
+};
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum ZipItem<L, R> {
+ Left(L),
+ Right(R),
+ Both(L, R),
+}
+
+pub fn zip_hash_map<'m, K, K1, K2, V1, V2, S>(
+ map1: &'m HashMap<K1, V1, S>,
+ map2: &'m HashMap<K2, V2, S>,
+) -> impl Iterator<Item = (&'m K, ZipItem<&'m V1, &'m V2>)>
+where
+ K1: Borrow<K> + Hash + Eq,
+ K2: Borrow<K> + Hash + Eq,
+ K: Eq + Hash + 'm,
+ S: BuildHasher + Default,
+{
+ let keys: HashSet<&K, S> = map1
+ .keys()
+ .map(|k| k.borrow())
+ .chain(map2.keys().map(|k| k.borrow()))
+ .collect();
+ keys.into_iter()
+ .map(|key| match (map1.get(key), map2.get(key)) {
+ (Some(value_a), Some(value_b)) => (key, ZipItem::Both(value_a, value_b)),
+ (Some(v), None) => (key, ZipItem::Left(v)),
+ (None, Some(v)) => (key, ZipItem::Right(v)),
+ (None, None) => unreachable!("Iterator was built from a.keys.chain(b.keys)"),
+ })
+}
+
+pub fn zip_btree_map<'m, K, K1, K2, V1, V2>(
+ map1: &'m BTreeMap<K1, V1>,
+ map2: &'m BTreeMap<K2, V2>,
+) -> impl Iterator<Item = (&'m K, ZipItem<&'m V1, &'m V2>)>
+where
+ K1: Borrow<K> + Ord,
+ K2: Borrow<K> + Ord,
+ K: Eq + Ord + 'm,
+{
+ let keys: BTreeSet<&K> = map1
+ .keys()
+ .map(|k| k.borrow())
+ .chain(map2.keys().map(|k| k.borrow()))
+ .collect();
+ let mut map1_iter = map1.iter().peekable();
+ let mut map2_iter = map2.iter().peekable();
+ keys.into_iter().map(move |key| {
+ match (
+ map1_iter.next_if(|(k, _)| (**k).borrow() == key),
+ map2_iter.next_if(|(k, _)| (**k).borrow() == key),
+ ) {
+ (Some((_, value_a)), Some((_, value_b))) => (key, ZipItem::Both(value_a, value_b)),
+ (Some((_, v)), None) => (key, ZipItem::Left(v)),
+ (None, Some((_, v))) => (key, ZipItem::Right(v)),
+ (None, None) => unreachable!("Iterator was built from a.keys.chain(b.keys)"),
+ }
+ })
+}
+
+/// Merge two iterators according to the order, like how it's done in merge sort.
+pub fn merge_descending_iters<'t, T: Ord + 't>(
+ iter1: impl IntoIterator<Item = &'t T> + 't,
+ iter2: impl IntoIterator<Item = &'t T> + 't,
+) -> impl Iterator<Item = &'t T> {
+ // Reverse the comparators so we can make use of the fact that Some(X) > None.
+ let mut iter1 = iter1.into_iter().peekable();
+ let mut iter2 = iter2.into_iter().peekable();
+ std::iter::from_fn(move || {
+ if iter1.peek() > iter2.peek() {
+ iter1.next()
+ } else {
+ iter2.next()
+ }
+ })
+}
+
+pub enum Either<L, R> {
+ Left(L),
+ Right(R),
+}
+
+impl<L, R> Either<L, R> {
+ pub fn into_iter<I>(self) -> impl Iterator<Item = I>
+ where
+ L: Iterator<Item = I>,
+ R: Iterator<Item = I>,
+ {
+ let (l_iter, r_iter) = match self {
+ Either::Left(l) => (Some(l), None),
+ Either::Right(r) => (None, Some(r)),
+ };
+ l_iter
+ .into_iter()
+ .flatten()
+ .chain(r_iter.into_iter().flatten())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::{BTreeMap, BTreeSet, HashMap};
+
+ use crate::utils::{zip_btree_map, ZipItem};
+
+ use super::{merge_descending_iters, zip_hash_map};
+
+ #[test]
+ fn test_zip_hash_map() {
+ let english = HashMap::<_, _>::from_iter([(0, "zero"), (1, "one"), (2, "two")]);
+ let spanish = HashMap::<_, _>::from_iter([(1, "uno"), (2, "dos"), (3, "tres")]);
+ let zipped: HashMap<_, _> = zip_hash_map(&english, &spanish).collect();
+ assert_eq!(
+ zipped,
+ HashMap::from_iter([
+ (&0, ZipItem::Left(&"zero")),
+ (&1, ZipItem::Both(&"one", &"uno")),
+ (&2, ZipItem::Both(&"two", &"dos")),
+ (&3, ZipItem::Right(&"tres")),
+ ])
+ )
+ }
+
+ #[test]
+ fn test_zip_btree_map() {
+ let english = BTreeMap::from_iter([(0, "zero"), (1, "one"), (2, "two")]);
+ let spanish = BTreeMap::from_iter([(1, "uno"), (2, "dos"), (3, "tres")]);
+ let zipped: BTreeMap<_, _> = zip_btree_map(&english, &spanish).collect();
+ assert_eq!(
+ zipped,
+ BTreeMap::from_iter([
+ (&0, ZipItem::Left(&"zero")),
+ (&1, ZipItem::Both(&"one", &"uno")),
+ (&2, ZipItem::Both(&"two", &"dos")),
+ (&3, ZipItem::Right(&"tres")),
+ ])
+ )
+ }
+
+ /// The implementations for [`zip_hash_map`] and [`zip_btree_map`] should be the same, except
+ /// for iteration order.
+ #[derive_fuzztest::proptest]
+ fn zip_results_equal(map1: Vec<(u8, u8)>, map2: Vec<(u8, u8)>) {
+ let hashmap1 = HashMap::<_, _>::from_iter(map1.clone());
+ let hashmap2 = HashMap::<_, _>::from_iter(map2.clone());
+ let btreemap1 = BTreeMap::from_iter(map1.clone());
+ let btreemap2 = BTreeMap::from_iter(map2.clone());
+ let zipped_hashmap = zip_hash_map(&hashmap1, &hashmap2);
+ let zipped_btreemap = zip_btree_map(&btreemap1, &btreemap2);
+
+ assert_eq!(
+ zipped_hashmap.collect::<BTreeSet<_>>(),
+ zipped_btreemap.collect::<BTreeSet<_>>()
+ );
+ }
+
+ #[test]
+ fn test_merge_iters() {
+ let iter1 = [5, 4, 3, 2, 1];
+ let iter2 = [8, 6, 4, 2];
+ let merged = merge_descending_iters(&iter1, &iter2).collect::<Vec<_>>();
+ assert_eq!([&8, &6, &5, &4, &4, &3, &2, &2, &1].as_slice(), &merged);
+ }
+}
diff --git a/betosync/submerge/crdt/src/vector_data.rs b/betosync/submerge/crdt/src/vector_data.rs
new file mode 100644
index 0000000..44b81d5
--- /dev/null
+++ b/betosync/submerge/crdt/src/vector_data.rs
@@ -0,0 +1,585 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A vector data with one entry per node, where each node can only modify their own entry.
+//!
+//! See [`VectorData`] for details.
+
+use std::{cmp::max_by_key, collections::BTreeMap};
+
+use crate::{
+ delta::{AsDeltaMut, AsDeltaRef},
+ utils::{zip_btree_map, Either, ZipItem},
+ ContentEq, CrdtState, UpdateContext,
+};
+use arbitrary::Arbitrary;
+use derive_where::derive_where;
+use serde::{Deserialize, Serialize};
+
+/// An entry in the vector data.
+///
+/// When `data` is modified, the `version` must also be incremented.
+#[derive(Debug, Clone, Arbitrary, PartialEq, Eq, Serialize, Deserialize)]
+struct VecEntry<V> {
+ version: u32,
+ data: Option<V>,
+}
+
+/// A vector of data with one entry per node, where each node can only modify their own entry.
+///
+/// The name draws similarity to how "vector clock" is a vector of logical clocks and how "version
+/// vector" is a vector of version numbers.
+///
+/// The data is tagged with a monotonically increasing version number, which is guaranteed to be
+/// unique because there is only a single writer.
+///
+/// When this type is merged, the vector is merged entry-by-entry, taking the entry with the higher
+/// version number. Since the local version number is always incremented on data change, an equal
+/// version number must imply that the data `V` is also equal.
+///
+/// # Implementations
+/// * See [`VectorDataRead`] for methods on read-only references of [`VectorData`].
+/// * See [`VectorDataWrite`] for methods on mutable references of [`VectorData`].
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Arbitrary)]
+#[derive_where(Default)]
+pub struct VectorData<N: Ord, V> {
+ elements: BTreeMap<N, VecEntry<V>>,
+}
+
+/// Error applying a change to [`VectorData`] because the version number will overflow.
+#[derive(Debug)]
+pub struct VersionOverflow;
+
+/// Read-only operations for this vector data CRDT.
+///
+/// ## See also
+/// * See [`VectorData`] for a description of this vector data type.
+/// * See [`VectorDataWrite`] for mutating operations on this vector data type.
+pub trait VectorDataRead<N: Ord, V>: AsDeltaRef<VectorData<N, V>> {
+ /// Get all values from the vector.
+ ///
+ /// # Returns
+ /// A map with values from every node. Clients can consider using functions like `fold` to
+ /// compute an aggregate value from this.
+ fn entries(&self) -> BTreeMap<&N, &V>
+ where
+ N: 'static,
+ V: 'static,
+ {
+ let iter_opt = match (self.base(), self.delta()) {
+ (None, None) => None,
+ (None, Some(v)) | (Some(v), None) => Some(Either::Left(v.elements.iter())),
+ (Some(base), Some(delta)) => {
+ let vec = zip_btree_map(&base.elements, &delta.elements)
+ .map(|(k, item)| match item {
+ ZipItem::Left(l) => (k, l),
+ ZipItem::Right(r) => (k, r),
+ ZipItem::Both(_, r) => (k, r),
+ })
+ .collect::<Vec<_>>()
+ .into_iter();
+ Some(Either::Right(vec))
+ }
+ };
+ iter_opt
+ .into_iter()
+ .flat_map(|e| e.into_iter())
+ .filter_map(|(k, v)| Some((k, v.data.as_ref()?)))
+ .collect()
+ }
+
+ /// Copies the data without the associated metadata and returns the plain type.
+ ///
+ /// See also: [`crate::HasPlainRepresentation`].
+ fn to_plain(&self) -> BTreeMap<N, V>
+ where
+ N: Clone + 'static,
+ V: Clone + 'static,
+ {
+ self.entries()
+ .into_iter()
+ .map(|(k, v)| (k.clone(), v.clone()))
+ .collect()
+ }
+}
+
+/// Mutating operations for this vector data CRDT.
+///
+/// ## See also
+/// * See [`VectorData`] for a description of this vector data type.
+/// * See [`VectorDataRead`] for read-only operations of this vector data type.
+pub trait VectorDataWrite<N: Ord, V>: VectorDataRead<N, V> + AsDeltaMut<VectorData<N, V>> {
+ /// Set the value to `value` at the given `id`.
+ ///
+ /// # Returns
+ /// `true` if the value was inserted or updated, or `false` if `value` is already equal to the
+ /// entry in the vector.
+ fn set(&mut self, id: N, value: Option<V>) -> Result<bool, VersionOverflow>
+ where
+ N: 'static,
+ V: PartialEq + 'static,
+ {
+ let current_delta_entry = self.delta().and_then(|delta| delta.elements.get(&id));
+ let current_base_entry = self.base().and_then(|b| b.elements.get(&id));
+ let current_entry = max_by_key(current_base_entry, current_delta_entry, |entry_opt| {
+ entry_opt.map(|e| e.version)
+ });
+ let new_version = match current_entry {
+ Some(entry) => {
+ if value == entry.data {
+ return Ok(false);
+ }
+ entry.version.checked_add(1).ok_or(VersionOverflow)?
+ }
+ None => 1,
+ };
+ let _ = self.delta_mut().elements.insert(
+ id,
+ VecEntry {
+ version: new_version,
+ data: value,
+ },
+ );
+ Ok(true)
+ }
+
+ /// Sets the value to `None`, and bumps the version number by `increment`.
+ ///
+ /// Intended for testing only, so tests can artificially create a set with a large version
+ /// number, and test the behavior when the version number overflows.
+ #[cfg(any(test, feature = "testing"))]
+ fn bump_version_number_for_testing(
+ &mut self,
+ id: N,
+ increment: u32,
+ ) -> Result<(), VersionOverflow> {
+ let current_delta_entry = self.delta().and_then(|delta| delta.elements.get(&id));
+ let current_base_entry = self.base().and_then(|b| b.elements.get(&id));
+ let current_entry = max_by_key(current_base_entry, current_delta_entry, |entry_opt| {
+ entry_opt.map(|e| e.version)
+ });
+ let new_version = match current_entry {
+ Some(entry) => entry
+ .version
+ .checked_add(increment)
+ .ok_or(VersionOverflow)?,
+ None => increment,
+ };
+ let _ = self.delta_mut().elements.insert(
+ id,
+ VecEntry {
+ version: new_version,
+ data: None,
+ },
+ );
+ Ok(())
+ }
+
+ /// Apply the changes in the plain representation into this CRDT. This assumes that all changes
+ /// in the plain representation were made by the calling node, updating the associated CRDT
+ /// metadata in the process.
+ ///
+ /// After applying the changes, [`to_plain`][VectorDataRead::to_plain] should return the same
+ /// value as `plain`.
+ ///
+ /// # Returns
+ ///
+ /// `true` if applying the change results in an update in the CRDT state, or false if Returns
+ /// true if applying the change results in an update in the CRDT state, or false if `plain` is
+ /// the same as [`to_plain`][VectorDataRead::to_plain] to begin with. This can be used to avoid
+ /// sending unnecessary update messages over the network, or writing changes to disk
+ /// unnecessarily.
+ fn apply_changes(
+ &mut self,
+ ctx: &impl UpdateContext<N>,
+ mut plain: BTreeMap<N, V>,
+ ) -> Result<bool, VectorDataError>
+ where
+ N: Clone + 'static,
+ V: Clone + Eq + 'static,
+ {
+ let updater = ctx.updater();
+ let entries: BTreeMap<_, _> = self.entries().into_iter().collect();
+ // Validate the plain representation, making sure only the entry for the updater is changed.
+ for (key, item) in zip_btree_map::<N, N, &N, V, &V>(&plain, &entries) {
+ match item {
+ ZipItem::Left(_) | ZipItem::Right(_) => {
+ if key != updater {
+ return Err(VectorDataError::MismatchedNode);
+ }
+ }
+ ZipItem::Both(plain, crdt) => {
+ if key != updater && plain != *crdt {
+ return Err(VectorDataError::MismatchedNode);
+ }
+ }
+ }
+ }
+
+ let plain_entry = plain.remove(updater);
+ if self.entries().remove(updater) == plain_entry.as_ref() {
+ return Ok(false);
+ }
+ Ok(self.set(updater.clone(), plain_entry)?)
+ }
+}
+
+impl<N, V> CrdtState for VectorData<N, V>
+where
+ N: Ord + Clone,
+ V: Clone + Eq,
+{
+ fn merge(a: &Self, b: &Self) -> Self {
+ Self {
+ elements: zip_btree_map(&a.elements, &b.elements)
+ .map(|(key, item)| match item {
+ ZipItem::Left(value) | ZipItem::Right(value) => (key.clone(), value.clone()),
+ ZipItem::Both(value_a, value_b) => (
+ key.clone(),
+ max_by_key(value_a, value_b, |v| v.version).clone(),
+ ),
+ })
+ .collect(),
+ }
+ }
+
+ #[cfg(any(test, feature = "checker"))]
+ fn is_valid_collection<'a>(collection: impl IntoIterator<Item = &'a Self>) -> bool
+ where
+ Self: 'a,
+ {
+ use crate::checker::utils::IterExt as _;
+
+ // Convergence requirement: Entries from the same node that have the same version number
+ // must contain the same data. This is enforced for local modifications by always
+ // incrementing the version number when new data is `set`. Concurrent modifications are not
+ // allowed because each node can only write to its own entry in the vector.
+ collection
+ .into_iter()
+ .iter_pairs_unordered()
+ .all(|(s1, s2)| {
+ s1.elements.keys().chain(s2.elements.keys()).all(|k| {
+ match (s1.elements.get(k), s2.elements.get(k)) {
+ (Some(v1), Some(v2)) => v1 == v2 || v1.version != v2.version,
+ _ => true,
+ }
+ })
+ })
+ }
+}
+
+impl<N, V> ContentEq for VectorData<N, V>
+where
+ N: Ord,
+ V: PartialEq,
+{
+ fn content_eq(&self, other: &Self) -> bool {
+ self == other
+ }
+}
+
+impl<N: Ord, V, T: AsDeltaRef<VectorData<N, V>>> VectorDataRead<N, V> for T {}
+impl<N: Ord, V, T: AsDeltaMut<VectorData<N, V>>> VectorDataWrite<N, V> for T {}
+
+/// Error returned from [`VectorDataWrite::apply_changes`].
+#[derive(Debug, PartialEq, Eq)]
+pub enum VectorDataError {
+ /// Changes cannot be applied because data corresponding to a node other than the local node has
+ /// been modified.
+ MismatchedNode,
+ /// Error applying a change to [`VectorData`] because the version number will overflow.
+ VersionOverflow,
+}
+
+impl From<VersionOverflow> for VectorDataError {
+ fn from(_: VersionOverflow) -> Self {
+ VectorDataError::VersionOverflow
+ }
+}
+
+/// Checker to help implement invariant tests over arbitrary operations on a vector data.
+///
+/// Requires the feature _`checker`_.
+#[cfg(any(test, feature = "checker"))]
+pub mod checker {
+ use std::{collections::BTreeMap, fmt::Debug};
+
+ use arbitrary::Arbitrary;
+
+ use crate::{
+ checker::simulation::{Operation, SimulationContext},
+ delta::{AsDeltaRef, DeltaMut},
+ vector_data::VectorDataRead,
+ };
+
+ use super::{VectorData, VectorDataWrite};
+
+ /// Mutation operations on a [`VectorDataWrite`].
+ ///
+ /// Can be used with [`Arbitrary`] to generate arbitrary operations to be applied on the vector
+ /// data.
+ #[derive(Debug, Clone, Arbitrary)]
+ #[allow(missing_docs)]
+ pub enum VecDataOp<N: Ord, V> {
+ Set { node: N, value: V },
+ ApplyChanges { node: N, plain: BTreeMap<N, V> },
+ }
+
+ impl<N, V> Operation<VectorData<N, V>, N> for VecDataOp<N, V>
+ where
+ N: Debug + Ord + Clone + 'static,
+ V: Debug + PartialEq + Eq + Clone + 'static,
+ {
+ fn apply(self, mut state: DeltaMut<VectorData<N, V>>, ctx: &SimulationContext<N>) {
+ let before = state.as_ref().merge();
+ let before_entries = before.entries();
+ match self {
+ VecDataOp::Set { node, value } => {
+ let result = state.set(node.clone(), Some(value.clone()));
+ let entries = state.entries();
+ if result.is_ok() {
+ assert_eq!(entries.get(&node), Some(&&value));
+ }
+ assert_non_updater_entries_unchanged(node, before_entries, entries);
+ }
+ VecDataOp::ApplyChanges { node, plain } => {
+ let result = state.apply_changes(&ctx.context(&node), plain.clone());
+ if result.is_ok() {
+ assert_eq!(plain, state.to_plain());
+ }
+ assert_non_updater_entries_unchanged(node, before_entries, state.entries());
+ }
+ }
+ }
+ }
+
+ fn assert_non_updater_entries_unchanged<N, V>(
+ updater: N,
+ mut before_entries: BTreeMap<&N, &V>,
+ mut after_entries: BTreeMap<&N, &V>,
+ ) where
+ N: Ord + Eq + Clone + Debug + 'static,
+ V: Clone + Eq + Debug + 'static,
+ {
+ let _ = before_entries.remove(&updater);
+ let _ = after_entries.remove(&updater);
+ assert_eq!(before_entries, after_entries);
+ }
+}
+
+#[cfg(feature = "proto")]
+mod proto {
+ use submerge_internal_proto::{FromProto, FromProtoError, NodeMapping, ToProto};
+
+ use super::{VecEntry, VectorData};
+
+ impl FromProto for VectorData<String, Vec<u8>> {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeVectorData;
+
+ fn from_proto(proto: &Self::Proto, node_ids: &[String]) -> Result<Self, FromProtoError> {
+ Ok(Self {
+ elements: proto
+ .elements
+ .iter()
+ .map(|elem| {
+ Ok((
+ node_ids
+ .get(elem.node as usize)
+ .ok_or(FromProtoError::MissingNodeId)?
+ .clone(),
+ VecEntry {
+ version: elem.version,
+ data: elem.value.clone(),
+ },
+ ))
+ })
+ .collect::<Result<_, _>>()?,
+ })
+ }
+ }
+
+ impl ToProto for VectorData<String, Vec<u8>> {
+ type Proto = submerge_internal_proto::protos::submerge::SubmergeVectorData;
+
+ fn to_proto(&self, node_ids: &mut NodeMapping<String>) -> Self::Proto {
+ submerge_internal_proto::protos::submerge::SubmergeVectorData {
+ elements: self
+ .elements
+ .iter()
+ .map(|(node, v)| {
+ submerge_internal_proto::protos::submerge::submerge_vector_data::VecElement {
+ node: node_ids.get_index(node),
+ value: v.data.clone(),
+ version: v.version,
+ ..Default::default()
+ }
+ })
+ .collect(),
+ ..Default::default()
+ }
+ }
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn vector_data_roundtrip(vector_data: VectorData<String, Vec<u8>>) {
+ let mut node_ids = NodeMapping::default();
+ assert_eq!(
+ VectorData::from_proto(&vector_data.to_proto(&mut node_ids), &node_ids.into_vec())
+ .unwrap(),
+ vector_data
+ );
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::BTreeMap;
+
+ use crate::{
+ checker::test_fakes::FakeContext,
+ vector_data::{VectorDataError, VectorDataRead, VectorDataWrite},
+ CrdtState,
+ };
+
+ use super::VectorData;
+
+ #[test]
+ fn set_value() {
+ let mut v = VectorData::<u8, u8>::default();
+ let _ = v.set(0, Some(66)).unwrap();
+ let _ = v.set(1, Some(88)).unwrap();
+ assert_eq!(BTreeMap::from_iter([(&0, &66), (&1, &88)]), v.entries());
+ }
+
+ #[test]
+ fn version_overflow() {
+ let mut v = VectorData::<u8, u8>::default();
+ v.bump_version_number_for_testing(0, u32::MAX).unwrap();
+ let _ = v.set(0, Some(66)).unwrap_err();
+ let _ = v.set(1, Some(88)).unwrap();
+ assert_eq!(BTreeMap::from_iter([(&1, &88)]), v.entries());
+ }
+
+ #[test]
+ fn apply_changes() {
+ let mut v = VectorData::<u8, u8>::default();
+ let _ = v.set(0, Some(66)).unwrap();
+ let _ = v.set(1, Some(88)).unwrap();
+ let mut plain = v.to_plain();
+ let _ = plain.entry(0).and_modify(|e| *e = 99);
+
+ let changed = v.apply_changes(&FakeContext::new(0, 0_u8), plain).unwrap();
+ assert!(changed);
+
+ assert_eq!(BTreeMap::from_iter([(&0, &99), (&1, &88)]), v.entries());
+ }
+
+ #[test]
+ fn apply_changes_mismatched() {
+ let mut v = VectorData::<u8, u8>::default();
+ let _ = v.set(0, Some(66)).unwrap();
+ let _ = v.set(1, Some(88)).unwrap();
+ let mut plain = v.to_plain();
+ let _ = plain.entry(1).and_modify(|e| *e = 99);
+
+ let result = v.apply_changes(&FakeContext::new(0, 0_u8), plain);
+ assert_eq!(VectorDataError::MismatchedNode, result.unwrap_err());
+ }
+
+ #[test]
+ fn merge() {
+ let mut v_parent = VectorData::<u8, u8>::default();
+ let _ = v_parent.set(0, Some(66)).unwrap();
+
+ let mut child1 = v_parent.clone();
+ let _ = child1.set(1, Some(44)).unwrap();
+
+ let _ = v_parent.set(0, Some(88)).unwrap();
+ let mut child2 = v_parent.clone();
+ let _ = child2.set(2, Some(22)).unwrap();
+
+ let v_merged = VectorData::merge(&child1, &child2);
+ assert_eq!(
+ BTreeMap::from_iter([(&0, &88), (&1, &44), (&2, &22)]),
+ v_merged.entries()
+ );
+ }
+
+ #[test]
+ fn merge_undefined_behavior() {
+ let mut v1 = VectorData::<u8, u8>::default();
+ let _ = v1.set(1, Some(88)).unwrap();
+
+ let mut v2 = VectorData::<u8, u8>::default();
+ let _ = v2.set(1, Some(44)).unwrap();
+
+ let _ = VectorData::merge(&v1, &v2);
+ // This is undefined behavior (in the generic sense of the word), it should never happen
+ // since set(1, 44) should always happen after observing set(1, 88) and thus increment the
+ // version number
+ }
+
+ #[test]
+ fn test_apply_changes() {
+ let mut vector_data = VectorData::<u8, u8>::default();
+ let plain = BTreeMap::from_iter([(1, 1)]);
+ let _ = vector_data
+ .apply_changes(&FakeContext::new(1, 0_u8), plain.clone())
+ .unwrap();
+
+ assert_eq!(plain, vector_data.to_plain());
+ }
+
+ #[test]
+ fn test_apply_changes_mismatch() {
+ let mut vector_data = VectorData::<u8, u8>::default();
+ let plain = BTreeMap::from_iter([(1, 1)]);
+ // Apply changes from Node 2, but trying to change data on Node 1
+ assert_eq!(
+ Err(VectorDataError::MismatchedNode),
+ vector_data.apply_changes(&FakeContext::new(2, 0_u8), plain.clone())
+ );
+ }
+
+ #[test]
+ fn test_init_from_plain() {
+ let plain = BTreeMap::from_iter([(1, 1)]);
+ let mut vector_data = VectorData::default();
+ let _ = vector_data
+ .apply_changes(&FakeContext::new(1, 0_u8), plain.clone())
+ .unwrap();
+ assert_eq!(plain, vector_data.to_plain());
+ }
+
+ #[test]
+ fn test_init_from_plain_empty() {
+ let plain: BTreeMap<u8, u8> = BTreeMap::new();
+ let mut vector_data = VectorData::default();
+ let _ = vector_data
+ .apply_changes(&FakeContext::new(1, 0_u8), plain.clone())
+ .unwrap();
+ assert_eq!(plain, vector_data.to_plain());
+ }
+
+ #[test]
+ fn test_init_from_plain_mismatch() {
+ let plain = BTreeMap::from_iter([(1, 1)]);
+ let mut vector_data = VectorData::default();
+ assert_eq!(
+ Err(VectorDataError::MismatchedNode),
+ vector_data.apply_changes(&FakeContext::new(2, 0_u8), plain.clone()),
+ );
+ }
+}
diff --git a/betosync/submerge/distributed_time/Cargo.toml b/betosync/submerge/distributed_time/Cargo.toml
new file mode 100644
index 0000000..b735438
--- /dev/null
+++ b/betosync/submerge/distributed_time/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "distributed_time"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+license.workspace = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+submerge_internal_proto = { workspace = true, optional = true }
+
+# Dependencies from crates.io
+arbitrary.workspace = true
+derive-where.workspace = true
+num-traits.workspace = true
+serde.workspace = true
+thiserror.workspace = true
+
+[dev-dependencies]
+derive_fuzztest.workspace = true
+
+[features]
+default = []
+proto = ["dep:submerge_internal_proto"]
+
+[lints]
+workspace = true
diff --git a/betosync/submerge/distributed_time/fuzz/.gitignore b/betosync/submerge/distributed_time/fuzz/.gitignore
new file mode 100644
index 0000000..1a45eee
--- /dev/null
+++ b/betosync/submerge/distributed_time/fuzz/.gitignore
@@ -0,0 +1,4 @@
+target
+corpus
+artifacts
+coverage
diff --git a/betosync/submerge/distributed_time/fuzz/Cargo.lock b/betosync/submerge/distributed_time/fuzz/Cargo.lock
new file mode 100644
index 0000000..6b84d2c
--- /dev/null
+++ b/betosync/submerge/distributed_time/fuzz/Cargo.lock
@@ -0,0 +1,579 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+dependencies = [
+ "derive_arbitrary",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[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 = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[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 = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "derive-fuzztest"
+version = "0.1.0"
+dependencies = [
+ "derive-fuzztest-macro",
+ "proptest",
+ "proptest-arbitrary-interop",
+]
+
+[[package]]
+name = "derive-fuzztest-macro"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "derive-where"
+version = "1.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "derive_arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[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 = "distributed-time"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "derive-where",
+ "derive_more",
+ "num-traits",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "distributed-time-fuzz"
+version = "0.0.0"
+dependencies = [
+ "arbitrary",
+ "derive-fuzztest",
+ "distributed-time",
+ "libfuzzer-sys",
+ "proptest-arb",
+]
+
+[[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.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"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[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 = "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 = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.151"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
+
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
+dependencies = [
+ "arbitrary",
+ "cc",
+ "once_cell",
+]
+
+[[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.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
+[[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 = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[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.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",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "proptest-arb"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "proptest",
+ "proptest-arb-attr-macro",
+ "proptest-arbitrary-interop",
+]
+
+[[package]]
+name = "proptest-arb-attr-macro"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "proptest-arbitrary-interop"
+version = "0.1.0"
+source = "git+https://github.com/brson/proptest-arbitrary-interop.git?branch=incorrect-format#9ae407e9805feb109b3d49cc737166bda7e698c3"
+dependencies = [
+ "arbitrary",
+ "proptest",
+]
+
+[[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.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_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.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[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.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
+dependencies = [
+ "bitflags 2.4.2",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[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 = "ryu"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
+
+[[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.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 2.0.48",
+]
+
+[[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 = "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 = "tempfile"
+version = "3.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[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.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[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.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[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.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[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.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
diff --git a/betosync/submerge/distributed_time/fuzz/Cargo.toml b/betosync/submerge/distributed_time/fuzz/Cargo.toml
new file mode 100644
index 0000000..9eaec3e
--- /dev/null
+++ b/betosync/submerge/distributed_time/fuzz/Cargo.toml
@@ -0,0 +1,44 @@
+[package]
+name = "distributed_time_fuzz"
+version.workspace = true
+publish = false
+edition.workspace = true
+license.workspace = true
+
+[package.metadata]
+cargo-fuzz = true
+
+[dependencies]
+arbitrary.workspace = true
+derive_fuzztest.workspace = true
+distributed_time.workspace = true
+
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
+
+[lints]
+workspace = true
+
+[[bin]]
+name = "compound_timestamp_total_order"
+doc = false
+
+[[bin]]
+name = "hlc_increment"
+doc = false
+
+[[bin]]
+name = "hlc_relation_consistency"
+doc = false
+
+[[bin]]
+name = "hlc_uniqueness"
+doc = false
+
+[[bin]]
+name = "vector_clock_increment"
+doc = false
+
+[[bin]]
+name = "vector_clock_lub"
+doc = false
diff --git a/betosync/submerge/distributed_time/fuzz/src/bin/compound_timestamp_total_order.rs b/betosync/submerge/distributed_time/fuzz/src/bin/compound_timestamp_total_order.rs
new file mode 100644
index 0000000..4896d16
--- /dev/null
+++ b/betosync/submerge/distributed_time/fuzz/src/bin/compound_timestamp_total_order.rs
@@ -0,0 +1,58 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that the timestamp comparison implementation forms a total order, as required by [`Ord`].
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use std::cmp::Ordering;
+
+use arbitrary::Arbitrary;
+use distributed_time::{compound_timestamp::CompoundTimestamp, vector_clock::VectorClock};
+
+#[derive(Debug, Arbitrary, Clone)]
+enum TestCase {
+ Reflexivity(CompoundTimestamp<VectorClock<u8>>),
+ Antisymmetry([CompoundTimestamp<VectorClock<u8>>; 2]),
+ Transitivity([CompoundTimestamp<VectorClock<u8>>; 3]),
+}
+
+#[derive_fuzztest::fuzztest]
+fn compound_timestamp_total_order(t: TestCase) {
+ match t {
+ TestCase::Reflexivity(t) => {
+ assert_eq!(Ordering::Equal, t.cmp(&t));
+ assert!(t.eq(&t));
+ }
+ TestCase::Antisymmetry(t) => {
+ assert!(match t[0].cmp(&t[1]) {
+ Ordering::Less => t[1] > t[0],
+ Ordering::Equal => t[1] == t[0],
+ Ordering::Greater => t[1] < t[0],
+ });
+ }
+ TestCase::Transitivity(t) => {
+ let max = (&t[0]).max(&t[1]).max(&t[2]);
+ assert!(max >= &t[0], "max={max:?} t[0]={:?}", t[0]);
+ assert!(max >= &t[1], "max={max:?} t[1]={:?}", t[1]);
+ assert!(max >= &t[2], "max={max:?} t[2]={:?}", t[2]);
+
+ let min = (&t[0]).min(&t[1]).min(&t[2]);
+ assert!(min <= &t[0], "min={min:?} t[0]={:?}", t[0]);
+ assert!(min <= &t[1], "min={min:?} t[1]={:?}", t[1]);
+ assert!(min <= &t[2], "min={min:?} t[2]={:?}", t[2]);
+ }
+ }
+}
diff --git a/betosync/submerge/distributed_time/fuzz/src/bin/hlc_increment.rs b/betosync/submerge/distributed_time/fuzz/src/bin/hlc_increment.rs
new file mode 100644
index 0000000..ba50fde
--- /dev/null
+++ b/betosync/submerge/distributed_time/fuzz/src/bin/hlc_increment.rs
@@ -0,0 +1,40 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Test that after incrementing an HLC, it is always greater than before increment.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use distributed_time::{
+ fake_timestamp::FakeTimestamp, hybrid_logical_clock::HybridLogicalTimestamp, TotalTimestamp,
+};
+
+#[derive_fuzztest::fuzztest]
+fn hlc_increment(
+ timestamp: HybridLogicalTimestamp<u8>,
+ updater: u8,
+ wall_clock_time: FakeTimestamp,
+) {
+ #[allow(clippy::eq_op)]
+ {
+ // This might seem tautological, but it verifies that our `PartialEq` implementation works
+ // as expected.
+ assert_eq!(timestamp, timestamp);
+ }
+ if let Ok(incremented) = timestamp.increment(&updater, &wall_clock_time) {
+ assert!(incremented > timestamp);
+ assert!(timestamp < incremented);
+ }
+}
diff --git a/betosync/submerge/distributed_time/fuzz/src/bin/hlc_relation_consistency.rs b/betosync/submerge/distributed_time/fuzz/src/bin/hlc_relation_consistency.rs
new file mode 100644
index 0000000..542a3fa
--- /dev/null
+++ b/betosync/submerge/distributed_time/fuzz/src/bin/hlc_relation_consistency.rs
@@ -0,0 +1,43 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that the `Ord`, `PartialOrd`, and `PartialEq` implementations are consistent with each
+//! other, as required in their documentation.
+
+#![cfg_attr(fuzzing, no_main)]
+
+use std::cmp::Ordering;
+
+use distributed_time::hybrid_logical_clock::HybridLogicalTimestamp;
+
+#[derive_fuzztest::fuzztest]
+fn hlc_relation_consistency(
+ timestamp1: HybridLogicalTimestamp<u8>,
+ timestamp2: HybridLogicalTimestamp<u8>,
+) {
+ match timestamp1.cmp(×tamp2) {
+ std::cmp::Ordering::Less => {
+ assert_eq!(Some(Ordering::Less), timestamp1.partial_cmp(×tamp2));
+ assert!(!timestamp1.eq(×tamp2))
+ }
+ std::cmp::Ordering::Equal => {
+ assert_eq!(Some(Ordering::Equal), timestamp1.partial_cmp(×tamp2));
+ assert!(timestamp1.eq(×tamp2))
+ }
+ std::cmp::Ordering::Greater => {
+ assert_eq!(Some(Ordering::Greater), timestamp1.partial_cmp(×tamp2));
+ assert!(!timestamp1.eq(×tamp2))
+ }
+ }
+}
diff --git a/betosync/submerge/distributed_time/fuzz/src/bin/hlc_uniqueness.rs b/betosync/submerge/distributed_time/fuzz/src/bin/hlc_uniqueness.rs
new file mode 100644
index 0000000..b6560dc
--- /dev/null
+++ b/betosync/submerge/distributed_time/fuzz/src/bin/hlc_uniqueness.rs
@@ -0,0 +1,55 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that any increment / least-upper-bound paths won't generate two events with the same
+//! timestamp.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use std::collections::{BTreeMap, BTreeSet};
+
+use arbitrary::Arbitrary;
+use distributed_time::{
+ fake_timestamp::FakeTimestamp, hybrid_logical_clock::HybridLogicalTimestamp, TotalTimestamp,
+};
+
+#[derive(Debug, Clone, Arbitrary)]
+enum Op {
+ Increment {
+ updater: u8,
+ timestamp: FakeTimestamp,
+ },
+}
+
+#[derive_fuzztest::fuzztest]
+fn hlc_uniqueness(starting_timestamp: HybridLogicalTimestamp<u8>, operations: Vec<Op>) {
+ let mut seen_timestamps = BTreeSet::new();
+ let _ = seen_timestamps.insert(starting_timestamp.clone());
+ let mut map = BTreeMap::new();
+ let _ = map.insert(0_u8, starting_timestamp.clone());
+ for op in operations {
+ match op {
+ Op::Increment { updater, timestamp } => {
+ let entry = map
+ .entry(updater)
+ .or_insert_with(|| starting_timestamp.clone());
+ if let Ok(new_timestamp) = entry.increment(&updater, ×tamp) {
+ assert!(seen_timestamps.insert(new_timestamp.clone()));
+ *entry = new_timestamp;
+ };
+ }
+ }
+ }
+}
diff --git a/betosync/submerge/distributed_time/fuzz/src/bin/vector_clock_increment.rs b/betosync/submerge/distributed_time/fuzz/src/bin/vector_clock_increment.rs
new file mode 100644
index 0000000..5663f74
--- /dev/null
+++ b/betosync/submerge/distributed_time/fuzz/src/bin/vector_clock_increment.rs
@@ -0,0 +1,29 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that a vector clock is greater after being incremented.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use std::cmp::Ordering;
+
+use distributed_time::{vector_clock::VectorClock, DistributedClock as _};
+
+#[derive_fuzztest::fuzztest]
+fn vector_clock_increment(clock: VectorClock<u16>, id: u16) {
+ if let Ok(incremented) = clock.increment(&id) {
+ assert_eq!(Some(Ordering::Greater), incremented.partial_cmp(&clock));
+ }
+}
diff --git a/betosync/submerge/distributed_time/fuzz/src/bin/vector_clock_lub.rs b/betosync/submerge/distributed_time/fuzz/src/bin/vector_clock_lub.rs
new file mode 100644
index 0000000..e96c3b8
--- /dev/null
+++ b/betosync/submerge/distributed_time/fuzz/src/bin/vector_clock_lub.rs
@@ -0,0 +1,33 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that [`VectorClock::least_upper_bound`] indeed returns a least upper bound.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+#![allow(clippy::indexing_slicing)]
+
+use distributed_time::{vector_clock::VectorClock, DistributedClock};
+
+#[derive_fuzztest::fuzztest]
+fn vectror_clock_lub(a: VectorClock<u16>, b: VectorClock<u16>) {
+ let lub = VectorClock::least_upper_bound(&a, &b);
+ assert!(lub >= a);
+ assert!(lub >= b);
+ let a_dots = a.into_dots();
+ let b_dots = b.into_dots();
+ for (node, lub_value) in lub.into_dots() {
+ assert!(Some(&lub_value) == a_dots.get(&node) || Some(&lub_value) == b_dots.get(&node));
+ }
+}
diff --git a/betosync/submerge/distributed_time/src/compound_timestamp.rs b/betosync/submerge/distributed_time/src/compound_timestamp.rs
new file mode 100644
index 0000000..615e184
--- /dev/null
+++ b/betosync/submerge/distributed_time/src/compound_timestamp.rs
@@ -0,0 +1,321 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A compound timestamp that combines a
+//! [`HybridLogicalTimestamp`][crate::hybrid_logical_clock::HybridLogicalTimestamp] with a
+//! [`DistributedClock`] (like a vector clock).
+//!
+//! See the documentation for [`CompoundTimestamp`].
+
+use std::{cmp::Ordering, fmt::Debug};
+
+use arbitrary::Arbitrary;
+use derive_where::derive_where;
+use serde::{Deserialize, Serialize};
+
+use crate::{
+ hybrid_logical_clock::UnnamedHybridLogicalTimestamp, DistributedClock, NonSemanticOrd,
+ TimestampOverflow, TotalTimestamp, WallTimestampProvider,
+};
+
+/// A compound timestamp that combines a
+/// [`HybridLogicalTimestamp`][crate::hybrid_logical_clock::HybridLogicalTimestamp] with a
+/// [`DistributedClock`] (like a vector clock).
+///
+/// The combination provides both last-writer-wins semantics that obeys "potential causality" (which
+/// `HybridLogicalTimestamp` provides), and true causality checks (the ability to tell whether two
+/// events are concurrent or not) to detect concurrent changes (which are potentially conflicts).
+///
+/// This is designed to provide the information necessary for both last-writer-wins registers and
+/// multi-value registers. This allows us to implement a register that provides both `get()` for
+/// last-writer-wins behavior, and `get_all()` for multi-value behavior.
+#[derive(Clone, Debug, Serialize, Deserialize, Arbitrary)]
+#[derive_where(PartialEq, Eq)]
+#[serde(bound(serialize = "D: Serialize", deserialize = "D: Deserialize<'de>"))]
+pub struct CompoundTimestamp<D: PartialOrd + Eq> {
+ /// The partial-order component of the timestamp. This component is capable of determining
+ /// whether two events are concurrent or not, using its `PartialOrd` implementation. If two
+ /// events are concurrent, its `partial_cmp` should return `None`.
+ pub distributed_clock: D,
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp,
+}
+
+impl<D> CompoundTimestamp<D>
+where
+ D: PartialOrd + Eq + NonSemanticOrd,
+{
+ /// Whether the two given timestamps are comparable.
+ ///
+ /// For comparable timestamps, the results from comparing the distributed clocks `D` and
+ /// comparing the underlying logical timestamp should always be consistent for non-concurrent
+ /// events. This implementation guarantees that this always return true except for instances
+ /// obtained from the `arbitrary` implementation.
+ pub fn is_comparable(a: &Self, b: &Self) -> bool {
+ if let Some(distributed_clock_cmp) = a.distributed_clock.partial_cmp(&b.distributed_clock) {
+ distributed_clock_cmp == a.hybrid_logical_timestamp.cmp(&b.hybrid_logical_timestamp)
+ } else {
+ true
+ }
+ }
+
+ /// Return the causality order of this compound timestamp.
+ ///
+ /// The causality order is the same as `D::partial_cmp`, which should be the same as
+ /// [`Self::cmp`] if the event is not concurrent. Because of the consistency requirements in
+ /// [`PartialOrd`] and [`Ord`], `CompoundTimestamp::partial_cmp` never returns `None`. Thus we
+ /// have this function to allow extracting the causality information out from this compound
+ /// timestamp.
+ pub fn causality_order(&self, other: &Self) -> Option<Ordering> {
+ // Technically the ordering of the distributed clock should match that of `self`, so the
+ // `map` part should not be necessary. But for the simplicity of comparing arbitrarily
+ // generated timestamps in invariant testing, we always use `self` as the ordering, and only
+ // use `distributed_clock` to detect concurrency.
+ self.distributed_clock
+ .partial_cmp(&other.distributed_clock)
+ .map(|_| self.cmp(other))
+ }
+
+ /// Calculate the least-upper-bound of two given timestamps.
+ ///
+ /// The result `T` in addition to satisfying `T >= a` and `T >= b` (least-upper-bound
+ /// definition with respect to `Ord`), this implementation also guarantees that
+ /// `causality_order(T, a) matches Some(Greater | Equal)` and `causality_order(T, b) matches
+ /// Some(Greater | Equal)` (least-upper-bound definition with respect to the partial order
+ /// defined by `causality_order`).
+ pub fn least_upper_bound<'a>(iter: impl IntoIterator<Item = &'a Self>) -> Option<Self>
+ where
+ D: DistributedClock + Clone + 'a,
+ {
+ let mut acc: Option<(D, &UnnamedHybridLogicalTimestamp)> = None;
+ for timestamp in iter {
+ match acc {
+ Some((ref mut clock, ref mut hlc)) => {
+ clock.least_upper_bound_in_place(×tamp.distributed_clock);
+ if ×tamp.hybrid_logical_timestamp > hlc {
+ *hlc = ×tamp.hybrid_logical_timestamp;
+ }
+ }
+ None => {
+ acc = Some((
+ timestamp.distributed_clock.clone(),
+ ×tamp.hybrid_logical_timestamp,
+ ));
+ }
+ }
+ }
+ acc.map(|(d, hlc)| Self {
+ distributed_clock: d,
+ hybrid_logical_timestamp: hlc.clone(),
+ })
+ }
+}
+
+impl<D> PartialOrd for CompoundTimestamp<D>
+where
+ D: PartialOrd + Eq + NonSemanticOrd,
+{
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl<D> Ord for CompoundTimestamp<D>
+where
+ D: PartialOrd + Eq + NonSemanticOrd,
+{
+ fn cmp(&self, other: &Self) -> Ordering {
+ match self
+ .hybrid_logical_timestamp
+ .cmp(&other.hybrid_logical_timestamp)
+ {
+ Ordering::Equal => self
+ .distributed_clock
+ .non_semantic_cmp(&other.distributed_clock),
+ order => order,
+ }
+ }
+}
+
+impl<D> TotalTimestamp for CompoundTimestamp<D>
+where
+ D: DistributedClock + NonSemanticOrd + Default + Clone,
+{
+ type NodeId = D::NodeId;
+
+ fn increment(
+ &self,
+ updater: &Self::NodeId,
+ timestamp_provider: &impl WallTimestampProvider,
+ ) -> Result<Self, TimestampOverflow> {
+ Ok(Self {
+ distributed_clock: self.distributed_clock.increment(updater)?,
+ hybrid_logical_timestamp: self
+ .hybrid_logical_timestamp
+ .increment(timestamp_provider)?,
+ })
+ }
+
+ fn new_with_context(
+ updater: &Self::NodeId,
+ timestamp_provider: &impl WallTimestampProvider,
+ ) -> Self {
+ Self {
+ distributed_clock: D::default().increment(updater).expect(
+ "Timestamp should not overflow when incrementing from the default instance",
+ ),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(timestamp_provider, 0),
+ }
+ }
+}
+
+#[cfg(feature = "proto")]
+mod proto {
+ use submerge_internal_proto::{FromProto, FromProtoError, NodeMapping, ToProto};
+
+ use crate::{hybrid_logical_clock::UnnamedHybridLogicalTimestamp, vector_clock::VectorClock};
+
+ use super::CompoundTimestamp;
+
+ impl CompoundTimestamp<VectorClock<String>> {
+ /// Construct protos for the given compound timestamp.
+ ///
+ /// Returns a pair `(HybridLogicalTimestampProto, VectorClockProto)`.
+ pub fn to_proto(
+ &self,
+ node_ids: &mut NodeMapping<String>,
+ ) -> (
+ submerge_internal_proto::protos::submerge::HybridLogicalTimestamp,
+ submerge_internal_proto::protos::submerge::CompressedVectorClock,
+ ) {
+ (
+ self.hybrid_logical_timestamp.to_proto(node_ids),
+ self.distributed_clock.to_proto(node_ids),
+ )
+ }
+
+ /// Construct a compound timestamp from the given protos.
+ pub fn from_proto(
+ timestamp: &submerge_internal_proto::protos::submerge::HybridLogicalTimestamp,
+ vector_clock: &submerge_internal_proto::protos::submerge::CompressedVectorClock,
+ node_ids: &[String],
+ ) -> Result<Self, FromProtoError> {
+ Ok(CompoundTimestamp {
+ distributed_clock: VectorClock::from_proto(vector_clock, node_ids)?,
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::from_proto(
+ timestamp, node_ids,
+ )?,
+ })
+ }
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn compound_timestamp_round_trip(compound_timestamp: CompoundTimestamp<VectorClock<String>>) {
+ let mut node_ids = NodeMapping::default();
+ let (hlc_proto, vector_clock_proto) = compound_timestamp.to_proto(&mut node_ids);
+ assert_eq!(
+ CompoundTimestamp::from_proto(&hlc_proto, &vector_clock_proto, &node_ids.into_vec())
+ .unwrap(),
+ compound_timestamp
+ );
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::cmp::Ordering;
+
+ use crate::{
+ compound_timestamp::CompoundTimestamp, fake_timestamp::FakeTimestamp,
+ hybrid_logical_clock::UnnamedHybridLogicalTimestamp, vector_clock::VectorClock,
+ TotalTimestamp,
+ };
+
+ #[test]
+ fn new_with_context() {
+ let t =
+ CompoundTimestamp::<VectorClock<u16>>::new_with_context(&0, &FakeTimestamp(123_u32));
+ assert_eq!(
+ CompoundTimestamp {
+ distributed_clock: VectorClock::new([(0, 1)]),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(
+ &FakeTimestamp(123_u32),
+ 0
+ )
+ },
+ t
+ )
+ }
+
+ #[test]
+ fn hybrid_timestamp_eq() {
+ let t1 = CompoundTimestamp {
+ distributed_clock: VectorClock::new([(1, 0), (2, 0)]),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(0_u32), 0),
+ };
+ let t2 = CompoundTimestamp {
+ distributed_clock: VectorClock::default(),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(0_u32), 0),
+ };
+
+ assert_eq!(
+ t1, t2,
+ "Non-existent vector clock entries should default to 0"
+ );
+ }
+
+ #[test]
+ fn hybrid_timestamp_concurrent_same_wall_clock() {
+ let t1 = CompoundTimestamp {
+ distributed_clock: VectorClock::new([(0, 1)]),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(0_u32), 0),
+ };
+ let t2 = CompoundTimestamp {
+ distributed_clock: VectorClock::new([(0, 1)]),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(2_u32), 0),
+ };
+
+ assert_ne!(
+ Some(Ordering::Equal),
+ t1.partial_cmp(&t2),
+ "Hybrid timestamps with different HLCs should never be equal"
+ );
+ }
+
+ #[test]
+ fn test_incomparable_timestamp() {
+ let t1 = CompoundTimestamp {
+ distributed_clock: VectorClock::<u16>::default(),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(2_u32), 0),
+ };
+ let t2 = CompoundTimestamp {
+ distributed_clock: VectorClock::default(),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(1_u32), 0),
+ };
+ assert!(!CompoundTimestamp::is_comparable(&t1, &t2));
+ }
+
+ #[test]
+ fn test_timestamp_comparison() {
+ let t1 = CompoundTimestamp {
+ distributed_clock: VectorClock::new([(1, 5), (2, 2)]),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(10_u32), 0),
+ };
+ let t2 = CompoundTimestamp {
+ distributed_clock: VectorClock::new([(1, 4), (2, 3)]),
+ hybrid_logical_timestamp: UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(30_u32), 0),
+ };
+ assert!(t1 < t2);
+ }
+}
diff --git a/betosync/submerge/distributed_time/src/fake_timestamp.rs b/betosync/submerge/distributed_time/src/fake_timestamp.rs
new file mode 100644
index 0000000..cd8ca79
--- /dev/null
+++ b/betosync/submerge/distributed_time/src/fake_timestamp.rs
@@ -0,0 +1,87 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A fake timestamp suitable for use in testing.
+//!
+//! See [`FakeTimestamp`].
+
+use arbitrary::Arbitrary;
+use serde::{Deserialize, Serialize};
+use std::{fmt::Debug, marker::PhantomData};
+
+use crate::{TimestampOverflow, TotalTimestamp, WallTimestampProvider};
+
+/// A fake timestamp suitable for use in testing.
+///
+/// This timestamp contains the numeric timestamp value and can be used as the
+/// [`WallTimestampProvider`] type during testing.
+///
+/// This fake timestamp is generic over the underlying type so that the search space can be
+/// configured to match the needs of a test when used with [`Arbitrary`].
+#[derive(
+ Clone, Default, Debug, Arbitrary, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash,
+)]
+pub struct FakeTimestamp<U: Into<u64> + TryFrom<u64> + Clone + Ord = u8>(pub U);
+
+impl<U: Into<u64> + TryFrom<u64> + Clone + Ord> WallTimestampProvider for FakeTimestamp<U> {
+ fn now(&self) -> u64 {
+ self.0.clone().into()
+ }
+}
+
+/// A fake [`TotalTimestamp`] that is backed by a `u8`.
+#[derive(Clone, Default, Debug, Arbitrary, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct FakeTotalTimestamp<N> {
+ timestamp: u8,
+ _marker: PhantomData<N>,
+}
+
+impl<N> FakeTotalTimestamp<N> {
+ /// Create a new instance from the given timestamp.
+ pub fn new(timestamp: u8) -> Self {
+ Self {
+ timestamp,
+ _marker: PhantomData,
+ }
+ }
+}
+
+#[allow(clippy::panic)]
+impl<N: Ord> TotalTimestamp for FakeTotalTimestamp<N> {
+ type NodeId = N;
+
+ fn increment(
+ &self,
+ _updater: &N,
+ _timestamp_provider: &impl WallTimestampProvider,
+ ) -> Result<Self, TimestampOverflow>
+ where
+ Self: Sized,
+ {
+ Ok(Self {
+ timestamp: self.timestamp.checked_add(1).ok_or(TimestampOverflow)?,
+ _marker: PhantomData,
+ })
+ }
+
+ fn new_with_context(_updater: &N, timestamp_provider: &impl WallTimestampProvider) -> Self {
+ Self {
+ timestamp: timestamp_provider
+ .now()
+ .try_into()
+ .unwrap_or_else(|_| panic!()),
+ _marker: PhantomData,
+ }
+ }
+}
diff --git a/betosync/submerge/distributed_time/src/hybrid_logical_clock.rs b/betosync/submerge/distributed_time/src/hybrid_logical_clock.rs
new file mode 100644
index 0000000..fd97ee2
--- /dev/null
+++ b/betosync/submerge/distributed_time/src/hybrid_logical_clock.rs
@@ -0,0 +1,242 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! The hybrid logical clock is a hybrid between a wall clock (which captures physical time), and a
+//! logical clock (which captures causality).
+//!
+//! See the documentation for [`HybridLogicalTimestamp`].
+
+use std::fmt::Debug;
+
+use arbitrary::Arbitrary;
+use derive_where::derive_where;
+use serde::{Deserialize, Serialize};
+
+use crate::{TimestampOverflow, TotalTimestamp, WallTimestampProvider};
+
+/// The hybrid logical clock is a hybrid between a wall clock (which captures physical time), and a
+/// logical clock (which captures causality).
+///
+/// The usage is similar to a [Lamport timestamp](https://en.wikipedia.org/wiki/Lamport_timestamp),
+/// which can be compared between any pair of timestamps and get a total order, such that if a
+/// happens-before b, then HLC(a) < HLC(b).
+///
+/// If two events are concurrent (neither happens before the other), we do not know whether HLC(a)
+/// or HLC(b) is larger, but the event with the later physical time (as determined by the device's
+/// local clock) will be more likely to be greater.
+///
+/// This timestamp is also tagged with its updater, guaranteeing uniqueness (i.e. HLC(a) == HLC(b)
+/// implies a == b).
+///
+/// See this paper for details and proofs about its properties:
+/// <https://cse.buffalo.edu/tech-reports/2014-04.pdf>.
+///
+/// # Params
+/// - `W`: The wall-clock timestamp provider that is used to get the current wall-clock time. HLC
+/// assumes that this wall clock time can be skewed and changed in any direction, and it
+/// won't cause HLC to violate causality. However, for concurrent events, the smaller the
+/// clock skew, the more likely it is for this timestamp to be consistent with the user's
+/// observed sequence of events.
+#[derive(Debug, Serialize, Deserialize, Arbitrary)]
+#[derive_where(Clone, PartialOrd, Ord, PartialEq, Eq, Hash; N, UnnamedHybridLogicalTimestamp)]
+#[serde(bound(serialize = "N: Serialize", deserialize = "N: Deserialize<'de>"))]
+pub struct HybridLogicalTimestamp<N>
+where
+ N: Ord + Clone,
+{
+ time: UnnamedHybridLogicalTimestamp,
+ updater: N,
+}
+
+impl<N> TotalTimestamp for HybridLogicalTimestamp<N>
+where
+ N: Ord + Clone,
+{
+ type NodeId = N;
+
+ fn new_with_context(updater: &N, timestamp_provider: &impl WallTimestampProvider) -> Self {
+ Self {
+ time: UnnamedHybridLogicalTimestamp::new(timestamp_provider, 0),
+ updater: updater.clone(),
+ }
+ }
+
+ fn increment(
+ &self,
+ updater: &N,
+ timestamp_provider: &impl WallTimestampProvider,
+ ) -> Result<Self, TimestampOverflow> {
+ Ok(Self {
+ time: self.time.increment(timestamp_provider)?,
+ updater: updater.clone(),
+ })
+ }
+}
+
+/// The unnamed version of HLC which does not record the updater node. This is not unique by itself
+/// (i.e. HLC(a) == HLC(b) does not imply a == b).
+///
+/// If you need the uniqueness property, use [`HybridLogicalTimestamp`] instead.
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Arbitrary)]
+pub(crate) struct UnnamedHybridLogicalTimestamp {
+ /// The logical time of this timestamp. When incremented, the incrementing node will take the
+ /// `max(current.logical_time, local_physical_time)`, thus preserving the monotonicity of the
+ /// values.
+ pub logical_time: u64,
+ /// The causality counter used to track the causality between different clocks that has the same
+ /// logical time. This can be non-zero due to clock skew, where the node received an event with
+ /// a "future" timestamp and tries to increment it. In that case the new `logical_time` will be
+ /// the same as the old `logical_time`, but this `causality` field will be incremented to make
+ /// sure the timestamp is greater than the old one.
+ pub causality: u8,
+}
+
+impl UnnamedHybridLogicalTimestamp {
+ /// Increment the timestamp.
+ ///
+ /// This ensures that the returned timestamp H' > self, and H' >= time_context.now().
+ pub fn increment(
+ &self,
+ time_provider: &impl WallTimestampProvider,
+ ) -> Result<Self, TimestampOverflow> {
+ let logical_time = self.logical_time.max(time_provider.now());
+ let causality = if logical_time == self.logical_time {
+ self.causality.checked_add(1).ok_or(TimestampOverflow)?
+ } else {
+ 0
+ };
+ Ok(Self {
+ logical_time,
+ causality,
+ })
+ }
+
+ /// Create a new timestamp at the given time context.
+ pub fn new(time_provider: &impl WallTimestampProvider, causality: u8) -> Self {
+ Self {
+ logical_time: time_provider.now(),
+ causality,
+ }
+ }
+}
+
+#[cfg(feature = "proto")]
+mod proto {
+ use submerge_internal_proto::{FromProto, FromProtoError, NodeMapping, ToProto};
+
+ use super::{HybridLogicalTimestamp, UnnamedHybridLogicalTimestamp};
+
+ impl FromProto for UnnamedHybridLogicalTimestamp {
+ type Proto = submerge_internal_proto::protos::submerge::HybridLogicalTimestamp;
+
+ fn from_proto(proto: &Self::Proto, _node_ids: &[String]) -> Result<Self, FromProtoError> {
+ Ok(Self {
+ logical_time: proto.logical_time,
+ causality: proto
+ .causality
+ .try_into()
+ .map_err(|_| FromProtoError::NumericOverflow)?,
+ })
+ }
+ }
+
+ impl ToProto for UnnamedHybridLogicalTimestamp {
+ type Proto = submerge_internal_proto::protos::submerge::HybridLogicalTimestamp;
+
+ fn to_proto(&self, _node_ids: &mut NodeMapping<String>) -> Self::Proto {
+ submerge_internal_proto::protos::submerge::HybridLogicalTimestamp {
+ logical_time: self.logical_time,
+ causality: self.causality.into(),
+ ..Default::default()
+ }
+ }
+ }
+
+ impl HybridLogicalTimestamp<String> {
+ /// Construct a proto from this timestamp.
+ ///
+ /// Returns a pair `(HybridLogicalTimestampProto, updater)`, where `updater` is the index of the
+ /// updater in `NodeMapping`.
+ pub fn to_proto(
+ &self,
+ node_ids: &mut NodeMapping<String>,
+ ) -> (
+ submerge_internal_proto::protos::submerge::HybridLogicalTimestamp,
+ u32,
+ ) {
+ (
+ self.time.to_proto(node_ids),
+ node_ids.get_index(&self.updater),
+ )
+ }
+
+ /// Construct a timestamp from the given proto and the updater.
+ pub fn from_proto(
+ proto: &submerge_internal_proto::protos::submerge::HybridLogicalTimestamp,
+ updater: u32,
+ node_ids: &[String],
+ ) -> Result<Self, FromProtoError> {
+ Ok(HybridLogicalTimestamp {
+ time: UnnamedHybridLogicalTimestamp::from_proto(proto, node_ids)?,
+ updater: node_ids
+ .get(updater as usize)
+ .ok_or(FromProtoError::MissingNodeId)?
+ .clone(),
+ })
+ }
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn unnamed_hlc_round_trip(hlc: UnnamedHybridLogicalTimestamp) {
+ let mut node_ids = NodeMapping::default();
+ assert_eq!(
+ UnnamedHybridLogicalTimestamp::from_proto(
+ &hlc.to_proto(&mut node_ids),
+ &node_ids.into_vec()
+ )
+ .unwrap(),
+ hlc
+ );
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn hlc_round_trip(hlc: HybridLogicalTimestamp<String>) {
+ let mut node_ids = NodeMapping::default();
+ let (timestamp_proto, updater) = hlc.to_proto(&mut node_ids);
+ assert_eq!(
+ HybridLogicalTimestamp::from_proto(×tamp_proto, updater, &node_ids.into_vec())
+ .unwrap(),
+ hlc
+ );
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::cmp::Ordering;
+
+ use crate::fake_timestamp::FakeTimestamp;
+
+ use super::UnnamedHybridLogicalTimestamp;
+
+ #[test]
+ fn test_unnamed_hybrid_logical_timestamp_ord() {
+ let t1 = UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(1_u8), 0);
+ let t2 = UnnamedHybridLogicalTimestamp::new(&FakeTimestamp(2_u8), 0);
+ assert_eq!(Ordering::Less, t1.cmp(&t2));
+ assert!(t1 < t2);
+ }
+}
diff --git a/betosync/submerge/distributed_time/src/lib.rs b/betosync/submerge/distributed_time/src/lib.rs
new file mode 100644
index 0000000..f51e02b
--- /dev/null
+++ b/betosync/submerge/distributed_time/src/lib.rs
@@ -0,0 +1,170 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Traits and type implementations for time-tracking in distributed systems.
+//!
+//! See these provided implementations:
+//! - [`VectorClock`][`vector_clock::VectorClock`]
+//! - [`HybridLogicalTimestamp`][hybrid_logical_clock::HybridLogicalTimestamp]
+//! - [`CompoundTimestamp`][compound_timestamp::CompoundTimestamp]
+//!
+//! And traits:
+//! - [`TotalTimestamp`]
+//! - [`DistributedClock`]
+//! - [`WallTimestampProvider`]
+
+pub mod compound_timestamp;
+pub mod fake_timestamp;
+pub mod hybrid_logical_clock;
+pub mod vector_clock;
+
+use arbitrary::Arbitrary;
+use std::{
+ cmp::Ordering,
+ fmt::Debug,
+ time::{Duration, SystemTime, UNIX_EPOCH},
+};
+use thiserror::Error;
+
+/// An error returned when the operation will otherwise cause the timestamp value to overflow.
+#[derive(Debug, Clone, PartialEq, Eq, Error)]
+#[error("TimestampOverflow")]
+pub struct TimestampOverflow;
+
+/// A total-ordered timestamp.
+///
+/// This timestamp must implement `Ord` and follow the requirements there. In particular, make sure
+/// that there cannot be any cycles where `a < b < c < a`.
+pub trait TotalTimestamp: Ord {
+ /// The node ID type. Can be `()` if the timestamp does not need to track metadata per-node.
+ type NodeId;
+
+ /// Create a new timestamp with the given context.
+ ///
+ /// The physical component of the timestamp comes from `time_context`, whereas the logical
+ /// component will be at its initial "bottom" state.
+ fn new_with_context(
+ updater: &Self::NodeId,
+ timestamp_provider: &impl WallTimestampProvider,
+ ) -> Self;
+
+ /// Increment the timestamp.
+ ///
+ /// The incremented timestamp must be larger than `self`.
+ fn increment(
+ &self,
+ updater: &Self::NodeId,
+ timestamp_provider: &impl WallTimestampProvider,
+ ) -> Result<Self, TimestampOverflow>
+ where
+ Self: Sized;
+
+ /// Increment the given timestamp if it is `Some`, or create a new one with the given context.
+ fn increment_or_new(
+ timestamp: Option<&Self>,
+ updater: &Self::NodeId,
+ timestamp_provider: &impl WallTimestampProvider,
+ ) -> Result<Self, TimestampOverflow>
+ where
+ Self: Sized,
+ {
+ match timestamp {
+ Some(t) => t.increment(updater, timestamp_provider),
+ None => Ok(Self::new_with_context(updater, timestamp_provider)),
+ }
+ }
+}
+
+/// A distributed clock that tracks causality, like vector clock.
+///
+/// The [`PartialOrd`] implementations must obey the happens-before causality order, and return
+/// `None` if the events are concurrent.
+pub trait DistributedClock: PartialOrd + Eq {
+ /// The node ID type. Can be `()` if the timestamp does not need to track metadata per-node.
+ type NodeId;
+
+ /// Increment the timestamp.
+ ///
+ /// The incremented timestamp must be larger than `self`.
+ fn increment(&self, ctx: &Self::NodeId) -> Result<Self, TimestampOverflow>
+ where
+ Self: Sized;
+
+ /// Calculate the least upper bound of the given timestamps and update `self` in-place.
+ ///
+ /// A resulting timestamp `T` must satisfy `self' >= self` and `self' >= other`.
+ fn least_upper_bound_in_place(&mut self, other: &Self);
+
+ /// Calculate the least upper bound of the given timestamps.
+ ///
+ /// A resulting timestamp `T` must satisfy `T >= a` and `T >= b`.
+ fn least_upper_bound(a: &Self, b: &Self) -> Self
+ where
+ Self: Clone + Sized,
+ {
+ let mut new_timestamp = a.clone();
+ new_timestamp.least_upper_bound_in_place(b);
+ new_timestamp
+ }
+}
+
+/// A provider for the current wall clock time.
+///
+/// This wall-clock is typically used as a tie-breaker for concurrent changes in last-writer-wins
+/// scenarios. The clock may go backwards in time (not required to be monotonic).
+///
+/// When used in a [`HybridLogicalTimestamp`][hybrid_logical_clock::HybridLogicalTimestamp], a
+/// skewed timestamp may unfairly bias concurrent changes to a particular node, and may affect
+/// future updates' ability to effectively tie-break using the timestamp, but it will not affect
+/// data consistency. (In the worst case, a heavily skewed clock will cause the timestamps to behave
+/// as if they are Lamport timestamps.)
+pub trait WallTimestampProvider: Clone {
+ /// Returns the number of milliseconds since Unix epoch.
+ fn now(&self) -> u64;
+}
+
+/// An implementation of [`WallTimestampProvider`] that is implemented using `std` APIs.
+#[derive(Clone, Debug, Default, Arbitrary)]
+pub struct SystemTimeProvider;
+
+impl WallTimestampProvider for SystemTimeProvider {
+ fn now(&self) -> u64 {
+ let epoch_millis = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .unwrap_or(Duration::ZERO)
+ .as_millis();
+ // Converting from u128 to u64 overflowed. Saturate to u64::MAX
+ epoch_millis.try_into().unwrap_or(u64::MAX)
+ }
+}
+
+/// Same as [`Ord`], but the order may not have a semantic meaning.
+///
+/// Implementations can be used to obtain a total order in cases that just needs an
+/// arbitrary-but-consistent order, like in [`std::collections::BTreeSet`] or
+/// [`std::collections::BTreeMap`].
+///
+/// Unlike [`Ord`], types that implement both [`NonSemanticOrd`] and [`PartialOrd`] are allowed to
+/// return `None` from [`PartialOrd::partial_cmp`]. If `partial_cmp` returns a value that's not
+/// `None`, `non_semantic_cmp` must return the same value.
+pub trait NonSemanticOrd: Eq {
+ /// This method returns an [`Ordering`] between `self` and `other`.
+ fn non_semantic_cmp(&self, other: &Self) -> Ordering;
+}
+
+impl<T: Ord> NonSemanticOrd for T {
+ fn non_semantic_cmp(&self, other: &Self) -> Ordering {
+ self.cmp(other)
+ }
+}
diff --git a/betosync/submerge/distributed_time/src/vector_clock.rs b/betosync/submerge/distributed_time/src/vector_clock.rs
new file mode 100644
index 0000000..aa07b0f
--- /dev/null
+++ b/betosync/submerge/distributed_time/src/vector_clock.rs
@@ -0,0 +1,234 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A vector clock implementation.
+//!
+//! See the documentation for [`VectorClock`].
+
+use std::{cmp::Ordering, collections::BTreeMap, hash::Hash};
+
+use arbitrary::Arbitrary;
+use derive_where::derive_where;
+use serde::{Deserialize, Serialize};
+
+use crate::{DistributedClock, NonSemanticOrd, TimestampOverflow};
+
+/// A vector clock which offers partial ordering of events.
+///
+/// See also: <https://en.wikipedia.org/wiki/Vector_clock>
+///
+/// # Params
+/// - `N`: The node ID.
+#[derive(Debug, Clone, Arbitrary, Serialize, Deserialize)]
+#[derive_where(Default)]
+#[serde(transparent)]
+pub struct VectorClock<N: Ord> {
+ dots: BTreeMap<N, u32>,
+}
+
+impl<N: Ord> VectorClock<N> {
+ /// Create a new instance from the given iterator or (node_id, clock_value) pairs.
+ pub fn new(dots: impl IntoIterator<Item = (N, u32)>) -> Self {
+ Self {
+ dots: dots.into_iter().collect(),
+ }
+ }
+
+ /// Convert this clock into its underlying map of "dots", which are the (node, version number)
+ /// pairs that represent what events happened before this clock.
+ pub fn into_dots(self) -> BTreeMap<N, u32> {
+ self.dots
+ }
+}
+
+impl<N: Ord> NonSemanticOrd for VectorClock<N> {
+ fn non_semantic_cmp(&self, other: &Self) -> Ordering {
+ self.dots.cmp(&other.dots)
+ }
+}
+
+impl<N: Ord + Hash> Hash for VectorClock<N> {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.dots.hash(state);
+ }
+}
+
+impl<N: Ord> PartialEq for VectorClock<N> {
+ fn eq(&self, other: &Self) -> bool {
+ self.dots
+ .iter()
+ .all(|(k, v)| v == other.dots.get(k).unwrap_or(&0))
+ && other
+ .dots
+ .iter()
+ .all(|(k, v)| self.dots.get(k).unwrap_or(&0) == v)
+ }
+}
+
+impl<N: Ord> Eq for VectorClock<N> {}
+
+impl<N: Ord> PartialOrd for VectorClock<N> {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ let mut compare_results = self.dots.keys().chain(other.dots.keys()).map(|k| {
+ self.dots
+ .get(k)
+ .unwrap_or(&0)
+ .cmp(other.dots.get(k).unwrap_or(&0))
+ });
+ compare_results.try_fold(Ordering::Equal, |acc, r| match (acc, r) {
+ (Ordering::Greater, Ordering::Less) => None,
+ (Ordering::Less, Ordering::Greater) => None,
+ (Ordering::Equal, r) => Some(r),
+ _ => Some(acc),
+ })
+ }
+}
+
+impl<N> DistributedClock for VectorClock<N>
+where
+ N: Ord + Clone,
+{
+ type NodeId = N;
+
+ fn increment(&self, updater: &N) -> Result<Self, TimestampOverflow> {
+ let mut copy = self.dots.clone();
+ let _ = copy.insert(
+ updater.clone(),
+ copy.get(updater)
+ .unwrap_or(&0)
+ .checked_add(1)
+ .ok_or(TimestampOverflow)?,
+ );
+ Ok(Self { dots: copy })
+ }
+
+ fn least_upper_bound_in_place(&mut self, other: &Self) {
+ for (k, v) in &other.dots {
+ let self_value = self.dots.entry(k.clone()).or_default();
+ *self_value = *v.max(self_value);
+ }
+ }
+}
+
+#[cfg(feature = "proto")]
+mod proto {
+ use submerge_internal_proto::{FromProto, FromProtoError, NodeMapping, ToProto};
+
+ use super::VectorClock;
+
+ impl FromProto for VectorClock<String> {
+ type Proto = submerge_internal_proto::protos::submerge::CompressedVectorClock;
+
+ fn from_proto(proto: &Self::Proto, node_ids: &[String]) -> Result<Self, FromProtoError> {
+ if proto.nodes.len() != proto.values.len() {
+ return Err(FromProtoError::MismatchedVectorClockLength);
+ }
+ Ok(Self {
+ dots: proto
+ .nodes
+ .iter()
+ .zip(proto.values.iter())
+ .map(|(n, v)| {
+ Ok((
+ node_ids
+ .get(*n as usize)
+ .ok_or(FromProtoError::MissingNodeId)?
+ .clone(),
+ *v,
+ ))
+ })
+ .collect::<Result<_, _>>()?,
+ })
+ }
+ }
+
+ impl VectorClock<String> {
+ /// Construct a new `VectorClock` from its transferable proto representation.
+ pub fn from_transferable_proto(
+ proto: &submerge_internal_proto::protos::submerge::VectorClock,
+ ) -> Result<Self, FromProtoError> {
+ Ok(Self {
+ dots: proto.node_clock_values.clone().into_iter().collect(),
+ })
+ }
+
+ /// Convert this `VectorClock` to its transferable proto representation.
+ pub fn to_transferable_proto(
+ &self,
+ ) -> submerge_internal_proto::protos::submerge::VectorClock {
+ submerge_internal_proto::protos::submerge::VectorClock {
+ node_clock_values: self.dots.clone().into_iter().collect(),
+ ..Default::default()
+ }
+ }
+ }
+
+ impl ToProto for VectorClock<String> {
+ type Proto = submerge_internal_proto::protos::submerge::CompressedVectorClock;
+
+ fn to_proto(&self, node_ids: &mut NodeMapping<String>) -> Self::Proto {
+ let (nodes, values) = self
+ .dots
+ .iter()
+ .map(|(node, v)| (node_ids.get_index(node), *v))
+ .unzip();
+ submerge_internal_proto::protos::submerge::CompressedVectorClock {
+ nodes,
+ values,
+ ..Default::default()
+ }
+ }
+ }
+
+ #[cfg(test)]
+ #[derive_fuzztest::proptest]
+ fn vector_clock_round_trip(vector_clock: VectorClock<String>) {
+ let mut node_ids = NodeMapping::default();
+ assert_eq!(
+ VectorClock::from_proto(&vector_clock.to_proto(&mut node_ids), &node_ids.into_vec())
+ .unwrap(),
+ vector_clock
+ );
+ assert_eq!(
+ VectorClock::from_transferable_proto(&vector_clock.to_transferable_proto()).unwrap(),
+ vector_clock
+ );
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::BTreeMap;
+
+ use crate::DistributedClock;
+
+ use super::VectorClock;
+
+ #[test]
+ fn test_inc() {
+ let mut dots = BTreeMap::new();
+ let _ = dots.insert(0, 0);
+ let _ = dots.insert(1, 0);
+ let _ = dots.insert(2, 0);
+ let clock = VectorClock { dots };
+ let incremented = clock.increment(&0).unwrap();
+
+ let mut expected_dots: BTreeMap<u16, u32> = BTreeMap::new();
+ let _ = expected_dots.insert(0, 1);
+ let _ = expected_dots.insert(1, 0);
+ let _ = expected_dots.insert(2, 0);
+
+ assert_eq!(expected_dots, incremented.dots);
+ }
+}
diff --git a/betosync/submerge/submerge/Cargo.toml b/betosync/submerge/submerge/Cargo.toml
new file mode 100644
index 0000000..4937de0
--- /dev/null
+++ b/betosync/submerge/submerge/Cargo.toml
@@ -0,0 +1,47 @@
+[package]
+name = "submerge"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+publish.workspace = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+crdt.workspace = true
+distributed_time.workspace = true
+submerge_internal_proto = { workspace = true, optional = true }
+
+arbitrary.workspace = true
+bincode = { workspace = true, optional = true }
+derive-where.workspace = true
+log.workspace = true
+protobuf = { workspace = true, optional = true }
+serde.workspace = true
+thiserror.workspace = true
+
+[features]
+default = []
+checker = ["crdt/checker", "dep:bincode"]
+proto = ["dep:protobuf", "dep:submerge_internal_proto", "crdt/proto", "distributed_time/proto"]
+enable-required-features = ["checker", "proto"]
+
+[lints]
+workspace = true
+
+[dev-dependencies]
+bincode.workspace = true
+crdt = { workspace = true, features = ["checker"] }
+derive_fuzztest.workspace = true
+hex.workspace = true
+protobuf.workspace = true
+serde_json.workspace = true
+serde.workspace = true
+
+[[test]]
+name = "bincode_size"
+required-features = ["checker"]
+
+[[test]]
+name = "proto_size"
+required-features = ["checker", "proto"]
diff --git a/betosync/submerge/submerge/fuzz/.gitignore b/betosync/submerge/submerge/fuzz/.gitignore
new file mode 100644
index 0000000..1a45eee
--- /dev/null
+++ b/betosync/submerge/submerge/fuzz/.gitignore
@@ -0,0 +1,4 @@
+target
+corpus
+artifacts
+coverage
diff --git a/betosync/submerge/submerge/fuzz/Cargo.toml b/betosync/submerge/submerge/fuzz/Cargo.toml
new file mode 100644
index 0000000..f9274b0
--- /dev/null
+++ b/betosync/submerge/submerge/fuzz/Cargo.toml
@@ -0,0 +1,43 @@
+[package]
+name = "submerge-fuzz"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+publish = false
+
+[package.metadata]
+cargo-fuzz = true
+
+[dependencies]
+crdt = { workspace = true, features = ["checker"] }
+distributed_time.workspace = true
+submerge.workspace = true
+
+arbitrary.workspace = true
+arrayvec.workspace = true
+derive_fuzztest.workspace = true
+env_logger.workspace = true
+itertools.workspace = true
+log.workspace = true
+serde.workspace = true
+thiserror.workspace = true
+
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
+
+[features]
+nolog = ["log/release_max_level_off"]
+
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = [
+ "cfg(fuzzing)",
+ "cfg(rust_analyzer)",
+] }
+
+[[bin]]
+name = "submerge_checker"
+doc = false
+
+[[bin]]
+name = "delta_round_trip"
+doc = false
diff --git a/betosync/submerge/submerge/fuzz/src/actor.rs b/betosync/submerge/submerge/fuzz/src/actor.rs
new file mode 100644
index 0000000..ae9fb2d
--- /dev/null
+++ b/betosync/submerge/submerge/fuzz/src/actor.rs
@@ -0,0 +1,544 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::{cell::RefCell, convert::Infallible, fmt::Debug, hash::RandomState, ops::Range, rc::Rc};
+
+use arbitrary::{Arbitrary, Unstructured};
+use arrayvec::ArrayVec;
+use crdt::{
+ checker::test_fakes::TriState,
+ delta::DeltaMut,
+ lww_crdt_container::LwwCrdtContainerWrite,
+ lww_map::LwwMapWrite,
+ register::RegisterWrite,
+ set::SetWrite,
+ typed_crdt::{TypedCrdt, TypedCrdtMut},
+ vector_data::VectorDataWrite,
+};
+use distributed_time::{
+ fake_timestamp::FakeTimestamp, vector_clock::VectorClock, WallTimestampProvider,
+};
+use log::debug;
+use submerge::{
+ CommitUpdateError, DataStore, DeltaDocument, Document, DocumentHandle, DocumentTransaction,
+ NetworkInterface, StorageInterface, SubmergeMap, SubmergeRegister, SubmergeSet,
+ SubmergeVectorData, UpdateContext,
+};
+
+use crate::{DeferredArbitrary, TestDataStoreConfig};
+
+/// Stores context information resulting from an actor's handling of input messages.
+///
+/// All messages generated by an actor goes through this context to be passed back up to the harness
+/// for dispatching.
+pub trait ActorContext {
+ fn send_message(&mut self, sender: u16, dest: u16, msg: ActorMessage) -> usize;
+}
+
+/// Represents an actor in the submerge simulation checker.
+///
+/// This should contain the local states for each actor, and a reference to the network.
+#[derive(Debug)]
+pub struct Actor {
+ pub id: u16,
+ local_clock: ActorClock,
+ network: ActorNetwork,
+ pub data_store: DataStore<TestDataStoreConfig>,
+}
+
+impl Actor {
+ /// Create a new actor.
+ ///
+ /// `peers` is a range of IDs of other peers known to this actor. This range can contain the
+ /// local `id`; it will be filtered out when sending a broadcast.
+ pub fn new(id: u16, peers: Range<u16>) -> Self {
+ let local_clock = ActorClock::new(id);
+ let network = ActorNetwork::new(id, peers);
+ Self {
+ id,
+ local_clock: local_clock.clone(),
+ network: network.clone(),
+ data_store: DataStore::new(id, network, ActorStorage::new(id), local_clock),
+ }
+ }
+
+ /// Create an actor on a particular document.
+ ///
+ /// The document actor mutably borrows from this actor, and is mainly an optimization to avoid
+ /// calling `borrow_or_create_document` repeatedly during the simulator run.
+ pub fn document_actor(&mut self) -> DocumentActor {
+ DocumentActor {
+ id: self.id,
+ local_clock: self.local_clock.clone(),
+ network: self.network.clone(),
+ doc_handle: self.data_store.open_or_create_document("test_doc").unwrap(),
+ }
+ }
+}
+
+/// Same as [`Actor`], but only operates on a single document rather than a whole [`DataStore`].
+///
+/// This is mainly an optimization to avoid calling `borrow_or_create_document` repeatedly during
+/// the simulator run.
+#[derive(Debug)]
+pub struct DocumentActor {
+ pub id: u16,
+ local_clock: ActorClock,
+ network: ActorNetwork,
+ pub doc_handle: DocumentHandle<TestDataStoreConfig>,
+}
+
+impl DocumentActor {
+ pub fn deliver_message(
+ &mut self,
+ sender_id: u16,
+ msg: ActorMessage,
+ ctx: &mut impl ActorContext,
+ ) {
+ match msg {
+ ActorMessage::DeltaDocument {
+ update_message,
+ merge_to_transaction,
+ } => {
+ let result = if merge_to_transaction {
+ let mut transaction = DocumentTransaction::new(&mut self.doc_handle);
+ let merge_result = transaction.merge_network_delta(&update_message);
+ if merge_result.is_err() {
+ merge_result
+ } else {
+ transaction.commit().map_err(|e| match e {
+ submerge::TransactionError::StorageError(storage_err) => {
+ CommitUpdateError::StorageError(storage_err)
+ }
+ _ => unreachable!(
+ "The simulator should not overflow the timestamp with its limited input size"
+ ),
+ })
+ }
+ } else {
+ self.doc_handle.commit_network_update(&update_message)
+ };
+ match result {
+ Ok(()) => {}
+ Err(CommitUpdateError::StorageError(_)) => unreachable!(),
+ Err(CommitUpdateError::OlderBaseNeeded {
+ required_base_version,
+ }) => {
+ let msg_id = ctx.send_message(
+ self.id,
+ sender_id,
+ ActorMessage::RequestDelta {
+ base_version: required_base_version.clone(),
+ },
+ );
+ debug!(
+ "Node {} OlderBaseNeeded. Version={required_base_version:?}. Sent msg #{msg_id}",
+ self.id,
+ );
+ }
+ }
+ }
+ ActorMessage::RequestDelta { base_version } => {
+ let delta = Rc::new(self.doc_handle.snapshot().calculate_delta(base_version));
+ let msg_id = ctx.send_message(
+ self.id,
+ sender_id,
+ ActorMessage::DeltaDocument {
+ update_message: delta.clone(),
+ merge_to_transaction: true,
+ },
+ );
+ debug!(
+ "Node {} Processing delta request. Sent response #{msg_id}. Delta = {:?}",
+ self.id, delta
+ );
+ }
+ }
+ }
+
+ fn change_clock(&mut self, new_clock_value: u64, _ctx: &mut impl ActorContext) {
+ if let Ok(clock_value) = u8::try_from(new_clock_value) {
+ self.local_clock.timestamp.borrow_mut().0 = clock_value;
+ }
+ }
+
+ pub fn apply_op(
+ &mut self,
+ op: ActorOp,
+ ctx: &mut impl ActorContext,
+ unstructured: &mut Unstructured,
+ ) -> arbitrary::Result<()> {
+ match op {
+ ActorOp::NodeOp { key, op } => {
+ debug!("Node {:?} setting {:?}", self.id, key);
+ let mut transaction = DocumentTransaction::new(&mut self.doc_handle);
+ let (ctx, mut doc) = transaction.get_context_and_root_mut();
+ match doc
+ .get_child_or_default::<SubmergeMap<TestDataStoreConfig>>(ctx)
+ .expect("The simulator should not overflow the timestamp with its limited input size")
+ {
+ Some(root_map) => {
+ apply_op_on_node(root_map, ctx, &key, op, unstructured)?;
+ }
+ None => {
+ debug!("Cannot set field with incompatible type.")
+ }
+ };
+ match transaction.commit() {
+ Ok(()) => {}
+ Err(e) => debug!("Transaction error: {e:?}"),
+ };
+ debug!(
+ "Node {:?} set done. New state={:?}",
+ self.id,
+ self.doc_handle.snapshot(),
+ );
+ }
+ ActorOp::ChangeClock { timestamp } => {
+ debug!("Node {:?} setting clock to {:?}", self.id, timestamp.0);
+ self.change_clock(timestamp.0 as u64, ctx);
+ }
+ }
+ self.network.flush_to_context(ctx);
+
+ fn apply_op_on_node<K: Debug>(
+ mut map: DeltaMut<SubmergeMap<TestDataStoreConfig>>,
+ context: &UpdateContext<TestDataStoreConfig>,
+ key: &[K],
+ op: NodeOp,
+ unstructured: &mut Unstructured,
+ ) -> Result<(), arbitrary::Error> {
+ let string_key = format!("{:?}", key[0]);
+ if key.len() == 1 {
+ match op {
+ NodeOp::ChangeType { new_type } => {
+ let value: TypedCrdt<_, _> = match new_type {
+ SubmergeType::Register => {
+ SubmergeRegister::<TestDataStoreConfig>::default().into()
+ }
+ SubmergeType::Set => {
+ SubmergeSet::<TestDataStoreConfig>::default().into()
+ }
+ SubmergeType::VectorData => {
+ SubmergeVectorData::<TestDataStoreConfig>::default().into()
+ }
+ SubmergeType::Map => {
+ SubmergeMap::<TestDataStoreConfig>::default().into()
+ }
+ SubmergeType::None => {
+ if let Err(e) = map.remove_value(context, string_key) {
+ debug!("Cannot set field. {e:?}");
+ }
+ return Ok(());
+ }
+ };
+ if let Err(e) = map.set_value(context, string_key, value) {
+ debug!("Cannot set value. {e:?}")
+ };
+ }
+ NodeOp::ModifyExisting {
+ op: mut existing_node_op,
+ } => match map.get_value_mut(context, string_key.clone()).unwrap() {
+ Some(TypedCrdtMut::Map(_)) => {}
+ Some(TypedCrdtMut::Set(set)) => {
+ let set_op = existing_node_op.instantiate_set(unstructured)?;
+ set_op.apply(set);
+ }
+ Some(TypedCrdtMut::Register(register)) => {
+ let reg_op = existing_node_op.instantiate_register(unstructured)?;
+ reg_op.apply(context, register);
+ }
+ Some(TypedCrdtMut::VectorData(vector_data)) => {
+ let vector_data_op =
+ existing_node_op.instantiate_vector_data(unstructured)?;
+ vector_data_op.apply(context, vector_data);
+ }
+ None => {}
+ },
+ }
+ } else {
+ match map
+ .get_child_or_default::<SubmergeMap<TestDataStoreConfig>>(context, string_key)
+ .expect("The simulator should not overflow the timestamp with its limited input size")
+ {
+ Some(child_map) => {
+ apply_op_on_node(child_map, context, &key[1..], op, unstructured)?;
+ }
+ None => {
+ debug!("Cannot set field. Incompatible type");
+ }
+ }
+ }
+ Ok(())
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum ActorMessage {
+ DeltaDocument {
+ update_message: Rc<DeltaDocument<TestDataStoreConfig>>,
+ merge_to_transaction: bool,
+ },
+ RequestDelta {
+ base_version: VectorClock<u16>,
+ },
+}
+
+fn arbitrary_nonempty_array_vec<'a, T: Arbitrary<'a>, const CAP: usize>(
+ u: &mut Unstructured<'a>,
+) -> arbitrary::Result<ArrayVec<T, CAP>> {
+ let len = u.int_in_range(1..=CAP)?.min(u.len());
+ if u.is_empty() {
+ return Err(arbitrary::Error::NotEnoughData);
+ }
+ (0..len).map(|_| u.arbitrary()).collect::<Result<_, _>>()
+}
+
+#[derive(Clone, Debug, Arbitrary)]
+pub enum ActorOp {
+ NodeOp {
+ #[arbitrary(with = arbitrary_nonempty_array_vec)]
+ key: ArrayVec<TriState, 3>,
+ op: NodeOp,
+ },
+ ChangeClock {
+ timestamp: FakeTimestamp,
+ },
+}
+
+#[derive(Clone, Debug, Arbitrary)]
+pub enum NodeOp {
+ ChangeType {
+ new_type: SubmergeType,
+ },
+ ModifyExisting {
+ #[arbitrary(with = |_| Ok(DeferredArbitrary::Pending))]
+ op: DeferredArbitrary<ExistingNodeOp>,
+ },
+}
+
+#[derive(Clone, Debug, Arbitrary)]
+pub enum ExistingNodeOp {
+ RegOp(RegOp),
+ SetOp(SetOp),
+ VectorDataOp(VectorDataOp),
+}
+
+impl DeferredArbitrary<ExistingNodeOp> {
+ fn instantiate_set(&mut self, u: &mut Unstructured) -> arbitrary::Result<&SetOp> {
+ let set_op: SetOp = u.arbitrary()?;
+ match self.instantiate_with(|| Ok(ExistingNodeOp::SetOp(set_op)))? {
+ ExistingNodeOp::SetOp(op) => Ok(op),
+ _ => unreachable!(),
+ }
+ }
+
+ fn instantiate_register(&mut self, u: &mut Unstructured) -> arbitrary::Result<&RegOp> {
+ let reg_op: RegOp = u.arbitrary()?;
+ match self.instantiate_with(|| Ok(ExistingNodeOp::RegOp(reg_op)))? {
+ ExistingNodeOp::RegOp(op) => Ok(op),
+ _ => unreachable!(),
+ }
+ }
+
+ fn instantiate_vector_data(
+ &mut self,
+ u: &mut Unstructured,
+ ) -> arbitrary::Result<&VectorDataOp> {
+ let vector_data_op: VectorDataOp = u.arbitrary()?;
+ match self.instantiate_with(|| Ok(ExistingNodeOp::VectorDataOp(vector_data_op)))? {
+ ExistingNodeOp::VectorDataOp(op) => Ok(op),
+ _ => unreachable!(),
+ }
+ }
+}
+
+#[derive(Clone, Debug, Arbitrary)]
+pub enum RegOp {
+ Set { value: u8 },
+}
+
+impl RegOp {
+ fn apply(
+ &self,
+ ctx: &UpdateContext<TestDataStoreConfig>,
+ mut reg: impl RegisterWrite<Vec<u8>, VectorClock<u16>>,
+ ) {
+ match self {
+ RegOp::Set { value } => {
+ let _ = reg.set(ctx, vec![*value]);
+ }
+ }
+ }
+}
+
+#[derive(Clone, Debug, Arbitrary)]
+pub enum SetOp {
+ Modify { added: Vec<u8>, removed: Vec<u8> },
+}
+
+impl SetOp {
+ fn apply(&self, mut set: impl SetWrite<Vec<u8>, RandomState>) {
+ match self {
+ SetOp::Modify { added, removed } => {
+ for v in added {
+ let _ = set.add(vec![*v]);
+ }
+ for v in removed {
+ set.remove(vec![*v]);
+ }
+ }
+ }
+ }
+}
+
+#[derive(Clone, Debug, Arbitrary)]
+pub enum VectorDataOp {
+ Set { value: Option<u8> },
+}
+
+impl VectorDataOp {
+ fn apply(
+ &self,
+ ctx: &UpdateContext<TestDataStoreConfig>,
+ mut vector_data: impl VectorDataWrite<u16, Vec<u8>>,
+ ) {
+ match self {
+ VectorDataOp::Set { value } => {
+ let _ = vector_data.set(ctx.node_id, value.map(|v| vec![v]));
+ }
+ }
+ }
+}
+
+#[derive(Debug, Clone, Arbitrary)]
+pub enum SubmergeType {
+ Register,
+ Set,
+ VectorData,
+ Map,
+ None,
+}
+
+/// The local clock for an actor.
+///
+/// The contained timestamp is put in `Rc<RefCell<_>>` so that it can be mutated (to simulate a
+/// clock value change) while it is held by the data store.
+#[derive(Debug, Clone)]
+pub struct ActorClock {
+ timestamp: Rc<RefCell<FakeTimestamp>>,
+}
+
+impl ActorClock {
+ pub fn new(_id: u16) -> Self {
+ Self {
+ timestamp: Rc::new(RefCell::new(FakeTimestamp(100))),
+ }
+ }
+}
+
+impl WallTimestampProvider for ActorClock {
+ fn now(&self) -> u64 {
+ self.timestamp.borrow().now()
+ }
+}
+
+/// Network implementation that buffers the messages.
+///
+/// This simulates locally buffered messages that are pending to be sent to other peers. In
+/// `apply_op`, the mutation operations generate messages which are then buffered here. Before
+/// returning, `apply_op` calls `flush_to_context` to flush these outgoing messages to the
+/// `NetworkHarness` which is responsible for scrambling the message order and delivering them to
+/// other peers.
+#[derive(Debug, Clone)]
+pub struct ActorNetwork {
+ self_id: u16,
+ peers: Range<u16>,
+ buffered_messages: Rc<RefCell<ArrayVec<ActorMessage, 10>>>,
+}
+
+impl ActorNetwork {
+ pub fn new(id: u16, peers: Range<u16>) -> Self {
+ Self {
+ self_id: id,
+ peers,
+ buffered_messages: Default::default(),
+ }
+ }
+
+ pub fn flush_to_context(&mut self, ctx: &mut impl ActorContext) {
+ let mut messages = self.buffered_messages.borrow_mut();
+ while let Some(msg) = messages.pop() {
+ for peer in self.peers.clone() {
+ if peer != self.self_id {
+ let msg_id = ctx.send_message(self.self_id, peer, msg.clone());
+ debug!(
+ "Node {sender} sent message #{msg_id} to Node {dest}",
+ sender = self.self_id,
+ dest = peer
+ );
+ }
+ }
+ }
+ }
+}
+
+impl NetworkInterface<TestDataStoreConfig> for ActorNetwork {
+ fn on_new_update(&mut self, _doc_id: &str, document: DeltaDocument<TestDataStoreConfig>) {
+ self.buffered_messages
+ .borrow_mut()
+ .push(ActorMessage::DeltaDocument {
+ update_message: Rc::new(document),
+ merge_to_transaction: false,
+ })
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct ActorStorage {
+ id: u16,
+ pub persisted_doc: Option<Document<TestDataStoreConfig>>,
+}
+
+impl ActorStorage {
+ pub fn new(id: u16) -> Self {
+ Self {
+ id,
+ persisted_doc: None,
+ }
+ }
+}
+
+impl StorageInterface<TestDataStoreConfig> for ActorStorage {
+ type Error = Infallible;
+
+ fn on_new_update(
+ &mut self,
+ _doc_id: &str,
+ document: &Document<TestDataStoreConfig>,
+ ) -> Result<(), Infallible> {
+ debug!("Writing to storage of {:#?}: {document:?}", self.id);
+ self.persisted_doc = Some(document.clone());
+ Ok(())
+ }
+
+ fn read_from_storage(
+ &self,
+ _doc_id: &str,
+ ) -> Result<Option<Document<TestDataStoreConfig>>, Self::Error> {
+ Ok(self.persisted_doc.clone())
+ }
+}
diff --git a/betosync/submerge/submerge/fuzz/src/bin/delta_round_trip.rs b/betosync/submerge/submerge/fuzz/src/bin/delta_round_trip.rs
new file mode 100644
index 0000000..688f9f2
--- /dev/null
+++ b/betosync/submerge/submerge/fuzz/src/bin/delta_round_trip.rs
@@ -0,0 +1,27 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+#![cfg_attr(fuzzing, no_main)]
+
+use submerge::Document;
+use submerge_fuzz::TestDataStoreConfig;
+
+#[derive_fuzztest::fuzztest]
+fn delta_round_trip(doc: Document<TestDataStoreConfig>) {
+ let roundtrip = doc
+ .clone()
+ .into_full_delta()
+ .into_document(&Document::default());
+ assert_eq!(doc, roundtrip.unwrap());
+}
diff --git a/betosync/submerge/submerge/fuzz/src/bin/submerge_checker.rs b/betosync/submerge/submerge/fuzz/src/bin/submerge_checker.rs
new file mode 100644
index 0000000..0ef3070
--- /dev/null
+++ b/betosync/submerge/submerge/fuzz/src/bin/submerge_checker.rs
@@ -0,0 +1,196 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! A simulation checker for the `submerge` implementation.
+
+#![cfg_attr(fuzzing, no_main)]
+
+use std::collections::BTreeMap;
+
+use arbitrary::{Arbitrary, Unstructured};
+
+use itertools::Itertools;
+use log::debug;
+use submerge_fuzz::{
+ actor::{Actor, ActorContext, ActorMessage, ActorOp},
+ log_msg, DeferredArbitrary, ExtraEntropy,
+};
+
+#[derive_fuzztest::fuzztest]
+fn simulation(sim: SubmergeSimulation, arb: ExtraEntropy) {
+ env_logger::builder().is_test(true).try_init();
+ sim.test(arb)
+}
+
+#[derive(Debug, Default)]
+pub struct NetworkHarness {
+ messages: BTreeMap<usize, MessageEnvelope>,
+ next_message_id: usize,
+}
+
+#[derive(Debug, Clone)]
+pub struct MessageEnvelope {
+ message: ActorMessage,
+ message_id: usize,
+ sender: u16,
+ dest: u16,
+}
+
+impl ActorContext for NetworkHarness {
+ fn send_message(&mut self, sender: u16, dest: u16, msg: ActorMessage) -> usize {
+ let id = self.next_message_id;
+ let old_value = self.messages.insert(
+ id,
+ MessageEnvelope {
+ message: msg,
+ message_id: id,
+ sender,
+ dest,
+ },
+ );
+ assert!(old_value.is_none());
+ self.next_message_id += 1;
+ id
+ }
+}
+
+const NUM_ACTORS: u16 = 4;
+
+#[derive(Debug, Clone, Arbitrary)]
+enum SimulatorOp {
+ ActorOp {
+ #[arbitrary(with = |u: &mut Unstructured| u.int_in_range(0..=(NUM_ACTORS - 1)))]
+ node: u16,
+ op: ActorOp,
+ },
+ DeliverMessage {
+ #[arbitrary(with = |_: &mut Unstructured| Ok(DeferredArbitrary::Pending))]
+ index: DeferredArbitrary<usize>,
+ consume: bool,
+ },
+}
+
+#[derive(Debug, Clone, Arbitrary)]
+pub struct SubmergeSimulation {
+ ops: Vec<SimulatorOp>,
+}
+
+impl SubmergeSimulation {
+ pub fn test(self, mut arb: ExtraEntropy) {
+ let mut unstructured = arb.unstructured();
+ let mut network = NetworkHarness::default();
+ let mut actors: [_; NUM_ACTORS as usize] =
+ std::array::from_fn(|id| Actor::new(u16::try_from(id).unwrap(), 0..NUM_ACTORS));
+ let mut document_actors = actors.map_mut(|actor| actor.document_actor());
+ let ops_len = self.ops.len();
+ let _ = (|| -> arbitrary::Result<_> {
+ for op in self.ops {
+ if unstructured.is_empty() {
+ break;
+ }
+ match op {
+ SimulatorOp::ActorOp { node, op } => {
+ document_actors[node as usize].apply_op(
+ op.clone(),
+ &mut network,
+ &mut unstructured,
+ )?;
+ }
+ SimulatorOp::DeliverMessage { mut index, consume } => {
+ let index = index.instantiate_with(|| {
+ unstructured.choose_index(network.next_message_id)
+ })?;
+ let msg_option = if consume {
+ network.messages.remove(index)
+ } else {
+ network.messages.get(index).cloned()
+ };
+ if let Some(msg_envelope) = msg_option {
+ debug!(
+ "Delivering message #{id} ({sender} -> {dest}). Consume={consume}",
+ id = msg_envelope.message_id,
+ sender = msg_envelope.sender,
+ dest = msg_envelope.dest
+ );
+ document_actors[msg_envelope.dest as usize].deliver_message(
+ msg_envelope.sender,
+ msg_envelope.message,
+ &mut network,
+ );
+ }
+ }
+ }
+ }
+ Ok(())
+ })();
+ log_msg!(
+ "==> Executing {} operations. {} messages generated",
+ ops_len,
+ network.next_message_id
+ );
+
+ while let Some((_, msg_envelope)) = network.messages.pop_last() {
+ debug!(
+ "Delivering remaining message #{id} ({sender} -> {dest})",
+ id = msg_envelope.message_id,
+ sender = msg_envelope.sender,
+ dest = msg_envelope.dest
+ );
+ document_actors[msg_envelope.dest as usize].deliver_message(
+ msg_envelope.sender,
+ msg_envelope.message,
+ &mut network,
+ );
+ }
+
+ for (actor1, actor2) in document_actors.iter().tuple_windows() {
+ // All nodes should have the same in-memory representation
+ assert_eq!(
+ actor1.doc_handle.snapshot(),
+ actor2.doc_handle.snapshot(),
+ "Snapshots for actors {} and {} differ",
+ actor1.id,
+ actor2.id
+ );
+ }
+
+ for actor in actors.iter() {
+ // All nodes should have the same persisted representation
+ assert_eq!(
+ actor.data_store.snapshot("test_doc"),
+ actor.data_store.storage().persisted_doc,
+ "Actor {}: in-memory snapshot should match persisted document",
+ actor.id
+ );
+ }
+ }
+}
+
+trait ArrayExt<T, const N: usize> {
+ /// Similar to `array.map`, but operates on mutable references of the elements instead of
+ /// consuming the original array.
+ fn map_mut<'t, R>(&'t mut self, f: impl FnMut(&'t mut T) -> R) -> [R; N]
+ where
+ T: 't;
+}
+
+impl<T, const N: usize> ArrayExt<T, N> for [T; N] {
+ fn map_mut<'t, R>(&'t mut self, f: impl FnMut(&'t mut T) -> R) -> [R; N]
+ where
+ T: 't,
+ {
+ let mut iter = self.iter_mut().map(f);
+ std::array::from_fn(|_| iter.next().unwrap())
+ }
+}
diff --git a/betosync/submerge/submerge/fuzz/src/fuzz_metrics.rs b/betosync/submerge/submerge/fuzz/src/fuzz_metrics.rs
new file mode 100644
index 0000000..188489c
--- /dev/null
+++ b/betosync/submerge/submerge/fuzz/src/fuzz_metrics.rs
@@ -0,0 +1,30 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::cell::Cell;
+
+thread_local! {
+ pub static COUNTER: Cell<usize> = Cell::default();
+}
+
+/// Log a message during fuzzing. This will be executed one every 8192 fuzz runs so as to not spam
+/// the terminal output.
+#[macro_export]
+macro_rules! log_msg {
+ ($($arg:tt)*) => {
+ if $crate::fuzz_metrics::COUNTER.replace($crate::fuzz_metrics::COUNTER.get().wrapping_add(1)) % 8192 == 0 {
+ eprintln!($($arg)*);
+ }
+ };
+}
diff --git a/betosync/submerge/submerge/fuzz/src/lib.rs b/betosync/submerge/submerge/fuzz/src/lib.rs
new file mode 100644
index 0000000..352c05c
--- /dev/null
+++ b/betosync/submerge/submerge/fuzz/src/lib.rs
@@ -0,0 +1,109 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::fmt::Debug;
+
+use actor::{ActorClock, ActorContext, ActorMessage, ActorNetwork, ActorStorage};
+use arbitrary::{Arbitrary, Unstructured};
+use submerge::DataStoreConfig;
+
+pub mod actor;
+pub mod fuzz_metrics;
+
+#[derive(Debug)]
+pub struct TestDataStoreConfig;
+
+impl DataStoreConfig for TestDataStoreConfig {
+ type ValueType = Vec<u8>;
+ type NodeId = u16;
+ type Network = ActorNetwork;
+ type Storage = ActorStorage;
+ type TimestampProvider = ActorClock;
+}
+
+#[derive(Debug, Clone)]
+pub struct MessageEnvelope {
+ pub message: ActorMessage,
+ pub message_id: usize,
+ pub sender: u16,
+ pub dest: u16,
+}
+
+/// A network harness simulating an eventual-delivery system.
+///
+/// This system may deliver messages out-of-order or deliver duplicate messages, but it guarantees
+/// that each message is delivered at least once.
+#[derive(Debug, Default)]
+pub struct NetworkHarness {
+ pub messages: Vec<MessageEnvelope>,
+ pub next_message_id: usize,
+}
+
+impl ActorContext for NetworkHarness {
+ /// Called by an actor when it wants to send a message.
+ fn send_message(&mut self, sender: u16, dest: u16, msg: ActorMessage) -> usize {
+ let id = self.next_message_id;
+ self.messages.push(MessageEnvelope {
+ message: msg,
+ message_id: id,
+ sender,
+ dest,
+ });
+ self.next_message_id += 1;
+ id
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct ExtraEntropy(Vec<u8>);
+
+impl<'a> Arbitrary<'a> for ExtraEntropy {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ // Custom Arbitrary implementation that takes every byte that's remaining.
+ Ok(ExtraEntropy(u.bytes(u.len())?.to_vec()))
+ }
+}
+
+impl ExtraEntropy {
+ pub fn unstructured(&mut self) -> Unstructured<'_> {
+ Unstructured::new(&self.0)
+ }
+}
+
+/// An arbitrary implementation that deferred its instantiation.
+///
+/// Instead of instantiating the whole test setup up-front, together with [`ExtraEntropy`] this
+/// allows part of the test to be instantiated at runtime. This avoids wasting entropy (incoming
+/// bytes from the fuzzer) on operations that won't be applicable in the current state.
+#[derive(Debug, Clone)]
+pub enum DeferredArbitrary<T> {
+ Pending,
+ Instantiated(T),
+}
+
+impl<T> DeferredArbitrary<T> {
+ pub fn instantiate_with<'a>(
+ &mut self,
+ f: impl FnOnce() -> arbitrary::Result<T>,
+ ) -> arbitrary::Result<&T>
+ where
+ T: Arbitrary<'a> + Debug,
+ {
+ *self = Self::Instantiated(f()?);
+ match self {
+ DeferredArbitrary::Pending => unreachable!(),
+ DeferredArbitrary::Instantiated(value) => Ok(value),
+ }
+ }
+}
diff --git a/betosync/submerge/submerge/src/lib.rs b/betosync/submerge/submerge/src/lib.rs
new file mode 100644
index 0000000..1839af0
--- /dev/null
+++ b/betosync/submerge/submerge/src/lib.rs
@@ -0,0 +1,697 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! submerge — data store designed for syncing
+//!
+//! submerge is an always-available, eventually consistent data store designed for use to
+//! synchronize data across multiple devices.
+//!
+//! # Data model
+//!
+//! The data model for submerge consists of four types:
+//! 1. `Map` — the map is the main structural type in submerge's data model. The key for the map is
+//! a string, and the value for the map is recursively another CRDT type.
+//! 2. `Set` — a CRDT set (_causal-length set_) that can contain multiple unordered values.
+//! 3. `Register` — a CRDT register that can contain a single atomically updated value.
+//! 4. `VectorData` — a vector with an entry for each node in the system, where entries can only be
+//! written to by its owner.
+//!
+//! The value type is currently defined as raw bytes, but this can be changed in the future to allow
+//! dynamic typing.
+//!
+//! # Interfaces
+//! See [`NetworkInterface`] and [`StorageInterface`].
+
+use std::{
+ borrow::BorrowMut, cmp::Ordering, collections::hash_map::RandomState, fmt::Debug, hash::Hash,
+};
+
+use arbitrary::Arbitrary;
+use crdt::{
+ delta::DeltaMut,
+ lww_crdt_container::LwwCrdtContainer,
+ lww_map::LwwMap,
+ register::Register,
+ set::Set,
+ typed_crdt::TypedCrdt,
+ vector_data::{VectorData, VersionOverflow},
+ ContentEq, CrdtState,
+};
+use derive_where::derive_where;
+use distributed_time::{
+ hybrid_logical_clock::HybridLogicalTimestamp, vector_clock::VectorClock, DistributedClock,
+ TimestampOverflow, WallTimestampProvider,
+};
+use log::{debug, info};
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+
+#[cfg(feature = "proto")]
+pub mod proto;
+
+#[cfg(any(test, feature = "checker"))]
+pub mod testing;
+
+/// The ID of a document.
+pub type DocId = String;
+
+type ValueType<Conf> = <Conf as DataStoreConfig>::ValueType;
+type NodeId<Conf> = <Conf as DataStoreConfig>::NodeId;
+
+/// Type alias for the map type for the given [`DataStoreConfig`].
+pub type SubmergeMap<Conf> = LwwMap<
+ String,
+ TypedCrdt<ValueType<Conf>, NodeId<Conf>>,
+ HybridLogicalTimestamp<NodeId<Conf>>,
+ NodeId<Conf>,
+>;
+/// Type alias for the register type for the given [`DataStoreConfig`].
+pub type SubmergeRegister<Conf> = Register<ValueType<Conf>, VectorClock<NodeId<Conf>>>;
+/// Type alias for the set type for the given [`DataStoreConfig`].
+pub type SubmergeSet<Conf> = Set<ValueType<Conf>, RandomState>;
+/// Type alias for the vector data type for the given [`DataStoreConfig`].
+pub type SubmergeVectorData<Conf> = VectorData<NodeId<Conf>, ValueType<Conf>>;
+
+/// Type alias for the container type for the given [`DataStoreConfig`].
+pub type SubmergeContainer<Conf> = LwwCrdtContainer<
+ TypedCrdt<ValueType<Conf>, NodeId<Conf>>,
+ HybridLogicalTimestamp<NodeId<Conf>>,
+ NodeId<Conf>,
+>;
+
+/// A submerge document that implements the data model as described in the module documentation.
+///
+/// This document is a CRDT and guarantees eventual consistency. Instances of document should be
+/// created by a [`DataStore`] via [`DataStore::open_or_create_document`].
+#[derive(Serialize, Deserialize)]
+#[derive_where(Default, Debug, Clone, PartialEq, Eq)]
+#[serde(bound(
+ serialize = "Conf::NodeId: Serialize, Conf::ValueType: Serialize",
+ deserialize = "Conf::NodeId: for<'a> Deserialize<'a>, Conf::ValueType: for<'a> Deserialize<'a>"
+))]
+pub struct Document<Conf: DataStoreConfig> {
+ /// The root container stored in this document.
+ pub root: SubmergeContainer<Conf>,
+ /// The version of this document. This is a vector clock that is incremented every time a
+ /// transaction is committed, used to ensure causal consistency is not violated by incoming
+ /// update messages.
+ pub version: VectorClock<Conf::NodeId>,
+}
+
+// Manual implementation of Arbitrary to specify the correct trait bounds.
+impl<'a, Conf: DataStoreConfig> Arbitrary<'a> for Document<Conf>
+where
+ Conf::NodeId: Arbitrary<'a>,
+ Conf::ValueType: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self {
+ root: u.arbitrary()?,
+ version: u.arbitrary()?,
+ })
+ }
+}
+
+impl<Conf: DataStoreConfig> Document<Conf> {
+ /// Calculates the delta needed to update from the given `base_version`.
+ ///
+ /// The `base_version` must be a version vector from a replica of the same document. The
+ /// returned document can be applied using [`DeltaDocument::into_document`] by supplying a base
+ /// that is at least as new as `base_version`.
+ pub fn calculate_delta(&self, base_version: VectorClock<Conf::NodeId>) -> DeltaDocument<Conf> {
+ if self.version <= base_version {
+ // Return an empty delta
+ return DeltaDocument {
+ root: SubmergeContainer::<Conf>::default(),
+ base_version,
+ version: self.version.clone(),
+ };
+ }
+ DeltaDocument {
+ root: self.root.calculate_delta(&base_version),
+ base_version,
+ version: self.version.clone(),
+ }
+ }
+
+ /// Transform this document into a full delta document.
+ ///
+ /// This "full" delta document is going to contain all data in this document, thus allowing it
+ /// to be applied from any version.
+ pub fn into_full_delta(self) -> DeltaDocument<Conf> {
+ DeltaDocument {
+ root: self.root,
+ base_version: VectorClock::default(),
+ version: self.version,
+ }
+ }
+}
+
+/// A delta document that holds a delta update for a document.
+///
+/// This delta contains information about the changes between [`base_version`][Self::base_version]
+/// and [`version`][Self::version], and can be merged with documents as least as new as
+/// `base_version` without violating causal consistency.
+///
+/// This is serialized and sent over the wire when an update is made or requested by a remote peer.
+#[derive(Serialize, Deserialize)]
+#[derive_where(Clone, PartialEq, Eq)]
+#[derive_where(Debug)]
+#[serde(bound(
+ serialize = "Conf::NodeId: Serialize, Conf::ValueType: Serialize",
+ deserialize = "Conf::NodeId: for<'a> Deserialize<'a>, Conf::ValueType: for<'a> Deserialize<'a>"
+))]
+pub struct DeltaDocument<Conf: DataStoreConfig> {
+ /// The root of the delta component of the document.
+ pub root: SubmergeContainer<Conf>,
+ /// The base version at which this delta starts. This represents the version of the base
+ /// component at which this delta is being constructed, meaning that to recreate the resulting
+ /// document, the base document must be at least as new as this version.
+ pub base_version: VectorClock<Conf::NodeId>,
+ /// The version at which the delta document is at.
+ pub version: VectorClock<Conf::NodeId>,
+}
+
+impl<'a, Conf: DataStoreConfig> Arbitrary<'a> for DeltaDocument<Conf>
+where
+ Conf::NodeId: Arbitrary<'a>,
+ Conf::ValueType: Arbitrary<'a>,
+{
+ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self {
+ root: u.arbitrary()?,
+ base_version: u.arbitrary()?,
+ version: u.arbitrary()?,
+ })
+ }
+}
+
+/// Error returned when a delta is not applicable to the given base.
+#[derive(Debug, Clone, Error)]
+#[error("Delta is not applicable to the given base")]
+pub struct DeltaNotApplicable;
+
+impl<Conf: DataStoreConfig> DeltaDocument<Conf> {
+ /// Merge this delta document into the given `base`.
+ ///
+ /// The given `base` should be generated with [`Document::calculate_delta`] or
+ /// [`DeltaOwned::into_delta`][crdt::delta::DeltaOwned::into_delta], and must be at least as new
+ /// as the [`base_version`][Self::base_version], or `Err(DeltaNotApplicable)` will be returned.
+ ///
+ /// The resulting document will contain all changes from both this delta and the `base`, merged
+ /// together according to the CRDT definitions.
+ pub fn into_document(
+ self,
+ base: &Document<Conf>,
+ ) -> Result<Document<Conf>, DeltaNotApplicable> {
+ if let None | Some(Ordering::Less) = base.version.partial_cmp(&self.base_version) {
+ Err(DeltaNotApplicable)
+ } else {
+ Ok(Document {
+ root: CrdtState::merge(&self.root, &base.root),
+ version: VectorClock::least_upper_bound(&self.version, &base.version),
+ })
+ }
+ }
+}
+
+/// Error when trying to commit a transaction to a document.
+#[derive(Error)]
+#[derive_where(Debug; StorageError<Conf>)]
+pub enum TransactionError<Conf: DataStoreConfig> {
+ /// Cannot perform the transaction because the timestamp overflowed.
+ #[error("Cannot perform the transaction because the timestamp overflowed.")]
+ TimestampOverflow,
+ /// Cannot perform the transaction because the version number overflowed.
+ #[error("Cannot perform the transaction because the version number overflowed.")]
+ VersionOverflow,
+ /// The storage interface failed to persist the resulting data.
+ #[error(transparent)]
+ StorageError(StorageError<Conf>),
+}
+
+impl<Conf: DataStoreConfig> From<TimestampOverflow> for TransactionError<Conf> {
+ fn from(_: TimestampOverflow) -> Self {
+ TransactionError::TimestampOverflow
+ }
+}
+
+impl<Conf: DataStoreConfig> From<VersionOverflow> for TransactionError<Conf> {
+ fn from(_: VersionOverflow) -> Self {
+ TransactionError::VersionOverflow
+ }
+}
+
+/// The network interface for a data store.
+///
+/// When the data store is modified, updated documents will be generated and passed to
+/// `on_new_update`. The network interface should make a best-effort to send the update messages to
+/// other nodes on the network, potentially retrying if certain nodes are unreachable.
+///
+/// It is safe for the network interface to send the same messages multiple times, or out of order.
+/// Eventual consistency of the documents are guaranteed as long as the update messages are
+/// eventually delivered.
+///
+/// The data store will not wait for the network message to be sent or delivered. If the network
+/// layer wishes to get the latest state to send over the network, it can do so by calling
+/// [`DataStore::snapshot`].
+pub trait NetworkInterface<Conf: DataStoreConfig> {
+ /// Called when a new update to the data store should be sent to to other nodes over the
+ /// network.
+ ///
+ /// Failed updates are ignored and reconciled when the device reconnects. Alternatively, the
+ /// network implementation can get the latest snapshot from the datastore and sync that.
+ fn on_new_update(&mut self, doc_id: &str, document: DeltaDocument<Conf>);
+}
+
+/// The storage interface for a data store.
+///
+/// When the data store is modified, or when new update messages are received from the network,
+/// `on_new_update` will be called to request the storage layer to persist the latest state. The
+/// storage API is fallible, and failure to persist the data will result in a corresponding
+/// [`TransactionError`].
+pub trait StorageInterface<Conf: DataStoreConfig> {
+ /// The error type for this storage.
+ type Error: Debug;
+
+ /// Called when a new update to the data store should be persisted to storage.
+ fn on_new_update(&mut self, doc_id: &str, document: &Document<Conf>)
+ -> Result<(), Self::Error>;
+
+ /// Read a serialized document from storage.
+ ///
+ /// This function returns `None` if there are no documents with the given `doc_id`.
+ fn read_from_storage(&self, doc_id: &str) -> Result<Option<Document<Conf>>, Self::Error>;
+}
+
+/// The update context for a given submerge data store.
+#[derive_where(Debug, Clone)]
+pub struct UpdateContext<Conf: DataStoreConfig> {
+ /// The local node ID representing the device this code is running on. This ID must be unique
+ /// among the group that participates in syncing this [`DataStore`].
+ pub node_id: Conf::NodeId,
+ /// The timestamp provider that gives the wall-clock time. See [`WallTimestampProvider`].
+ pub timestamp_provider: Conf::TimestampProvider,
+ /// The version of the document.
+ ///
+ /// The CRDT implementations assume that this context is monotonically increasing, meaning that
+ /// the version may be copied into the CRDT tree, and any later operations' context must return
+ /// a version newer than or equal to the copied version value.
+ ///
+ /// See [`crdt::UpdateContext::version`].
+ pub version: VectorClock<Conf::NodeId>,
+}
+
+impl<Conf: DataStoreConfig> crdt::UpdateContext<Conf::NodeId> for UpdateContext<Conf> {
+ type WallClock = Conf::TimestampProvider;
+
+ fn updater(&self) -> &Conf::NodeId {
+ &self.node_id
+ }
+
+ fn timestamp_provider(&self) -> &Self::WallClock {
+ &self.timestamp_provider
+ }
+
+ fn version(&self) -> &VectorClock<Conf::NodeId> {
+ &self.version
+ }
+}
+
+/// A document with its metadata.
+///
+/// This handle holds the document together with the node-specific metadata needed to update it,
+/// such as the local node ID and the wall-clock timestamp provider.
+#[derive_where(Debug)]
+pub struct DocumentHandle<Conf: DataStoreConfig> {
+ id: DocId,
+ doc: Document<Conf>,
+ metadata: NodeMetadata<Conf>,
+}
+
+/// Type-level configuration for the [`DataStore`].
+///
+/// Implementations do not need to be instantiable, and can be implemented by unit structs or even
+/// zero-variant enums.
+pub trait DataStoreConfig: Sized {
+ /// The type representing each node in this data store. This node ID is assigned when the
+ /// [`DataStore`] is constructed, must be unique among all of the syncing devices, and must not
+ /// be reused.
+ type NodeId: Ord + Clone + Debug + 'static;
+ /// The value type shared between all types in the data model. Values in `Set`, `Register`, and
+ /// `VectorData` all store instances of this type.
+ type ValueType: Eq + Hash + Clone + Debug + 'static;
+ /// The network interface used to send update messages to other peer nodes.
+ ///
+ /// See [`NetworkInterface`].
+ type Network: NetworkInterface<Self> + Debug + Clone;
+ /// The storage interface used to persist new versions of the document, and read persisted
+ /// documents back from disk.
+ ///
+ /// See [`StorageInterface`].
+ type Storage: StorageInterface<Self> + Debug + Clone;
+ /// The timestamp provider used to provide wall clock timestamps.
+ ///
+ /// See [`WallTimestampProvider`] for the format and requirements of the timestamps.
+ type TimestampProvider: WallTimestampProvider + Debug + Clone;
+}
+
+/// A transaction for a given document.
+///
+/// This transaction temporarily accumulates mutations until it is [`commit`][Self::commit] is
+/// called.
+#[derive_where(Debug; Handle)]
+pub struct DocumentTransaction<Handle, Conf>
+where
+ Handle: BorrowMut<DocumentHandle<Conf>>,
+ Conf: DataStoreConfig,
+{
+ /// The document handle this transaction is for.
+ handle: Handle,
+ /// The delta component of this transaction (containing changes made during this transaction).
+ delta: SubmergeContainer<Conf>,
+ /// The update context for this document transaction.
+ update_context: UpdateContext<Conf>,
+ /// The delta from network update only. This is a subset of the overall delta.
+ network_delta: SubmergeContainer<Conf>,
+}
+
+impl<Handle, Conf> DocumentTransaction<Handle, Conf>
+where
+ Handle: BorrowMut<DocumentHandle<Conf>>,
+ Conf: DataStoreConfig,
+{
+ /// Create a transaction for this document.
+ ///
+ /// The function for both are similar, the main difference is that [`DocumentTransaction`] uses
+ /// the explicit [`commit`][DocumentTransaction::commit] method, whereas
+ /// [`DocumentHandle::modify`] automatically commits when the closure exits.
+ pub fn new(handle: Handle) -> Self {
+ let handle_borrowed = handle.borrow();
+ let update_context = UpdateContext {
+ node_id: handle_borrowed.metadata.node_id.clone(),
+ timestamp_provider: handle_borrowed.metadata.timestamp_provider.clone(),
+ version: handle_borrowed.doc.version.clone(),
+ };
+ Self {
+ handle,
+ delta: Default::default(),
+ update_context,
+ network_delta: Default::default(),
+ }
+ }
+
+ /// Get a mutable reference to the root document and the update context.
+ pub fn get_context_and_root_mut(
+ &mut self,
+ ) -> (&UpdateContext<Conf>, DeltaMut<'_, SubmergeContainer<Conf>>) {
+ // Returns both items in one call just to work around partial borrow issues.
+ let handle = self.handle.borrow_mut();
+ (
+ &self.update_context,
+ DeltaMut {
+ base: Some(&handle.doc.root),
+ delta: &mut self.delta,
+ },
+ )
+ }
+
+ /// Commit the modifications in this transaction to the data store.
+ ///
+ /// ## Returns
+ /// * `Ok` if the network update was successfully committed
+ /// * [`VersionOverflow`][TransactionError::VersionOverflow] if the update would cause the
+ /// version timestamp to overflow. This happens when the node has made more than [`u32::MAX`]
+ /// changes to the datastore.
+ /// * [`StorageError`][TransactionError::StorageError] if it failed to persist the updated data.
+ /// See the documentation for the [`StorageInterface`] implementation for details on how to
+ /// handle the error.
+ pub fn commit(mut self) -> Result<(), TransactionError<Conf>> {
+ let handle = self.handle.borrow_mut();
+ let merged = CrdtState::merge(&handle.doc.root, &self.delta);
+ let merged_network_only = CrdtState::merge(&handle.doc.root, &self.network_delta);
+ // Network change should consider both content change and subtree_version change.
+ let has_network_change = merged_network_only != handle.doc.root;
+ // Local change should only consider content change.
+ let has_local_change = !merged.content_eq(&merged_network_only);
+ if !has_network_change && !has_local_change {
+ info!("No updates were made");
+ return Ok(());
+ }
+ let new_version = if has_local_change {
+ self.update_context
+ .version
+ .increment(&handle.metadata.node_id)?
+ } else {
+ self.update_context.version
+ };
+ let new_root = if has_local_change {
+ merged
+ } else {
+ // Use network merge if there is no local change. This is to ensure any subtree_version
+ // change caused by local read won't be committed.
+ merged_network_only
+ };
+ let updated_doc = Document {
+ root: new_root,
+ version: new_version.clone(),
+ };
+ // Send network update if there is a local change.
+ if has_local_change {
+ let delta_doc = DeltaDocument {
+ root: self.delta,
+ base_version: handle.doc.version.clone(),
+ version: new_version,
+ };
+ handle.metadata.network.on_new_update(&handle.id, delta_doc);
+ }
+ handle
+ .metadata
+ .storage
+ .on_new_update(&handle.id, &updated_doc)
+ .map_err(TransactionError::StorageError)?;
+ handle.doc = updated_doc;
+ Ok(())
+ }
+
+ /// Get the update context for the local node in this data store.
+ pub fn update_context(&self) -> &UpdateContext<Conf> {
+ &self.update_context
+ }
+
+ /// Merge a network delta into this transaction.
+ pub fn merge_network_delta(
+ &mut self,
+ delta: &DeltaDocument<Conf>,
+ ) -> Result<(), CommitUpdateError<Conf>> {
+ if let Some(Ordering::Less) | None =
+ self.update_context.version.partial_cmp(&delta.base_version)
+ {
+ return Err(CommitUpdateError::OlderBaseNeeded {
+ required_base_version: self.update_context.version.clone(),
+ });
+ }
+ if self.update_context.version >= delta.version {
+ debug!(
+ "Discard obsolete network update. our version={:?}. their version={:?}.",
+ self.update_context.version, delta.version,
+ );
+ return Ok(());
+ }
+ self.delta = CrdtState::merge(&self.delta, &delta.root);
+ self.network_delta = CrdtState::merge(&self.network_delta, &delta.root);
+ self.update_context.version =
+ VectorClock::least_upper_bound(&self.update_context.version, &delta.version);
+ Ok(())
+ }
+}
+
+/// Error type used for [`DocumentHandle::commit_network_update`].
+#[derive(Debug, Error)]
+#[allow(missing_docs)]
+pub enum CommitUpdateError<Conf: DataStoreConfig> {
+ #[error(transparent)]
+ StorageError(StorageError<Conf>),
+ #[error("Delta update needs to cover to an older base to be applied")]
+ OlderBaseNeeded {
+ /// The base version needed to successfully update this data store.
+ required_base_version: VectorClock<Conf::NodeId>,
+ },
+}
+
+/// Error type used for [`DocumentHandle::modify`].
+#[derive(Error)]
+#[derive_where(Debug; E)]
+pub enum ModifyError<E, Conf: DataStoreConfig> {
+ /// Failed to commit the modifications. See [`TransactionError`].
+ #[error(transparent)]
+ TransactionFailed(#[from] TransactionError<Conf>),
+ /// Custom error type returned from the caller-provided closure.
+ Custom(E),
+}
+
+type StorageError<Conf> = <<Conf as DataStoreConfig>::Storage as StorageInterface<Conf>>::Error;
+
+impl<Conf> DocumentHandle<Conf>
+where
+ Conf: DataStoreConfig,
+{
+ /// Get the document ID for the document.
+ pub fn doc_id(&self) -> &DocId {
+ &self.id
+ }
+
+ /// Get a snapshot of the document.
+ pub fn snapshot(&self) -> &Document<Conf> {
+ &self.doc
+ }
+
+ /// Modify the document.
+ ///
+ /// This method takes a closure that the caller can use to mutate the document. If the closure
+ /// returns success, the mutated document is committed, persisting the result to disk and
+ /// sending update messages over the network.
+ ///
+ /// If the modify closure returns an error or panics, the mutated document is discarded and the
+ /// persisted data remains in the previous state.
+ pub fn modify<'a, E, F>(&'a mut self, modify: F) -> Result<(), ModifyError<E, Conf>>
+ where
+ F: for<'d> FnOnce(
+ DeltaMut<'d, SubmergeContainer<Conf>>,
+ &UpdateContext<Conf>,
+ ) -> Result<(), E>,
+ {
+ let mut transaction = DocumentTransaction::new(self);
+ let (ctx, doc) = transaction.get_context_and_root_mut();
+ match modify(doc, ctx) {
+ Ok(_) => {
+ transaction.commit()?;
+ Ok(())
+ }
+ Err(e) => Err(ModifyError::Custom(e)),
+ }
+ }
+
+ /// Handle an update message from the network.
+ ///
+ /// Merge any changes received from the incoming update message, and persist the result.
+ pub fn commit_network_update(
+ &mut self,
+ update_message: &DeltaDocument<Conf>,
+ ) -> Result<(), CommitUpdateError<Conf>> {
+ if let Some(Ordering::Less) | None =
+ self.doc.version.partial_cmp(&update_message.base_version)
+ {
+ // Possible violation of causal consistency: We got a delta update but we are not
+ // strictly newer than its base. Return an error to ask the updater for a wider delta.
+ return Err(CommitUpdateError::OlderBaseNeeded {
+ required_base_version: self.doc.version.clone(),
+ });
+ }
+ if self.doc.version >= update_message.version {
+ debug!(
+ "Discard obsolete network update. our version={:?}. their version={:?}.",
+ self.doc.version, update_message.version,
+ );
+ return Ok(());
+ }
+ let updated_document = Document {
+ root: CrdtState::merge(&self.doc.root, &update_message.root),
+ version: VectorClock::least_upper_bound(&self.doc.version, &update_message.version),
+ };
+ debug!(
+ "Commit network update:\norig={:?}\nupdate={:?}\nnew={:?}",
+ self.doc, update_message, updated_document
+ );
+ self.metadata
+ .storage
+ .on_new_update(&self.id, &updated_document)
+ .map_err(CommitUpdateError::StorageError)?;
+ self.doc = updated_document;
+ Ok(())
+ }
+}
+
+/// Contains the necessary metadata to update a document.
+#[derive_where(Debug, Clone)]
+pub struct NodeMetadata<Conf: DataStoreConfig> {
+ network: Conf::Network,
+ storage: Conf::Storage,
+ node_id: Conf::NodeId,
+ timestamp_provider: Conf::TimestampProvider,
+}
+
+/// A `DataStore` manages multiple [`Document`]s.
+///
+/// The `DataStore` is a unit of configuration that shares the same network and storage interfaces.
+/// This means that all documents in the same `DataStore` are assumed to be synced with the same
+/// group and persisted to the same location.
+///
+/// As mentioned above, [`Document`] is the unit of causality tracking. Causal consistency is not
+/// guaranteed across different [`Documents`][Document] within the same `DataStore`.
+#[derive(Debug)]
+pub struct DataStore<Conf: DataStoreConfig> {
+ metadata: NodeMetadata<Conf>,
+}
+
+impl<Conf: DataStoreConfig> DataStore<Conf> {
+ /// Create a new data store.
+ pub fn new(
+ local_id: Conf::NodeId,
+ network: Conf::Network,
+ storage: Conf::Storage,
+ timestamp_provider: Conf::TimestampProvider,
+ ) -> Self {
+ Self {
+ metadata: NodeMetadata {
+ network,
+ storage,
+ node_id: local_id,
+ timestamp_provider,
+ },
+ }
+ }
+
+ /// Open the document with the given ID, creating it if it doesn't already exist.
+ pub fn open_or_create_document(
+ &mut self,
+ id: impl Into<DocId>,
+ ) -> Result<DocumentHandle<Conf>, StorageError<Conf>>
+ where
+ SubmergeContainer<Conf>: Default,
+ {
+ let id = id.into();
+ let doc = self
+ .metadata
+ .storage
+ .read_from_storage(&id)?
+ .unwrap_or_default();
+ Ok(DocumentHandle {
+ id,
+ doc,
+ metadata: self.metadata.clone(),
+ })
+ }
+
+ /// Get a snapshot of what is currently in the document.
+ pub fn snapshot(&self, id: impl Into<DocId>) -> Option<Document<Conf>> {
+ let id = id.into();
+ self.metadata.storage.read_from_storage(&id).ok()?
+ }
+
+ /// Gets the storage interface for this data store.
+ pub fn storage(&self) -> &Conf::Storage {
+ &self.metadata.storage
+ }
+}
diff --git a/betosync/submerge/submerge/src/proto.rs b/betosync/submerge/submerge/src/proto.rs
new file mode 100644
index 0000000..88bd9b9
--- /dev/null
+++ b/betosync/submerge/submerge/src/proto.rs
@@ -0,0 +1,135 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Defines the proto serialization of submerge documents.
+
+use distributed_time::vector_clock::VectorClock;
+use protobuf::Message;
+use submerge_internal_proto::{FromProto, FromProtoError, NodeMapping, ToProto};
+use thiserror::Error;
+
+use crate::{DataStoreConfig, DeltaDocument, Document, SubmergeContainer};
+
+/// Error when serializing or deserializing a proto.
+#[derive(Debug, Error)]
+pub enum ProtoSerdeError {
+ /// Error deserializing the proto into its corresponding Rust type.
+ #[error(transparent)]
+ FromProtoError(#[from] FromProtoError),
+ /// Error in serializing or deserializing bytes into the proto-generated type.
+ #[error(transparent)]
+ ProtobufError(#[from] protobuf::Error),
+}
+
+impl<Conf> Document<Conf>
+where
+ Conf: DataStoreConfig<NodeId = String, ValueType = Vec<u8>>,
+{
+ fn to_proto(&self) -> submerge_internal_proto::protos::submerge::Document {
+ let mut node_ids = NodeMapping::default();
+ let root = Some(self.root.to_proto(&mut node_ids));
+ let version = self.version.to_proto(&mut node_ids);
+ submerge_internal_proto::protos::submerge::Document {
+ node_ids: node_ids.into_vec(),
+ version: Some(version).into(),
+ root: root.into(),
+ ..Default::default()
+ }
+ }
+
+ /// Convert the Document to a serialized proto that can be persisted.
+ pub fn to_proto_bytes(&self) -> Result<Vec<u8>, ProtoSerdeError> {
+ Ok(self.to_proto().write_to_bytes()?)
+ }
+
+ fn from_proto(
+ proto: &submerge_internal_proto::protos::submerge::Document,
+ ) -> Result<Self, FromProtoError> {
+ Ok(Document {
+ root: SubmergeContainer::<Conf>::from_proto(&proto.root, &proto.node_ids)?,
+ version: VectorClock::from_proto(&proto.version, &proto.node_ids)?,
+ })
+ }
+
+ /// Construct a Document from the given serialized proto.
+ pub fn from_proto_bytes(proto_bytes: &[u8]) -> Result<Self, ProtoSerdeError> {
+ Ok(Self::from_proto(
+ &submerge_internal_proto::protos::submerge::Document::parse_from_bytes(proto_bytes)?,
+ )?)
+ }
+}
+
+impl<Conf> DeltaDocument<Conf>
+where
+ Conf: DataStoreConfig<NodeId = String, ValueType = Vec<u8>>,
+{
+ fn to_proto(&self) -> submerge_internal_proto::protos::submerge::DeltaDocument {
+ let mut node_ids = NodeMapping::default();
+ let root = Some(self.root.to_proto(&mut node_ids));
+ let version = self.version.to_proto(&mut node_ids);
+ let base_version = self.base_version.to_proto(&mut node_ids);
+ submerge_internal_proto::protos::submerge::DeltaDocument {
+ node_ids: node_ids.into_vec(),
+ version: Some(version).into(),
+ base_version: Some(base_version).into(),
+ root: root.into(),
+ ..Default::default()
+ }
+ }
+
+ /// Convert the DeltaDocument to a serialized proto that can be sent over the wire.
+ pub fn to_proto_bytes(&self) -> Result<Vec<u8>, ProtoSerdeError> {
+ Ok(self.to_proto().write_to_bytes()?)
+ }
+
+ fn from_proto(
+ proto: &submerge_internal_proto::protos::submerge::DeltaDocument,
+ ) -> Result<Self, FromProtoError> {
+ Ok(Self {
+ root: SubmergeContainer::<Conf>::from_proto(&proto.root, &proto.node_ids)?,
+ base_version: VectorClock::from_proto(&proto.base_version, &proto.node_ids)?,
+ version: VectorClock::from_proto(&proto.version, &proto.node_ids)?,
+ })
+ }
+
+ /// Construct a DeltaDocument from the given serialized proto.
+ pub fn from_proto_bytes(proto_bytes: &[u8]) -> Result<Self, ProtoSerdeError> {
+ Ok(Self::from_proto(
+ &submerge_internal_proto::protos::submerge::DeltaDocument::parse_from_bytes(
+ proto_bytes,
+ )?,
+ )?)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{testing::SerializationTestDataStoreConfig, DeltaDocument, Document};
+
+ #[derive_fuzztest::proptest]
+ fn document_round_trip(doc: Document<SerializationTestDataStoreConfig>) {
+ assert_eq!(
+ Document::<SerializationTestDataStoreConfig>::from_proto(&doc.to_proto()).unwrap(),
+ doc
+ );
+ }
+
+ #[derive_fuzztest::proptest]
+ fn delta_document_round_trip(doc: DeltaDocument<SerializationTestDataStoreConfig>) {
+ assert_eq!(
+ DeltaDocument::<SerializationTestDataStoreConfig>::from_proto(&doc.to_proto()).unwrap(),
+ doc
+ );
+ }
+}
diff --git a/betosync/submerge/submerge/src/testing.rs b/betosync/submerge/submerge/src/testing.rs
new file mode 100644
index 0000000..28879c8
--- /dev/null
+++ b/betosync/submerge/submerge/src/testing.rs
@@ -0,0 +1,79 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Utilities and fake implementations for testing.
+
+use distributed_time::WallTimestampProvider;
+
+use crate::{DataStoreConfig, DeltaDocument, Document, NetworkInterface, StorageInterface};
+
+/// A [`DataStoreConfig`] for testing serialization.
+pub struct SerializationTestDataStoreConfig;
+
+impl DataStoreConfig for SerializationTestDataStoreConfig {
+ type NodeId = String;
+ type ValueType = Vec<u8>;
+ type Network = TestNetworkInterface;
+ type Storage = TestStorageInterface;
+ type TimestampProvider = TestTimestampProvider;
+}
+
+/// A network interface for testing that panics when a new update a received.
+#[derive(Debug, Clone)]
+pub struct TestNetworkInterface;
+
+impl NetworkInterface<SerializationTestDataStoreConfig> for TestNetworkInterface {
+ fn on_new_update(
+ &mut self,
+ _doc_id: &str,
+ _document: DeltaDocument<SerializationTestDataStoreConfig>,
+ ) {
+ unimplemented!()
+ }
+}
+
+/// A storage interface for testing that panics when an update is received
+#[derive(Debug, Clone)]
+pub struct TestStorageInterface;
+
+impl StorageInterface<SerializationTestDataStoreConfig> for TestStorageInterface {
+ type Error = ();
+
+ fn on_new_update(
+ &mut self,
+ _doc_id: &str,
+ _document: &Document<SerializationTestDataStoreConfig>,
+ ) -> Result<(), Self::Error> {
+ unimplemented!()
+ }
+
+ fn read_from_storage(
+ &self,
+ _doc_id: &str,
+ ) -> Result<Option<Document<SerializationTestDataStoreConfig>>, Self::Error> {
+ unimplemented!()
+ }
+}
+
+/// A test timestamp provider that returns a fixed timestamp.
+#[derive(Debug, Clone)]
+pub struct TestTimestampProvider;
+
+impl WallTimestampProvider for TestTimestampProvider {
+ fn now(&self) -> u64 {
+ // An arbitrary, but somewhat realistic timestamp, so that the sizes are still accurate even
+ // if integers are encoded with, say, variable-length-quantity.
+ 1720000000000
+ }
+}
diff --git a/betosync/submerge/submerge/tests/bincode_size.rs b/betosync/submerge/submerge/tests/bincode_size.rs
new file mode 100644
index 0000000..5a06068
--- /dev/null
+++ b/betosync/submerge/submerge/tests/bincode_size.rs
@@ -0,0 +1,192 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! This is a metrics collector for the size of bincode-encoded messages, disguised as a test.
+//!
+//! Both network update messages and persistent storage contains the same data type so this is
+//! applicable to both. However, storage always writes the full [`Document`], including elements
+//! that are unchanged, whereas network updates will only send a [`DeltaDocument`], either generated
+//! by the mutation operation, or by a call to [`Document::calculate_delta`].
+
+use std::mem::size_of;
+
+use crdt::{
+ lww_map::LwwMapWrite, register::RegisterWrite, set::SetWrite, vector_data::VectorDataWrite,
+};
+use distributed_time::{vector_clock::VectorClock, DistributedClock};
+use submerge::{
+ testing::{SerializationTestDataStoreConfig, TestTimestampProvider},
+ DataStoreConfig, SubmergeContainer, SubmergeMap, SubmergeRegister, SubmergeSet,
+ SubmergeVectorData, UpdateContext,
+};
+
+#[test]
+fn vector_clock_bincode_size() {
+ // Opportunities for optimization:
+ // - Turn on varint encoding
+ // https://docs.rs/bincode/1.3.3/bincode/config/trait.Options.html#method.with_varint_encoding.
+ // - For longer node IDs that are not u16 (e.g. generated UUIDs), store them externally since
+ // the node ID tends to be repeated a lot. e.g.
+ // { "nodes": ["long-uuid-1", "long-uuid-2"], { ..., clock: [1, 2], ..., clock: [1, 0] } }
+ // instead of
+ // { { ..., clock: { "long-uuid-1": 1, "long-uuid-2": 2}, ..., clock: { "long-uuid-1": 1, "long-uuid-2": 0} } }
+ let mut clock =
+ VectorClock::<<SerializationTestDataStoreConfig as DataStoreConfig>::NodeId>::default();
+ assert_eq!(8, bincode::serialize(&clock).unwrap().len());
+
+ fn expected_size(num_nodes: usize) -> usize {
+ // See https://github.com/bincode-org/bincode/blob/trunk/docs/spec.md#collections
+ 8 // length of the vector clock, encoded as u64
+ + num_nodes * 13 // Two bytes each for the key
+ + num_nodes * 4 // Four bytes each for the value
+ }
+
+ for i in 1_usize..=10 {
+ clock = clock.increment(&format!("{i:0>5}")).unwrap();
+ let serialized = bincode::serialize(&clock).unwrap();
+ assert_eq!(
+ expected_size(i),
+ serialized.len(),
+ "iteration {i}. Serialized={serialized:?}. clock={clock:?}"
+ );
+ }
+}
+
+#[test]
+fn empty_bincode_size() {
+ let doc = SubmergeContainer::<SerializationTestDataStoreConfig>::default();
+ assert_eq!(9, bincode::serialize(&doc).unwrap().len());
+}
+
+#[test]
+fn map_bincode_size() {
+ fn create_map_with_size(size: usize) -> SubmergeMap<SerializationTestDataStoreConfig> {
+ let ctx = UpdateContext::<SerializationTestDataStoreConfig> {
+ node_id: "12345".to_string(),
+ timestamp_provider: TestTimestampProvider,
+ version: VectorClock::default(),
+ };
+ let mut map = SubmergeMap::<SerializationTestDataStoreConfig>::default();
+ for i in 0..size {
+ map.remove_value(&ctx, format!("{i:0>5}")).unwrap();
+ }
+ map
+ }
+
+ fn expected_size(key_size: usize, num_items: usize) -> usize {
+ let node_id_size = 5;
+ (key_size + node_id_size + 52) * num_items + 8
+ }
+
+ let (actual_size, expected_size): (Vec<_>, Vec<_>) = [10, 100, 1000, 1000]
+ .into_iter()
+ .map(|size| {
+ let doc = create_map_with_size(size);
+ (
+ bincode::serialize(&doc).unwrap().len(),
+ expected_size(5, size),
+ )
+ })
+ .unzip();
+ assert_eq!(actual_size, expected_size);
+}
+
+#[test]
+fn register_bincode_size() {
+ fn expected_size(data_size: usize) -> usize {
+ data_size + 46
+ }
+
+ let ctx = UpdateContext::<SerializationTestDataStoreConfig> {
+ node_id: 0.to_string(),
+ timestamp_provider: TestTimestampProvider,
+ version: VectorClock::default(),
+ };
+ let mut data = SubmergeRegister::<SerializationTestDataStoreConfig>::default();
+
+ for i in [0, 10, 100, 1000] {
+ data.set(&ctx, vec![0_u8; i]).unwrap();
+ assert_eq!(expected_size(i), bincode::serialize(&data).unwrap().len());
+ }
+}
+
+#[test]
+fn set_bincode_size() {
+ fn expected_size(data_size: usize, num_items: usize) -> usize {
+ (data_size + 13) * num_items + 8
+ }
+
+ let mut data = SubmergeSet::<SerializationTestDataStoreConfig>::default();
+ for i in [1_u32, 10, 100, 1000] {
+ for v in (0..i).map(|i| i.to_be_bytes().to_vec()) {
+ let _ = data.add(v).unwrap();
+ }
+ assert_eq!(
+ expected_size(size_of::<u32>(), i as usize),
+ bincode::serialize(&data).unwrap().len()
+ );
+ }
+
+ let mut data = SubmergeSet::<SerializationTestDataStoreConfig>::default();
+ for i in [1_u64, 10, 100, 1000] {
+ for v in (0..i).map(|i| i.to_be_bytes().to_vec()) {
+ let _ = data.add(v).unwrap();
+ }
+ assert_eq!(
+ expected_size(size_of::<u64>(), usize::try_from(i).unwrap()),
+ bincode::serialize(&data).unwrap().len()
+ );
+ }
+}
+
+#[test]
+fn vector_data_bincode_size() {
+ fn expected_size(data_size: usize, num_nodes: usize) -> usize {
+ const KEY_SIZE: usize = 5;
+ (KEY_SIZE + 8 // Key = Length prefix + key
+ // Each VecEntry = {version: u32, data: Option<Vec<u8>>}
+ + 4 // u32 version
+ + 1 // Option has a 1-byte overhead
+ + 8 // 8-byte length prefix of Vec<u8>
+ + data_size
+ ) * num_nodes
+ // Map overhead = Length prefix = 8 bytes
+ + 8
+ }
+
+ let (expected, actual): (Vec<_>, Vec<_>) = [0_usize, 1, 10, 100]
+ .into_iter()
+ .map(|x| {
+ let mut data = SubmergeVectorData::<SerializationTestDataStoreConfig>::default();
+ for i in 0..x {
+ let _ = data.set(format!("{i:0>5}"), Some(vec![1_u8; 10])).unwrap();
+ }
+ (
+ expected_size(10, x),
+ bincode::serialize(&data).unwrap().len(),
+ )
+ })
+ .collect();
+ assert_eq!(expected, actual);
+
+ // Changing value does not change the size
+ let mut data = SubmergeVectorData::<SerializationTestDataStoreConfig>::default();
+ for i in 0..100 {
+ let _ = data.set(String::from("00000"), Some(vec![i; 10])).unwrap();
+ }
+ assert_eq!(
+ expected_size(10, 1),
+ bincode::serialize(&data).unwrap().len()
+ );
+}
diff --git a/betosync/submerge/submerge/tests/proto_size.rs b/betosync/submerge/submerge/tests/proto_size.rs
new file mode 100644
index 0000000..5a7ce8c
--- /dev/null
+++ b/betosync/submerge/submerge/tests/proto_size.rs
@@ -0,0 +1,234 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! This is a metrics collector for the size of proto-encoded messages, disguised as a test.
+//!
+//! Both network update messages and persistent storage contains the same data type so this is
+//! applicable to both. However, storage always writes the full [`Document`], including elements
+//! that are unchanged, whereas network updates will only send a [`DeltaDocument`], either generated
+//! by the mutation operation, or by a call to [`Document::calculate_delta`].
+//!
+//! [Protoscope](https://github.com/protocolbuffers/protoscope) can be used to inspect binary
+//! serialized protobufs to see how they are encoded.
+//!
+//! e.g. `xxd -r -ps <<< "0a0e120a000000000000000000001801" | protoscope`
+//!
+//! Also see https://protobuf.dev/programming-guides/encoding/#packed for how to interpret the
+//! output. As an approximation, `<number>: { ... }` is a TLV that incurs 2 bytes of overhead, and
+//! `<number>: <another number>` incurs 2 bytes, assuming the numeric value is less than 128.
+
+use std::mem::size_of;
+
+use crdt::{
+ lww_map::LwwMapWrite, register::RegisterWrite, set::SetWrite, vector_data::VectorDataWrite,
+};
+use distributed_time::{vector_clock::VectorClock, DistributedClock};
+use protobuf::Message;
+use submerge::{
+ testing::{SerializationTestDataStoreConfig, TestTimestampProvider},
+ DataStoreConfig, SubmergeContainer, SubmergeMap, SubmergeRegister, SubmergeSet,
+ SubmergeVectorData, UpdateContext,
+};
+use submerge_internal_proto::{NodeMapping, ToProto};
+
+const PRINT_PROTOSCOPE_COMMANDS: bool = true;
+
+#[test]
+fn vector_clock_proto_size() {
+ let mut node_mapping = NodeMapping::default();
+ let mut clock =
+ VectorClock::<<SerializationTestDataStoreConfig as DataStoreConfig>::NodeId>::default();
+ assert_eq!(
+ 0,
+ clock
+ .to_proto(&mut node_mapping)
+ .write_to_bytes()
+ .unwrap()
+ .len()
+ );
+
+ fn expected_size(num_nodes: usize) -> usize {
+ 4 // 4 bytes overhead (2 fields with type and length)
+ + num_nodes * 2 // 2 bytes per each value (assuming varint value < 128)
+ }
+
+ for i in 1_usize..=10 {
+ clock = clock.increment(&i.to_string()).unwrap();
+ let serialized = clock.to_proto(&mut node_mapping).write_to_bytes().unwrap();
+ assert_eq!(
+ expected_size(i),
+ serialized.len(),
+ "iteration {i}. Serialized={serialized:?}. clock={clock:?}"
+ );
+ }
+}
+
+#[test]
+fn empty_proto_size() {
+ let mut node_mapping = NodeMapping::default();
+ let doc = SubmergeContainer::<SerializationTestDataStoreConfig>::default();
+ assert_eq!(
+ 2,
+ doc.to_proto(&mut node_mapping)
+ .write_to_bytes()
+ .unwrap()
+ .len()
+ );
+}
+
+#[test]
+fn map_proto_size() {
+ fn create_map_with_size(size: usize) -> SubmergeMap<SerializationTestDataStoreConfig> {
+ let ctx = UpdateContext::<SerializationTestDataStoreConfig> {
+ node_id: "0".to_string(),
+ timestamp_provider: TestTimestampProvider,
+ version: VectorClock::default(),
+ };
+ let mut map = SubmergeMap::<SerializationTestDataStoreConfig>::default();
+ for i in 0..size {
+ map.remove_value(&ctx, format!("{i:05}")).unwrap();
+ }
+ map
+ }
+
+ fn expected_size(key_size: usize, num_items: usize) -> usize {
+ (key_size + 25) * num_items
+ }
+
+ let mut node_mapping = NodeMapping::default();
+ let (actual_size, expected_size): (Vec<_>, Vec<_>) = [10, 100, 1000]
+ .into_iter()
+ .map(|size| {
+ let doc = create_map_with_size(size);
+ let bytes = doc.to_proto(&mut node_mapping).write_to_bytes().unwrap();
+ if PRINT_PROTOSCOPE_COMMANDS {
+ eprintln!(
+ "Map with {size} null elements: {}",
+ protoscope_command(&bytes)
+ );
+ }
+ (bytes.len(), expected_size(5, size))
+ })
+ .unzip();
+ assert_eq!(expected_size, actual_size);
+}
+
+#[test]
+fn register_proto_size() {
+ fn expected_size(data_size: usize) -> usize {
+ match data_size {
+ 0 => 21,
+ 1..=127 => data_size + 23,
+ 128.. => data_size + 25,
+ }
+ }
+
+ let mut node_mapping = NodeMapping::default();
+ let ctx = UpdateContext::<SerializationTestDataStoreConfig> {
+ node_id: "0".to_string(),
+ timestamp_provider: TestTimestampProvider,
+ version: VectorClock::default(),
+ };
+ let mut data = SubmergeRegister::<SerializationTestDataStoreConfig>::default();
+
+ for i in [0, 10, 100, 1000] {
+ data.set(&ctx, vec![0_u8; i]).unwrap();
+ let bytes = data.to_proto(&mut node_mapping).write_to_bytes().unwrap();
+ if PRINT_PROTOSCOPE_COMMANDS {
+ eprintln!("Register with {i} elements: {}", protoscope_command(&bytes));
+ }
+ assert_eq!(expected_size(i), bytes.len());
+ }
+}
+
+#[test]
+fn set_proto_size() {
+ fn expected_size(data_size: usize, num_items: usize) -> usize {
+ (data_size + 6) * num_items
+ }
+
+ let mut node_mapping = NodeMapping::default();
+ let mut data = SubmergeSet::<SerializationTestDataStoreConfig>::default();
+ let (expected, actual): (Vec<_>, Vec<_>) = [1_u32, 10, 100, 1000]
+ .iter()
+ .map(|i| {
+ for v in (0..*i).map(|i| i.to_be_bytes().to_vec()) {
+ let _ = data.add(v).unwrap();
+ }
+ let bytes = data.to_proto(&mut node_mapping).write_to_bytes().unwrap();
+ if PRINT_PROTOSCOPE_COMMANDS {
+ eprintln!("Set with {i} elements: {}", protoscope_command(&bytes));
+ }
+ (expected_size(size_of::<u32>(), *i as usize), bytes.len())
+ })
+ .unzip();
+ assert_eq!(expected, actual);
+
+ let mut data = SubmergeSet::<SerializationTestDataStoreConfig>::default();
+ for i in [1_u64, 10, 100, 1000] {
+ for v in (0..i).map(|i| i.to_be_bytes().to_vec()) {
+ let _ = data.add(v).unwrap();
+ }
+ assert_eq!(
+ expected_size(size_of::<u64>(), usize::try_from(i).unwrap()),
+ data.to_proto(&mut node_mapping)
+ .write_to_bytes()
+ .unwrap()
+ .len()
+ );
+ }
+}
+
+#[test]
+fn vector_data_proto_size() {
+ fn expected_size(data_size: usize, num_nodes: usize) -> usize {
+ (data_size + 8) * num_nodes // 8 bytes of overhead per node
+ - 2 // For the first node, the node_id is omitted because the default is 0.
+ }
+
+ let mut node_mapping = NodeMapping::default();
+ let mut data = SubmergeVectorData::<SerializationTestDataStoreConfig>::default();
+
+ let (expected, actual): (Vec<_>, Vec<_>) = [1_usize, 10, 100]
+ .iter()
+ .map(|x| {
+ for i in 0..*x {
+ let _ = data.set(i.to_string(), Some(vec![0_u8; 10])).unwrap();
+ }
+ let bytes = data.to_proto(&mut node_mapping).write_to_bytes().unwrap();
+ if PRINT_PROTOSCOPE_COMMANDS {
+ eprintln!("VectorData with {x} nodes: {}", protoscope_command(&bytes));
+ }
+ (expected_size(10, *x), bytes.len())
+ })
+ .unzip();
+ assert_eq!(expected, actual);
+
+ // Changing value does not change the size
+ let _ = data.set(0.to_string(), Some(vec![1_u8; 10])).unwrap();
+ assert_eq!(
+ expected_size(10, 100),
+ data.to_proto(&mut node_mapping)
+ .write_to_bytes()
+ .unwrap()
+ .len()
+ );
+}
+
+/// Print the protoscope command to inspect the given serialized proto.
+///
+/// See https://github.com/protocolbuffers/protoscope.
+fn protoscope_command(bytes: &[u8]) -> String {
+ format!("xxd -r -ps <<< \"{}\" | protoscope", hex::encode(bytes))
+}
diff --git a/betosync/submerge/submerge_internal_proto/Cargo.toml b/betosync/submerge/submerge_internal_proto/Cargo.toml
new file mode 100644
index 0000000..c831acd
--- /dev/null
+++ b/betosync/submerge/submerge_internal_proto/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "submerge_internal_proto"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+publish.workspace = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+protobuf.workspace = true
+thiserror.workspace = true
+
+[features]
+default = ["cargo"]
+cargo = []
+
+[lints]
+workspace = true
+
+[build-dependencies]
+protobuf-codegen.workspace = true
diff --git a/betosync/submerge/submerge_internal_proto/build.rs b/betosync/submerge/submerge_internal_proto/build.rs
new file mode 100644
index 0000000..9ea6d6f
--- /dev/null
+++ b/betosync/submerge/submerge_internal_proto/build.rs
@@ -0,0 +1,30 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Build file for submerge
+
+use protobuf_codegen::Customize;
+
+fn main() {
+ protobuf_codegen::Codegen::new()
+ .protoc()
+ // All inputs and imports from the inputs must reside in `includes` directories.
+ .includes(["proto"])
+ // Inputs must reside in some of include paths.
+ .input("proto/submerge.proto")
+ .customize(Customize::default().gen_mod_rs(true))
+ .cargo_out_dir("proto")
+ .protoc_extra_arg("--experimental_allow_proto3_optional")
+ .run_from_script()
+}
diff --git a/betosync/submerge/submerge_internal_proto/proto/submerge.proto b/betosync/submerge/submerge_internal_proto/proto/submerge.proto
new file mode 100644
index 0000000..70b25bb
--- /dev/null
+++ b/betosync/submerge/submerge_internal_proto/proto/submerge.proto
@@ -0,0 +1,213 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+syntax = "proto3";
+
+package ambient.mdp.submerge;
+
+// A Submerge Document.
+//
+// This is the serialized version of `submerge::Document` in Rust, primarily
+// used as the format for persistent storage.
+//
+// Next ID: 4
+message Document {
+ // The ID of nodes in this document. Because the node IDs tend to be repeated
+ // many times in a document, for example in vector clocks, and they may be
+ // long like 36 character UUIDs, they are extracted out to the top level. The
+ // rest of this document then refers to the nodes by their index in this
+ // node_ids list.
+ repeated string node_ids = 1;
+ // The version of the document. This is used to compare against incoming
+ // DeltaDocuments (a.k.a. update messages) to determine whether the update is
+ // ready to be applied.
+ CompressedVectorClock version = 2;
+ // The root of the data tree.
+ SubmergeContainer root = 3;
+}
+
+// A Submerge Delta Document.
+//
+// This is also known as an update message. Corresponds to the
+// `submerge::DeltaDocument` struct in Rust, and is primarily used as the format
+// for network updates.
+//
+// Next ID: 5
+message DeltaDocument {
+ // The ID of nodes in this document. Because the node IDs tend to be repeated
+ // many times in a document, for example in vector clocks, and they may be
+ // long like 36 character UUIDs, they are extracted out to the top level. The
+ // rest of this document then refers to the nodes by their index in this
+ // node_ids list.
+ repeated string node_ids = 1;
+ // The version a document will be at after applying this delta.
+ CompressedVectorClock version = 2;
+ // The base_version of this delta document. Information that hasn't changed
+ // since this base_version won't be part of this delta, so Submerge documents
+ // will verify that it is as least as new as the base before applying this
+ // delta.
+ CompressedVectorClock base_version = 3;
+ // The root of the data tree, only containing delta information that has
+ // changed since base_version.
+ SubmergeContainer root = 4;
+}
+
+// A container in Submerge that contains one of Submerge's CRDT types.
+//
+// When thinking of a Submerge document as a tree of CRDTs, each container can
+// be thought of as a "node" (although the term "node" is generally not used in
+// Submerge to describe this, and is instead reserved to describe the clients or
+// actors making changes to a document).
+//
+// Corresponds to the `LwwCrdtContainer` type in Rust.
+//
+// Next ID: 8
+message SubmergeContainer {
+ // The node that made the last update to the type of this container.
+ //
+ // The value is the index of the node in `node_ids` of Document or
+ // DeltaDocument.
+ optional uint32 updater = 1;
+ // The timestamp tracking when the container type was last updated (excluding
+ // any changes to the children).
+ HybridLogicalTimestamp timestamp = 2;
+ // The version vector tracking when this subtree (this node or any of its
+ // descendants) were last updated.
+ CompressedVectorClock subtree_version = 3;
+
+ // The CRDT value contained in this container.
+ //
+ // Note: This value is optional; zero or one of the following fields can be
+ // set in valid constructions of this message.
+ oneof value {
+ SubmergeMap map = 4;
+ SubmergeSet set = 5;
+ SubmergeRegister register = 6;
+ SubmergeVectorData vector_data = 7;
+ }
+}
+
+// A map of Submerge containers.
+//
+// Corresponds to the `LwwMap` type in Rust.
+//
+// Next ID: 2
+message SubmergeMap {
+ map<string, SubmergeContainer> elements = 1;
+}
+
+// A set of values.
+//
+// Corresponds to the `Set` type in Rust.
+//
+// Next ID: 2
+message SubmergeSet {
+ // Next ID: 4
+ message SetElement {
+ bytes value = 1;
+ uint32 generation = 2;
+ bool removed = 3;
+ }
+
+ repeated SetElement elements = 1;
+}
+
+// A register containing a "single" atomically updated value.
+//
+// "Single" is in quotes here because the value type can be a compound type or a
+// collection, but the register's merging logic will treat it as one unit, and
+// apply any updates atomically without merging.
+//
+// Corresponds to the `Register` type in Rust.
+//
+// Next ID: 2
+message SubmergeRegister {
+ // Next ID: 4
+ message RegisterElement {
+ optional bytes value = 1;
+ CompressedVectorClock vector_clock = 2;
+ HybridLogicalTimestamp hlc = 3;
+ }
+
+ repeated RegisterElement elements = 1;
+}
+
+// A vector data type.
+//
+// This type contains an area for each node to write to, and is therefore
+// guaranteed to merge without conflicts.
+//
+// Corresponds to the `VectorData` type in Rust.
+//
+// Next ID: 2
+message SubmergeVectorData {
+ // Next ID: 4
+ message VecElement {
+ uint32 node = 1;
+ optional bytes value = 2;
+ uint32 version = 3;
+ }
+
+ repeated VecElement elements = 1;
+}
+
+// Timestamp types
+
+// A compressed vector clock or version vector.
+//
+// Because the node IDs tend to be repeated many times in a document, and they
+// may be long, like 36 character UUIDs, they are extracted out to the top level
+// of a document. The nodes in this vector clock refers to the index of a node ID
+// in the `node_ids` list in the `Document` or `DeltaDocument` types. If a self-
+// contained version of vector clock is needed, use `VectorClock` instead.
+//
+// Despite the name, this message can be used as both vector clocks and version
+// vectors, depending on the usage.
+//
+// Corresponds to the `VectorClock` type in Rust.
+//
+// Next ID: 3
+message CompressedVectorClock {
+ repeated uint32 nodes = 1 [packed = true];
+ repeated uint32 values = 2 [packed = true];
+}
+
+// A hybrid logical timestamp.
+//
+// This timestamp tracks both wall-clock time and causality. Sorting entries by
+// this timestamp will result in an order consistent with causality with a bias
+// towards tie-breaking with wall-clock time.
+//
+// Corresponds to the `UnnamedHybridLogicalTimestamp` type in Rust.
+//
+// Next ID: 3
+message HybridLogicalTimestamp {
+ uint64 logical_time = 1;
+ uint32 causality = 2;
+}
+
+
+// A vector clock or version vector.
+//
+// Despite the name, this message can be used as both vector clocks and version
+// vectors, depending on the usage.
+//
+// This message is self-contained and can be directly sent to remote devices.
+//
+// Next ID: 2
+message VectorClock {
+ // A map from the node ID to the clock value for that node.
+ map<string, uint32> node_clock_values = 1;
+}
+
diff --git a/betosync/submerge/submerge_internal_proto/src/lib.rs b/betosync/submerge/submerge_internal_proto/src/lib.rs
new file mode 100644
index 0000000..47b84a7
--- /dev/null
+++ b/betosync/submerge/submerge_internal_proto/src/lib.rs
@@ -0,0 +1,127 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Internal crate for Submerge, containing the protobuf schema definition and corresponding traits.
+//!
+//! This is an internal implementation of Submerge. To enable proto serialization / deserialization,
+//! use `submerge` with the `proto` feature.
+
+use std::collections::BTreeMap;
+
+use thiserror::Error;
+
+/// Module containing types generated from the proto schema.
+#[allow(clippy::unwrap_used, clippy::panic, clippy::cast_possible_truncation)]
+#[cfg(feature = "cargo")]
+pub mod protos {
+ include!(concat!(env!("OUT_DIR"), "/proto/mod.rs"));
+}
+
+#[cfg(not(feature = "cargo"))]
+pub use submerge_internal_proto_proto as protos;
+
+/// A type that can be converted into a proto.
+pub trait ToProto {
+ /// The proto-generated type to be converted into.
+ type Proto;
+
+ /// Converts this type into a proto.
+ ///
+ /// # Arguments
+ ///
+ /// * `node_ids`: A mutable mapping from string node IDs to its index. This is used to compress
+ /// the payload since the node ID may be long and may appear multiple times in a document
+ /// (e.g. in each vector data and vector clock). The resulting list of `node_ids` will be
+ /// included in the top level Document or DeltaDocument proto.
+ fn to_proto(&self, node_ids: &mut NodeMapping<String>) -> Self::Proto;
+}
+
+/// Error type when conversion from proto failed.
+#[derive(Debug, Error)]
+pub enum FromProtoError {
+ /// The proto references a node ID index that is non-existent on the node_ids list given.
+ ///
+ /// This is an error in the input proto payload, and typically indicates a serialization error,
+ /// such as using the wrong `NodeMapping` when serializing the `Document` or `DeltaDocument`.
+ #[error("Referenced Node ID missing")]
+ MissingNodeId,
+ /// Numeric value overflowed.
+ ///
+ /// Protobufs only support 32-bit and 64-bit integers, while some values in the document are u8
+ /// or u16. If the deserialization code encounters a numeric value that cannot fit in the target
+ /// type, this error will be returned. This typically indicates an error in the serialization
+ /// code.
+ #[error("Numeric value overflowed")]
+ NumericOverflow,
+ /// Missing a required field.
+ ///
+ /// Proto 3 treats all non-primitive fields as optional, but in Submerge some fields are
+ /// required to be successfully parsed into their corresponding Rust types.
+ #[error("Required field missing")]
+ MissingRequiredField,
+ /// Length of the elements in the vector clock mismatched.
+ ///
+ /// A vector clock proto contains two repeated fields that are zipped together into a map, and
+ /// therefore must have the same length.
+ #[error("Mismatched vector clock")]
+ MismatchedVectorClockLength,
+}
+
+/// A trait indicating it can be constructed from a proto.
+pub trait FromProto: Sized {
+ /// The proto-generated type to parse into the corresponding Rust type.
+ type Proto;
+
+ /// Construct an instance from the given proto.
+ fn from_proto(proto: &Self::Proto, node_ids: &[String]) -> Result<Self, FromProtoError>;
+}
+
+/// A mapping between node IDs and its corresponding index.
+///
+/// This is used to compress the serialized data as the node IDs are often long (e.g. 36-character
+/// UUID) and appears in a document multiple times. This structure collects all of the node IDs used
+/// during serialization and allows them to be replaced with the numeric index.
+#[derive(Debug, Default, PartialEq, Eq)]
+pub struct NodeMapping<N: Ord>(BTreeMap<N, u32>);
+
+impl<N: Ord + Clone> NodeMapping<N> {
+ /// Gets the index for the given `node_id`.
+ ///
+ /// If the `node_id` doesn't already exist, it will be added to the map.
+ pub fn get_index(&mut self, node_id: &N) -> u32 {
+ let len = self.0.len();
+ *self
+ .0
+ .entry(node_id.clone())
+ .or_insert(len.try_into().expect("Too many node IDs"))
+ }
+
+ /// Converts this node mapping into a vector.
+ ///
+ /// The resulting vector will contain all the node IDs sorted by its value. Since the indices
+ /// are allocated in a contiguous range, the vec index can be used to look up the corresponding
+ /// node ID.
+ pub fn into_vec(self) -> Vec<N> {
+ let reverse_map = self
+ .0
+ .into_iter()
+ .map(|(k, v)| (v, k))
+ .collect::<BTreeMap<_, _>>();
+ debug_assert_eq!(
+ reverse_map.keys().map(|v| *v as usize).collect::<Vec<_>>(),
+ (0..reverse_map.len()).collect::<Vec<_>>()
+ );
+ reverse_map.values().cloned().collect()
+ }
+}
diff --git a/betosync/submerge/submerge_java/.gitattributes b/betosync/submerge/submerge_java/.gitattributes
new file mode 100644
index 0000000..097f9f9
--- /dev/null
+++ b/betosync/submerge/submerge_java/.gitattributes
@@ -0,0 +1,9 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# Linux start script should use lf
+/gradlew text eol=lf
+
+# These are Windows script files and should use crlf
+*.bat text eol=crlf
+
diff --git a/betosync/submerge/submerge_java/.gitignore b/betosync/submerge/submerge_java/.gitignore
new file mode 100644
index 0000000..57c9990
--- /dev/null
+++ b/betosync/submerge/submerge_java/.gitignore
@@ -0,0 +1,9 @@
+# Ignore Gradle project-specific cache directory
+.gradle
+
+# Ignore Gradle build output directory
+build
+/lib/bin
+
+# Ignore IDEA dir
+/.idea
diff --git a/betosync/submerge/submerge_java/Cargo.toml b/betosync/submerge/submerge_java/Cargo.toml
new file mode 100644
index 0000000..fcfd750
--- /dev/null
+++ b/betosync/submerge/submerge_java/Cargo.toml
@@ -0,0 +1,34 @@
+[package]
+name = "submerge_java"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+license.workspace = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+# JNI wants a .so or equivalent
+crate-type = ["cdylib"]
+
+[dependencies]
+crdt = { workspace = true, features = ["testing"] }
+distributed_time.workspace = true
+submerge = { workspace = true, features = ["proto"] }
+submerge_internal_proto.workspace = true
+
+anyhow.workspace = true
+arbitrary.workspace = true
+derive-where.workspace = true
+handle_map.workspace = true
+jni.workspace = true
+log.workspace = true
+parking_lot = { version = "0.12.3", features = ["arc_lock"] }
+pourover.workspace = true
+protobuf.workspace = true
+thiserror.workspace = true
+
+[dev-dependencies]
+
+[lints]
+workspace = true
diff --git a/betosync/submerge/submerge_java/gradle/wrapper/gradle-wrapper.jar b/betosync/submerge/submerge_java/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e644113
--- /dev/null
+++ b/betosync/submerge/submerge_java/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/betosync/submerge/submerge_java/gradle/wrapper/gradle-wrapper.properties b/betosync/submerge/submerge_java/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..09523c0
--- /dev/null
+++ b/betosync/submerge/submerge_java/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/betosync/submerge/submerge_java/gradlew b/betosync/submerge/submerge_java/gradlew
new file mode 100755
index 0000000..1aa94a4
--- /dev/null
+++ b/betosync/submerge/submerge_java/gradlew
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/betosync/submerge/submerge_java/gradlew.bat b/betosync/submerge/submerge_java/gradlew.bat
new file mode 100644
index 0000000..25da30d
--- /dev/null
+++ b/betosync/submerge/submerge_java/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/betosync/submerge/submerge_java/lib/build.gradle.kts b/betosync/submerge/submerge_java/lib/build.gradle.kts
new file mode 100644
index 0000000..616c253
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/build.gradle.kts
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+import net.ltgt.gradle.errorprone.errorprone
+
+plugins {
+ `java-library`
+ id("com.adarshr.test-logger") version "4.0.0"
+ id("net.ltgt.errorprone") version "4.0.0"
+ kotlin("jvm") version "2.0.0"
+}
+
+repositories {
+ mavenCentral()
+ google()
+}
+
+dependencies {
+ compileOnly("org.checkerframework:checker-qual:3.47.0")
+ compileOnly("com.google.errorprone:error_prone_annotations:2.29.2")
+ compileOnly("androidx.annotation:annotation:1.6.0")
+ errorprone("com.google.errorprone:error_prone_core:2.29.2")
+ implementation(project(":cooperative_cleaner"))
+
+ // Use JUnit test framework.
+ testCompileOnly("androidx.annotation:annotation:1.6.0")
+ testImplementation("junit:junit:4.13")
+ testImplementation("com.google.truth:truth:1.1.4")
+ testImplementation("org.mockito:mockito-core:5.+")
+}
+
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(11)
+ }
+}
+
+task<Exec>("cargo") {
+ commandLine("cargo", "build", "--color=always")
+}
+
+tasks.withType<JavaCompile> {
+ dependsOn("cargo")
+ options.errorprone {
+ error("CheckReturnValue")
+ error("UnnecessaryStaticImport")
+ error("WildcardImport")
+ error("RemoveUnusedImports")
+ error("ReturnMissingNullable")
+ error("FieldMissingNullable")
+ error("AnnotationPosition")
+ error("CheckedExceptionNotThrown")
+ error("NonFinalStaticField")
+ error("InvalidLink")
+ error("MustBeClosedChecker")
+ }
+ options.compilerArgs.addAll(arrayOf("-Xlint:rawtypes,unchecked", "-Werror"))
+}
+
+tasks.test {
+ systemProperty("java.library.path", "$projectDir/../../../target/debug")
+ testLogging {
+ showStandardStreams = true
+ }
+ jvmArgs = mutableListOf("-Xcheck:jni")
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/Converter.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/Converter.java
new file mode 100644
index 0000000..3b16f9e
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/Converter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+/**
+ * A converter for {@link DataStore} that serializes and deserializes the value type {@code V} into
+ * bytes. The data store only considers equality of the underlying bytes, so this converter and the
+ * type implementation `V` should ensure that {@code V1.equals(V2)} if and only if {@code
+ * Arrays.equals(serialize(V1), serialize(V2))}.
+ *
+ * <p>Notably, protobufs do not have a canonical serialized representation in general, and should be
+ * avoided for this purpose.
+ */
+public interface Converter<V> {
+ /** Converts the given bytes into the corresponding typed value. */
+ V deserialize(byte[] bytes);
+
+ /** Converts the given value into the corresponding serialized bytes. */
+ byte[] serialize(V value);
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/DataStore.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/DataStore.java
new file mode 100644
index 0000000..2b383d6
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/DataStore.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.Nullable;
+import com.google.android.submerge.DocumentTransaction.DocumentTransactionHandleId;
+import com.google.android.submerge.SubmergeCleaner.Cleanable;
+import com.google.android.submerge.VersionVector.VersionVectorHandleId;
+import com.google.errorprone.annotations.MustBeClosed;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A distributed data store.
+ *
+ * <p>The data store manages multiple documents, and is a unit of configuration that shares the same
+ * network and storage interfaces. This means that all documents in the same data store are assumed
+ * to be synced with the same group and persisted to the same location.
+ *
+ * <p>As mentioned above, document (see {@link DocumentTransaction}) is the unit of causality
+ * tracking. Causal consistency is not guaranteed across different documents within the same data
+ * store.
+ */
+public class DataStore<T> implements AutoCloseable {
+
+ /** The handle used as a key to find the associated native {@code DataStore} in a handle map. */
+ private final DataStoreHandle handle;
+
+ /** The converter used to convert between the value type {@code T} and {@code byte[]}. */
+ private final Converter<T> converter;
+
+ /**
+ * Creates a new data store.
+ *
+ * @param localId The local ID identifying this local nodes within the group. This ID must be
+ * unique amongst all nodes that syncs this data store, including nodes that used to, but no
+ * longer, participate in the sync.
+ * @param networkInterface The network interface the is responsible for dispatching update
+ * messages to other nodes in the syncing group when new transactions are committed to the
+ * data store.
+ * @param storageInterface The storage interface that is responsible for persisting the contents
+ * of the data store onto disk.
+ * @param timestampProvider Provider for the wall clock time.
+ * @param converter The converter used to convert between the value type {@code T} and {@code
+ * byte[]}.
+ */
+ @MustBeClosed
+ public DataStore(
+ String localId,
+ NetworkInterface networkInterface,
+ StorageInterface storageInterface,
+ TimestampProvider timestampProvider,
+ Converter<T> converter) {
+ @DataStoreHandleId
+ long handle = nativeInit(localId, networkInterface, storageInterface, timestampProvider);
+ this.handle = new DataStoreHandle(this, handle);
+ this.converter = converter;
+ }
+
+ /**
+ * Creates a new document transaction.
+ *
+ * <p>Creates a new document transaction for the given {@code docId}. If a document for the given
+ * {@code docId} already exists, it will open that document and create a transaction for it.
+ * Otherwise, a new document at that ID is created.
+ *
+ * @param docId The document ID to open or create.
+ * @return The newly created document transaction that can be used to mutate the document.
+ */
+ @MustBeClosed
+ public DocumentTransaction<T> newDocumentTransaction(String docId) {
+ synchronized (this.handle) {
+ return new DocumentTransaction<>(
+ nativeNewDocumentTransaction(this.handle.handle, docId), docId, this.converter);
+ }
+ }
+
+ /**
+ * Returns a "full" update message.
+ *
+ * <p>The update message can be applied to any document (including an empty one) via {@link
+ * #commitNetworkUpdate(String, byte[])} to catch up to the current state.
+ *
+ * @param docId The document ID to get the full update message.
+ */
+ @Nullable
+ public byte[] getFullUpdateMessage(String docId) {
+ synchronized (this.handle) {
+ return nativeGetFullUpdateMessage(this.handle.handle, docId);
+ }
+ }
+
+ /**
+ * Returns the current version for the document with {@code docId}.
+ *
+ * <p>The returned version can be used to {@link #calculateDelta(String, VersionVector)} on a peer
+ * to get an update message to catch our data store up to the peer's version.
+ */
+ public VersionVector getDocumentVersion(String docId) {
+ synchronized (this.handle) {
+ return new VersionVector(nativeGetDocumentVersion(this.handle.handle, docId));
+ }
+ }
+
+ /**
+ * Calculates the delta needed to update from the given {@code baseVersion}.
+ *
+ * <p>The {@code baseVersion} must be a version vector from a replica of the same document. The
+ * returned document can be applied using {@link #commitNetworkUpdate(String, byte[])} on a
+ * document that is at least as new as {@code baseVersion}.
+ */
+ @Nullable
+ public byte[] calculateDelta(String docId, VersionVector baseVersion) {
+ synchronized (this.handle) {
+ return nativeCalculateDelta(this.handle.handle, docId, baseVersion.getHandle());
+ }
+ }
+
+ /**
+ * Commits changes from a given network update message.
+ *
+ * @param docId the ID of the document to which the update message belongs to.
+ * @param updateMessage the update message previously given to {@link
+ * NetworkInterface#onNewUpdate(String, byte[])} on a remote node.
+ * @throws TransactionException if the transaction cannot be committed, for example if there are
+ * failures while trying to write to disk.
+ * @throws OlderBaseNeededException if the {@code updateMessage} given doesn't cover an old enough
+ * base to be applied.
+ * @throws RuntimeException if this transaction has already been committed.
+ */
+ public void commitNetworkUpdate(String docId, byte[] updateMessage)
+ throws TransactionException, OlderBaseNeededException {
+ synchronized (this.handle) {
+ nativeCommitNetworkUpdate(this.handle.handle, docId, updateMessage);
+ }
+ }
+
+ @Override
+ public void close() {
+ this.handle.close();
+ }
+
+ private static final class DataStoreHandle extends Cleanable {
+
+ @GuardedBy("this")
+ private @DataStoreHandleId long handle;
+
+ DataStoreHandle(Object owner, @DataStoreHandleId long handle) {
+ super(owner);
+ this.handle = handle;
+ }
+
+ @Override
+ public synchronized void clean() {
+ if (this.handle != -1) {
+ nativeClose(this.handle);
+ this.handle = -1;
+ }
+ }
+ }
+
+ /**
+ * Annotation on the {@code long} handles that specifics it corresponds to the Rust {@code
+ * DataStoreHandle} type.
+ *
+ * <p>Currently this annotation is not checked by any automated tooling, but something like
+ * Checker Framework could be configured in the future.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+ @interface DataStoreHandleId {}
+
+ static {
+ System.loadLibrary("submerge_java");
+ }
+
+ private static native @DataStoreHandleId long nativeInit(
+ String localId,
+ NetworkInterface networkInterface,
+ StorageInterface storageInterface,
+ TimestampProvider timestampProvider);
+
+ private static native void nativeClose(@DataStoreHandleId long handle);
+
+ private static native @DocumentTransactionHandleId long nativeNewDocumentTransaction(
+ @DataStoreHandleId long handle, String docId);
+
+ @Nullable
+ private static native byte[] nativeGetFullUpdateMessage(
+ @DataStoreHandleId long handle, String docId);
+
+ @Nullable
+ private static native byte[] nativeCalculateDelta(
+ @DataStoreHandleId long handle, String docId, @VersionVectorHandleId long baseVersionHandle);
+
+ private static native void nativeCommitNetworkUpdate(
+ @DataStoreHandleId long handle, String docId, byte[] updateMessage);
+
+ private static native @VersionVectorHandleId long nativeGetDocumentVersion(
+ @DataStoreHandleId long handle, String docId);
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/DocumentTransaction.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/DocumentTransaction.java
new file mode 100644
index 0000000..94214fe
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/DocumentTransaction.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.Nullable;
+import com.google.android.submerge.SubmergeCleaner.Cleanable;
+import com.google.android.submerge.SubmergeDataType.SubmergeDataTypeHandleId;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A transaction for a document.
+ *
+ * <p>A document is the unit of causality tracking in Submerge. This means that if a node makes a
+ * modification A to the document, all other syncing nodes are guaranteed to observe all events that
+ * happened before A before they observe modification A.
+ *
+ * <p>The transaction temporarily stores any modifications to the underlying document. When {@link
+ * #commit()} is called, those changes are committed to storage and dispatched to the network. If
+ * this object is freed without calling {@link #commit()}, this transaction is considered aborted.
+ */
+public class DocumentTransaction<T> implements AutoCloseable {
+
+ private final String docId;
+ private final Converter<T> converter;
+ private final DocumentTransactionHandle handle;
+
+ DocumentTransaction(
+ @DocumentTransactionHandleId long handle, String docId, Converter<T> converter) {
+ this.handle = new DocumentTransactionHandle(this, handle);
+ this.docId = docId;
+ this.converter = converter;
+ }
+
+ /**
+ * Returns the root of this document.
+ *
+ * <p>Modifications to the returned value are kept within this transaction until {@link #commit()}
+ * is called.
+ *
+ * <p>The root document may be {@code null}, or one of the implementations of {@link
+ * SubmergeDataType}. To allow future evolution of the document schema, you may want to set a
+ * {@link SubmergeMap} at the root.
+ */
+ @Nullable
+ public SubmergeDataType<T> getRoot() {
+ synchronized (this.handle) {
+ @SubmergeDataTypeHandleId long rootHandle = nativeGetRoot(this.handle.handle);
+ return SubmergeDataType.create(rootHandle, this.converter);
+ }
+ }
+
+ /**
+ * Sets the root of this document.
+ *
+ * <p>Modifications are kept within this transaction until {@link #commit()} is called.
+ *
+ * <p>To allow future evolution of the document schema, you may want to set a {@link SubmergeMap}
+ * at the root.
+ *
+ * @param root The new root. Can be {@code null}, in which case this node will become empty. This
+ * {@code root} passed in must be owned by the caller, typically just created using, say,
+ * {@link DataStore#newMap()}.
+ * @throws TimestampOverflowException if the timestamp overflowed in the process of setting this
+ * new child. When this happens the old child is retained. This happens when there are more
+ * than 255 modifications in one second, or if the timestamp is larger than 2^32 seconds since
+ * Unix epoch.
+ * @throws OwnershipException if the {@code root} passed in is not owned by the caller, indicating
+ * a programmer error. See the documentation for {@code root} above.
+ */
+ public void setRoot(@Nullable SubmergeDataType<T> root) throws TimestampOverflowException {
+ synchronized (this.handle) {
+ if (root != null) {
+ @SubmergeDataTypeHandleId
+ long rootHandle = nativeSetRoot(this.handle.handle, root.takeHandle());
+ root.setHandleLocked(rootHandle);
+ } else {
+ nativeSetRoot(this.handle.handle, -1);
+ }
+ }
+ }
+
+ /**
+ * Creates a new register for this document.
+ *
+ * <p>After initialization the register should be added into the document using {@link
+ * DocumentTransaction#setRoot(SubmergeDataType)} or {@link SubmergeMap#setChild(String,
+ * SubmergeDataType)}.
+ *
+ * <p>This register must only be used within this document.
+ *
+ * @see SubmergeRegister
+ */
+ public SubmergeRegister<T> newRegister() {
+ synchronized (this.handle) {
+ if (this.handle.handle == -1) {
+ throw new IllegalStateException("Cannot create new data types while closed");
+ }
+ return new SubmergeRegister<>(nativeNewRegister(this.handle.handle), converter);
+ }
+ }
+
+ /**
+ * Creates a new map for this document.
+ *
+ * <p>After initialization the map should be added into the document using {@link
+ * DocumentTransaction#setRoot(SubmergeDataType)} or {@link SubmergeMap#setChild(String,
+ * SubmergeDataType)}.
+ *
+ * <p>This map must only be used within this document.
+ *
+ * @see SubmergeMap
+ */
+ public SubmergeMap<T> newMap() {
+ synchronized (this.handle) {
+ if (this.handle.handle == -1) {
+ throw new IllegalStateException("Cannot create new data types while closed");
+ }
+ return new SubmergeMap<>(nativeNewMap(this.handle.handle), converter);
+ }
+ }
+
+ /**
+ * Creates a new set for this document.
+ *
+ * <p>After initialization the set should be added into the document using {@link
+ * DocumentTransaction#setRoot(SubmergeDataType)} or {@link SubmergeMap#setChild(String,
+ * SubmergeDataType)}.
+ *
+ * <p>This set must only be used within this document.
+ *
+ * @see SubmergeSet
+ */
+ public synchronized SubmergeSet<T> newSet() {
+ synchronized (this.handle) {
+ if (this.handle.handle == -1) {
+ throw new IllegalStateException("Cannot create new data types while closed");
+ }
+ return new SubmergeSet<>(nativeNewSet(this.handle.handle), converter);
+ }
+ }
+
+ /**
+ * Creates a new vector data for this document.
+ *
+ * <p>After initialization the vector data should be added into the document using {@link
+ * DocumentTransaction#setRoot(SubmergeDataType)} or {@link SubmergeMap#setChild(String,
+ * SubmergeDataType)}.
+ *
+ * <p>This vector data must only be used within this document.
+ *
+ * @see SubmergeVectorData
+ */
+ public synchronized SubmergeVectorData<T> newVectorData() {
+ synchronized (this.handle) {
+ if (this.handle.handle == -1) {
+ throw new IllegalStateException("Cannot create new data types while closed");
+ }
+ return new SubmergeVectorData<>(nativeNewVectorData(this.handle.handle), converter);
+ }
+ }
+
+ /** Returns the ID of the document for this transaction. */
+ public String getDocumentId() {
+ return this.docId;
+ }
+
+ /**
+ * Commits this transaction to the document.
+ *
+ * <p>The transaction can only be committed (at most) once.
+ *
+ * @throws TransactionException if the transaction cannot be committed, for example if there are
+ * failures while trying to write to disk.
+ * @throws RuntimeException if this transaction has already been committed.
+ */
+ public void commit() throws TransactionException {
+ synchronized (this.handle) {
+ nativeCommit(this.handle.takeHandle());
+ }
+ }
+
+ /**
+ * Merges an update from the network into the current transaction.
+ *
+ * @param update The update message from the network.
+ * @throws OlderBaseNeededException if the {@code updateMessage} given doesn't cover an old enough
+ * base to be applied.
+ */
+ public void mergeNetworkUpdate(byte[] update) throws OlderBaseNeededException {
+ synchronized (this.handle) {
+ nativeMergeNetworkUpdate(this.handle.handle, update);
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this.handle) {
+ if (this.handle.handle == -1) {
+ return "DocumentTransaction { handle=null }";
+ } else {
+ return "DocumentTransaction { handle=" + nativeToString(this.handle.handle) + " }";
+ }
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ this.handle.close();
+ }
+
+ private static final class DocumentTransactionHandle extends Cleanable {
+ /** A handle to the native {@code DocumentTransaction}. */
+ @GuardedBy("this")
+ private @DocumentTransactionHandleId long handle;
+
+ DocumentTransactionHandle(Object owner, @DocumentTransactionHandleId long handle) {
+ super(owner);
+ this.handle = handle;
+ }
+
+ @Override
+ public synchronized void clean() {
+ if (this.handle != -1) {
+ nativeClose(takeHandle());
+ this.handle = -1;
+ }
+ }
+
+ @GuardedBy("this")
+ private @DocumentTransactionHandleId long takeHandle() {
+ long handle = this.handle;
+ this.handle = -1;
+ return handle;
+ }
+ }
+
+ /**
+ * Annotation on the {@code long} handles that specifics it corresponds to the Rust {@code
+ * DocumentTransactionHandle} type.
+ *
+ * <p>Currently this annotation is not checked by any automated tooling, but something like
+ * Checker Framework could be configured in the future.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+ @interface DocumentTransactionHandleId {}
+
+ static {
+ System.loadLibrary("submerge_java");
+ }
+
+ private static native @SubmergeDataTypeHandleId long nativeGetRoot(
+ long documentTransactionHandle);
+
+ @CanIgnoreReturnValue
+ private static native @SubmergeDataTypeHandleId long nativeSetRoot(
+ long documentTransactionHandle, long ownedHandle) throws TimestampOverflowException;
+
+ private static native void nativeCommit(@DocumentTransactionHandleId long handle)
+ throws TransactionException;
+
+ private static native void nativeMergeNetworkUpdate(
+ @DocumentTransactionHandleId long handle, byte[] update)
+ throws OlderBaseNeededException;
+
+ private static native void nativeClose(@DocumentTransactionHandleId long handle);
+
+ private static native String nativeToString(@DocumentTransactionHandleId long handle);
+
+ private static native @SubmergeDataTypeHandleId long nativeNewMap(
+ @DocumentTransactionHandleId long handle);
+
+ private static native @SubmergeDataTypeHandleId long nativeNewRegister(
+ @DocumentTransactionHandleId long handle);
+
+ private static native @SubmergeDataTypeHandleId long nativeNewSet(
+ @DocumentTransactionHandleId long handle);
+
+ private static native @SubmergeDataTypeHandleId long nativeNewVectorData(
+ @DocumentTransactionHandleId long handle);
+}
\ No newline at end of file
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/GenerationExhaustedException.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/GenerationExhaustedException.java
new file mode 100644
index 0000000..a184d5a
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/GenerationExhaustedException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+/**
+ * Cannot add new element to a {@link SubmergeSet} because the generation number space is exhausted.
+ */
+public class GenerationExhaustedException extends Exception {
+ GenerationExhaustedException(String msg) {
+ super(msg);
+ }
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/NetworkInterface.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/NetworkInterface.java
new file mode 100644
index 0000000..78f9c7e
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/NetworkInterface.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+/**
+ * The network interface for a {@link DataStore}.
+ *
+ * <p>When a {@link DocumentTransaction} is committed, updated messages will be generated and passed
+ * to {@link #onNewUpdate(String, byte[])}. The network interface should make a best-effort to send
+ * the update messages to other nodes on the network, retrying as desired.
+ *
+ * <p>It is safe for the network interface to send the same messages multiple times, or out of
+ * order. Eventual consistency of the documents are guaranteed as long as the update messages are
+ * eventually delivered.
+ *
+ * <p>The data store will not wait for the network message to be sent or delivered. If the network
+ * layer wishes to get an update message on demand, it can call {@link
+ * DataStore#calculateDelta(String, VersionVector)} or {@link
+ * DataStore#getFullUpdateMessage(String)}.
+ */
+public interface NetworkInterface {
+ /**
+ * Notifies the network interface that a new update is available.
+ *
+ * <p>This is called when a new local modification is committed to the datastore. Implementations
+ * for this method must be synchronize and return quickly. If the implementation needs to spend
+ * longer time to, say, talk to the server, it should copy the bytes to be processed later and
+ * return from this method.
+ *
+ * @param docId The document ID the update is for.
+ * @param updateMessage The serialized update message. This update message can be sent over the
+ * network to a peer device to be committed to the remote database using {@link
+ * DataStore#commitNetworkUpdate(String, byte[])}. This contains enough information to update
+ * a document that is at least as new as the local document. If this is not new enough, an
+ * exception {@link OlderBaseNeededException} will be thrown when {@link
+ * DataStore#commitNetworkUpdate(String, byte[])} is called.
+ */
+ void onNewUpdate(String docId, byte[] updateMessage);
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/OlderBaseNeededException.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/OlderBaseNeededException.java
new file mode 100644
index 0000000..75c0c94
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/OlderBaseNeededException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+/**
+ * Exception thrown when trying to commit a network update but the delta update doesn't cover the
+ * required base version. {@link #getBaseVersionNeeded()} can be used to retrieve the required base
+ * version, which can then be passed to {@link DataStore#calculateDelta(VersionVector)} to calculate
+ * a recent enough update message.
+ */
+public class OlderBaseNeededException extends Exception {
+ private final VersionVector baseVersionNeeded;
+
+ OlderBaseNeededException(VersionVector baseVersionNeeded) {
+ super("Update message with older base version is needed");
+ this.baseVersionNeeded = baseVersionNeeded;
+ }
+
+ /**
+ * Returns the base version that is needed to update this database.
+ */
+ public VersionVector getBaseVersionNeeded() {
+ return this.baseVersionNeeded;
+ }
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/OwnershipException.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/OwnershipException.java
new file mode 100644
index 0000000..8ebc87e
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/OwnershipException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+/**
+ * Exception that is thrown when ownership requirements are violated.
+ *
+ * <p>Some of the Submerge APIs have ownership requirements such that the same object cannot be used
+ * multiple times. These are programmer errors — these exceptions should be fixed by modifying the
+ * code rather than adding try-catch blocks.
+ */
+public class OwnershipException extends RuntimeException {
+ OwnershipException(String msg) {
+ super(msg);
+ }
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/StorageInterface.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/StorageInterface.java
new file mode 100644
index 0000000..bbbfcb6
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/StorageInterface.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.Nullable;
+import java.io.IOException;
+
+/**
+ * The storage interface for a data store.
+ *
+ * <p>When a {@link DocumentTransaction} is committed, or when new update messages are received from
+ * the network, {@link #onNewUpdate(String, byte[])} will be called to request the storage layer to
+ * persist the latest state.
+ *
+ * <p>If there are failures persisting to disk, implementations should throw a {@link
+ * StorageException}, which will cause the corresponding {@link DocumentTransaction#commit()} to
+ * throw {@link TransactionException}.
+ */
+public interface StorageInterface {
+
+ /** Exception thrown when the storage failed to persist the given document. */
+ public static class StorageException extends IOException {}
+
+ /**
+ * Called when a new update to the data store should be persisted to storage.
+ *
+ * <p>If this call returned without throwing an exception, subsequent calls to {@link
+ * #readFromStorage} should return {@code serializedDoc} unless overwritten by another call to
+ * this method.
+ *
+ * @param docId the ID of the document associated with the document.
+ * @param serializedDoc the serialized document to be persisted.
+ * @throws StorageException if the persist operation failed.
+ */
+ void onNewUpdate(String docId, byte[] serializedDoc) throws StorageException;
+
+ /**
+ * Reads a serialized document from storage.
+ *
+ * @param docId The ID of the document to read from storage.
+ * @return the document as previously passed to {@link #onNewUpdate(String, byte[])}, or {@code
+ * null} if there are no documents with the given {@code docId}.
+ * @throws StorageException if it failed to read from storage.
+ */
+ @Nullable
+ byte[] readFromStorage(String docId) throws StorageException;
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeCleaner.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeCleaner.java
new file mode 100644
index 0000000..9879365
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeCleaner.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.NonNull;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
+import com.google.android.cooperativecleaner.CooperativeCleaner.Registration;
+
+public final class SubmergeCleaner {
+ private static final CooperativeCleaner cleaner = new CooperativeCleaner();
+
+ /** Gets the singleton instance of {@link CooperativeCleaner} used in the submerge library. */
+ public static CooperativeCleaner cleaner() {
+ return cleaner;
+ }
+
+ /**
+ * A cleanable type that can be used with a {@link CooperativeCleaner}.
+ *
+ * <p>If an object {@code Foo} owns a resource {@code bar} that needs to be cleaned, instead of
+ * directly storing {@code bar} in a field, {@code Foo} would instead define a {@code static class
+ * CleanableBar extends Cleanable} and hold that in a private field instead.
+ *
+ * <p>This object must not have references to the {@code owner} to allow the owner to be garbage
+ * collected. To avoid accidentally creating those references, {@code Bar} should be static when
+ * defined as a nested class.
+ */
+ public abstract static class Cleanable implements AutoCloseable {
+ @NonNull private final Registration registration;
+
+ /**
+ * Creates a cleanable instance.
+ *
+ * <p>This will tie the lifetime of this {@code Cleanable} with the {@code owner} such that when
+ * the {@code owner} gets garbage collected, {@link #clean()} will (eventually) be called to
+ * free up the underlying resource.
+ */
+ protected Cleanable(Object owner) {
+ registration = SubmergeCleaner.cleaner().register(owner, this::clean);
+ }
+
+ /** Cleans up any resources owned by this object. */
+ protected abstract void clean();
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>{@link #close} is different from {@link #clean} in that {@link #close} will also
+ * cooperatively clean up any phantom references that are waiting to be cleaned.
+ */
+ @Override
+ public void close() {
+ registration.close();
+ }
+ }
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeDataType.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeDataType.java
new file mode 100644
index 0000000..b703b20
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeDataType.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.google.android.submerge.SubmergeCleaner.Cleanable;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** An abstract base class for data types supported by Submerge's data model. */
+public abstract class SubmergeDataType<T> implements AutoCloseable {
+
+ @IntDef({
+ CrdtType.CRDT_TYPE_NONE,
+ CrdtType.CRDT_TYPE_MAP,
+ CrdtType.CRDT_TYPE_SET,
+ CrdtType.CRDT_TYPE_REGISTER,
+ CrdtType.CRDT_TYPE_VECTOR_DATA,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface CrdtType {
+ static final int CRDT_TYPE_NONE = 0;
+ static final int CRDT_TYPE_MAP = 1;
+ static final int CRDT_TYPE_SET = 2;
+ static final int CRDT_TYPE_REGISTER = 3;
+ static final int CRDT_TYPE_VECTOR_DATA = 4;
+ }
+
+ protected final Converter<T> converter;
+
+ /**
+ * Annotation on the {@code long} handles that specifics it corresponds to the Rust {@code
+ * SubmergeDataTypeReferenceHandle} type.
+ *
+ * <p>Currently this annotation is not checked by any automated tooling, but something like
+ * Checker Framework could be configured in the future.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+ @interface SubmergeDataTypeHandleId {}
+
+ private final SubmergeDataTypeHandle handle;
+
+ protected SubmergeDataType(@SubmergeDataTypeHandleId long handle, Converter<T> converter) {
+ this.handle = new SubmergeDataTypeHandle(this, handle);
+ this.converter = converter;
+ }
+
+ void setHandleLocked(@SubmergeDataTypeHandleId long handle) {
+ synchronized (this.handle) {
+ this.handle.handle = handle;
+ }
+ }
+
+ @SubmergeDataTypeHandleId
+ long getHandle() {
+ return this.handle.get();
+ }
+
+ @SubmergeDataTypeHandleId
+ long takeHandle() {
+ return this.handle.take();
+ }
+
+ @Override
+ public void close() {
+ this.handle.close();
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this.handle) {
+ return nativeToString(this.handle.handle);
+ }
+ }
+
+ @Nullable
+ static <T> SubmergeDataType<T> create(
+ @SubmergeDataTypeHandleId long handle, @NonNull Converter<T> converter) {
+ @CrdtType int type = nativeGetCrdtType(handle);
+ switch (type) {
+ case CrdtType.CRDT_TYPE_NONE:
+ return null;
+ case CrdtType.CRDT_TYPE_MAP:
+ return new SubmergeMap<>(handle, converter);
+ case CrdtType.CRDT_TYPE_SET:
+ return new SubmergeSet<>(handle, converter);
+ case CrdtType.CRDT_TYPE_REGISTER:
+ return new SubmergeRegister<>(handle, converter);
+ case CrdtType.CRDT_TYPE_VECTOR_DATA:
+ return new SubmergeVectorData<>(handle, converter);
+ default:
+ throw new RuntimeException("Unrecognized type");
+ }
+ }
+
+ private static final class SubmergeDataTypeHandle extends Cleanable {
+ @GuardedBy("this")
+ private @SubmergeDataTypeHandleId long handle = -1;
+
+ SubmergeDataTypeHandle(Object owner, @SubmergeDataTypeHandleId long handle) {
+ super(owner);
+ this.handle = handle;
+ }
+
+ @Override
+ public synchronized void clean() {
+ if (this.handle != -1) {
+ nativeClose(this.handle);
+ this.handle = -1;
+ }
+ }
+
+ synchronized @SubmergeDataTypeHandleId long take() {
+ long handle = this.handle;
+ this.handle = -1;
+ return handle;
+ }
+
+ synchronized @SubmergeDataTypeHandleId long get() {
+ return this.handle;
+ }
+ }
+
+ static {
+ System.loadLibrary("submerge_java");
+ }
+
+ @CrdtType
+ private static native int nativeGetCrdtType(@SubmergeDataTypeHandleId long handle);
+
+ private static native void nativeClose(@SubmergeDataTypeHandleId long handle);
+
+ private static native String nativeToString(@SubmergeDataTypeHandleId long handle);
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeMap.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeMap.java
new file mode 100644
index 0000000..260ec64
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeMap.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.Nullable;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A distributed map type.
+ *
+ * <p>Keys to this map are {@link String Strings} and values are {@link SubmergeDataType
+ * SubmergeDataTypes}.
+ *
+ * <p>This is the main structural element of Submerge's data model, and may be used to encode a the
+ * schema of the document.
+ *
+ * <p>When there are concurrent {@link #setChild(String, SubmergeDataType)} calls at the same key,
+ * this map merges them using last-writer-wins via a modified version of the wall-clock timestamp.
+ */
+// TODO: Add schema example to the documentation.
+public class SubmergeMap<T> extends SubmergeDataType<T> {
+ SubmergeMap(@SubmergeDataTypeHandleId long handle, Converter<T> converter) {
+ super(handle, converter);
+ }
+
+ /**
+ * Returns the child at the given key, or {@code null} if the value does not exist.
+ *
+ * <p>Modifications to this child will be merged according to the child's merging rules.
+ */
+ @Nullable
+ public SubmergeDataType<T> getChild(String key) {
+ @SubmergeDataTypeHandleId long handle = nativeGetChild(this.getHandle(), key);
+ return SubmergeDataType.create(handle, converter);
+ }
+
+ /**
+ * Sets the child value at the given key.
+ *
+ * <p>If there are concurrent modifications to the child value (via {@link #getChild(String)}),
+ * this new {@code child} will be used and the remote modifications via {@code getChild} are
+ * discarded.
+ *
+ * <p>If two nodes concurrently modify the same key using {@link #setChild(String,
+ * SubmergeDataType)}, the writer with a larger timestamp value is more likely to win.
+ *
+ * @param key the key to set the child at.
+ * @param child the child to be set at the given key. This {@code child} must be owned by the
+ * caller, typically just created using, say, {@link DataStore#newMap()}.
+ * @throws TimestampOverflowException if the timestamp overflowed in the process of setting this
+ * new child. When this happens the old child is retained. This happens when there are more
+ * than 255 modifications in one second, or if the timestamp is larger than 2^32 seconds since
+ * Unix epoch.
+ * @throws OwnershipException if the {@code child} passed in is not owned by the caller,
+ * indicating a programmer error. See the documentation for {@code child} above.
+ */
+ public void setChild(String key, SubmergeDataType<T> child) throws TimestampOverflowException {
+ synchronized (child) {
+ @SubmergeDataTypeHandleId
+ long childHandle = nativeSetChild(this.getHandle(), key, child.getHandle());
+ child.setHandleLocked(childHandle);
+ }
+ }
+
+ /**
+ * Removes the child value at the given key.
+ *
+ * <p>This merges using the same rules as {@link #setChild(String, SubmergeDataType)}, and is
+ * equivalent to {@code setChild(key, null)}.
+ *
+ * @param key the key to set the child at.
+ * @throws TimestampOverflowException if the timestamp overflowed in the process of removing this
+ * child. When this happens the old child is retained. This happens when there are more than
+ * 255 modifications in one second, or if the timestamp is larger than 2^32 seconds since Unix
+ * epoch.
+ */
+ public void removeChild(String key) throws TimestampOverflowException {
+ nativeRemoveChild(this.getHandle(), key);
+ }
+
+ /** Returns all available keys in this map that has a child. */
+ public Set<String> keySet() {
+ return new HashSet<>(Arrays.asList(nativeKeySet(this.getHandle())));
+ }
+
+ @Override
+ public synchronized String toString() {
+ return "SubmergeMap {" + super.toString() + "}";
+ }
+
+ static {
+ System.loadLibrary("submerge_java");
+ }
+
+ private static native @SubmergeDataTypeHandleId long nativeGetChild(
+ @SubmergeDataTypeHandleId long handle, String key);
+
+ private static native @SubmergeDataTypeHandleId long nativeSetChild(
+ @SubmergeDataTypeHandleId long handle, String key, @SubmergeDataTypeHandleId long childHandle)
+ throws TimestampOverflowException;
+
+ private static native void nativeRemoveChild(@SubmergeDataTypeHandleId long handle, String key)
+ throws TimestampOverflowException;
+
+ private static native String[] nativeKeySet(@SubmergeDataTypeHandleId long handle);
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeRegister.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeRegister.java
new file mode 100644
index 0000000..d859c82
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeRegister.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A distributed register, which is a container that updates and merges atomically.
+ *
+ * <p>The value with the latest timestamp in the register can be retrieved using {@link #get()}. To
+ * get all concurrent values in the register (sometimes called "conflicts"), use {@link #getAll()}.
+ *
+ * <p>When {@link #set} is called, it will overwrite all values in the set with the new value
+ * (sometimes referred to as "resolving the conflict").
+ */
+public class SubmergeRegister<T> extends SubmergeDataType<T> {
+ SubmergeRegister(@SubmergeDataTypeHandleId long handle, Converter<T> converter) {
+ super(handle, converter);
+ }
+
+ /**
+ * Sets the value of this register.
+ *
+ * <p>If there are concurrent updates to the register, all values are kept and can be retrieved
+ * using {@link #getAll()}. When {@link #get()} is used, the value with the latest associated
+ * timestamp is returned.
+ *
+ * <p>This overrides all values currently in the register, implicitly resolving all "conflicts"
+ * from concurrent modifications that happened before.
+ *
+ * @param value the value to be set in this register
+ * @throws TimestampOverflowException if the timestamp overflowed in the process of setting this
+ * new value. When this happens the register retains the old value. This happens when there
+ * are more than 255 modifications in one second, or if the wall clock timestamp is larger
+ * than 2^32 seconds since Unix epoch.
+ */
+ public void set(T value) throws TimestampOverflowException {
+ nativeSet(this.getHandle(), this.converter.serialize(value));
+ }
+
+ /**
+ * Returns the latest value in this register.
+ *
+ * <p>{@code null} may be returned if this register has not been initialized. Otherwise, the
+ * latest value in this register, according to the timestamp, is returned. (Last-writer-wins
+ * semantics).
+ */
+ @Nullable
+ public T get() {
+ byte[] rawValue = nativeGet(this.getHandle());
+ return this.converter.deserialize(rawValue);
+ }
+
+ /**
+ * Returns the list of all values concurrently added in this register.
+ *
+ * <p>This can be thought of as the list of all "conflicting" values in this register. The
+ * returned list is ordered from newest to oldest, meaning that in the typical case {@code
+ * getAll().get(0)} is the same as {@code get()}.
+ */
+ public List<T> getAll() {
+ byte[][] rawList = nativeGetAll(this.getHandle());
+ List<T> result = new ArrayList<>(rawList.length);
+ for (byte[] rawItem : rawList) {
+ result.add(this.converter.deserialize(rawItem));
+ }
+ return result;
+ }
+
+ @Override
+ public synchronized String toString() {
+ return "SubmergeRegister {" + super.toString() + "}";
+ }
+
+ static {
+ System.loadLibrary("submerge_java");
+ }
+
+ private static native void nativeSet(@SubmergeDataTypeHandleId long handle, byte[] value)
+ throws TimestampOverflowException;
+
+ private static native byte[] nativeGet(@SubmergeDataTypeHandleId long handle);
+
+ private static native byte[][] nativeGetAll(@SubmergeDataTypeHandleId long handle);
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeSet.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeSet.java
new file mode 100644
index 0000000..3fe1c29
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeSet.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.VisibleForTesting;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import java.util.HashSet;
+import java.util.Set;
+import org.checkerframework.checker.signedness.qual.Unsigned;
+
+/**
+ * A distributed set.
+ *
+ * <p>When there are concurrent modifications on the same value, they are merged by comparing number
+ * of add and remove operations on that value. Since multiple consecutive "add" operations has the
+ * same effect as adding once in a set, it is treated as one "add" operation. So the number of "add"
+ * operations and the number of "remove" operations differ by at most one. If there are more "adds"
+ * than "removes", then the value is considered added. Otherwise if they are the same, then the
+ * value is considered removed.
+ *
+ * <p>The underlying implementation is a "Causal Length Set". See the Rust documentation for more
+ * details on the implementation.
+ */
+public class SubmergeSet<T> extends SubmergeDataType<T> {
+ SubmergeSet(@SubmergeDataTypeHandleId long handle, Converter<T> converter) {
+ super(handle, converter);
+ }
+
+ /**
+ * Adds {@code value} into the set.
+ *
+ * <p>If {@code value} is already in the set, this does nothing.
+ *
+ * @return {@code true} if the set has changed. {@code false} if the value is already in the set.
+ * @throws GenerationExhaustedException if the generation number for this item is exhausted,
+ * meaning that this item has been added and removed 2^32 times.
+ */
+ @CanIgnoreReturnValue
+ public boolean add(T value) throws GenerationExhaustedException {
+ return nativeAdd(this.getHandle(), this.converter.serialize(value));
+ }
+
+ /**
+ * Removes {@code value} from the set.
+ *
+ * <p>If {@code value} is not in the set, this does nothing.
+ *
+ * @return {@code true} if the value is removed. {@code false} if the value was not in the set to
+ * begin with.
+ */
+ @CanIgnoreReturnValue
+ public boolean remove(T value) {
+ return nativeRemove(this.getHandle(), this.converter.serialize(value));
+ }
+
+ /**
+ * Bumps the generation number for the given {@code value}.
+ *
+ * <p>This is exposed only for testing behaviors where the generation number is large or
+ * overflows. While calling this will not affect the convergence of the data in the store, it is
+ * unnecessary to use this in production.
+ *
+ * @param value the value that will be added to the set and its associated generation value
+ * bumped.
+ * @param increment the increase in generation number.
+ * @throws GenerationExhaustedException if the generation number cannot be increased by {@code
+ * increment} because it would overflow.
+ */
+ @VisibleForTesting
+ public void bumpGenerationNumberForTesting(T value, @Unsigned int increment)
+ throws GenerationExhaustedException {
+ nativeBumpGenerationNumberForTesting(
+ this.getHandle(), this.converter.serialize(value), increment);
+ }
+
+ /** Returns the list of entries in this set. */
+ public Set<T> entries() {
+ byte[][] rawEntries = nativeEntries(this.getHandle());
+ Set<T> entries = new HashSet<>(rawEntries.length);
+ for (byte[] entry : rawEntries) {
+ entries.add(this.converter.deserialize(entry));
+ }
+ return entries;
+ }
+
+ /** Returns {@code true} if {@code value} is in the set. */
+ public boolean contains(T value) {
+ return nativeContains(this.getHandle(), this.converter.serialize(value));
+ }
+
+ @Override
+ public synchronized String toString() {
+ return "SubmergeSet {" + super.toString() + "}";
+ }
+
+ static {
+ System.loadLibrary("submerge_java");
+ }
+
+ private static native boolean nativeAdd(@SubmergeDataTypeHandleId long handle, byte[] value)
+ throws GenerationExhaustedException;
+
+ private static native boolean nativeRemove(@SubmergeDataTypeHandleId long handle, byte[] value);
+
+ private static native void nativeBumpGenerationNumberForTesting(
+ @SubmergeDataTypeHandleId long handle, byte[] value, @Unsigned int increment)
+ throws GenerationExhaustedException;
+
+ private static native byte[][] nativeEntries(@SubmergeDataTypeHandleId long handle);
+
+ private static native boolean nativeContains(@SubmergeDataTypeHandleId long handle, byte[] value);
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeVectorData.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeVectorData.java
new file mode 100644
index 0000000..26ba593
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/SubmergeVectorData.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.VisibleForTesting;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import java.util.HashMap;
+import java.util.Map;
+import org.checkerframework.checker.signedness.qual.Unsigned;
+
+/**
+ * A vector of data with one entry per node, where each node can only modify their own entry.
+ *
+ * <p>The name draws similarity to how "vector clock" is a vector of logical clocks and how "version
+ * vector" is a vector of version numbers.
+ *
+ * <p>The data is tagged with a monotonically increasing version number, which is guaranteed to be
+ * unique because there is only a single writer.
+ *
+ * <p>When this type is merged, the vector is merged entry-by-entry, taking the entry with the
+ * higher version number. Since the local version number is always incremented on data change, an
+ * equal version number must imply that the data is also equal.
+ */
+public class SubmergeVectorData<T> extends SubmergeDataType<T> {
+ SubmergeVectorData(@SubmergeDataTypeHandleId long handle, Converter<T> converter) {
+ super(handle, converter);
+ }
+
+ /**
+ * Sets the value to {@code value} in the entry associated with the local node.
+ *
+ * @return {@code true} if the value was inserted or updated, or {@code false} if {@code value} is
+ * already equal to the entry in the vector.
+ * @throws VersionOverflowException if the version number for the local node will overflow, which
+ * means that this entry has been modified 2^32 times.
+ */
+ @CanIgnoreReturnValue
+ public boolean set(T value) throws VersionOverflowException {
+ return nativeSet(this.getHandle(), this.converter.serialize(value));
+ }
+
+ /**
+ * Bumps the version number for testing, setting the value to {@code null}.
+ *
+ * @param increment the amount to increase the version number by.
+ * @throws VersionOverflowException if the version number would overflow after increasing by
+ * {@code increment}.
+ */
+ @VisibleForTesting
+ public void bumpVersionNumberForTesting(@Unsigned int increment) throws VersionOverflowException {
+ nativeBumpVersionNumberForTesting(this.getHandle(), increment);
+ }
+
+ /**
+ * Returns the map of entries in this vector data.
+ *
+ * <p>The key of the map are the node IDs, and the values are the data set previously using {@link
+ * #set}.
+ */
+ public Map<String, T> entries() {
+ Map<String, byte[]> rawEntries = nativeEntries(this.getHandle());
+ Map<String, T> entries = new HashMap<>(rawEntries.size());
+ for (Map.Entry<String, byte[]> entry : rawEntries.entrySet()) {
+ entries.put(entry.getKey(), this.converter.deserialize(entry.getValue()));
+ }
+ return entries;
+ }
+
+ @Override
+ public synchronized String toString() {
+ return "SubmergeVectorData {" + super.toString() + "}";
+ }
+
+ static {
+ System.loadLibrary("submerge_java");
+ }
+
+ private static native boolean nativeSet(@SubmergeDataTypeHandleId long handle, byte[] value)
+ throws VersionOverflowException;
+
+ private static native void nativeBumpVersionNumberForTesting(
+ @SubmergeDataTypeHandleId long handle, @Unsigned int increment) throws VersionOverflowException;
+
+ private static native Map<String, byte[]> nativeEntries(@SubmergeDataTypeHandleId long handle);
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/TimestampOverflowException.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/TimestampOverflowException.java
new file mode 100644
index 0000000..491dd8e
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/TimestampOverflowException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+/** Exception thrown when the timestamp cannot be incremented because it would overflow. */
+public class TimestampOverflowException extends Exception {
+ TimestampOverflowException(String msg) {
+ super(msg);
+ }
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/TimestampProvider.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/TimestampProvider.java
new file mode 100644
index 0000000..b96d029
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/TimestampProvider.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import org.checkerframework.checker.signedness.qual.Unsigned;
+
+/**
+ * A provider for the current wall clock time.
+ *
+ * <p>This wall-clock is typically used as a tie-breaker for concurrent changes in last-writer-wins
+ * scenarios. The clock may go backwards in time (not required to be monotonic). A skewed timestamp
+ * may unfairly bias concurrent changes to a particular node, and may affect future updates' ability
+ * to effectively tie-break using the timestamp, but it will not affect data consistency.
+ *
+ * <p>(In the worst case, a heavily skewed clock will cause the timestamps to behave as if they are
+ * Lamport timestamps.)
+ */
+public interface TimestampProvider {
+ /**
+ * Returns the number of milliseconds since Unix epoch. This number is treated as an unsigned
+ * long. (See the {@code *Unsigned} functions in {@link Long}).
+ */
+ @Unsigned
+ long now();
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/TransactionException.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/TransactionException.java
new file mode 100644
index 0000000..bc6fc49
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/TransactionException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+/**
+ * Exception thrown when a transaction failed to commit.
+ *
+ * <p>When this is thrown, the associated document transaction is aborted and not committed to disk
+ * or to the network.
+ */
+// TODO: Provide a way to retrieve the changes back if the transaction failed.
+public class TransactionException extends Exception {
+
+ TransactionException(String message) {
+ super(message);
+ }
+
+ TransactionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/VersionOverflowException.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/VersionOverflowException.java
new file mode 100644
index 0000000..d9836c0
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/VersionOverflowException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+/**
+ * Error applying a change to {@link SubmergeVectorData} because the version number will overflow.
+ */
+public class VersionOverflowException extends Exception {
+ VersionOverflowException(String msg) {
+ super(msg);
+ }
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/VersionVector.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/VersionVector.java
new file mode 100644
index 0000000..933728e
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/VersionVector.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.IntDef;
+import com.google.android.submerge.SubmergeCleaner.Cleanable;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+public final class VersionVector implements AutoCloseable {
+
+ public static final int OLDER = -1;
+ public static final int EQUAL = 0;
+ public static final int NEWER = 1;
+ public static final int CONCURRENT = 2;
+
+ @IntDef({OLDER, EQUAL, NEWER, CONCURRENT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ComparisonResult {}
+
+ private final VersionVectorHandle handle;
+
+ @SuppressWarnings("unused") // Also used by native
+ VersionVector(@VersionVectorHandleId long handle) {
+ this.handle = new VersionVectorHandle(this, handle);
+ }
+
+ @Override
+ public void close() {
+ this.handle.close();
+ }
+
+ public byte[] toByteArray() {
+ return nativeToByteArray(getHandle());
+ }
+
+ public static VersionVector fromByteArray(byte[] data) {
+ return new VersionVector(nativeFromByteArray(data));
+ }
+
+ /**
+ * Compares this version vector with another.
+ *
+ * @param other The other version vector to compare against.
+ * @return The {@link ComparisonResult} of the operation. OLDER if this is older than
+ * {@code other}, NEWER if this is newer than {@code other}, and so on.
+ */
+ @ComparisonResult
+ public int compare(VersionVector other) {
+ return nativeCompare(getHandle(), other.getHandle());
+ }
+
+ @VersionVectorHandleId
+ long getHandle() {
+ return this.handle.get();
+ }
+
+ private static final class VersionVectorHandle extends Cleanable {
+ @GuardedBy("this")
+ private @VersionVectorHandleId long handle;
+
+ VersionVectorHandle(Object owner, @VersionVectorHandleId long handle) {
+ super(owner);
+ this.handle = handle;
+ }
+
+ @Override
+ protected synchronized void clean() {
+ if (this.handle != -1) {
+ nativeClose(this.handle);
+ this.handle = -1;
+ }
+ }
+
+ private synchronized @VersionVectorHandleId long get() {
+ return this.handle;
+ }
+ }
+
+ /**
+ * Annotation on the {@code long} handles that specifics it corresponds to the Rust {@code
+ * VersionVectorHandle} type.
+ *
+ * <p>Currently this annotation is not checked by any automated tooling, but something like
+ * Checker Framework could be configured in the future.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+ @interface VersionVectorHandleId {}
+
+ private static native void nativeClose(@VersionVectorHandleId long handle);
+
+ private static native byte[] nativeToByteArray(@VersionVectorHandleId long handle);
+
+ private static native @VersionVectorHandleId long nativeFromByteArray(byte[] data);
+
+ private static native int nativeCompare(
+ @VersionVectorHandleId long handle, @VersionVectorHandleId long other);
+}
diff --git a/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/package-info.java b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/package-info.java
new file mode 100644
index 0000000..4a403a7
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/main/java/com/google/android/submerge/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+@CheckReturnValue
+package com.google.android.submerge;
+
+import com.google.errorprone.annotations.CheckReturnValue;
diff --git a/betosync/submerge/submerge_java/lib/src/test/java/com/google/android/submerge/DataStoreTest.java b/betosync/submerge/submerge_java/lib/src/test/java/com/google/android/submerge/DataStoreTest.java
new file mode 100644
index 0000000..ee74723
--- /dev/null
+++ b/betosync/submerge/submerge_java/lib/src/test/java/com/google/android/submerge/DataStoreTest.java
@@ -0,0 +1,958 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.submerge;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+import androidx.annotation.Nullable;
+import com.google.android.submerge.StorageInterface.StorageException;
+import com.google.android.submerge.SubmergeDataType.SubmergeDataTypeHandleId;
+import com.google.errorprone.annotations.MustBeClosed;
+import java.util.HashMap;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(JUnit4.class)
+public class DataStoreTest {
+ @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock private NetworkInterface networkInterface;
+
+ @Spy StorageInterface storageInterface = new Storage();
+
+ @Mock private TimestampProvider timestampProvider;
+
+ private DataStore<String> dataStore;
+
+ @Before
+ @SuppressWarnings("MustBeClosed")
+ public void setUp() throws StorageException {
+ Mockito.lenient()
+ .when(timestampProvider.now())
+ .thenAnswer((unused) -> System.currentTimeMillis() / 1000L);
+ this.dataStore = createDataStore("test_actor");
+ }
+
+ @MustBeClosed
+ private DataStore<String> createDataStore(String actor) {
+ return new DataStore<>(
+ actor, networkInterface, storageInterface, timestampProvider, new StringConverter());
+ }
+
+ @After
+ public void cleanUp() {
+ this.dataStore.close();
+ }
+
+ @Test
+ public void testOpenDocument() throws TransactionException {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ assertThat(doc.getRoot()).isNull();
+ assertThat(doc.getDocumentId()).isEqualTo("mydoc");
+ doc.commit();
+ }
+ }
+
+ @Test
+ public void testCommitTwice() throws TransactionException {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ doc.commit();
+ RuntimeException e = assertThrows(RuntimeException.class, doc::commit);
+ assertThat(e).hasMessageThat().isEqualTo("Cannot commit the same transaction twice");
+ }
+ }
+
+ @Test
+ public void testCommitEmptyTransaction() throws Exception {
+ // Create a document.
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc");
+ SubmergeMap<String> root = doc.newMap();
+ SubmergeRegister<String> r = doc.newRegister()) {
+ doc.setRoot(root);
+ root.setChild("child", r);
+ doc.commit();
+ }
+ reset(networkInterface);
+ reset(storageInterface);
+
+ // Read and commit again with no change.
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc");
+ SubmergeMap<String> root = (SubmergeMap<String>) doc.getRoot();
+ SubmergeRegister<String> r = (SubmergeRegister<String>) root.getChild("child")) {
+ assertThat(root).isNotNull();
+ assertThat(r).isNotNull();
+ doc.commit();
+ }
+
+ // Verify nothing happens.
+ verify(storageInterface, only()).readFromStorage(anyString());
+ verifyNoInteractions(networkInterface);
+ }
+
+ @SuppressWarnings("MustBeClosed")
+ @Test
+ public void newDataType_whileClosed_shouldThrow() throws Exception {
+ DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc");
+ doc.close();
+
+ assertThrows(IllegalStateException.class, () -> doc.newMap());
+ assertThrows(IllegalStateException.class, () -> doc.newSet());
+ assertThrows(IllegalStateException.class, () -> doc.newRegister());
+ assertThrows(IllegalStateException.class, () -> doc.newVectorData());
+ }
+
+ @Test
+ public void testToString() {
+ assertThat(dataStore.toString()).isNotEmpty();
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ assertThat(doc.toString()).isNotEmpty();
+ try (SubmergeMap<String> map = doc.newMap()) {
+ assertThat(map.toString()).isNotEmpty();
+ }
+ try (SubmergeRegister<String> register = doc.newRegister()) {
+ assertThat(register.toString()).isNotEmpty();
+ }
+ try (SubmergeSet<String> set = doc.newSet()) {
+ assertThat(set.toString()).isNotEmpty();
+ }
+ try (SubmergeVectorData<String> vectorData = doc.newVectorData()) {
+ assertThat(vectorData.toString()).isNotEmpty();
+ }
+ }
+ }
+
+ @Test
+ public void testClose() {
+ // Close should be idempotent
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ try (SubmergeMap<String> map = doc.newMap()) {
+ map.close();
+ map.close();
+ }
+ try (SubmergeRegister<String> register = doc.newRegister()) {
+ register.close();
+ register.close();
+ }
+ try (SubmergeSet<String> set = doc.newSet()) {
+ set.close();
+ set.close();
+ }
+ try (SubmergeVectorData<String> vectorData = doc.newVectorData()) {
+ vectorData.close();
+ vectorData.close();
+ }
+ dataStore.close();
+ dataStore.close();
+ doc.close();
+ doc.close();
+ }
+ verifyNoInteractions(networkInterface);
+ }
+
+ @Test
+ public void testGetRoot() throws TransactionException {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ assertThat(doc.getRoot()).isNull();
+ doc.commit();
+ }
+ }
+
+ @Test
+ public void testSetRoot() throws TimestampOverflowException, TransactionException {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc");
+ SubmergeMap<String> map = doc.newMap()) {
+ doc.setRoot(map);
+ assertThat(doc.getRoot()).isInstanceOf(SubmergeMap.class);
+ doc.commit();
+ }
+ }
+
+ @Test
+ public void testSetRootNull() throws TimestampOverflowException, TransactionException {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc");
+ SubmergeMap<String> map = doc.newMap()) {
+ doc.setRoot(map);
+ assertThat(doc.getRoot()).isInstanceOf(SubmergeMap.class);
+ doc.setRoot(null);
+ assertThat(doc.getRoot()).isNull();
+ doc.commit();
+ }
+ }
+
+ @Test
+ public void testSetRootTimestampOverflow()
+ throws TimestampOverflowException, TransactionException {
+ // Always return the same wall-clock timestamp to trigger timestamp overflow
+ when(timestampProvider.now()).thenReturn(1L);
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ for (int i = 0; i < 256; i++) {
+ doc.setRoot(doc.newMap());
+ }
+ assertThrows(TimestampOverflowException.class, () -> doc.setRoot(doc.newMap()));
+ }
+ }
+
+ @Test
+ public void testSet()
+ throws TimestampOverflowException, GenerationExhaustedException, TransactionException {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeSet<String> set = doc.newSet();
+ set.add("Hello");
+ set.add("World");
+ doc.setRoot(set);
+
+ SubmergeSet<String> actualSet = (SubmergeSet<String>) doc.getRoot();
+ assertThat(actualSet.contains("Hello")).isTrue();
+ assertThat(actualSet.contains("World")).isTrue();
+ assertThat(actualSet.contains("Mars")).isFalse();
+
+ assertThat(actualSet.entries()).containsExactly("Hello", "World");
+
+ actualSet.remove("World");
+ assertThat(actualSet.contains("World")).isFalse();
+ doc.commit();
+ verify(networkInterface, only()).onNewUpdate(anyString(), any());
+ }
+ }
+
+ @Test
+ public void testSetGenerationExhausted() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeSet<String> set = doc.newSet();
+ set.bumpGenerationNumberForTesting("Hello", (int) 4294967295L);
+ set.remove("Hello");
+ assertThrows(GenerationExhaustedException.class, () -> set.add("Hello"));
+ }
+ }
+
+ @Test
+ public void testRegister() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> reg = doc.newRegister();
+ reg.set("Hello world");
+ doc.setRoot(reg);
+ assertThat(doc.getRoot()).isInstanceOf(SubmergeRegister.class);
+ String registerValue = ((SubmergeRegister<String>) doc.getRoot()).get();
+ assertThat(registerValue).isEqualTo("Hello world");
+ doc.commit();
+ verify(networkInterface, only()).onNewUpdate(anyString(), any());
+ }
+ Mockito.reset(networkInterface);
+
+ // Reopen the doc and check that the set value is still there
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> reg = (SubmergeRegister<String>) doc.getRoot();
+ String registerValue = reg.get();
+ assertThat(registerValue).isEqualTo("Hello world");
+ }
+ }
+
+ @Test
+ public void testRegisterTimestampOverflow() throws Exception {
+ // Always return the same wall-clock timestamp to trigger timestamp overflow
+ when(timestampProvider.now()).thenReturn(1L);
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> register = doc.newRegister();
+ for (int i = 0; i < 256; i++) {
+ register.set(i + "");
+ }
+ assertThrows(TimestampOverflowException.class, () -> register.set("257"));
+ }
+ }
+
+ @Test
+ public void testVectorData() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ doc.setRoot(doc.newVectorData());
+ SubmergeVectorData<String> child = (SubmergeVectorData<String>) doc.getRoot();
+ child.set("hello I am testy");
+ assertThat(child.entries()).hasSize(1);
+ assertThat(child.entries().get("nonexistent")).isNull();
+ assertThat(child.entries()).containsEntry("test_actor", "hello I am testy");
+ doc.commit();
+ }
+ }
+
+ @Test
+ public void testVectorDataVersionOverflow() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeVectorData<String> vectorData = doc.newVectorData();
+ vectorData.bumpVersionNumberForTesting((int) 4294967295L);
+ assertThrows(VersionOverflowException.class, () -> vectorData.set("new data"));
+ }
+ }
+
+ @Test
+ public void testMap() throws TimestampOverflowException, TransactionException {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ doc.setRoot(doc.newMap());
+ SubmergeMap<String> map = (SubmergeMap<String>) doc.getRoot();
+ SubmergeRegister<String> child1 = doc.newRegister();
+ child1.set("Hello");
+ map.setChild("child1", child1);
+ SubmergeVectorData<String> child2 = doc.newVectorData();
+ map.setChild("child2", child2);
+
+ SubmergeRegister<String> actualChild1 = (SubmergeRegister<String>) map.getChild("child1");
+ assertThat(actualChild1.get()).isEqualTo("Hello");
+
+ SubmergeVectorData<String> actualChild2 = (SubmergeVectorData<String>) map.getChild("child2");
+ assertThat(actualChild2.entries()).isEmpty();
+
+ doc.commit();
+ }
+ }
+
+ @Test
+ public void testPassNonOwnedChild() throws TimestampOverflowException, TransactionException {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc");
+ DataStore<String> dataStore2 = createDataStore("test_actor2");
+ DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc2")) {
+ SubmergeMap<String> child = doc.newMap();
+ doc.setRoot(child);
+ assertThrows(OwnershipException.class, () -> doc2.setRoot(child));
+ doc.commit();
+ doc2.commit();
+ }
+ }
+
+ @Test
+ public void testPassChildFromDifferentDoc()
+ throws TimestampOverflowException, TransactionException {
+ try (DataStore<String> dataStore2 = createDataStore("test_actor2");
+ DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc");
+ DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc2")) {
+ SubmergeMap<String> child = doc.newMap();
+ doc.setRoot(child);
+ assertThrows(OwnershipException.class, () -> doc2.setRoot(doc.getRoot()));
+ doc.commit();
+ doc2.commit();
+ }
+ }
+
+ @Test
+ public void testOnNewUpdateStorageError()
+ throws TimestampOverflowException, TransactionException, StorageException {
+ doThrow(StorageException.class).when(storageInterface).onNewUpdate(anyString(), any());
+
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ doc.setRoot(doc.newMap());
+ TransactionException e = assertThrows(TransactionException.class, doc::commit);
+ assertThat(e).hasCauseThat().isInstanceOf(StorageException.class);
+ }
+ }
+
+ @Test
+ public void testCommitNetworkUpdate() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hello");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+ verify(networkInterface).onNewUpdate(eq("mydoc"), captor.capture());
+
+ // Pretend the following runs on another device
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter())) {
+ dataStore2.commitNetworkUpdate("mydoc", captor.getValue());
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc2.getRoot();
+ assertThat(register.get()).isEqualTo("Hello");
+ }
+ }
+ }
+
+ @Test
+ public void testMergeNetworkUpdate() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hello");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+ verify(networkInterface).onNewUpdate(eq("mydoc"), captor.capture());
+ reset(networkInterface);
+
+ // Pretend the following runs on another device
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter())) {
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ doc2.mergeNetworkUpdate(captor.getValue());
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc2.getRoot();
+ assertThat(register.get()).isEqualTo("Hello");
+ doc2.commit();
+ }
+
+ // Verify no network update triggered.
+ verifyNoInteractions(networkInterface);
+
+ // Verify that the change is persisted
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc2.getRoot();
+ assertThat(register.get()).isEqualTo("Hello");
+ }
+ }
+ }
+
+ @Test
+ public void testMergeNetworkUpdate_olderBaseNeeded() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hello");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ reset(networkInterface);
+
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hi there");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+ verify(networkInterface).onNewUpdate(eq("mydoc"), captor.capture());
+ reset(networkInterface);
+
+ // Pretend the following runs on another device
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter())) {
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ OlderBaseNeededException e =
+ assertThrows(
+ OlderBaseNeededException.class, () -> doc2.mergeNetworkUpdate(captor.getValue()));
+
+ // Simulate a round trip between devices to make sure the vector clock's serialization is
+ // correct.
+ try (VersionVector v = e.getBaseVersionNeeded();
+ VersionVector convertedV = VersionVector.fromByteArray(v.toByteArray())) {
+ byte[] deltaMessage = dataStore.calculateDelta("mydoc", convertedV);
+ doc2.mergeNetworkUpdate(deltaMessage);
+ doc2.commit();
+ }
+ }
+
+ // Verify no network update triggered.
+ verifyNoInteractions(networkInterface);
+
+ // Verify that the change is persisted
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc2.getRoot();
+ assertThat(register.get()).isEqualTo("Hi there");
+ }
+ }
+ }
+
+ @Test
+ public void testMergeNetworkUpdate_invalidProtoBuf_noOp() throws Exception {
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter())) {
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ doc2.mergeNetworkUpdate(new byte[0]);
+ assertThat(doc2.getRoot()).isNull();
+ doc2.commit();
+ }
+ }
+
+ // Verify no network update triggered.
+ verifyNoInteractions(networkInterface);
+ }
+
+ @Test
+ public void testMergeNetworkUpdate_noCommit_storageUnchanged() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hello");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+ verify(networkInterface).onNewUpdate(eq("mydoc"), captor.capture());
+ reset(networkInterface);
+
+ // Pretend the following runs on another device
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter())) {
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ doc2.mergeNetworkUpdate(captor.getValue());
+ // No commit.
+ }
+
+ // Verify no network update triggered.
+ verifyNoInteractions(networkInterface);
+
+ // Verify that the change is not persisted
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ assertThat(doc2.getRoot()).isNull();
+ }
+ }
+ }
+
+ @Test
+ public void testMergeNetworkUpdate_hasLocalChange() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hello");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+ verify(networkInterface).onNewUpdate(eq("mydoc"), captor.capture());
+ reset(networkInterface);
+
+ // Pretend the following runs on another device
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter())) {
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ doc2.mergeNetworkUpdate(captor.getValue());
+ SubmergeRegister<String> child = (SubmergeRegister<String>) doc2.getRoot();
+ child.set("Hi");
+ doc2.commit();
+ }
+
+ // Verify that the change is persisted
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc2.getRoot();
+ assertThat(register.get()).isEqualTo("Hi");
+ }
+ }
+
+ // Verify and capture the network update.
+ captor = ArgumentCaptor.forClass(byte[].class);
+ verify(networkInterface).onNewUpdate(eq("mydoc"), captor.capture());
+ reset(networkInterface);
+
+ // Send the network update back to the first device.
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ doc.mergeNetworkUpdate(captor.getValue());
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc.getRoot();
+ assertThat(register.get()).isEqualTo("Hi");
+ doc.commit();
+ }
+
+ // Verify no network update triggered.
+ verifyNoInteractions(networkInterface);
+
+ // Verify that the change is persisted
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc.getRoot();
+ assertThat(register.get()).isEqualTo("Hi");
+ }
+ }
+
+ @Test(timeout = 10000)
+ public void testMapGetSetChild_doesNotDeadlock() throws Exception {
+ // Submerge data types are stored in handle map, which holds items in shards. Handle maps have
+ // a footgun where it's easy for calling code to deadlock on itself (b/440130634). This test
+ // sets many child elements to a map to ensure that different sharding scenarios are covered.
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeMap<String> root = doc.newMap();
+ doc.setRoot(root);
+ for (int i = 0; i < 50; i++) {
+ SubmergeMap<String> map = doc.newMap();
+ root.setChild("map" + i, map);
+ assertThat(root.getChild("map" + i)).isNotNull();
+ }
+ doc.commit();
+ }
+ }
+
+ @Test
+ public void testCommitFullUpdate() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hello");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+ verify(networkInterface).onNewUpdate(eq("mydoc"), captor.capture());
+
+ assertThat(dataStore.getFullUpdateMessage("mydoc")).isEqualTo(captor.getValue());
+
+ // Pretend the following runs on another device
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter())) {
+ dataStore2.commitNetworkUpdate("mydoc", dataStore.getFullUpdateMessage("mydoc"));
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc2.getRoot();
+ assertThat(register.get()).isEqualTo("Hello");
+ }
+ }
+ }
+
+ @Test
+ public void testOlderBaseNeeded() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hello");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ Mockito.reset(networkInterface);
+
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hi there");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+ verify(networkInterface).onNewUpdate(eq("mydoc"), captor.capture());
+
+ // Pretend the following runs on another device
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter())) {
+ OlderBaseNeededException e =
+ assertThrows(
+ OlderBaseNeededException.class,
+ () -> dataStore2.commitNetworkUpdate("mydoc", captor.getValue()));
+
+ // Simulate a round trip between devices to make sure the vector clock's serialization is
+ // correct.
+ try (VersionVector v = e.getBaseVersionNeeded();
+ VersionVector convertedV = VersionVector.fromByteArray(v.toByteArray())) {
+ byte[] deltaMessage = dataStore.calculateDelta("mydoc", convertedV);
+ dataStore2.commitNetworkUpdate("mydoc", deltaMessage);
+ }
+
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc2.getRoot();
+ assertThat(register.get()).isEqualTo("Hi there");
+ }
+ }
+ }
+
+ @Test
+ public void testReadFromStorage() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> child = doc.newRegister();
+ child.set("Hello");
+ doc.setRoot(child);
+ doc.commit();
+ }
+
+ ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+ verify(storageInterface).onNewUpdate(eq("mydoc"), captor.capture());
+
+ Storage storage2 = new Storage();
+ storage2.map.put("mydoc", captor.getValue());
+
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2", networkInterface, storage2, timestampProvider, new StringConverter())) {
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ SubmergeRegister<String> register = (SubmergeRegister<String>) doc2.getRoot();
+ assertThat(register.get()).isEqualTo("Hello");
+ }
+ }
+ }
+
+ @Test
+ public void testMapSetChildBeforeAddingToDocument() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeMap<String> map = doc.newMap();
+ SubmergeRegister<String> register = doc.newRegister();
+ register.set("test");
+ assertThrows(OwnershipException.class, () -> map.setChild("key1", register));
+ }
+ }
+
+ @Test
+ public void testGetDocumentVersion() throws Exception {
+ String firstMessage =
+ "Hello this is the first message, deliberately long to make sure its "
+ + "delta message is longer than the second transaction";
+
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeMap<String> map = doc.newMap();
+ SubmergeRegister<String> register = doc.newRegister();
+ register.set(firstMessage);
+ doc.setRoot(map);
+ map.setChild("key1", register);
+ doc.commit();
+ }
+
+ byte[] updateMessage1, updateMessage2;
+
+ Storage storage2 = new Storage();
+
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2", networkInterface, storage2, timestampProvider, new StringConverter())) {
+ // Simulate a round trip between devices to make sure the vector clock's serialization is
+ // correct.
+ try (VersionVector v = dataStore2.getDocumentVersion("mydoc");
+ VersionVector convertedV = VersionVector.fromByteArray(v.toByteArray())) {
+ updateMessage1 = dataStore.calculateDelta("mydoc", convertedV);
+ dataStore2.commitNetworkUpdate("mydoc", updateMessage1);
+ }
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ SubmergeMap<String> map = (SubmergeMap<String>) doc2.getRoot();
+ SubmergeRegister<String> register = (SubmergeRegister<String>) map.getChild("key1");
+ assertThat(register.get()).isEqualTo(firstMessage);
+ }
+ }
+
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeMap<String> map = (SubmergeMap<String>) doc.getRoot();
+ SubmergeRegister<String> register = doc.newRegister();
+ register.set("Bye");
+ map.setChild("key2", register);
+ doc.commit();
+ }
+
+ try (DataStore<String> dataStore2 =
+ new DataStore<>(
+ "test_actor2", networkInterface, storage2, timestampProvider, new StringConverter())) {
+ updateMessage2 = dataStore.calculateDelta("mydoc", dataStore2.getDocumentVersion("mydoc"));
+ dataStore2.commitNetworkUpdate("mydoc", updateMessage2);
+ try (DocumentTransaction<String> doc2 = dataStore2.newDocumentTransaction("mydoc")) {
+ SubmergeMap<String> map = (SubmergeMap<String>) doc2.getRoot();
+ SubmergeRegister<String> register1 = (SubmergeRegister<String>) map.getChild("key1");
+ SubmergeRegister<String> register2 = (SubmergeRegister<String>) map.getChild("key2");
+ assertThat(register1.get()).isEqualTo(firstMessage);
+ assertThat(register2.get()).isEqualTo("Bye");
+ }
+ }
+
+ // Since the first update contains a long message, its message length should be longer than the
+ // second update if delta calculation is working correctly.
+ assertThat(updateMessage1.length).isGreaterThan(updateMessage2.length);
+ }
+
+ @Test
+ public void testSubmergeDataTypeCreate() {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ {
+ SubmergeRegister<String> register = doc.newRegister();
+ @SubmergeDataTypeHandleId long handle = register.takeHandle();
+ assertThat(SubmergeDataType.create(handle, new StringConverter()))
+ .isInstanceOf(SubmergeRegister.class);
+ }
+
+ {
+ SubmergeMap<String> map = doc.newMap();
+ @SubmergeDataTypeHandleId long handle = map.takeHandle();
+ assertThat(SubmergeDataType.create(handle, new StringConverter()))
+ .isInstanceOf(SubmergeMap.class);
+ }
+
+ {
+ SubmergeSet<String> set = doc.newSet();
+ @SubmergeDataTypeHandleId long handle = set.takeHandle();
+ assertThat(SubmergeDataType.create(handle, new StringConverter()))
+ .isInstanceOf(SubmergeSet.class);
+ }
+
+ {
+ SubmergeVectorData<String> vectorData = doc.newVectorData();
+ @SubmergeDataTypeHandleId long handle = vectorData.takeHandle();
+ assertThat(SubmergeDataType.create(handle, new StringConverter()))
+ .isInstanceOf(SubmergeVectorData.class);
+ }
+ }
+ }
+
+ @Test
+ public void testMapKeySet() throws Exception {
+ try (DocumentTransaction<String> doc = dataStore.newDocumentTransaction("mydoc")) {
+ SubmergeMap<String> map = doc.newMap();
+ doc.setRoot(map);
+
+ SubmergeRegister<String> child1 = doc.newRegister();
+ child1.set("value1");
+ map.setChild("key1", child1);
+
+ SubmergeRegister<String> child2 = doc.newRegister();
+ child2.set("value2");
+ map.setChild("key2", child2);
+
+ assertThat(map.keySet()).containsExactly("key1", "key2");
+
+ map.removeChild("key1");
+ assertThat(map.keySet()).containsExactly("key2");
+ }
+ }
+
+ @Test
+ public void testDeserializeVersionVector_invalidFormat() {
+ assertThrows(RuntimeException.class, () -> VersionVector.fromByteArray(new byte[] {1, 2, 3}));
+ }
+
+ @Test
+ public void testVersionVectorCompare() throws Exception {
+ VersionVector v1, v2, v3, v4, v5;
+
+ // Manually create version vectors by serializing/deserializing the rust type.
+ // This is a bit roundabout, but it's the only way to create a VersionVector with specific
+ // contents from the Java side for testing.
+ try (DataStore<String> ds1 =
+ new DataStore<>(
+ "actor1",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter());
+ DataStore<String> ds2 =
+ new DataStore<>(
+ "actor2",
+ networkInterface,
+ new Storage(),
+ timestampProvider,
+ new StringConverter())) {
+
+ try (DocumentTransaction<String> doc1 = ds1.newDocumentTransaction("mydoc")) {
+ doc1.setRoot(doc1.newMap()); // Make a non-empty commit
+ doc1.commit(); // version: actor1 -> 1
+ v1 = ds1.getDocumentVersion("mydoc");
+ }
+
+ try (DocumentTransaction<String> doc1 = ds1.newDocumentTransaction("mydoc")) {
+ ((SubmergeMap<String>) doc1.getRoot()).setChild("key", doc1.newRegister()); // Make a change
+ doc1.commit(); // version: actor1 -> 2
+ v2 = ds1.getDocumentVersion("mydoc");
+ }
+
+ try (DocumentTransaction<String> doc2 = ds2.newDocumentTransaction("mydoc")) {
+ doc2.setRoot(doc2.newMap()); // Make a non-empty commit
+ doc2.commit(); // version: actor2 -> 1
+ v3 = ds2.getDocumentVersion("mydoc");
+ }
+ }
+
+ v4 = VersionVector.fromByteArray(v1.toByteArray()); // v4 is identical to v1
+ v5 = VersionVector.fromByteArray(v2.toByteArray()); // v5 is a descendant of v1
+
+ // An ancestor is older than a descendant.
+ assertThat(v1.compare(v2)).isEqualTo(VersionVector.OLDER);
+ // A descendant is newer than an ancestor.
+ assertThat(v2.compare(v1)).isEqualTo(VersionVector.NEWER);
+
+ // Concurrent vectors are concurrent.
+ assertThat(v1.compare(v3)).isEqualTo(VersionVector.CONCURRENT);
+ assertThat(v3.compare(v1)).isEqualTo(VersionVector.CONCURRENT);
+
+ // An identical vector is equal.
+ assertThat(v1.compare(v4)).isEqualTo(VersionVector.EQUAL);
+
+ // A descendant is newer than its ancestor.
+ assertThat(v5.compare(v1)).isEqualTo(VersionVector.NEWER);
+ }
+
+ private static class StringConverter implements Converter<String> {
+
+ @Override
+ public String deserialize(byte[] bytes) {
+ return new String(bytes, UTF_8);
+ }
+
+ @Override
+ public byte[] serialize(String value) {
+ return value.getBytes(UTF_8);
+ }
+ }
+
+ private static class Storage implements StorageInterface {
+
+ private final HashMap<String, byte[]> map = new HashMap<>();
+
+ @Override
+ public void onNewUpdate(String docId, byte[] serializedDoc) throws StorageException {
+ map.put(docId, serializedDoc);
+ }
+
+ @Nullable
+ @Override
+ public byte[] readFromStorage(String docId) throws StorageException {
+ return map.get(docId);
+ }
+ }
+}
diff --git a/betosync/submerge/submerge_java/settings.gradle.kts b/betosync/submerge/submerge_java/settings.gradle.kts
new file mode 100644
index 0000000..29df2fe
--- /dev/null
+++ b/betosync/submerge/submerge_java/settings.gradle.kts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * The settings file is used to specify which projects to include in your build.
+ * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.7/userguide/multi_project_builds.html in the Gradle documentation.
+ */
+
+rootProject.name = "submerge-java"
+include("lib")
+
+include("cooperative_cleaner")
+project(":cooperative_cleaner").projectDir = File("../../../common/cooperative_cleaner/lib")
diff --git a/betosync/submerge/submerge_java/src/class.rs b/betosync/submerge/submerge_java/src/class.rs
new file mode 100644
index 0000000..68b7ad9
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class.rs
@@ -0,0 +1,26 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+pub mod data_store;
+pub mod document_transaction;
+pub mod exceptions;
+pub mod network_interface;
+pub mod storage_interface;
+pub mod submerge_data_type;
+pub mod submerge_map;
+pub mod submerge_register;
+pub mod submerge_set;
+pub mod submerge_vector_data;
+pub mod timestamp_provider;
+pub mod version_vector;
diff --git a/betosync/submerge/submerge_java/src/class/data_store.rs b/betosync/submerge/submerge_java/src/class/data_store.rs
new file mode 100644
index 0000000..1aec062
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/data_store.rs
@@ -0,0 +1,238 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use crdt::typed_crdt::{TypedCrdt, TypedCrdtMut};
+use handle_map::HandleLike;
+use jni::{
+ objects::{JByteArray, JClass, JObject, JString, JThrowable},
+ sys::jlong,
+ JNIEnv,
+};
+use pourover::{exception::runtime_exception, jni_method};
+use submerge::{
+ CommitUpdateError, DataStore, DataStoreConfig, DeltaDocument, DocumentTransaction, SubmergeMap,
+ SubmergeRegister, SubmergeSet, SubmergeVectorData,
+};
+
+use crate::{
+ class::{
+ exceptions::{
+ older_base_needed_exception, transaction_exception, transaction_exception_with_cause,
+ },
+ storage_interface::StorageError,
+ },
+ utils::declare_handle,
+};
+
+use super::{
+ document_transaction::DocumentTransactionHandle,
+ exceptions::try_throwable_submerge,
+ network_interface::{JavaNetwork, NetworkInterfaceJni},
+ storage_interface::{JavaStorage, StorageInterfaceJni},
+ timestamp_provider::{JavaTimestampProvider, TimestampProviderJni},
+ version_vector::VersionVectorHandle,
+};
+
+/// A [`DataStoreConfig`] for use by Java.
+pub enum JavaDataStoreConfig {}
+
+impl DataStoreConfig for JavaDataStoreConfig {
+ type NodeId = String;
+ type ValueType = Vec<u8>;
+ type Network = JavaNetwork;
+ type Storage = JavaStorage;
+ type TimestampProvider = JavaTimestampProvider;
+}
+
+pub type SubmergeJavaMap = SubmergeMap<JavaDataStoreConfig>;
+pub type SubmergeJavaRegister = SubmergeRegister<JavaDataStoreConfig>;
+pub type SubmergeJavaSet = SubmergeSet<JavaDataStoreConfig>;
+pub type SubmergeJavaVectorData = SubmergeVectorData<JavaDataStoreConfig>;
+pub type SubmergeJavaTypedCrdt = TypedCrdt<Vec<u8>, String>;
+pub type SubmergeJavaTypedCrdtMut<'a> = TypedCrdtMut<'a, Vec<u8>, String>;
+
+declare_handle! {
+ pub struct DataStoreHandle for DataStore<JavaDataStoreConfig>;
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DataStore")]
+extern "system" fn nativeInit<'local>(
+ mut env: JNIEnv<'local>,
+ _class: JClass<'local>,
+ local_id: JString<'local>,
+ network: NetworkInterfaceJni<JObject<'local>>,
+ storage: StorageInterfaceJni<JObject<'local>>,
+ timestamp_provider: TimestampProviderJni<JObject<'local>>,
+) -> jlong {
+ try_throwable_submerge(&mut env, |env| {
+ let local_id = String::from(env.get_string(&local_id)?);
+ let network = JavaNetwork::new(env, network)?;
+ let storage = JavaStorage::new(env, storage)?;
+ let timestamp_provider = JavaTimestampProvider::new(env, timestamp_provider)?;
+ Ok(DataStoreHandle::allocate(|| {
+ DataStore::new(local_id, network, storage, timestamp_provider)
+ })?
+ .jlong())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DataStore")]
+extern "system" fn nativeClose<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) {
+ try_throwable_submerge(&mut env, |_env| {
+ let handle = DataStoreHandle::try_from(handle_id)?;
+ let _ = handle.deallocate()?;
+ Ok(())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DataStore")]
+extern "system" fn nativeNewDocumentTransaction<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+ doc_id: JString<'local>,
+) -> jlong {
+ try_throwable_submerge(&mut env, |env| {
+ let handle = DataStoreHandle::try_from(handle_id)?;
+ let doc_id: String = env.get_string(&doc_id)?.into();
+ let mut data_store = handle.get_mut()?;
+ let doc_handle = match data_store.open_or_create_document(doc_id) {
+ Ok(doc_handle) => doc_handle,
+ Err(StorageError::StorageException(e)) => Err(JThrowable::from(env.new_local_ref(e)?))?,
+ Err(e) => Err(runtime_exception(e.to_string()))?,
+ };
+ Ok(DocumentTransactionHandle::allocate(|| DocumentTransaction::new(doc_handle))?.jlong())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DataStore")]
+extern "system" fn nativeGetFullUpdateMessage<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+ doc_id: JString<'local>,
+) -> JByteArray<'local> {
+ try_throwable_submerge(&mut env, |env| {
+ let handle = DataStoreHandle::try_from(handle_id)?;
+ let doc_id: String = env.get_string(&doc_id)?.into();
+ let data_store = handle.get()?;
+ let snapshot = data_store.snapshot(doc_id);
+ if let Some(document) = snapshot {
+ let serialized_doc = document
+ .into_full_delta()
+ .to_proto_bytes()
+ .map_err(runtime_exception)?;
+ Ok(env.byte_array_from_slice(&serialized_doc)?)
+ } else {
+ Ok(JByteArray::default())
+ }
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DataStore")]
+extern "system" fn nativeCommitNetworkUpdate<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+ doc_id: JString<'local>,
+ update_message: JByteArray<'local>,
+) {
+ try_throwable_submerge(&mut env, |env| {
+ let handle = DataStoreHandle::try_from(handle_id)?;
+ let doc_id: String = env.get_string(&doc_id)?.into();
+ let mut data_store = handle.get_mut()?;
+ let mut doc_handle = match data_store.open_or_create_document(doc_id) {
+ Ok(doc_handle) => doc_handle,
+ Err(StorageError::StorageException(throwable)) => {
+ Err(transaction_exception_with_cause(
+ env,
+ "Transaction failed",
+ <&JThrowable>::from(throwable.as_obj()),
+ )?)?
+ }
+ Err(err) => Err(transaction_exception(err.to_string()))?,
+ };
+
+ let update_message = env.convert_byte_array(update_message)?;
+ let delta = DeltaDocument::<JavaDataStoreConfig>::from_proto_bytes(&update_message)
+ .map_err(runtime_exception)?;
+ match doc_handle.commit_network_update(&delta) {
+ Ok(_) => {}
+ Err(CommitUpdateError::StorageError(StorageError::StorageException(throwable))) => {
+ Err(transaction_exception_with_cause(
+ env,
+ "Transaction failed",
+ <&JThrowable>::from(throwable.as_obj()),
+ )?)?
+ }
+ Err(CommitUpdateError::OlderBaseNeeded {
+ required_base_version,
+ }) => {
+ let version_handle = VersionVectorHandle::allocate(|| required_base_version)?;
+ Err(older_base_needed_exception(env, version_handle)?)?
+ }
+ Err(err) => Err(transaction_exception(err.to_string()))?,
+ }
+ Ok(())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DataStore")]
+extern "system" fn nativeCalculateDelta<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+ doc_id: JString<'local>,
+ base_version_handle_id: jlong,
+) -> JByteArray<'local> {
+ try_throwable_submerge(&mut env, |env| {
+ let handle = DataStoreHandle::try_from(handle_id)?;
+ let doc_id: String = env.get_string(&doc_id)?.into();
+ let data_store = handle.get()?;
+ let snapshot = data_store.snapshot(doc_id);
+ if let Some(document) = snapshot {
+ let version = VersionVectorHandle::try_from(base_version_handle_id)?
+ .get()?
+ .clone();
+ let delta = document.calculate_delta(version.clone());
+ let serialized_delta = delta.to_proto_bytes().map_err(runtime_exception)?;
+ Ok(env.byte_array_from_slice(&serialized_delta)?)
+ } else {
+ Ok(JByteArray::default())
+ }
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DataStore")]
+extern "system" fn nativeGetDocumentVersion<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+ doc_id: JString<'local>,
+) -> jlong {
+ try_throwable_submerge(&mut env, |env| {
+ let handle = DataStoreHandle::try_from(handle_id)?;
+ let doc_id: String = env.get_string(&doc_id)?.into();
+ let data_store = handle.get()?;
+ let snapshot = data_store.snapshot(doc_id);
+ let version = snapshot
+ .map(|document| document.version)
+ .unwrap_or_default();
+ Ok(VersionVectorHandle::allocate(|| version)?.jlong())
+ })
+}
diff --git a/betosync/submerge/submerge_java/src/class/document_transaction.rs b/betosync/submerge/submerge_java/src/class/document_transaction.rs
new file mode 100644
index 0000000..1f2f680
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/document_transaction.rs
@@ -0,0 +1,253 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::{ops::Deref, sync::Arc};
+
+use crdt::lww_crdt_container::{LwwCrdtContainerRead, LwwCrdtContainerWrite};
+use handle_map::HandleLike;
+use jni::{
+ objects::{JByteArray, JClass, JString, JThrowable},
+ sys::jlong,
+ JNIEnv,
+};
+use pourover::{exception::runtime_exception, jni_method};
+use submerge::{
+ CommitUpdateError, DeltaDocument, DocumentHandle, DocumentTransaction, TransactionError,
+};
+
+use crate::{
+ class::{
+ exceptions::{
+ older_base_needed_exception, ownership_exception, transaction_exception,
+ transaction_exception_with_cause,
+ },
+ storage_interface::StorageError,
+ submerge_data_type::{CrdtType, JavaSubmergeDataType, SubmergeDataTypeHandle, TreeRoot},
+ version_vector::VersionVectorHandle,
+ },
+ utils::declare_handle,
+};
+
+use super::{
+ data_store::{
+ JavaDataStoreConfig, SubmergeJavaMap, SubmergeJavaRegister, SubmergeJavaSet,
+ SubmergeJavaVectorData,
+ },
+ exceptions::try_throwable_submerge,
+};
+
+pub(crate) type JavaDocumentTransaction =
+ DocumentTransaction<DocumentHandle<JavaDataStoreConfig>, JavaDataStoreConfig>;
+
+declare_handle! {
+ pub struct DocumentTransactionHandle for JavaDocumentTransaction;
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeGetRoot<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) -> jlong {
+ try_throwable_submerge(&mut env, |_env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)?;
+ let mut transaction = handle.get_mut()?;
+ let (_ctx, root) = transaction.get_context_and_root_mut();
+ let crdt_type = root.get_value().map(CrdtType::from);
+ Ok(SubmergeDataTypeHandle::allocate(|| JavaSubmergeDataType {
+ path: vec![],
+ crdt_type,
+ root_handle: TreeRoot::Document(handle),
+ })?
+ .jlong())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeSetRoot<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+ root_handle_id: jlong,
+) -> jlong {
+ try_throwable_submerge(&mut env, |_env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)?;
+ let mut transaction = handle.get_mut()?;
+ let (ctx, mut root) = transaction.get_context_and_root_mut();
+ let typed_crdt = if let Ok(root_handle) = SubmergeDataTypeHandle::try_from(root_handle_id) {
+ let owned_data_type = root_handle.deallocate()?;
+ let TreeRoot::Detached { root, .. } = owned_data_type.root_handle else {
+ Err(ownership_exception(
+ "Cannot set root which is already attached",
+ ))?
+ };
+ Arc::into_inner(root)
+ .ok_or_else(|| ownership_exception("Outstanding refs, cannot take ownership"))?
+ .into_inner()
+ } else {
+ None
+ };
+ let crdt_type = typed_crdt.as_ref().map(CrdtType::from);
+ match typed_crdt {
+ Some(v) => root.set_value(ctx, v)?,
+ None => root.remove_value(ctx)?,
+ }
+ Ok(SubmergeDataTypeHandle::allocate(|| JavaSubmergeDataType {
+ path: vec![],
+ crdt_type,
+ root_handle: TreeRoot::Document(handle),
+ })?
+ .jlong())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeClose<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) {
+ try_throwable_submerge(&mut env, |_env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)?;
+ let _ = handle.deallocate()?;
+ Ok(())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeCommit<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) {
+ try_throwable_submerge(&mut env, |env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)
+ .map_err(|_| runtime_exception("Cannot commit the same transaction twice"))?;
+ let transaction = handle.deallocate()?;
+ match transaction.commit() {
+ Ok(_) => {}
+ Err(TransactionError::StorageError(StorageError::StorageException(throwable))) => {
+ Err(transaction_exception_with_cause(
+ env,
+ "Transaction failed",
+ <&JThrowable>::from(throwable.as_obj()),
+ )?)?
+ }
+ Err(err) => Err(transaction_exception(err.to_string()))?,
+ }
+ Ok(())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeMergeNetworkUpdate<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+ update_message: JByteArray<'local>,
+) {
+ try_throwable_submerge(&mut env, |env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)?;
+ let mut transaction = handle.get_mut()?;
+ let update_message = env.convert_byte_array(update_message)?;
+ let delta = DeltaDocument::<JavaDataStoreConfig>::from_proto_bytes(&update_message)
+ .map_err(runtime_exception)?;
+ match transaction.merge_network_delta(&delta) {
+ Ok(_) => Ok(()),
+ Err(CommitUpdateError::OlderBaseNeeded {
+ required_base_version,
+ }) => {
+ let version_handle = VersionVectorHandle::allocate(|| required_base_version)?;
+ Err(older_base_needed_exception(env, version_handle)?)?
+ }
+ Err(err) => Err(runtime_exception(err.to_string()))?,
+ }
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeToString<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) -> JString<'local> {
+ try_throwable_submerge(&mut env, |env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)?;
+ let transaction_result = handle.get();
+ Ok(match transaction_result {
+ Ok(transaction) => env.new_string(format!("{:?}", transaction.deref()))?,
+ Err(_) => env.new_string("Handle not present")?,
+ })
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeNewMap<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) -> jlong {
+ try_throwable_submerge(&mut env, |_env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)?;
+ Ok(SubmergeDataTypeHandle::allocate(|| {
+ JavaSubmergeDataType::new_detached(SubmergeJavaMap::default().into(), handle)
+ })?
+ .jlong())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeNewRegister<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) -> jlong {
+ try_throwable_submerge(&mut env, |_env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)?;
+ Ok(SubmergeDataTypeHandle::allocate(|| {
+ JavaSubmergeDataType::new_detached(SubmergeJavaRegister::default().into(), handle)
+ })?
+ .jlong())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeNewSet<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) -> jlong {
+ try_throwable_submerge(&mut env, |_env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)?;
+ Ok(SubmergeDataTypeHandle::allocate(|| {
+ JavaSubmergeDataType::new_detached(SubmergeJavaSet::default().into(), handle)
+ })?
+ .jlong())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "DocumentTransaction")]
+extern "system" fn nativeNewVectorData<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) -> jlong {
+ try_throwable_submerge(&mut env, |_env| {
+ let handle = DocumentTransactionHandle::try_from(handle_id)?;
+ Ok(SubmergeDataTypeHandle::allocate(|| {
+ JavaSubmergeDataType::new_detached(SubmergeJavaVectorData::default().into(), handle)
+ })?
+ .jlong())
+ })
+}
diff --git a/betosync/submerge/submerge_java/src/class/exceptions.rs b/betosync/submerge/submerge_java/src/class/exceptions.rs
new file mode 100644
index 0000000..e8ca75a
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/exceptions.rs
@@ -0,0 +1,175 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use crdt::{set::GenerationExhausted, vector_data::VersionOverflow};
+use distributed_time::TimestampOverflow;
+use handle_map::{HandleMapFullError, HandleNotPresentError};
+use jni::{objects::JThrowable, JNIEnv};
+use pourover::{
+ desc::ClassDesc,
+ exception::{runtime_exception, ThrowableJniError, ThrowableJniResultExt},
+};
+
+use super::version_vector::{VersionVectorHandle, VersionVectorJni};
+
+pub fn ownership_exception(msg: impl Into<String>) -> jni::errors::Exception {
+ jni::errors::Exception {
+ class: "com/google/android/submerge/OwnershipException".into(),
+ msg: msg.into(),
+ }
+}
+
+pub fn generation_exhausted_exception(msg: impl Into<String>) -> jni::errors::Exception {
+ jni::errors::Exception {
+ class: "com/google/android/submerge/GenerationExhaustedException".into(),
+ msg: msg.into(),
+ }
+}
+
+pub fn version_overflow_exception(msg: impl Into<String>) -> jni::errors::Exception {
+ jni::errors::Exception {
+ class: "com/google/android/submerge/VersionOverflowException".into(),
+ msg: msg.into(),
+ }
+}
+
+pub fn timestamp_overflow_exception(msg: impl Into<String>) -> jni::errors::Exception {
+ jni::errors::Exception {
+ class: "com/google/android/submerge/TimestampOverflowException".into(),
+ msg: msg.into(),
+ }
+}
+
+pub fn transaction_exception(msg: impl Into<String>) -> jni::errors::Exception {
+ jni::errors::Exception {
+ class: "com/google/android/submerge/TransactionException".into(),
+ msg: msg.into(),
+ }
+}
+
+static TRANSACTION_EXCEPTION_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/submerge/TransactionException");
+
+pub fn transaction_exception_with_cause<'local>(
+ env: &mut JNIEnv<'local>,
+ msg: impl Into<String>,
+ cause: &JThrowable<'local>,
+) -> Result<JThrowable<'local>, jni::errors::Error> {
+ pourover::call_constructor!(
+ env,
+ TRANSACTION_EXCEPTION_CLASS,
+ "(Ljava/lang/String;Ljava/lang/Throwable;)V",
+ env.new_string(msg.into())?.as_ref(),
+ cause
+ )
+ .map(|obj| obj.into())
+}
+
+static OLDER_BASE_NEEDED_EXCEPTION_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/submerge/OlderBaseNeededException");
+
+pub fn older_base_needed_exception<'local>(
+ env: &mut JNIEnv<'local>,
+ version_handle: VersionVectorHandle,
+) -> Result<JThrowable<'local>, jni::errors::Error> {
+ let version_java = VersionVectorJni::construct(env, version_handle)?;
+ pourover::call_constructor!(
+ env,
+ OLDER_BASE_NEEDED_EXCEPTION_CLASS,
+ "(Lcom/google/android/submerge/VersionVector;)V",
+ &version_java.0
+ )
+ .map(|obj| obj.into())
+}
+
+pub struct SubmergeJavaError<'local>(ThrowableJniError<'local>);
+
+impl From<HandleNotPresentError> for SubmergeJavaError<'_> {
+ fn from(_: HandleNotPresentError) -> Self {
+ Self(ThrowableJniError::from(runtime_exception(
+ "Handle not present",
+ )))
+ }
+}
+
+impl From<HandleMapFullError> for SubmergeJavaError<'_> {
+ fn from(_: HandleMapFullError) -> Self {
+ Self(ThrowableJniError::from(runtime_exception(
+ "Handle map full",
+ )))
+ }
+}
+
+impl From<jni::errors::Exception> for SubmergeJavaError<'_> {
+ fn from(exception: jni::errors::Exception) -> Self {
+ Self(ThrowableJniError::from(exception))
+ }
+}
+
+impl From<jni::errors::Error> for SubmergeJavaError<'_> {
+ fn from(error: jni::errors::Error) -> Self {
+ Self(ThrowableJniError::from(error))
+ }
+}
+
+impl<'local> From<JThrowable<'local>> for SubmergeJavaError<'local> {
+ fn from(throwable: JThrowable<'local>) -> Self {
+ Self(ThrowableJniError::from(throwable))
+ }
+}
+
+impl From<TimestampOverflow> for SubmergeJavaError<'_> {
+ fn from(_: TimestampOverflow) -> Self {
+ Self(ThrowableJniError::from(timestamp_overflow_exception(
+ "Timestamp overflow",
+ )))
+ }
+}
+
+impl From<GenerationExhausted> for SubmergeJavaError<'_> {
+ fn from(_: GenerationExhausted) -> Self {
+ Self(ThrowableJniError::from(generation_exhausted_exception(
+ "Generation number exhausted",
+ )))
+ }
+}
+
+impl From<VersionOverflow> for SubmergeJavaError<'_> {
+ fn from(_: VersionOverflow) -> Self {
+ Self(ThrowableJniError::from(version_overflow_exception(
+ "Version overflow",
+ )))
+ }
+}
+
+impl<'local> From<ThrowableJniError<'local>> for SubmergeJavaError<'local> {
+ fn from(value: ThrowableJniError<'local>) -> Self {
+ Self(value)
+ }
+}
+
+impl<'local> From<SubmergeJavaError<'local>> for ThrowableJniError<'local> {
+ fn from(value: SubmergeJavaError<'local>) -> Self {
+ value.0
+ }
+}
+
+pub fn try_throwable_submerge<'local, R: Default>(
+ env: &mut JNIEnv<'local>,
+ func: impl FnOnce(&mut JNIEnv<'local>) -> Result<R, SubmergeJavaError<'local>>,
+) -> R {
+ func(env)
+ .map_err(ThrowableJniError::from)
+ .unwrap_or_throw(env)
+}
diff --git a/betosync/submerge/submerge_java/src/class/network_interface.rs b/betosync/submerge/submerge_java/src/class/network_interface.rs
new file mode 100644
index 0000000..85d6c15
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/network_interface.rs
@@ -0,0 +1,95 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::sync::Arc;
+
+use jni::{
+ objects::{GlobalRef, JObject},
+ JNIEnv, JavaVM,
+};
+use log::error;
+use pourover::desc::ClassDesc;
+use submerge::{DeltaDocument, NetworkInterface};
+
+use super::data_store::JavaDataStoreConfig;
+
+#[derive(Debug, Clone)]
+pub struct JavaNetwork {
+ jobject: NetworkInterfaceJni<GlobalRef>,
+ vm: Arc<JavaVM>,
+}
+
+impl JavaNetwork {
+ pub fn new<'local>(
+ env: &mut JNIEnv<'local>,
+ network: NetworkInterfaceJni<JObject<'local>>,
+ ) -> Result<Self, jni::errors::Error> {
+ Ok(Self {
+ jobject: network.to_global_ref(env)?,
+ vm: Arc::new(env.get_java_vm()?),
+ })
+ }
+}
+
+impl NetworkInterface<JavaDataStoreConfig> for JavaNetwork {
+ fn on_new_update(&mut self, doc_id: &str, document: DeltaDocument<JavaDataStoreConfig>) {
+ let block = || -> Result<(), anyhow::Error> {
+ let mut env = self.vm.get_env()?;
+ self.jobject
+ .on_new_update(&mut env, doc_id, &document.to_proto_bytes()?)?;
+ Ok(())
+ };
+ match block() {
+ Ok(_) => {}
+ Err(err) => {
+ error!("Error sending network update: {err}");
+ }
+ }
+ }
+}
+
+#[repr(transparent)]
+#[derive(Debug, Clone)]
+pub struct NetworkInterfaceJni<Obj>(Obj);
+
+static NETWORK_INTERFACE_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/submerge/NetworkInterface");
+
+impl<'local, O: AsRef<JObject<'local>>> NetworkInterfaceJni<O> {
+ pub fn to_global_ref(
+ &self,
+ env: &mut JNIEnv<'_>,
+ ) -> Result<NetworkInterfaceJni<GlobalRef>, jni::errors::Error> {
+ Ok(NetworkInterfaceJni(env.new_global_ref(&self.0)?))
+ }
+
+ pub fn on_new_update(
+ &self,
+ env: &mut JNIEnv<'_>,
+ doc_id: &str,
+ serialized_doc: &[u8],
+ ) -> Result<(), jni::errors::Error> {
+ let serialized_doc = env.auto_local(env.byte_array_from_slice(serialized_doc)?);
+ pourover::call_method!(
+ env,
+ NETWORK_INTERFACE_CLASS,
+ "onNewUpdate",
+ "(Ljava/lang/String;[B)V",
+ self.0.as_ref(),
+ &env.new_string(doc_id)?,
+ &serialized_doc,
+ )?;
+ Ok(())
+ }
+}
diff --git a/betosync/submerge/submerge_java/src/class/storage_interface.rs b/betosync/submerge/submerge_java/src/class/storage_interface.rs
new file mode 100644
index 0000000..38a20a4
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/storage_interface.rs
@@ -0,0 +1,160 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::sync::Arc;
+
+use jni::{
+ objects::{GlobalRef, JObject},
+ JNIEnv, JavaVM,
+};
+use pourover::{desc::ClassDesc, exception::JniResultExt};
+use submerge::{proto::ProtoSerdeError, Document, StorageInterface};
+use thiserror::Error;
+
+use super::data_store::JavaDataStoreConfig;
+
+/// A wrapper struct around a Java-implemented storage interface that implements
+/// [`StorageInterface`].
+#[derive(Debug, Clone)]
+pub struct JavaStorage {
+ jobject: StorageInterfaceJni<GlobalRef>,
+ vm: Arc<JavaVM>,
+}
+
+impl JavaStorage {
+ pub fn new<'local>(
+ env: &mut JNIEnv<'local>,
+ storage: StorageInterfaceJni<JObject<'local>>,
+ ) -> Result<Self, jni::errors::Error> {
+ Ok(Self {
+ jobject: storage.to_global_ref(env)?,
+ vm: Arc::new(env.get_java_vm()?),
+ })
+ }
+}
+
+impl StorageInterface<JavaDataStoreConfig> for JavaStorage {
+ type Error = StorageError;
+
+ fn on_new_update(
+ &mut self,
+ doc_id: &str,
+ document: &Document<JavaDataStoreConfig>,
+ ) -> Result<(), StorageError> {
+ let mut env = self.vm.get_env()?;
+ self.jobject
+ .on_new_update(&mut env, doc_id, &document.to_proto_bytes()?)
+ .extract_exception(
+ &mut env,
+ "com/google/android/submerge/StorageInterface$StorageException",
+ )?
+ .map_err(|throwable| match env.new_global_ref(throwable) {
+ Ok(global_throwable) => StorageError::StorageException(global_throwable),
+ Err(err) => StorageError::JniFailure(err),
+ })?;
+ Ok(())
+ }
+
+ fn read_from_storage(
+ &self,
+ doc_id: &str,
+ ) -> Result<Option<Document<JavaDataStoreConfig>>, StorageError> {
+ let mut env = self.vm.get_env()?;
+ Ok(
+ if let Some(bytes) = self.jobject.read_from_storage(&mut env, doc_id)? {
+ Some(Document::from_proto_bytes(&bytes)?)
+ } else {
+ None
+ },
+ )
+ }
+}
+
+/// A transparent wrapper over a Java implemented `StorageInterface`.
+#[repr(transparent)]
+#[derive(Debug, Clone)]
+pub struct StorageInterfaceJni<Obj>(Obj);
+
+static STORAGE_INTERFACE_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/submerge/StorageInterface");
+
+#[derive(Debug, Error)]
+pub enum StorageError {
+ #[error("Failed to serialize / deserialize document")]
+ SerdeFailed(#[from] ProtoSerdeError),
+ #[error("Failed to store to storage")]
+ StorageException(GlobalRef),
+ #[error(transparent)]
+ JniFailure(#[from] jni::errors::Error),
+}
+
+impl From<protobuf::Error> for StorageError {
+ fn from(value: protobuf::Error) -> Self {
+ StorageError::SerdeFailed(ProtoSerdeError::ProtobufError(value))
+ }
+}
+
+impl<'local, O: AsRef<JObject<'local>>> StorageInterfaceJni<O> {
+ pub fn to_global_ref(
+ &self,
+ env: &mut JNIEnv<'_>,
+ ) -> Result<StorageInterfaceJni<GlobalRef>, jni::errors::Error> {
+ Ok(StorageInterfaceJni(env.new_global_ref(&self.0)?))
+ }
+
+ pub fn on_new_update(
+ &self,
+ env: &mut JNIEnv<'_>,
+ doc_id: &str,
+ serialized_doc: &[u8],
+ ) -> Result<(), jni::errors::Error> {
+ let serialized_doc = env.auto_local(env.byte_array_from_slice(serialized_doc)?);
+ let doc_id = env.new_string(doc_id)?;
+ pourover::call_method!(
+ env,
+ STORAGE_INTERFACE_CLASS,
+ "onNewUpdate",
+ "(Ljava/lang/String;[B)V",
+ self.0.as_ref(),
+ &doc_id,
+ &serialized_doc
+ )
+ }
+
+ pub fn read_from_storage(
+ &self,
+ env: &mut JNIEnv<'_>,
+ doc_id: &str,
+ ) -> Result<Option<Vec<u8>>, jni::errors::Error> {
+ let doc_id = env.new_string(doc_id)?;
+ match pourover::call_method!(
+ env,
+ STORAGE_INTERFACE_CLASS,
+ "readFromStorage",
+ "(Ljava/lang/String;)[B",
+ self.0.as_ref(),
+ &doc_id,
+ ) {
+ Ok(bytes) => {
+ if bytes.is_null() {
+ Ok(None)
+ } else {
+ let bytes = env.convert_byte_array(bytes)?;
+ Ok(Some(bytes))
+ }
+ }
+ Err(e) => Err(e),
+ }
+ }
+}
diff --git a/betosync/submerge/submerge_java/src/class/submerge_data_type.rs b/betosync/submerge/submerge_java/src/class/submerge_data_type.rs
new file mode 100644
index 0000000..5ae88bd
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/submerge_data_type.rs
@@ -0,0 +1,338 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::{
+ hash::{BuildHasher, Hash},
+ ops::{Deref, DerefMut},
+ sync::Arc,
+};
+
+use crdt::{
+ lww_crdt_container::CrdtUnion,
+ typed_crdt::{TypedCrdt, TypedCrdtMut, TypedCrdtRef},
+};
+use distributed_time::TimestampOverflow;
+use handle_map::{HandleLike, HandleNotPresentError};
+use jni::{
+ objects::{JClass, JString},
+ sys::{jint, jlong},
+ JNIEnv,
+};
+use parking_lot::{ArcRwLockWriteGuard, RwLock};
+use pourover::{exception::runtime_exception, jni_method};
+use submerge::UpdateContext;
+
+use crate::{
+ class::{
+ data_store::{JavaDataStoreConfig, SubmergeJavaTypedCrdt, SubmergeJavaTypedCrdtMut},
+ document_transaction::{DocumentTransactionHandle, JavaDocumentTransaction},
+ exceptions::{timestamp_overflow_exception, try_throwable_submerge, SubmergeJavaError},
+ },
+ utils::declare_handle,
+};
+
+#[derive(Debug, Clone)]
+pub enum CrdtType {
+ Map,
+ Register,
+ Set,
+ VectorData,
+}
+
+impl<V, N, S> From<&TypedCrdt<V, N, S>> for CrdtType
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default,
+{
+ fn from(value: &TypedCrdt<V, N, S>) -> Self {
+ match value {
+ TypedCrdt::Map(_) => CrdtType::Map,
+ TypedCrdt::Register(_) => CrdtType::Register,
+ TypedCrdt::Set(_) => CrdtType::Set,
+ TypedCrdt::VectorData(_) => CrdtType::VectorData,
+ }
+ }
+}
+
+impl<'d, V, N, S> From<TypedCrdtRef<'d, V, N, S>> for CrdtType
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default,
+{
+ fn from(value: TypedCrdtRef<V, N, S>) -> Self {
+ match value {
+ TypedCrdtRef::Map(_) => CrdtType::Map,
+ TypedCrdtRef::Register(_) => CrdtType::Register,
+ TypedCrdtRef::Set(_) => CrdtType::Set,
+ TypedCrdtRef::VectorData(_) => CrdtType::VectorData,
+ }
+ }
+}
+
+impl<'d, V, N, S> From<TypedCrdtMut<'d, V, N, S>> for CrdtType
+where
+ V: Eq + Hash + Clone,
+ N: Ord + Clone,
+ S: BuildHasher + Default,
+{
+ fn from(value: TypedCrdtMut<V, N, S>) -> Self {
+ match value {
+ TypedCrdtMut::Map(_) => CrdtType::Map,
+ TypedCrdtMut::Register(_) => CrdtType::Register,
+ TypedCrdtMut::Set(_) => CrdtType::Set,
+ TypedCrdtMut::VectorData(_) => CrdtType::VectorData,
+ }
+ }
+}
+
+/// The root of a CRDT tree.
+#[derive(Debug, Clone)]
+pub enum TreeRoot {
+ Document(DocumentTransactionHandle),
+ Detached {
+ root: Arc<RwLock<Option<SubmergeJavaTypedCrdt>>>,
+ doc_handle: DocumentTransactionHandle,
+ },
+}
+
+/// A reference to a node in a CRDT tree
+///
+/// This contains enough information to look up the corresponding [`TypedCrdtMut`] in the tree, and
+/// is the type exposed to the Java class `SubmergeDataType` (via a handle abstraction).
+#[derive(Debug, Clone)]
+pub struct JavaSubmergeDataType {
+ /// The path used to look up the node from the `root`.
+ ///
+ /// An empty vec corresponds to the node itself, and each element in the vec corresponds to one
+ /// layer down the tree.
+ pub path: Vec<String>,
+ /// The type of the referenced CRDT node.
+ ///
+ /// This is used to identify the referenced CRDT type to Java so it can create the corresponding
+ /// Java wrapper.
+ pub crdt_type: Option<CrdtType>,
+ /// The root to look up the node from.
+ ///
+ /// This can either be a document ([`DocumentTransactionHandle`]) or a detached [`TypedCrdt`].
+ pub root_handle: TreeRoot,
+}
+
+impl JavaSubmergeDataType {
+ pub fn new_detached(
+ typed_crdt: SubmergeJavaTypedCrdt,
+ doc_handle: DocumentTransactionHandle,
+ ) -> Self {
+ debug_assert!(doc_handle.get().is_ok());
+ JavaSubmergeDataType {
+ path: vec![],
+ crdt_type: Some((&typed_crdt).into()),
+ root_handle: TreeRoot::Detached {
+ root: Arc::new(RwLock::new(Some(typed_crdt))),
+ doc_handle,
+ },
+ }
+ }
+}
+
+declare_handle! {
+ pub struct SubmergeDataTypeHandle for JavaSubmergeDataType;
+}
+
+/// A guard object returned while reading a [`JavaSubmergeDataType`].
+///
+/// # Params
+/// * `R` - the type of read guard returned from [`handle_map::HandleMap::get`].
+/// * `W` - the type of write guard returned from [`handle_map::HandleMap::get_mut`].
+pub enum SubmergeDataTypeWriteGuard<'a, R, W>
+where
+ R: Deref<Target = JavaDocumentTransaction>,
+ W: DerefMut<Target = JavaDocumentTransaction>,
+{
+ Document {
+ submerge_data_type: &'a JavaSubmergeDataType,
+ doc: W,
+ },
+ Detached {
+ submerge_data_type: &'a JavaSubmergeDataType,
+ doc: R,
+ root: ArcRwLockWriteGuard<parking_lot::RawRwLock, Option<SubmergeJavaTypedCrdt>>,
+ },
+}
+
+impl<'a, R, W> SubmergeDataTypeWriteGuard<'a, R, W>
+where
+ R: Deref<Target = JavaDocumentTransaction>,
+ W: DerefMut<Target = JavaDocumentTransaction>,
+{
+ pub fn get_context_and_node_mut(
+ &mut self,
+ ) -> Result<
+ (
+ Option<SubmergeJavaTypedCrdtMut>,
+ &UpdateContext<JavaDataStoreConfig>,
+ ),
+ AccessError,
+ > {
+ match self {
+ SubmergeDataTypeWriteGuard::Document {
+ submerge_data_type,
+ doc,
+ } => {
+ let (ctx, root) = doc.get_context_and_root_mut();
+ let node = submerge_data_type.access_mut(root.into_value_mut(ctx)?, ctx)?;
+ Ok((node, ctx))
+ }
+ SubmergeDataTypeWriteGuard::Detached {
+ submerge_data_type,
+ doc,
+ root: owned,
+ } => {
+ let ctx = doc.update_context();
+ let node = submerge_data_type.access_mut(
+ owned.as_mut().and_then(|v| TypedCrdt::create_mut(None, v)),
+ ctx,
+ )?;
+ Ok((node, ctx))
+ }
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum AccessError {
+ TimestampOverflow,
+ TypeMismatch,
+}
+
+impl From<TimestampOverflow> for AccessError {
+ fn from(_: TimestampOverflow) -> Self {
+ Self::TimestampOverflow
+ }
+}
+
+impl From<AccessError> for SubmergeJavaError<'_> {
+ fn from(value: AccessError) -> Self {
+ match value {
+ AccessError::TimestampOverflow => {
+ timestamp_overflow_exception("Timestamp overflow").into()
+ }
+ AccessError::TypeMismatch => runtime_exception("Type mismatch").into(),
+ }
+ }
+}
+
+impl JavaSubmergeDataType {
+ pub fn lock_write(
+ &self,
+ ) -> Result<
+ SubmergeDataTypeWriteGuard<
+ impl Deref<Target = JavaDocumentTransaction> + '_,
+ impl DerefMut<Target = JavaDocumentTransaction> + '_,
+ >,
+ HandleNotPresentError,
+ > {
+ match &self.root_handle {
+ TreeRoot::Document(doc_handle) => {
+ let doc = doc_handle.get_mut()?;
+ Ok(SubmergeDataTypeWriteGuard::Document {
+ submerge_data_type: self,
+ doc,
+ })
+ }
+ TreeRoot::Detached { root, doc_handle } => {
+ let root_guard = root.write_arc();
+ let doc = doc_handle.get()?;
+ Ok(SubmergeDataTypeWriteGuard::Detached {
+ submerge_data_type: self,
+ doc,
+ root: root_guard,
+ })
+ }
+ }
+ }
+
+ fn access_mut<'a>(
+ &self,
+ root: Option<SubmergeJavaTypedCrdtMut<'a>>,
+ ctx: &UpdateContext<JavaDataStoreConfig>,
+ ) -> Result<Option<SubmergeJavaTypedCrdtMut<'a>>, AccessError> {
+ let mut node = root;
+ for key in &self.path {
+ let Some(TypedCrdtMut::Map(map)) = node else {
+ return Err(AccessError::TypeMismatch);
+ };
+ node = map.into_container_mut(key.clone()).into_value_mut(ctx)?;
+ }
+ Ok(node)
+ }
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeDataType")]
+extern "system" fn nativeGetCrdtType<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+) -> jint {
+ try_throwable_submerge(&mut env, |_env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data_type = data_handle.get()?;
+ Ok(match data_type.crdt_type {
+ Some(CrdtType::Map) => 1,
+ Some(CrdtType::Set) => 2,
+ Some(CrdtType::Register) => 3,
+ Some(CrdtType::VectorData) => 4,
+ None => 0,
+ })
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeDataType")]
+extern "system" fn nativeToString<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+) -> JString<'local> {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ Ok(env.new_string(format!("{:?}: {:?}", &*data, typed_crdt))?)
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeDataType")]
+extern "system" fn nativeClose<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+) {
+ try_throwable_submerge(&mut env, |_env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let _ = data_handle.deallocate()?;
+ Ok(())
+ })
+}
+
+pub(crate) fn type_string(typed_crdt: Option<SubmergeJavaTypedCrdtMut>) -> &'static str {
+ match typed_crdt {
+ Some(TypedCrdtMut::Map(_)) => "Map",
+ Some(TypedCrdtMut::Set(_)) => "Set",
+ Some(TypedCrdtMut::Register(_)) => "Register",
+ Some(TypedCrdtMut::VectorData(_)) => "VectorData",
+ None => "None",
+ }
+}
diff --git a/betosync/submerge/submerge_java/src/class/submerge_map.rs b/betosync/submerge/submerge_java/src/class/submerge_map.rs
new file mode 100644
index 0000000..b5023ff
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/submerge_map.rs
@@ -0,0 +1,175 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::sync::Arc;
+
+use crdt::{
+ lww_map::{LwwMapRead, LwwMapWrite},
+ typed_crdt::TypedCrdtMut,
+};
+use handle_map::HandleLike;
+use jni::{
+ objects::{JClass, JObjectArray, JString},
+ sys::jlong,
+ JNIEnv,
+};
+use pourover::{exception::runtime_exception, jni_method};
+
+use crate::{
+ class::submerge_data_type::{CrdtType, JavaSubmergeDataType, SubmergeDataTypeHandle, TreeRoot},
+ utils::collect_to_jarray,
+};
+
+use super::{
+ exceptions::{ownership_exception, try_throwable_submerge},
+ submerge_data_type::type_string,
+};
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeMap")]
+extern "system" fn nativeGetChild<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ key: JString<'local>,
+) -> jlong {
+ try_throwable_submerge(&mut env, |env| {
+ let new_data_type = {
+ // Create a scope for the SubmergeDataTypeHandle shard locks. They must be released
+ // before trying to allocate a new SubmergeDataTypeHandle for the return value.
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ let key = env.get_string(&key).map(String::from)?;
+ let Some(TypedCrdtMut::Map(map)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Map but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let crdt_type = map.get_value(&key).map(CrdtType::from);
+ let path = [&data.path, [key].as_slice()].concat();
+ let root_handle = data.root_handle.clone();
+ JavaSubmergeDataType {
+ path,
+ crdt_type,
+ root_handle,
+ }
+ };
+ Ok(SubmergeDataTypeHandle::allocate(|| new_data_type)?.jlong())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeMap")]
+extern "system" fn nativeSetChild<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ key: JString<'local>,
+ child_handle_id: jlong,
+) -> jlong {
+ try_throwable_submerge(&mut env, |env| {
+ let child_data_type = SubmergeDataTypeHandle::try_from(child_handle_id)?.deallocate()?;
+ let new_data = {
+ // Create a scope for the SubmergeDataTypeHandle shard locks. They must be released
+ // before trying to allocate a new SubmergeDataTypeHandle for the return value.
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ if !matches!(&data.root_handle, TreeRoot::Document(_)) {
+ return Err(ownership_exception(
+ "Map must be added to a document before calling Map.setChild",
+ ))?;
+ }
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, ctx) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Map(mut map)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Map but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let key = env.get_string(&key).map(String::from)?;
+ let TreeRoot::Detached { root: child, .. } = child_data_type.root_handle else {
+ Err(ownership_exception("Cannot set child which is not owned"))?
+ };
+ let child = Arc::into_inner(child)
+ .ok_or_else(|| ownership_exception("Outstanding refs, cannot take ownership"))?
+ .into_inner();
+ if let Some(value) = child {
+ map.set_value(ctx, key.clone(), value)?;
+ } else {
+ map.remove_value(ctx, key.clone())?;
+ }
+ JavaSubmergeDataType {
+ path: [data.path.as_slice(), [key].as_slice()].concat(),
+ crdt_type: child_data_type.crdt_type,
+ root_handle: data.root_handle.clone(),
+ }
+ };
+ Ok(SubmergeDataTypeHandle::allocate(|| new_data)?.jlong())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeMap")]
+extern "system" fn nativeRemoveChild<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ key: JString<'local>,
+) {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, ctx) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Map(mut map)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Map but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let key = env.get_string(&key).map(String::from)?;
+ map.remove_value(ctx, key)?;
+ Ok(())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeMap")]
+extern "system" fn nativeKeySet<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+) -> JObjectArray<'local> {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Map(map)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Map but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ Ok(collect_to_jarray(
+ env,
+ "java/lang/String",
+ map.keys()
+ .filter(|key| map.get_value(key).is_some())
+ .collect::<Vec<_>>()
+ .into_iter(),
+ |env, item| Ok(env.auto_local(env.new_string(item)?)),
+ )?)
+ })
+}
diff --git a/betosync/submerge/submerge_java/src/class/submerge_register.rs b/betosync/submerge/submerge_java/src/class/submerge_register.rs
new file mode 100644
index 0000000..a8d0322
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/submerge_register.rs
@@ -0,0 +1,106 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use crdt::{
+ register::{RegisterRead, RegisterWrite},
+ typed_crdt::TypedCrdtMut,
+};
+use handle_map::HandleLike;
+use jni::{
+ objects::{JByteArray, JClass, JObjectArray},
+ sys::jlong,
+ JNIEnv,
+};
+use pourover::{exception::runtime_exception, jni_method};
+
+use crate::{
+ class::{exceptions::try_throwable_submerge, submerge_data_type::SubmergeDataTypeHandle},
+ utils::collect_to_jarray,
+};
+
+use super::submerge_data_type::type_string;
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeRegister")]
+extern "system" fn nativeSet<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ value: JByteArray<'local>,
+) {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, ctx) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Register(mut register)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Register but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let value = env.convert_byte_array(value)?;
+ register.set(ctx, value)?;
+ Ok(())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeRegister")]
+extern "system" fn nativeGet<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+) -> JByteArray<'local> {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Register(register)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Register but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ Ok(match register.get() {
+ Some(bytes) => env.byte_array_from_slice(bytes)?,
+ None => JByteArray::default(), // Default is null
+ })
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeRegister")]
+extern "system" fn nativeGetAll<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+) -> JObjectArray<'local> {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Register(register)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Register but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ Ok(collect_to_jarray(
+ env,
+ "[B",
+ register.get_all().into_iter(),
+ |env, v| Ok(env.auto_local(env.byte_array_from_slice(v)?)),
+ )?)
+ })
+}
diff --git a/betosync/submerge/submerge_java/src/class/submerge_set.rs b/betosync/submerge/submerge_java/src/class/submerge_set.rs
new file mode 100644
index 0000000..4d13ff3
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/submerge_set.rs
@@ -0,0 +1,155 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use crdt::{
+ set::{SetRead, SetWrite},
+ typed_crdt::TypedCrdtMut,
+};
+use handle_map::HandleLike;
+use jni::{
+ objects::{JByteArray, JClass, JObjectArray},
+ sys::{jboolean, jint, jlong, JNI_FALSE, JNI_TRUE},
+ JNIEnv,
+};
+use pourover::{exception::runtime_exception, jni_method};
+
+use crate::{
+ class::{exceptions::try_throwable_submerge, submerge_data_type::SubmergeDataTypeHandle},
+ utils::collect_to_jarray,
+};
+
+use super::submerge_data_type::type_string;
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeSet")]
+extern "system" fn nativeAdd<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ value: JByteArray<'local>,
+) -> jboolean {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Set(mut set)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Set but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let value = env.convert_byte_array(value)?;
+ let added = set.add(value)?;
+ Ok(if added { JNI_TRUE } else { JNI_FALSE })
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeSet")]
+extern "system" fn nativeRemove<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ value: JByteArray<'local>,
+) -> jboolean {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Set(mut set)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Set but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let value = env.convert_byte_array(value)?;
+ let removed = set.remove(value);
+ Ok(if removed { JNI_TRUE } else { JNI_FALSE })
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeSet")]
+extern "system" fn nativeBumpGenerationNumberForTesting<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ value: JByteArray<'local>,
+ increment: jint,
+) {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Set(mut set)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Set but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let value = env.convert_byte_array(value)?;
+ #[allow(clippy::cast_sign_loss)]
+ set.bump_generation_number_for_testing(value, increment as u32)?;
+ Ok(())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeSet")]
+extern "system" fn nativeEntries<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+) -> JObjectArray<'local> {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Set(set)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Set but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ Ok(collect_to_jarray(
+ env,
+ "[B",
+ set.entries().into_iter(),
+ |env, item| Ok(env.auto_local(env.byte_array_from_slice(item)?)),
+ )?)
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeSet")]
+extern "system" fn nativeContains<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ value: JByteArray<'local>,
+) -> jboolean {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::Set(set)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected Set but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let value = env.convert_byte_array(value)?;
+ Ok(set.contains(&value).into())
+ })
+}
diff --git a/betosync/submerge/submerge_java/src/class/submerge_vector_data.rs b/betosync/submerge/submerge_java/src/class/submerge_vector_data.rs
new file mode 100644
index 0000000..136be9a
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/submerge_vector_data.rs
@@ -0,0 +1,112 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use crdt::{
+ typed_crdt::TypedCrdtMut,
+ vector_data::{VectorDataRead, VectorDataWrite},
+};
+use handle_map::HandleLike;
+use jni::{
+ objects::{JByteArray, JClass, JObject},
+ sys::{jboolean, jint, jlong, JNI_FALSE, JNI_TRUE},
+ JNIEnv,
+};
+use pourover::{exception::runtime_exception, jni_method};
+
+use crate::{
+ class::{exceptions::try_throwable_submerge, submerge_data_type::SubmergeDataTypeHandle},
+ java_map::HashMapJni,
+};
+
+use super::submerge_data_type::type_string;
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeVectorData")]
+extern "system" fn nativeSet<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ value: JByteArray<'local>,
+) -> jboolean {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, ctx) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::VectorData(mut vector_data)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected VectorData but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let value = env.convert_byte_array(value)?;
+ let changed = vector_data.set(ctx.node_id.clone(), Some(value))?;
+ Ok(if changed { JNI_TRUE } else { JNI_FALSE })
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeVectorData")]
+extern "system" fn nativeBumpVersionNumberForTesting<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+ increment: jint,
+) {
+ try_throwable_submerge(&mut env, |_env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, ctx) = guard.get_context_and_node_mut()?;
+ let Some(TypedCrdtMut::VectorData(mut vector_data)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected VectorData but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ #[allow(clippy::cast_sign_loss)]
+ vector_data.bump_version_number_for_testing(ctx.node_id.clone(), increment as u32)?;
+ Ok(())
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "SubmergeVectorData")]
+extern "system" fn nativeEntries<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data_handle_id: jlong,
+) -> HashMapJni<JObject<'local>> {
+ try_throwable_submerge(&mut env, |env| {
+ let data_handle = SubmergeDataTypeHandle::try_from(data_handle_id)?;
+ let data = data_handle.get()?;
+ let mut guard = data.lock_write()?;
+ let (typed_crdt, _) = guard.get_context_and_node_mut()?;
+
+ let Some(TypedCrdtMut::VectorData(vector_data)) = typed_crdt else {
+ Err(runtime_exception(format!(
+ "Expected VectorData but found {}",
+ type_string(typed_crdt)
+ )))?
+ };
+ let entries = vector_data.entries();
+ let java_map = HashMapJni::construct(
+ env,
+ entries.len().try_into().expect("Size should fit in i32"),
+ )?;
+ for (key, value) in entries {
+ let key = env.new_string(key)?;
+ let value = env.byte_array_from_slice(value)?;
+ let _ = java_map.put(env, key, value)?;
+ }
+ Ok(java_map)
+ })
+}
diff --git a/betosync/submerge/submerge_java/src/class/timestamp_provider.rs b/betosync/submerge/submerge_java/src/class/timestamp_provider.rs
new file mode 100644
index 0000000..0aaea06
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/timestamp_provider.rs
@@ -0,0 +1,86 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::sync::Arc;
+
+use distributed_time::WallTimestampProvider;
+use jni::{
+ objects::{GlobalRef, JObject},
+ sys::jlong,
+ JNIEnv, JavaVM,
+};
+use pourover::{desc::ClassDesc, exception::runtime_exception};
+
+/// A wrapper struct around a Java-implemented timestamp provider that implements
+/// [`WallTimestampProvider`].
+#[derive(Debug, Clone)]
+pub struct JavaTimestampProvider {
+ jobject: TimestampProviderJni<GlobalRef>,
+ vm: Arc<JavaVM>,
+}
+
+impl JavaTimestampProvider {
+ pub fn new<'local>(
+ env: &mut JNIEnv<'local>,
+ timestamp_provider: TimestampProviderJni<JObject<'local>>,
+ ) -> Result<Self, jni::errors::Error> {
+ Ok(Self {
+ jobject: timestamp_provider.to_global_ref(env)?,
+ vm: Arc::new(env.get_java_vm()?),
+ })
+ }
+}
+
+impl WallTimestampProvider for JavaTimestampProvider {
+ fn now(&self) -> u64 {
+ let mut env = self
+ .vm
+ .get_env()
+ .expect("Unable to get JVM env in infallible `now()`");
+ match self.jobject.now(&mut env) {
+ Ok(v) => u64::try_from(v).unwrap_or(0),
+ Err(jni::errors::Error::JavaException) => 0,
+ Err(e) => {
+ env.throw(runtime_exception(e))
+ .expect("Failed to throw Java exception");
+ // Return value is ignored by the JVM since an exception was thrown. Anyhow, using
+ // an incorrect timestamp won't affect the data consistency.
+ 0
+ }
+ }
+ }
+}
+
+/// A transparent wrapper over a Java implemented `TimestampProvider`.
+#[repr(transparent)]
+#[derive(Debug, Clone)]
+pub struct TimestampProviderJni<Obj>(Obj);
+
+static TIMESTAMP_PROVIDER_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/submerge/TimestampProvider");
+
+impl<'local, O: AsRef<JObject<'local>>> TimestampProviderJni<O> {
+ pub fn to_global_ref(
+ &self,
+ env: &mut JNIEnv<'_>,
+ ) -> Result<TimestampProviderJni<GlobalRef>, jni::errors::Error> {
+ Ok(TimestampProviderJni(env.new_global_ref(&self.0)?))
+ }
+
+ pub fn now(&self, env: &mut JNIEnv<'_>) -> Result<jlong, jni::errors::Error> {
+ let timestamp =
+ pourover::call_method!(env, TIMESTAMP_PROVIDER_CLASS, "now", "()J", self.0.as_ref(),)?;
+ Ok(timestamp)
+ }
+}
diff --git a/betosync/submerge/submerge_java/src/class/version_vector.rs b/betosync/submerge/submerge_java/src/class/version_vector.rs
new file mode 100644
index 0000000..982609f
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/class/version_vector.rs
@@ -0,0 +1,128 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use std::cmp::Ordering;
+
+use super::exceptions::try_throwable_submerge;
+use distributed_time::vector_clock::VectorClock;
+use handle_map::HandleLike;
+use jni::{
+ objects::{JByteArray, JClass, JObject},
+ sys::{jint, jlong},
+ JNIEnv,
+};
+use pourover::{desc::ClassDesc, exception::runtime_exception, jni_method};
+use protobuf::Message;
+
+use crate::utils::declare_handle;
+
+declare_handle! {
+ pub struct VersionVectorHandle for VectorClock<String>;
+}
+
+static VERSION_VECTOR_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/submerge/VersionVector");
+
+#[repr(transparent)]
+#[derive(Default)]
+pub struct VersionVectorJni<Obj>(pub Obj);
+
+impl<'local> VersionVectorJni<JObject<'local>> {
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ version_vector: VersionVectorHandle,
+ ) -> jni::errors::Result<Self> {
+ pourover::call_constructor!(
+ env,
+ VERSION_VECTOR_CLASS,
+ "(J)V",
+ jlong::try_from(version_vector.handle_id)
+ .expect("VersionVectorHandle's max_active_handles is 131072")
+ )
+ .map(Self)
+ }
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "VersionVector")]
+extern "system" fn nativeClose<'local>(
+ _env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) {
+ if let Ok(handle) = VersionVectorHandle::try_from(handle_id) {
+ let _ = handle.deallocate();
+ }
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "VersionVector")]
+extern "system" fn nativeToByteArray<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) -> JByteArray<'local> {
+ try_throwable_submerge(&mut env, |env| {
+ let handle = VersionVectorHandle::try_from(handle_id)?;
+ let version_vector = handle.get()?;
+ let bytes = version_vector
+ .to_transferable_proto()
+ .write_to_bytes()
+ .map_err(runtime_exception)?;
+ Ok(env.byte_array_from_slice(&bytes)?)
+ })
+}
+
+#[jni_method(package = "com.google.android.submerge", class = "VersionVector")]
+extern "system" fn nativeFromByteArray<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ data: JByteArray<'local>,
+) -> jlong {
+ try_throwable_submerge(&mut env, |env| {
+ let bytes = env.convert_byte_array(data)?;
+ let proto =
+ submerge_internal_proto::protos::submerge::VectorClock::parse_from_bytes(&bytes)
+ .map_err(runtime_exception)?;
+ let version_vector =
+ VectorClock::from_transferable_proto(&proto).map_err(runtime_exception)?;
+ Ok(VersionVectorHandle::allocate(|| version_vector)?.jlong())
+ })
+}
+
+const OLDER: jint = -1;
+const EQUAL: jint = 0;
+const NEWER: jint = 1;
+const CONCURRENT: jint = 2;
+
+#[jni_method(package = "com.google.android.submerge", class = "VersionVector")]
+extern "system" fn nativeCompare<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+ other_handle_id: jlong,
+) -> jint {
+ try_throwable_submerge(&mut env, |_| {
+ let handle = VersionVectorHandle::try_from(handle_id)?;
+ let self_version_vector = handle.get()?;
+ let other_handle = VersionVectorHandle::try_from(other_handle_id)?;
+ let other_version_vector = other_handle.get()?;
+ Ok(
+ match self_version_vector.partial_cmp(&other_version_vector) {
+ Some(Ordering::Less) => OLDER,
+ Some(Ordering::Equal) => EQUAL,
+ Some(Ordering::Greater) => NEWER,
+ None => CONCURRENT,
+ },
+ )
+ })
+}
diff --git a/betosync/submerge/submerge_java/src/java_map.rs b/betosync/submerge/submerge_java/src/java_map.rs
new file mode 100644
index 0000000..31cfc7b
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/java_map.rs
@@ -0,0 +1,65 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+#![allow(unused, dead_code)]
+
+use jni::{objects::JObject, JNIEnv};
+use pourover::desc::ClassDesc;
+
+static JAVA_HASH_MAP_CLASS: ClassDesc = ClassDesc::new("java/util/HashMap");
+
+#[repr(transparent)]
+#[derive(Default)]
+pub(crate) struct HashMapJni<Obj>(pub Obj);
+
+impl<'local> HashMapJni<JObject<'local>> {
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ initial_capacity: i32,
+ ) -> Result<Self, jni::errors::Error> {
+ pourover::call_constructor!(env, JAVA_HASH_MAP_CLASS, "(I)V", initial_capacity).map(Self)
+ }
+
+ pub fn put(
+ &self,
+ env: &mut JNIEnv<'local>,
+ key: impl AsRef<JObject<'local>>,
+ value: impl AsRef<JObject<'local>>,
+ ) -> Result<JObject<'local>, jni::errors::Error> {
+ pourover::call_method!(
+ env,
+ JAVA_HASH_MAP_CLASS,
+ "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+ &self.0,
+ key.as_ref(),
+ value.as_ref()
+ )
+ }
+
+ pub fn get(
+ &self,
+ env: &mut JNIEnv<'local>,
+ key: impl AsRef<JObject<'local>>,
+ ) -> Result<JObject<'local>, jni::errors::Error> {
+ pourover::call_method!(
+ env,
+ JAVA_HASH_MAP_CLASS,
+ "get",
+ "(Ljava/lang/Object;)Ljava/lang/Object;",
+ &self.0,
+ key.as_ref(),
+ )
+ }
+}
diff --git a/betosync/submerge/submerge_java/src/lib.rs b/betosync/submerge/submerge_java/src/lib.rs
new file mode 100644
index 0000000..0fc198b
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/lib.rs
@@ -0,0 +1,46 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Java bindings for submerge.
+//!
+//! This crate contains the JNI glue code that implements the corresponding Java `native` methods
+//! and forwards them to the [`submerge`] or [`crdt`] crates.
+//!
+//! This crate is not intended to be used by any other crates; only directly built as a dynamic
+//! library (e.g. .so file) to be loaded by System.loadLibrary in Java.
+//!
+//! ## Maintenance notes
+//!
+//! Since going through the JNI means we lose the help of the type system, there are some patterns
+//! we follow to help us organize things:
+//! 1. Handles are used to isolate access to Rust-owned data. See [`handle_map`] for more.
+//! 2. The types we expose (possibly via the handle indirection) may need to contain additional data
+//! for Java's APIs. In those cases, those wrapper structs are prefixed with "Java-". Similarly,
+//! the "Java-" prefix is added for type aliases on Rust types that are exposed through JNI.
+//! 3. For Java types that we need to access in Rust, the suffix "-Jni" is added. These are wrappers
+//! around `JObject`s.
+//! 4. The handles are `#[repr(transparent)]` and holds a `u64` which are implicitly transmuted into
+//! Java `long`s when it goes through JNI. In Java we rely on annotations to help document the
+//! types. Currently there are no automated tooling setup to check that those type annotations
+//! are respected in code.
+
+// Named lifetimes are being used to match jni crate conventions.
+// See: https://docs.rs/jni/latest/jni/struct.JNIEnv.html#lifetime-names
+#![allow(clippy::needless_lifetimes)]
+// Trivial casts needed for FFI and more readable than creating new bindings
+#![allow(trivial_casts)]
+
+mod class;
+mod java_map;
+mod utils;
diff --git a/betosync/submerge/submerge_java/src/utils.rs b/betosync/submerge/submerge_java/src/utils.rs
new file mode 100644
index 0000000..4ea676c
--- /dev/null
+++ b/betosync/submerge/submerge_java/src/utils.rs
@@ -0,0 +1,103 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use jni::{
+ descriptors::Desc,
+ objects::{AutoLocal, JClass, JObject, JObjectArray},
+ JNIEnv,
+};
+
+/// Collects an iterator of `I`, calling `item_into_jobject` on each element and collecting them
+/// into a `JObjectArray`.
+pub(crate) fn collect_to_jarray<'local, I, O>(
+ env: &mut JNIEnv<'local>,
+ element_class: impl Desc<'local, JClass<'local>>,
+ iter: impl ExactSizeIterator<Item = I>,
+ item_into_jobject: impl Fn(
+ &mut JNIEnv<'local>,
+ I,
+ ) -> Result<AutoLocal<'local, O>, jni::errors::Error>,
+) -> Result<JObjectArray<'local>, jni::errors::Error>
+where
+ O: Into<JObject<'local>> + AsRef<JObject<'local>>,
+{
+ let arr = env.new_object_array(
+ iter.len()
+ .try_into()
+ .expect("Array length should be convertible to i32"),
+ element_class,
+ JObject::default(),
+ )?;
+ for (i, item) in iter.enumerate() {
+ let item = item_into_jobject(env, item)?;
+ env.set_object_array_element(
+ &arr,
+ i.try_into()
+ .expect("Array index should be convertible to i32"),
+ item,
+ )?;
+ }
+ Ok(arr)
+}
+
+/// Define a handle suitable for JNI use.
+///
+/// In addition to declaring [handle_map::declare_handle_map!], this also generates conversions to
+/// and from `jlong` for use with JNI. The handle can be converted to `jlong` using
+/// `fooHandle.jlong()`. A `jlong` value can be converted back to `handle` using [`TryFrom`].
+///
+/// # Example
+/// ```no_run
+/// declare_handle! {
+/// pub struct FooHandle for Foo;
+/// }
+/// ```
+macro_rules! declare_handle {
+ ( pub struct $handlename:ident for $target:ty; ) => {
+ #[repr(C)]
+ #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+ pub struct $handlename {
+ pub(crate) handle_id: u64,
+ }
+
+ impl $handlename {
+ pub fn jlong(&self) -> jlong {
+ self.handle_id
+ .try_into()
+ .expect("Handle ID can't be larger than max_active_handles(131072)")
+ }
+ }
+
+ ::handle_map::declare_handle_map!(
+ ::handle_map::HandleMapDimensions {
+ num_shards: 4,
+ max_active_handles: 131072
+ },
+ $handlename,
+ $target
+ );
+
+ impl TryFrom<jlong> for $handlename {
+ type Error = ::handle_map::HandleNotPresentError;
+
+ fn try_from(value: jlong) -> Result<Self, Self::Error> {
+ Ok(Self {
+ handle_id: u64::try_from(value)
+ .map_err(|_| ::handle_map::HandleNotPresentError)?,
+ })
+ }
+ }
+ };
+}
+pub(crate) use declare_handle;
diff --git a/common/Cargo.lock b/common/Cargo.lock
index 1b19b5a..e328fbb 100644
--- a/common/Cargo.lock
+++ b/common/Cargo.lock
@@ -4,18 +4,30 @@
[[package]]
name = "addr2line"
-version = "0.22.0"
+version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
-name = "adler"
-version = "1.0.2"
+name = "adler2"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy 0.8.25",
+]
[[package]]
name = "aho-corasick"
@@ -27,6 +39,14 @@
]
[[package]]
+name = "analytics_logger"
+version = "0.1.0"
+dependencies = [
+ "thiserror 2.0.12",
+ "tokio",
+]
+
+[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -49,9 +69,9 @@
[[package]]
name = "anstream"
-version = "0.6.15"
+version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -64,49 +84,60 @@
[[package]]
name = "anstyle"
-version = "1.0.8"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
-version = "0.2.5"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.1.1"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.4"
+version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
- "windows-sys 0.52.0",
+ "once_cell",
+ "windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
-version = "1.0.86"
+version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+
+[[package]]
+name = "assert-json-diff"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12"
+dependencies = [
+ "serde",
+ "serde_json",
+]
[[package]]
name = "async-trait"
-version = "0.1.81"
+version = "0.1.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
+checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
dependencies = [
"proc-macro2",
"quote",
@@ -121,9 +152,9 @@
[[package]]
name = "autocfg"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "axum"
@@ -138,7 +169,7 @@
"futures-util",
"http 0.2.12",
"http-body 0.4.6",
- "hyper 0.14.30",
+ "hyper 0.14.32",
"itoa",
"matchit",
"memchr",
@@ -152,7 +183,7 @@
"serde_urlencoded",
"sync_wrapper 0.1.2",
"tokio",
- "tower",
+ "tower 0.4.13",
"tower-layer",
"tower-service",
]
@@ -176,17 +207,17 @@
[[package]]
name = "backtrace"
-version = "0.3.73"
+version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
- "cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -196,6 +227,26 @@
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
+name = "bindgen"
+version = "0.71.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
+dependencies = [
+ "bitflags 2.8.0",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.13.0",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn",
+]
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -203,15 +254,24 @@
[[package]]
name = "bitflags"
-version = "2.6.0"
+version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
+
+[[package]]
+name = "block2"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2"
+dependencies = [
+ "objc2",
+]
[[package]]
name = "bstr"
-version = "1.10.0"
+version = "1.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
+checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
dependencies = [
"memchr",
"serde",
@@ -233,15 +293,21 @@
[[package]]
name = "bumpalo"
-version = "3.16.0"
+version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
-version = "1.6.1"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
+checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
[[package]]
name = "cast"
@@ -251,9 +317,12 @@
[[package]]
name = "cc"
-version = "1.1.6"
+version = "1.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
+checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2"
+dependencies = [
+ "shlex",
+]
[[package]]
name = "cesu8"
@@ -262,6 +331,15 @@
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
+[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -269,9 +347,9 @@
[[package]]
name = "chrono"
-version = "0.4.38"
+version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
dependencies = [
"android-tzdata",
"iana-time-zone",
@@ -309,10 +387,21 @@
]
[[package]]
-name = "clap"
-version = "4.5.13"
+name = "clang-sys"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading 0.8.6",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff"
dependencies = [
"clap_builder",
"clap_derive",
@@ -320,9 +409,9 @@
[[package]]
name = "clap_builder"
-version = "4.5.13"
+version = "4.5.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
+checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
dependencies = [
"anstream",
"anstyle",
@@ -332,9 +421,9 @@
[[package]]
name = "clap_derive"
-version = "4.5.13"
+version = "4.5.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
+checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
dependencies = [
"heck",
"proc-macro2",
@@ -344,9 +433,29 @@
[[package]]
name = "clap_lex"
-version = "0.7.2"
+version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+[[package]]
+name = "clearcut_logger"
+version = "0.1.0"
+dependencies = [
+ "analytics_logger",
+ "anyhow",
+ "chrono",
+ "crossbeam",
+ "mockito",
+ "protobuf 3.7.2",
+ "protobuf-codegen 3.7.2",
+ "protobuf-json-mapping",
+ "rand",
+ "reqwest",
+ "tempfile",
+ "thiserror 2.0.12",
+ "tokio",
+ "tokio-macros",
+]
[[package]]
name = "cmd_runner"
@@ -367,9 +476,19 @@
[[package]]
name = "colorchoice"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+
+[[package]]
+name = "colored"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
+dependencies = [
+ "lazy_static",
+ "windows-sys 0.59.0",
+]
[[package]]
name = "combine"
@@ -393,9 +512,9 @@
[[package]]
name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "criterion"
@@ -409,7 +528,7 @@
"clap",
"criterion-plot",
"is-terminal",
- "itertools",
+ "itertools 0.10.5",
"num-traits",
"once_cell",
"oorandom",
@@ -430,7 +549,7 @@
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
- "itertools",
+ "itertools 0.10.5",
]
[[package]]
@@ -448,18 +567,18 @@
[[package]]
name = "crossbeam-channel"
-version = "0.5.13"
+version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
-version = "0.8.5"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
@@ -476,24 +595,24 @@
[[package]]
name = "crossbeam-queue"
-version = "0.3.11"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.20"
+version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
-version = "0.2.2"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
[[package]]
name = "directories"
@@ -517,6 +636,27 @@
]
[[package]]
+name = "dispatch2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
+dependencies = [
+ "bitflags 2.8.0",
+ "objc2",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -524,14 +664,37 @@
[[package]]
name = "encoding_rs"
-version = "0.8.34"
+version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]]
+name = "env_filter"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "jiff",
+ "log",
+]
+
+[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -539,34 +702,54 @@
[[package]]
name = "errno"
-version = "0.3.9"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
-name = "fastrand"
-version = "2.1.0"
+name = "fallible-iterator"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "file-header"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5568149106e77ae33bc3a2c3ef3839cbe63ffa4a8dd4a81612a6f9dfdbc2e9f"
+checksum = "f572687d306c51b578ca07b5b2b10ad334a486cb11673d0f88437f22d68eed06"
dependencies = [
"crossbeam",
"lazy_static",
"license",
- "thiserror",
+ "thiserror 1.0.69",
"walkdir",
]
[[package]]
+name = "flagging"
+version = "0.1.0"
+dependencies = [
+ "log",
+ "thiserror 2.0.12",
+]
+
+[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -598,9 +781,9 @@
[[package]]
name = "futures-channel"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
@@ -608,33 +791,33 @@
[[package]]
name = "futures-core"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-io"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-sink"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-io",
@@ -654,26 +837,38 @@
dependencies = [
"cfg-if",
"libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.13.3+wasi-0.2.2",
+ "windows-targets 0.52.6",
]
[[package]]
name = "gimli"
-version = "0.29.0"
+version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "glob"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "globset"
-version = "0.4.14"
+version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
+checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
dependencies = [
"aho-corasick",
"bstr",
@@ -683,17 +878,40 @@
]
[[package]]
-name = "h2"
-version = "0.4.5"
+name = "googletest"
+version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
+checksum = "06597b7d02ee58b9a37f522785ac15b9e18c6b178747c4439a6c03fbb35ea753"
+dependencies = [
+ "googletest_macro",
+ "num-traits",
+ "regex",
+ "rustversion",
+]
+
+[[package]]
+name = "googletest_macro"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31d9f07c9c19b855faebf71637be3b43f8e13a518aece5d61a3beee7710b4ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
- "http 1.1.0",
+ "http 1.2.0",
"indexmap",
"slab",
"tokio",
@@ -725,6 +943,24 @@
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+
+[[package]]
+name = "hashlink"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
+dependencies = [
+ "hashbrown 0.14.5",
+]
[[package]]
name = "heck"
@@ -734,9 +970,9 @@
[[package]]
name = "hermit-abi"
-version = "0.3.9"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "hex"
@@ -745,6 +981,15 @@
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
+name = "home"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -757,9 +1002,9 @@
[[package]]
name = "http"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
dependencies = [
"bytes",
"fnv",
@@ -784,7 +1029,7 @@
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
- "http 1.1.0",
+ "http 1.2.0",
]
[[package]]
@@ -795,16 +1040,16 @@
dependencies = [
"bytes",
"futures-util",
- "http 1.1.0",
+ "http 1.2.0",
"http-body 1.0.1",
"pin-project-lite",
]
[[package]]
name = "httparse"
-version = "1.9.4"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
+checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a"
[[package]]
name = "httpdate"
@@ -814,9 +1059,9 @@
[[package]]
name = "hyper"
-version = "0.14.30"
+version = "0.14.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
+checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
dependencies = [
"bytes",
"futures-channel",
@@ -837,17 +1082,18 @@
[[package]]
name = "hyper"
-version = "1.4.1"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
+checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2",
- "http 1.1.0",
+ "http 1.2.0",
"http-body 1.0.1",
"httparse",
+ "httpdate",
"itoa",
"pin-project-lite",
"smallvec",
@@ -857,13 +1103,13 @@
[[package]]
name = "hyper-rustls"
-version = "0.27.2"
+version = "0.27.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
+checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
dependencies = [
"futures-util",
- "http 1.1.0",
- "hyper 1.4.1",
+ "http 1.2.0",
+ "hyper 1.6.0",
"hyper-util",
"rustls",
"rustls-pki-types",
@@ -880,7 +1126,7 @@
dependencies = [
"bytes",
"http-body-util",
- "hyper 1.4.1",
+ "hyper 1.6.0",
"hyper-util",
"native-tls",
"tokio",
@@ -890,29 +1136,28 @@
[[package]]
name = "hyper-util"
-version = "0.1.6"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
+checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
- "http 1.1.0",
+ "http 1.2.0",
"http-body 1.0.1",
- "hyper 1.4.1",
+ "hyper 1.6.0",
"pin-project-lite",
"socket2",
"tokio",
- "tower",
"tower-service",
"tracing",
]
[[package]]
name = "iana-time-zone"
-version = "0.1.60"
+version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -932,40 +1177,169 @@
]
[[package]]
-name = "idna"
-version = "0.5.0"
+name = "icu_collections"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+
+[[package]]
+name = "icu_normalizer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "utf16_iter",
+ "utf8_iter",
+ "write16",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+
+[[package]]
+name = "icu_properties"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locid_transform",
+ "icu_properties_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+
+[[package]]
+name = "icu_provider"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_provider_macros",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_provider_macros"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "idna"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
]
[[package]]
name = "indexmap"
-version = "2.2.6"
+version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
dependencies = [
"equivalent",
- "hashbrown",
+ "hashbrown 0.15.2",
]
[[package]]
name = "ipnet"
-version = "2.9.0"
+version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "is-terminal"
-version = "0.4.12"
+version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
+checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37"
dependencies = [
"hermit-abi",
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -984,19 +1358,51 @@
]
[[package]]
-name = "itoa"
-version = "1.0.11"
+name = "itertools"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "java-locator"
-version = "0.1.7"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2abecabd9961c5e01405a6426687fcf1bd94a269927137e4c3cc1a7419b93fd"
+checksum = "09c46c1fe465c59b1474e665e85e1256c3893dd00927b8d55f63b09044c1e64f"
dependencies = [
"glob",
- "lazy_static",
+]
+
+[[package]]
+name = "jiff"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e"
+dependencies = [
+ "jiff-static",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
@@ -1010,9 +1416,9 @@
"combine",
"java-locator",
"jni-sys",
- "libloading",
+ "libloading 0.7.4",
"log",
- "thiserror",
+ "thiserror 1.0.69",
"walkdir",
"windows-sys 0.45.0",
]
@@ -1025,10 +1431,11 @@
[[package]]
name = "js-sys"
-version = "0.3.69"
+version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
+ "once_cell",
"wasm-bindgen",
]
@@ -1043,9 +1450,9 @@
[[package]]
name = "libc"
-version = "0.2.155"
+version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libloading"
@@ -1058,20 +1465,41 @@
]
[[package]]
+name = "libloading"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.8.0",
"libc",
]
[[package]]
-name = "license"
-version = "3.4.0+3.24.0"
+name = "libsqlite3-sys"
+version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7da1e0d845faf299a9fe5f201a918a0dc0d5fc22c7b9580a6a23fed3a912b37"
+checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "license"
+version = "3.5.0+3.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef560a8a3b3ac5fef31a9cfb8e4464cd1caa36c6bf1c1896b5d5a237e6486ee1"
dependencies = [
"reword",
"serde",
@@ -1080,9 +1508,15 @@
[[package]]
name = "linux-raw-sys"
-version = "0.4.14"
+version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "litemap"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
[[package]]
name = "lock_adapter"
@@ -1103,9 +1537,9 @@
[[package]]
name = "log"
-version = "0.4.22"
+version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "matchit"
@@ -1133,30 +1567,53 @@
[[package]]
name = "miniz_oxide"
-version = "0.7.4"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
+checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
dependencies = [
- "adler",
+ "adler2",
]
[[package]]
name = "mio"
-version = "1.0.1"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
- "hermit-abi",
"libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
[[package]]
-name = "native-tls"
-version = "0.2.12"
+name = "mockito"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
+checksum = "652cd6d169a36eaf9d1e6bce1a221130439a966d7f27858af66a33a66e9c4ee2"
+dependencies = [
+ "assert-json-diff",
+ "bytes",
+ "colored",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.6.0",
+ "hyper-util",
+ "log",
+ "rand",
+ "regex",
+ "serde_json",
+ "serde_urlencoded",
+ "similar",
+ "tokio",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c"
dependencies = [
"libc",
"log",
@@ -1206,19 +1663,58 @@
]
[[package]]
-name = "object"
-version = "0.36.2"
+name = "objc2"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
+checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
+dependencies = [
+ "bitflags 2.8.0",
+ "dispatch2",
+ "objc2",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
+dependencies = [
+ "bitflags 2.8.0",
+ "block2",
+ "libc",
+ "objc2",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
-version = "1.19.0"
+version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
[[package]]
name = "oorandom"
@@ -1238,11 +1734,11 @@
[[package]]
name = "openssl"
-version = "0.10.66"
+version = "0.10.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
+checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.8.0",
"cfg-if",
"foreign-types",
"libc",
@@ -1264,15 +1760,15 @@
[[package]]
name = "openssl-probe"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
-version = "0.9.103"
+version = "0.9.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
+checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07"
dependencies = [
"cc",
"libc",
@@ -1316,10 +1812,16 @@
]
[[package]]
-name = "pathdiff"
-version = "0.2.1"
+name = "paste"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pathdiff"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]]
name = "percent-encoding"
@@ -1328,19 +1830,49 @@
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
+name = "ph_client"
+version = "0.1.0"
+dependencies = [
+ "googletest",
+ "ph_client_sys",
+ "protobuf 4.32.0-release",
+ "protobuf-codegen 4.32.0-release",
+ "tokio",
+]
+
+[[package]]
+name = "ph_client_sys"
+version = "0.1.0"
+dependencies = [
+ "bindgen",
+ "cc",
+ "cfg-if",
+]
+
+[[package]]
+name = "ph_flag_source"
+version = "0.1.0"
+dependencies = [
+ "flagging",
+ "log",
+ "ph_client",
+ "thiserror 2.0.12",
+]
+
+[[package]]
name = "pin-project"
-version = "1.1.5"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
-version = "1.1.5"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67"
dependencies = [
"proc-macro2",
"quote",
@@ -1349,9 +1881,9 @@
[[package]]
name = "pin-project-lite"
-version = "0.2.14"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
@@ -1361,15 +1893,15 @@
[[package]]
name = "pkg-config"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "plotters"
-version = "0.3.6"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3"
+checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
dependencies = [
"num-traits",
"plotters-backend",
@@ -1380,20 +1912,35 @@
[[package]]
name = "plotters-backend"
-version = "0.3.6"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7"
+checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
[[package]]
name = "plotters-svg"
-version = "0.3.6"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705"
+checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
dependencies = [
"plotters-backend",
]
[[package]]
+name = "portable-atomic"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
name = "pourover"
version = "0.1.0"
dependencies = [
@@ -1415,24 +1962,115 @@
[[package]]
name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy 0.7.35",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.2.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
[[package]]
name = "proc-macro2"
-version = "1.0.86"
+version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1"
dependencies = [
"unicode-ident",
]
[[package]]
-name = "quote"
-version = "1.0.36"
+name = "protobuf"
+version = "3.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4"
+dependencies = [
+ "once_cell",
+ "protobuf-support",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "protobuf"
+version = "4.32.0-release"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "951f19b825af28f7c7ce3daaf71e4e8961394dcc410b3a0aac6e4e0e5b7e2d2b"
+dependencies = [
+ "cc",
+ "paste",
+]
+
+[[package]]
+name = "protobuf-codegen"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace"
+dependencies = [
+ "anyhow",
+ "once_cell",
+ "protobuf 3.7.2",
+ "protobuf-parse",
+ "regex",
+ "tempfile",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "protobuf-codegen"
+version = "4.32.0-release"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7756fa998bcc36bb53ff0d857043d96e734956b8ef46eff17d63f550db629b93"
+
+[[package]]
+name = "protobuf-json-mapping"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0d6e4be637b310d8a5c02fa195243328e2d97fa7df1127a27281ef1187fcb1d"
+dependencies = [
+ "protobuf 3.7.2",
+ "protobuf-support",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "protobuf-parse"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "log",
+ "protobuf 3.7.2",
+ "protobuf-support",
+ "tempfile",
+ "thiserror 1.0.69",
+ "which",
+]
+
+[[package]]
+name = "protobuf-support"
+version = "3.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6"
+dependencies = [
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
@@ -1464,7 +2102,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
]
[[package]]
@@ -1489,29 +2127,29 @@
[[package]]
name = "redox_syscall"
-version = "0.5.3"
+version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.8.0",
]
[[package]]
name = "redox_users"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
"libredox",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
name = "regex"
-version = "1.10.5"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
@@ -1521,9 +2159,9 @@
[[package]]
name = "regex-automata"
-version = "0.4.7"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
@@ -1532,15 +2170,15 @@
[[package]]
name = "regex-syntax"
-version = "0.8.4"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
-version = "0.12.5"
+version = "0.12.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
+checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
dependencies = [
"base64",
"bytes",
@@ -1549,10 +2187,10 @@
"futures-core",
"futures-util",
"h2",
- "http 1.1.0",
+ "http 1.2.0",
"http-body 1.0.1",
"http-body-util",
- "hyper 1.4.1",
+ "hyper 1.6.0",
"hyper-rustls",
"hyper-tls",
"hyper-util",
@@ -1568,16 +2206,17 @@
"serde",
"serde_json",
"serde_urlencoded",
- "sync_wrapper 1.0.1",
+ "sync_wrapper 1.0.2",
"system-configuration",
"tokio",
"tokio-native-tls",
+ "tower 0.5.2",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "winreg",
+ "windows-registry",
]
[[package]]
@@ -1597,7 +2236,7 @@
dependencies = [
"cc",
"cfg-if",
- "getrandom",
+ "getrandom 0.2.15",
"libc",
"spin",
"untrusted",
@@ -1605,29 +2244,49 @@
]
[[package]]
+name = "rusqlite"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
+dependencies = [
+ "bitflags 2.8.0",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
-name = "rustix"
-version = "0.38.34"
+name = "rustc-hash"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.8.0",
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
name = "rustls"
-version = "0.23.12"
+version = "0.23.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
+checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7"
dependencies = [
"once_cell",
"rustls-pki-types",
@@ -1638,25 +2297,24 @@
[[package]]
name = "rustls-pemfile"
-version = "2.1.2"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
- "base64",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
-version = "1.7.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
[[package]]
name = "rustls-webpki"
-version = "0.102.6"
+version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
+checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
"ring",
"rustls-pki-types",
@@ -1665,15 +2323,15 @@
[[package]]
name = "rustversion"
-version = "1.0.17"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
-version = "1.0.18"
+version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
[[package]]
name = "same-file"
@@ -1686,11 +2344,11 @@
[[package]]
name = "schannel"
-version = "0.1.23"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -1705,7 +2363,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.8.0",
"core-foundation",
"core-foundation-sys",
"libc",
@@ -1714,9 +2372,9 @@
[[package]]
name = "security-framework-sys"
-version = "2.11.1"
+version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
+checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
dependencies = [
"core-foundation-sys",
"libc",
@@ -1724,18 +2382,18 @@
[[package]]
name = "serde"
-version = "1.0.204"
+version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.204"
+version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
@@ -1744,11 +2402,12 @@
[[package]]
name = "serde_json"
-version = "1.0.120"
+version = "1.0.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
+checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
dependencies = [
"itoa",
+ "memchr",
"ryu",
"serde",
]
@@ -1782,6 +2441,12 @@
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1791,6 +2456,12 @@
]
[[package]]
+name = "similar"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
+
+[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1807,9 +2478,9 @@
[[package]]
name = "socket2"
-version = "0.5.7"
+version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -1825,6 +2496,27 @@
]
[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "storage"
+version = "0.1.0"
+dependencies = [
+ "directories",
+ "env_logger",
+ "log",
+ "objc2",
+ "objc2-foundation",
+ "rusqlite",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.12",
+]
+
+[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1838,9 +2530,9 @@
[[package]]
name = "syn"
-version = "2.0.72"
+version = "2.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
+checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
dependencies = [
"proc-macro2",
"quote",
@@ -1855,26 +2547,40 @@
[[package]]
name = "sync_wrapper"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
[[package]]
name = "system-configuration"
-version = "0.5.1"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.8.0",
"core-foundation",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
@@ -1882,30 +2588,41 @@
[[package]]
name = "tempfile"
-version = "3.10.1"
+version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
dependencies = [
"cfg-if",
"fastrand",
+ "getrandom 0.3.1",
+ "once_cell",
"rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
name = "thiserror"
-version = "1.0.63"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
- "thiserror-impl",
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl 2.0.12",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.63"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
@@ -1913,6 +2630,27 @@
]
[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1923,25 +2661,10 @@
]
[[package]]
-name = "tinyvec"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
-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.39.1"
+version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a"
+checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",
@@ -1957,9 +2680,9 @@
[[package]]
name = "tokio-macros"
-version = "2.4.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
@@ -1978,20 +2701,19 @@
[[package]]
name = "tokio-rustls"
-version = "0.26.0"
+version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
+checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
dependencies = [
"rustls",
- "rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-util"
-version = "0.7.11"
+version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
+checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
dependencies = [
"bytes",
"futures-core",
@@ -2017,22 +2739,37 @@
]
[[package]]
-name = "tower-layer"
-version = "0.3.2"
+name = "tower"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper 1.0.2",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
-version = "0.1.40"
+version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"log",
"pin-project-lite",
@@ -2041,9 +2778,9 @@
[[package]]
name = "tracing-core"
-version = "0.1.32"
+version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
]
@@ -2055,31 +2792,16 @@
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[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"
+version = "1.0.16"
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",
-]
+checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "unicode-segmentation"
-version = "1.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "untrusted"
@@ -2089,9 +2811,9 @@
[[package]]
name = "url"
-version = "2.5.2"
+version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [
"form_urlencoded",
"idna",
@@ -2099,6 +2821,18 @@
]
[[package]]
+name = "utf16_iter"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2111,6 +2845,12 @@
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2136,24 +2876,34 @@
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
-name = "wasm-bindgen"
-version = "0.2.92"
+name = "wasi"
+version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
+ "once_cell",
+ "rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
- "once_cell",
"proc-macro2",
"quote",
"syn",
@@ -2162,21 +2912,22 @@
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.42"
+version = "0.4.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
dependencies = [
"cfg-if",
"js-sys",
+ "once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2184,9 +2935,9 @@
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
@@ -2197,21 +2948,36 @@
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
[[package]]
name = "web-sys"
-version = "0.3.69"
+version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
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 = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2229,11 +2995,11 @@
[[package]]
name = "winapi-util"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2252,6 +3018,36 @@
]
[[package]]
+name = "windows-registry"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
+dependencies = [
+ "windows-result",
+ "windows-strings",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
+dependencies = [
+ "windows-result",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2294,6 +3090,15 @@
]
[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2472,32 +3277,151 @@
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
-name = "winreg"
-version = "0.52.0"
+name = "wit-bindgen-rt"
+version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
+checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
- "cfg-if",
- "windows-sys 0.48.0",
+ "bitflags 2.8.0",
]
[[package]]
-name = "xshell"
-version = "0.2.6"
+name = "write16"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437"
+checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+
+[[package]]
+name = "writeable"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+
+[[package]]
+name = "xshell"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d"
dependencies = [
"xshell-macros",
]
[[package]]
name = "xshell-macros"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852"
+checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547"
+
+[[package]]
+name = "yoke"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive 0.7.35",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
+dependencies = [
+ "zerocopy-derive 0.8.25",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+
+[[package]]
+name = "zerovec"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/common/Cargo.toml b/common/Cargo.toml
index 30689fc..928c82b 100644
--- a/common/Cargo.toml
+++ b/common/Cargo.toml
@@ -4,10 +4,10 @@
"cmd_runner",
"handle_map",
"lock_adapter",
- "oauth",
"pourover",
"pourover_macro",
]
+
default-members = ["build_scripts"]
resolver = "2"
@@ -37,6 +37,7 @@
# from crates.io
anyhow = "1.0.75"
+thiserror = "2.0.4"
arbitrary = "1.3.2"
clap = { version = "4.5.13", features = ["derive"] }
criterion = { version = "0.5.1", features = ["html_reports"] }
@@ -57,6 +58,9 @@
rand = "0.8.5"
reqwest = "0.12.5"
tokio = { version = "1.39.1", features = ["full"] }
+protobuf = "=3.7.2"
+protobuf-codegen = "=3.7.2"
+googletest = "0.14.2"
[workspace.package]
version = "0.1.0"
diff --git a/common/build_scripts/src/license.rs b/common/build_scripts/src/license.rs
index 113b7a0..b9c1861 100644
--- a/common/build_scripts/src/license.rs
+++ b/common/build_scripts/src/license.rs
@@ -51,6 +51,11 @@
"**/fuzz/artifacts/**",
"**/cmake-build-debug/**",
"**/tags",
+ "**/*.dll",
+ "**/*.dll.lib",
+ "**/*.so",
+ "**/*.a",
+ "**/cooperative_cleaner/lib/build/**",
],
};
diff --git a/common/build_scripts/src/main.rs b/common/build_scripts/src/main.rs
index 53fd178..2121730 100644
--- a/common/build_scripts/src/main.rs
+++ b/common/build_scripts/src/main.rs
@@ -20,7 +20,7 @@
license_checker::LicenseSubcommand,
};
use license::LICENSE_CHECKER;
-use xshell::Shell;
+use xshell::{cmd, Shell};
mod license;
@@ -42,6 +42,7 @@
License(LicenseSubcommand),
}
+#[rustfmt::skip]
fn main() -> anyhow::Result<()> {
let args = Cli::parse();
let root_dir = path::Path::new(
@@ -55,11 +56,13 @@
sh.change_dir(&root_dir);
match args.subcommand {
Subcommand::VerifyCi { cargo_options } => {
- cargo_options.check_workspace(&sh, "common")?;
+ cargo_options.check_workspace(&sh, "common", ["--features", "testing"])?;
FormatterOptions { reformat: false }.check_format(&sh)?;
LICENSE_CHECKER.check(&root_dir)?;
}
- Subcommand::CargoWorkspace(workspace) => workspace.run("common", &sh)?,
+ Subcommand::CargoWorkspace(workspace) => {
+ workspace.run(&sh, "common", ["--features", "testing"])?
+ }
Subcommand::License(license) => license.run(&LICENSE_CHECKER, &root_dir)?,
}
Ok(())
diff --git a/common/cmd_runner/src/cargo_workspace.rs b/common/cmd_runner/src/cargo_workspace.rs
index 9bc3f27..9562344 100644
--- a/common/cmd_runner/src/cargo_workspace.rs
+++ b/common/cmd_runner/src/cargo_workspace.rs
@@ -25,10 +25,15 @@
}
impl CargoWorkspaceSubcommand {
- pub fn run(&self, tag: &str, sh: &xshell::Shell) -> anyhow::Result<()> {
+ pub fn run<S: AsRef<OsStr>>(
+ &self,
+ sh: &xshell::Shell,
+ tag: &str,
+ args: impl IntoIterator<Item = S> + Copy,
+ ) -> anyhow::Result<()> {
match self {
CargoWorkspaceSubcommand::CheckWorkspace(cargo_options) => {
- cargo_options.check_workspace(sh, tag)
+ cargo_options.check_workspace(sh, tag, args)
}
CargoWorkspaceSubcommand::CheckFormat(formatter_options) => {
formatter_options.check_format(sh)
@@ -51,32 +56,40 @@
&self,
sh: &'sh xshell::Shell,
tag: &str,
- args: impl IntoIterator<Item = S>,
+ args: impl IntoIterator<Item = S> + Copy,
) -> xshell::Cmd<'sh> {
let locked = if self.locked { "--locked" } else { "" };
if self.coverage {
cmd!(
sh,
- "cargo llvm-cov {locked} {args...} --lcov --output-path target/{tag}.info -- --color=always"
+ "cargo llvm-cov {locked} --workspace {args...} --lcov --output-path target/{tag}.info -- --color=always"
)
} else {
- cmd!(sh, "cargo test {locked} {args...} -- --color=always")
+ cmd!(
+ sh,
+ "cargo test {locked} --workspace {args...} -- --color=always"
+ )
}
}
/// Run the default set of checks on a cargo workspace
- pub fn check_workspace(&self, sh: &xshell::Shell, tag: &str) -> anyhow::Result<()> {
- self.test(sh, tag, ["--workspace"]).run()?;
+ pub fn check_workspace<S: AsRef<OsStr>>(
+ &self,
+ sh: &xshell::Shell,
+ tag: &str,
+ args: impl IntoIterator<Item = S> + Copy,
+ ) -> anyhow::Result<()> {
+ self.test(sh, tag, args).run()?;
cmd!(
sh,
- "cargo clippy --all-targets --workspace -- --deny warnings"
+ "cargo clippy --all-targets --workspace {args...} -- --deny warnings"
)
.run()?;
cmd!(sh, "cargo deny --workspace check").run()?;
// ensure the docs are valid (cross-references to other code, etc)
cmd!(
sh,
- "cargo doc --quiet --workspace --no-deps --document-private-items
+ "cargo doc --quiet --workspace {args...} --no-deps --document-private-items
--target-dir target/dist_docs/{tag}"
)
.env("RUSTDOCFLAGS", "--deny warnings")
diff --git a/common/cmd_runner/src/fuzzers.rs b/common/cmd_runner/src/fuzzers.rs
index 2d0ca3b..8b0f8bd 100644
--- a/common/cmd_runner/src/fuzzers.rs
+++ b/common/cmd_runner/src/fuzzers.rs
@@ -39,6 +39,8 @@
struct CargoMetadataPackageTarget {
name: String,
kind: Vec<String>,
+ #[serde(rename = "required-features", default)]
+ required_features: Vec<String>,
}
/// A `bin` target defined for fuzzing. See [`iter_fuzz_binaries`].
@@ -46,6 +48,7 @@
pub struct FuzzTarget {
pub fuzz_dir: PathBuf,
pub target_name: String,
+ pub required_features: Vec<String>,
}
impl FuzzTarget {
@@ -54,10 +57,18 @@
let FuzzTarget {
fuzz_dir,
target_name,
+ required_features,
} = self;
+ let feature_names = (!required_features.is_empty()).then(|| required_features.join(","));
+ let feature_flags = feature_names
+ .as_deref()
+ .map(|names| ["--features", names])
+ .into_iter()
+ .flatten();
+
cmd!(
sh,
- "cargo +nightly fuzz run --fuzz-dir {fuzz_dir} {target_name} -- -runs=1000000 -max_total_time=30"
+ "cargo +nightly fuzz run --fuzz-dir {fuzz_dir} {feature_flags...} {target_name} -- -runs=1000000 -max_total_time=30"
)
.run()
}
@@ -92,6 +103,7 @@
.map(move |target| FuzzTarget {
fuzz_dir: fuzz_dir.clone(),
target_name: target.name.to_string(),
+ required_features: target.required_features,
})
}))
}
diff --git a/common/cooperative_cleaner/.gitattributes b/common/cooperative_cleaner/.gitattributes
new file mode 100644
index 0000000..097f9f9
--- /dev/null
+++ b/common/cooperative_cleaner/.gitattributes
@@ -0,0 +1,9 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# Linux start script should use lf
+/gradlew text eol=lf
+
+# These are Windows script files and should use crlf
+*.bat text eol=crlf
+
diff --git a/common/cooperative_cleaner/.gitignore b/common/cooperative_cleaner/.gitignore
new file mode 100644
index 0000000..ef4f262
--- /dev/null
+++ b/common/cooperative_cleaner/.gitignore
@@ -0,0 +1,11 @@
+# Ignore Gradle project-specific cache directory
+.gradle
+
+# Ignore Gradle build output directory
+build
+
+# Ignore IDEA dir
+/.idea
+
+# Ignore vscode-gradle build output directory
+/lib/bin
\ No newline at end of file
diff --git a/common/cooperative_cleaner/gradle/wrapper/gradle-wrapper.jar b/common/cooperative_cleaner/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..2c35211
--- /dev/null
+++ b/common/cooperative_cleaner/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/common/cooperative_cleaner/gradle/wrapper/gradle-wrapper.properties b/common/cooperative_cleaner/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..09523c0
--- /dev/null
+++ b/common/cooperative_cleaner/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/common/cooperative_cleaner/gradlew b/common/cooperative_cleaner/gradlew
new file mode 100755
index 0000000..f5feea6
--- /dev/null
+++ b/common/cooperative_cleaner/gradlew
@@ -0,0 +1,252 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/common/cooperative_cleaner/gradlew.bat b/common/cooperative_cleaner/gradlew.bat
new file mode 100644
index 0000000..9d21a21
--- /dev/null
+++ b/common/cooperative_cleaner/gradlew.bat
@@ -0,0 +1,94 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/common/cooperative_cleaner/lib/build.gradle.kts b/common/cooperative_cleaner/lib/build.gradle.kts
new file mode 100644
index 0000000..dba0308
--- /dev/null
+++ b/common/cooperative_cleaner/lib/build.gradle.kts
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+import net.ltgt.gradle.errorprone.errorprone;
+
+plugins {
+ `java-library`
+ id("net.ltgt.errorprone") version "4.0.0"
+}
+
+java {
+ // Gradle JUnit test finder doesn't support Java 21 class files.
+ sourceCompatibility = JavaVersion.VERSION_1_9
+ targetCompatibility = JavaVersion.VERSION_1_9
+}
+
+repositories {
+ mavenCentral()
+ google()
+}
+
+dependencies {
+ compileOnly("androidx.annotation:annotation:1.6.0")
+ implementation("com.google.errorprone:error_prone_core:2.28.0")
+
+ testImplementation("junit:junit:4.13")
+ testImplementation("com.google.truth:truth:1.1.4")
+ testImplementation("org.mockito:mockito-core:5.+")
+}
+
+tasks.withType<JavaCompile>().configureEach {
+ options.errorprone {
+ error("CheckReturnValue")
+ error("UnnecessaryStaticImport")
+ error("WildcardImport")
+ error("RemoveUnusedImports")
+ error("ReturnMissingNullable")
+ error("FieldMissingNullable")
+ error("AnnotationPosition")
+ error("CheckedExceptionNotThrown")
+ error("NonFinalStaticField")
+ error("InvalidLink")
+ }
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/CooperativeCleaner.java b/common/cooperative_cleaner/lib/src/main/java/com/google/android/cooperativecleaner/CooperativeCleaner.java
similarity index 95%
rename from nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/CooperativeCleaner.java
rename to common/cooperative_cleaner/lib/src/main/java/com/google/android/cooperativecleaner/CooperativeCleaner.java
index e420f71..2cbbaea 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/CooperativeCleaner.java
+++ b/common/cooperative_cleaner/lib/src/main/java/com/google/android/cooperativecleaner/CooperativeCleaner.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.android.nearby.presence.rust;
+package com.google.android.cooperativecleaner;
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
@@ -29,11 +29,11 @@
* A similar class to {@link java.lang.ref.Cleaner} that doesn't require a dedicated thread.
*
* <p>To avoid using a thread for the reference queue, clients are expected to call {@link
- * Registration#close()} on the object returned from {@link register(Object, Runnable)}. This will
+ * Registration#close()} on the object returned from {@link #register(Object, Runnable)}. This will
* perform two actions:
*
* <ol>
- * <li>Run the cleanup actions for any registered objects that have been finaized without {@code
+ * <li>Run the cleanup actions for any registered objects that have been finalized without {@code
* close()} being called.
* <li>Run the cleanup action for the object being closed.
* </ol>
@@ -88,7 +88,7 @@
*
* <p>The given {@code cleanupAction} <b>SHOULD NOT</b> hold a reference to {@code object}.
* Holding such a reference would mean that {@code object} is always reachable and it will never
- * be garbaged collected.
+ * be garbage collected.
*/
public Registration register(@UnknownInitialization Object object, Runnable cleanupAction) {
// The cast here is safe because it is impossible to get object back out of this reference.
@@ -156,7 +156,7 @@
this.clear();
// This object is no longer needed, so we can remove our reference to it so that it may be
- // garbaged collected.
+ // garbage collected.
refs.remove(this);
// Make sure that cleanupAction is only run once by taking the value before running it
diff --git a/common/cooperative_cleaner/lib/src/test/java/com/google/android/cooperativecleaner/CooperativeCleanerTest.java b/common/cooperative_cleaner/lib/src/test/java/com/google/android/cooperativecleaner/CooperativeCleanerTest.java
new file mode 100644
index 0000000..4e2a3e8
--- /dev/null
+++ b/common/cooperative_cleaner/lib/src/test/java/com/google/android/cooperativecleaner/CooperativeCleanerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.cooperativecleaner;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
+
+import com.google.android.cooperativecleaner.CooperativeCleaner.Registration;
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+
+@RunWith(JUnit4.class)
+public class CooperativeCleanerTest {
+
+ @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+ @Mock Runnable cleanupAction;
+
+ @Test
+ public void closeRegistration_callsCleanupAction() throws Exception {
+ Object object = new Object();
+ CooperativeCleaner cooperativeCleaner = new CooperativeCleaner();
+ Registration registration = cooperativeCleaner.register(object, cleanupAction);
+ registration.close();
+
+ verify(cleanupAction).run();
+ }
+
+ @Test
+ public void gc_callsCleanupAction() throws Exception {
+ CooperativeCleaner cooperativeCleaner = new CooperativeCleaner();
+ waitForGc(createCleanerRegisteredObject(cooperativeCleaner));
+
+ cooperativeCleaner.processQueuedObjects();
+ verify(cleanupAction).run();
+ }
+
+ private Object createCleanerRegisteredObject(CooperativeCleaner cooperativeCleaner) {
+ Object object = new Object();
+ Registration unused = cooperativeCleaner.register(object, cleanupAction);
+ return object;
+ }
+
+ private void waitForGc(Object object) throws InterruptedException {
+ ReferenceQueue<Object> queue = new ReferenceQueue<>();
+ // Need to keep this around to be notified when object is GC'd
+ PhantomReference<Object> ref = new PhantomReference<>(object, queue);
+
+ object = null;
+ System.gc();
+
+ assertThat(queue.remove(5000)).isSameInstanceAs(ref);
+ }
+}
diff --git a/common/cooperative_cleaner/settings.gradle.kts b/common/cooperative_cleaner/settings.gradle.kts
new file mode 100644
index 0000000..0fd84df
--- /dev/null
+++ b/common/cooperative_cleaner/settings.gradle.kts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+rootProject.name = "cooperative_cleaner"
+include("lib")
diff --git a/common/deny.toml b/common/deny.toml
index ff83d5e..fefbcaf 100644
--- a/common/deny.toml
+++ b/common/deny.toml
@@ -43,6 +43,8 @@
ignore = [
# comment explaining why we have to ignore it
# "RUSTSEC-FOO",
+ # Rust protobuf team is still using paste.
+ "RUSTSEC-2024-0436",
]
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
# lower than the range specified will be ignored. Note that ignored advisories
@@ -72,7 +74,7 @@
"MIT",
"Apache-2.0",
"BSD-3-Clause",
- "Unicode-DFS-2016",
+ "Unicode-3.0",
"ISC",
]
# The confidence threshold for detecting a license from license text.
@@ -188,4 +190,4 @@
# if not specified. If it is specified but empty, no registries are allowed.
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
# List of URLs for allowed Git repositories
-allow-git = []
\ No newline at end of file
+allow-git = []
diff --git a/common/handle_map/src/declare_handle_map.rs b/common/handle_map/src/declare_handle_map.rs
index 8710275..d4ef977 100644
--- a/common/handle_map/src/declare_handle_map.rs
+++ b/common/handle_map/src/declare_handle_map.rs
@@ -17,29 +17,23 @@
#[macro_export]
/// ```ignore
/// declare_handle_map! (
-/// $handle_module_name,
/// $map_dimension_provider,
/// $handle_type_name,
/// $wrapped_type,
/// )
/// ```
///
-/// Declares a new public module with name `handle_module_name` which implements handle functionality
-/// for the given struct `handle_type_name` which is `#[repr(C)]` and represents FFI-accessible
-/// handles to values of type `wrapped_type`. `handle_type_name` expects a struct with a single u64
-/// field `handle_id`.
+/// Implements handle functionality for the given struct `handle_type_name` which is `#[repr(C)]`
+/// and represents FFI-accessible handles to values of type `wrapped_type`. `handle_type_name`
+/// expects a struct with a single u64 field `handle_id`.
///
-/// Internal to the generated module, a new static `SingletonHandleMap` is created, where the
-/// maximum number of active handles and the number of shards are given by
-/// the dimensions returned by evaluation of the `map_dimension_provider` expression.
-///
-/// Note: `map_dimension_provider` will be evaluated within the defined module's scope,
-/// so you will likely need to use `super` to refer to definitions in the enclosing scope.
+/// Internal to the generated impl, a new static `SingletonHandleMap` is created, where the
+/// maximum number of active handles and the number of shards are given by the dimensions returned
+/// by evaluation of the `map_dimension_provider` expression.
///
/// # Example
-/// The following code defines an FFI-safe type `StringHandle` which references
-/// the `String` data-type, and uses it to define a (contrived)
-/// function `sample` which will print "Hello World".
+/// The following code defines an FFI-safe type `StringHandle` which references the `String`
+/// data-type, and uses it to define a (contrived) function `sample` which will print "Hello World".
///
/// ```
/// use core::ops::Deref;
@@ -57,9 +51,8 @@
/// }
///
/// declare_handle_map!(
-/// string_handle,
-/// super::get_string_handle_map_dimensions(),
-/// super::StringHandle,
+/// get_string_handle_map_dimensions(),
+/// StringHandle,
/// String
/// );
///
@@ -90,76 +83,76 @@
/// ```
macro_rules! declare_handle_map {
(
- $handle_module_name:ident,
$map_dimension_provider:expr,
$handle_type_name:ty,
$wrapped_type:ty
) => {
#[doc = ::core::concat!(
- "Macro-generated (via `handle_map::declare_handle_map!`) module which",
- " defines the `", ::core::stringify!($handle_module_name), "::",
- ::core::stringify!($handle_type_name), "` FFI-transmissible handle type ",
- " which references values of type `", ::core::stringify!($wrapped_type), "`."
- )]
- pub mod $handle_module_name {
- $crate::reexport::lazy_static::lazy_static! {
- static ref GLOBAL_HANDLE_MAP: $crate::HandleMap<$wrapped_type> =
- $crate::HandleMap::with_dimensions($map_dimension_provider);
- }
-
- pub (crate) fn get_current_allocation_count() -> u32 {
- GLOBAL_HANDLE_MAP.get_current_allocation_count()
- }
-
+ "A `#[repr(C)]` handle to a value of type `",
+ ::core::stringify!($wrapped_type), "`."
+ )]
+ impl $handle_type_name {
#[doc = ::core::concat!(
- "A `#[repr(C)]` handle to a value of type `",
- ::core::stringify!($wrapped_type), "`."
- )]
-
- impl $handle_type_name {
- /// Cast the given raw Handle to this HandleLike
- pub fn from_handle(handle: $crate::Handle) -> Self {
- Self { handle_id: handle.get_id() }
+ "Macro-generated (via `handle_map::declare_handle_map!`) module which",
+ " defines the `", ::core::stringify!($handle_module_name), "::",
+ ::core::stringify!($handle_type_name), "` FFI-transmissible handle type ",
+ " which references values of type `", ::core::stringify!($wrapped_type), "`."
+ )]
+ fn global_handle_map() -> &'static $crate::HandleMap<$wrapped_type> {
+ use ::core::ops::Deref;
+ $crate::reexport::lazy_static::lazy_static! {
+ static ref GLOBAL_HANDLE_MAP: $crate::HandleMap<$wrapped_type> =
+ $crate::HandleMap::with_dimensions($map_dimension_provider);
}
-
- /// Get this HandleLike as a raw Handle.
- pub fn get_as_handle(&self) -> $crate::Handle {
- $crate::Handle::from_id(self.handle_id)
- }
+ GLOBAL_HANDLE_MAP.deref()
}
- impl $crate::HandleLike for $handle_type_name {
- type Object = $wrapped_type;
- fn try_allocate<E: core::fmt::Debug>(
- initial_value_provider: impl FnOnce() -> Result<$wrapped_type, E>,
- ) -> Result<Self, $crate::HandleMapTryAllocateError<E>> {
- GLOBAL_HANDLE_MAP
- .try_allocate(initial_value_provider)
- .map(|derived_handle| Self { handle_id: derived_handle.get_id() })
- }
- fn allocate(
- initial_value_provider: impl FnOnce() -> $wrapped_type,
- ) -> Result<Self, $crate::HandleMapFullError> {
- GLOBAL_HANDLE_MAP
- .allocate(initial_value_provider)
- .map(|derived_handle| Self { handle_id: derived_handle.get_id() })
- }
- fn get(
- &self,
- ) -> Result<$crate::ObjectReadGuardImpl<$wrapped_type>, $crate::HandleNotPresentError>
- {
- GLOBAL_HANDLE_MAP.get(self.get_as_handle())
- }
- fn get_mut(
- &self,
- ) -> Result<
- $crate::ObjectReadWriteGuardImpl<$wrapped_type>,
- $crate::HandleNotPresentError,
- > {
- GLOBAL_HANDLE_MAP.get_mut(self.get_as_handle())
- }
- fn deallocate(self) -> Result<$wrapped_type, $crate::HandleNotPresentError> {
- GLOBAL_HANDLE_MAP.deallocate(self.get_as_handle())
- }
+
+ pub(crate) fn get_current_allocation_count() -> u32 {
+ Self::global_handle_map().get_current_allocation_count()
+ }
+
+ /// Cast the given raw Handle to this HandleLike
+ pub fn from_handle(handle: $crate::Handle) -> Self {
+ Self { handle_id: handle.get_id() }
+ }
+
+ /// Get this HandleLike as a raw Handle.
+ pub fn get_as_handle(&self) -> $crate::Handle {
+ $crate::Handle::from_id(self.handle_id)
+ }
+ }
+ impl $crate::HandleLike for $handle_type_name {
+ type Object = $wrapped_type;
+ fn try_allocate<E: core::fmt::Debug>(
+ initial_value_provider: impl FnOnce() -> Result<$wrapped_type, E>,
+ ) -> Result<Self, $crate::HandleMapTryAllocateError<E>> {
+ Self::global_handle_map()
+ .try_allocate(initial_value_provider)
+ .map(|derived_handle| Self { handle_id: derived_handle.get_id() })
+ }
+ fn allocate(
+ initial_value_provider: impl FnOnce() -> $wrapped_type,
+ ) -> Result<Self, $crate::HandleMapFullError> {
+ Self::global_handle_map()
+ .allocate(initial_value_provider)
+ .map(|derived_handle| Self { handle_id: derived_handle.get_id() })
+ }
+ fn get(
+ &self,
+ ) -> Result<impl ::core::ops::Deref<Target = Self::Object>, $crate::HandleNotPresentError>
+ {
+ Self::global_handle_map().get(self.get_as_handle())
+ }
+ fn get_mut(
+ &self,
+ ) -> Result<
+ impl ::core::ops::DerefMut<Target = Self::Object>,
+ $crate::HandleNotPresentError,
+ > {
+ Self::global_handle_map().get_mut(self.get_as_handle())
+ }
+ fn deallocate(self) -> Result<$wrapped_type, $crate::HandleNotPresentError> {
+ Self::global_handle_map().deallocate(self.get_as_handle())
}
}
};
diff --git a/common/handle_map/src/guard.rs b/common/handle_map/src/guard.rs
index 67d9787..486cfd5 100644
--- a/common/handle_map/src/guard.rs
+++ b/common/handle_map/src/guard.rs
@@ -12,132 +12,87 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::shard::{ShardMap, ShardValue};
use crate::Handle;
-use core::ops::{Deref, DerefMut};
-use lock_adapter::stdlib::RwMapping;
-use std::collections::HashMap;
+
+use lock_adapter::mapped_guard::RwMapping;
use std::marker::PhantomData;
-/// A RAII read lock guard for an object in a [`HandleMap`](crate::HandleMap)
-/// pointed-to by a given [`Handle`]. When this struct is
-/// dropped, the underlying read lock on the associated
-/// shard will be dropped.
-pub struct ObjectReadGuardImpl<'a, T: 'a> {
- pub(crate) guard: lock_adapter::stdlib::MappedRwLockReadGuard<
- 'a,
- <Self as ObjectReadGuard>::Arg,
- <Self as ObjectReadGuard>::Ret,
- <Self as ObjectReadGuard>::Mapping,
- >,
+/// A generic mapped read guard implementation.
+pub(crate) use lock_adapter::stdlib::MappedRwLockReadGuard as ReadGuard;
+
+/// A generic mapped read-write guard implementation.
+pub(crate) use lock_adapter::stdlib::MappedRwLockWriteGuard as ReadWriteGuard;
+
+/// The RwLock implementation to use
+pub(crate) use lock_adapter::stdlib::RwLock;
+
+/// The RwLock read guard implementation to use. Needs to be mapped to a value before being passed
+/// to clients.
+pub(crate) use lock_adapter::stdlib::RwLockReadGuard as RawReadGuard;
+
+/// The RwLock read-write guard implementation to use. Needs to be mapped to a value before being
+/// passed to clients.
+pub(crate) use lock_adapter::stdlib::RwLockWriteGuard as RawReadWriteGuard;
+
+/// An [`RwMapping`] implementation that looks up a type-erased [`ShardValue`] from a [`ShardMap`]
+/// with a given [`Handle`].
+pub struct LookupByHandle {
+ handle: Handle,
}
-/// Trait implemented for an ObjectReadGuard which defines the associated types of the guard and a
-/// mapping for how the guard is retrieved from its parent object
-pub trait ObjectReadGuard: Deref<Target = Self::Ret> {
- /// The mapping which defines how a guard is retrieved for `Self::Ret` from `Self::Arg`
- type Mapping: RwMapping<Arg = Self::Arg, Ret = Self::Ret>;
- /// The argument type input to the mapping functions
- type Arg;
- /// The Return type of the mapping functions
- type Ret;
-}
-
-impl<'a, T> Deref for ObjectReadGuardImpl<'a, T> {
- type Target = T;
-
- fn deref(&self) -> &Self::Target {
- self.guard.deref()
+impl LookupByHandle {
+ /// Create a mapping from the given [`Handle`]. This handle must exist in the guard's contained
+ /// [`ShardMap`]; if not, the mapping functions may panic.
+ pub fn new(handle: Handle) -> Self {
+ Self { handle }
}
}
-pub struct ObjectReadGuardMapping<'a, T> {
- pub(crate) handle: Handle,
- pub(crate) _marker: PhantomData<&'a T>,
-}
+impl RwMapping for LookupByHandle {
+ type Arg = ShardMap;
+ type Ret = ShardValue;
-impl<'a, T> RwMapping for ObjectReadGuardMapping<'a, T> {
- type Arg = HashMap<Handle, T>;
- type Ret = T;
-
- fn map<'b>(&self, arg: &'b Self::Arg) -> &'b Self::Ret {
- #[allow(clippy::expect_used)]
- arg.get(&self.handle).expect("We know that the entry exists, since we've locked the shard and already checked that it exists prior to handing out this new, mapped read-lock.")
- }
-
- fn map_mut<'b>(&self, arg: &'b mut Self::Arg) -> &'b mut Self::Ret {
- #[allow(clippy::expect_used)]
- arg.get_mut(&self.handle).expect("We know that the entry exists, since we've locked the shard and already checked that it exists prior to handing out this new, mapped read-lock.")
- }
-}
-
-impl<'a, T> ObjectReadGuard for ObjectReadGuardImpl<'a, T> {
- type Mapping = ObjectReadGuardMapping<'a, T>;
- type Arg = HashMap<Handle, T>;
- type Ret = T;
-}
-
-/// A RAII read-write lock guard for an object in a [`HandleMap`](crate::HandleMap)
-/// pointed-to by a given [`Handle`]. When this struct is
-/// dropped, the underlying read-write lock on the associated
-/// shard will be dropped.
-pub struct ObjectReadWriteGuardImpl<'a, T: 'a> {
- pub(crate) guard: lock_adapter::stdlib::MappedRwLockWriteGuard<
- 'a,
- <Self as ObjectReadWriteGuard>::Arg,
- <Self as ObjectReadWriteGuard>::Ret,
- <Self as ObjectReadWriteGuard>::Mapping,
- >,
-}
-
-/// Trait implemented for an object read guard which defines the associated types of the guard and a
-/// mapping for how the guard is retrieved from its parent object
-pub trait ObjectReadWriteGuard: Deref<Target = Self::Ret> + DerefMut<Target = Self::Ret> {
- /// The mapping which defines how a guard is retrieved for `Ret` from `Arg`
- type Mapping: RwMapping<Arg = Self::Arg, Ret = Self::Ret>;
- /// The argument type input to the mapping functions
- type Arg;
- /// The Return type of the mapping functions
- type Ret;
-}
-
-pub struct ObjectReadWriteGuardMapping<'a, T> {
- pub(crate) handle: Handle,
- pub(crate) _marker: PhantomData<&'a T>,
-}
-
-impl<'a, T> Deref for ObjectReadWriteGuardImpl<'a, T> {
- type Target = T;
-
- fn deref(&self) -> &Self::Target {
- self.guard.deref()
- }
-}
-
-impl<'a, T> DerefMut for ObjectReadWriteGuardImpl<'a, T> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- self.guard.deref_mut()
- }
-}
-
-impl<'a, T> ObjectReadWriteGuard for ObjectReadWriteGuardImpl<'a, T> {
- type Mapping = ObjectReadWriteGuardMapping<'a, T>;
- type Arg = HashMap<Handle, T>;
- type Ret = T;
-}
-
-impl<'a, T> RwMapping for ObjectReadWriteGuardMapping<'a, T> {
- type Arg = HashMap<Handle, T>;
- type Ret = T;
-
- fn map<'b>(&self, arg: &'b Self::Arg) -> &'b Self::Ret {
+ fn map<'a>(&self, arg: &'a Self::Arg) -> &'a Self::Ret {
#[allow(clippy::expect_used)]
arg.get(&self.handle)
- .expect("Caller must verify that provided hande exists")
+ .expect("Caller must verify that provided handle exists")
}
- fn map_mut<'b>(&self, arg: &'b mut Self::Arg) -> &'b mut Self::Ret {
+ fn map_mut<'a>(&self, arg: &'a mut Self::Arg) -> &'a mut Self::Ret {
#[allow(clippy::expect_used)]
arg.get_mut(&self.handle)
- .expect("Caller must verify that provided hande exists")
+ .expect("Caller must verify that provided handle exists")
+ }
+}
+
+/// An [`RwMapping`] implementation that performs downcast on a type-erased [`ShardValue`]
+/// instance. This is used to undo type erasure before giving a guard to a client. Type-erased
+/// values are always `'static`.
+pub struct Downcast<T> {
+ /// Keep `T` around for use in `impl RwMapping`.
+ _t: PhantomData<fn(T) -> T>,
+}
+
+impl<T: 'static> Downcast<T> {
+ pub fn new() -> Self {
+ Self { _t: PhantomData }
+ }
+}
+
+impl<T: 'static> RwMapping for Downcast<T> {
+ type Arg = ShardValue;
+ type Ret = T;
+
+ fn map<'a>(&self, arg: &'a Self::Arg) -> &'a Self::Ret {
+ #[allow(clippy::expect_used)]
+ arg.downcast_ref::<T>()
+ .expect("Caller must verify the type is correct")
+ }
+
+ fn map_mut<'a>(&self, arg: &'a mut Self::Arg) -> &'a mut Self::Ret {
+ #[allow(clippy::expect_used)]
+ arg.downcast_mut::<T>()
+ .expect("Caller must verify the type is correct")
}
}
diff --git a/common/handle_map/src/handle.rs b/common/handle_map/src/handle.rs
new file mode 100644
index 0000000..520beaf
--- /dev/null
+++ b/common/handle_map/src/handle.rs
@@ -0,0 +1,56 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+/// An individual handle to be given out by a [`HandleMap`][crate::HandleMap].
+/// This representation is untyped, and just a wrapper
+/// around a handle-id, in contrast to implementors of [`HandleLike`][crate::HandleLike].
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct Handle {
+ handle_id: u64,
+}
+
+impl From<&Handle> for Handle {
+ fn from(handle: &Handle) -> Self {
+ *handle
+ }
+}
+
+impl Handle {
+ /// Constructs a handle wrapping the given ID.
+ ///
+ /// No validity checks are done on the wrapped ID
+ /// to ensure that the given ID is active in
+ /// any specific handle-map, and the type
+ /// of the handle is not represented.
+ ///
+ /// As a result, this method is only useful for
+ /// allowing access to handles from an FFI layer.
+ pub fn from_id(handle_id: u64) -> Self {
+ Self { handle_id }
+ }
+
+ /// Gets the ID for this handle.
+ ///
+ /// Since the underlying handle is un-typed,`
+ /// this method is only suitable for
+ /// transmitting handles across an FFI layer.
+ pub fn get_id(&self) -> u64 {
+ self.handle_id
+ }
+
+ /// Derives the shard index from the handle id
+ pub(crate) fn get_shard_index(&self, num_shards: u8) -> usize {
+ (self.handle_id % (num_shards as u64)) as usize
+ }
+}
diff --git a/common/handle_map/src/lib.rs b/common/handle_map/src/lib.rs
index 43eeb19..f95feb7 100644
--- a/common/handle_map/src/lib.rs
+++ b/common/handle_map/src/lib.rs
@@ -16,263 +16,28 @@
//! a safer alternative to raw pointers for FFI interop.
use core::fmt::Debug;
-use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
+use core::ops::{Deref, DerefMut};
-pub mod declare_handle_map;
mod guard;
+mod handle;
+mod map;
+mod owned_handle;
+
pub(crate) mod shard;
-#[cfg(test)]
-mod tests;
-
-pub use guard::{ObjectReadGuardImpl, ObjectReadWriteGuardImpl};
-
-use shard::{HandleMapShard, ShardAllocationError};
+pub mod declare_handle_map;
#[doc(hidden)]
pub mod reexport {
pub use lazy_static;
}
-/// An individual handle to be given out by a [`HandleMap`].
-/// This representation is untyped, and just a wrapper
-/// around a handle-id, in contrast to implementors of `HandleLike`.
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-pub struct Handle {
- handle_id: u64,
-}
-
-impl From<&Handle> for Handle {
- fn from(handle: &Handle) -> Self {
- *handle
- }
-}
-
-impl Handle {
- /// Constructs a handle wrapping the given ID.
- ///
- /// No validity checks are done on the wrapped ID
- /// to ensure that the given ID is active in
- /// any specific handle-map, and the type
- /// of the handle is not represented.
- ///
- /// As a result, this method is only useful for
- /// allowing access to handles from an FFI layer.
- pub fn from_id(handle_id: u64) -> Self {
- Self { handle_id }
- }
-
- /// Gets the ID for this handle.
- ///
- /// Since the underlying handle is un-typed,`
- /// this method is only suitable for
- /// transmitting handles across an FFI layer.
- pub fn get_id(&self) -> u64 {
- self.handle_id
- }
-
- /// Derives the shard index from the handle id
- fn get_shard_index(&self, num_shards: u8) -> usize {
- (self.handle_id % (num_shards as u64)) as usize
- }
-}
-
-/// Error raised when attempting to allocate into a full handle-map.
-#[derive(Debug)]
-pub struct HandleMapFullError;
-
-/// Error raised when the entry for a given [`Handle`] doesn't exist.
-#[derive(Debug)]
-pub struct HandleNotPresentError;
-
-/// Errors which may be raised while attempting to allocate
-/// a handle from contents given by a (fallible) value-provider.
-#[derive(Debug)]
-pub enum HandleMapTryAllocateError<E: Debug> {
- /// The call to the value-provider for the allocation failed.
- ValueProviderFailed(E),
- /// We couldn't reserve a spot for the allocation, because
- /// the handle-map was full.
- HandleMapFull,
-}
-
-/// FFI-transmissible structure expressing the dimensions
-/// (max # of allocatable slots, number of shards) of a handle-map
-/// to be used upon initialization.
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub struct HandleMapDimensions {
- /// The number of shards which are employed
- /// by the associated handle-map.
- pub num_shards: u8,
- /// The maximum number of active handles which may be
- /// stored within the associated handle-map.
- pub max_active_handles: u32,
-}
-
-/// A thread-safe mapping from "handle"s [like pointers, but safer]
-/// to underlying structures, supporting allocations, reads, writes,
-/// and deallocations of objects behind handles.
-pub struct HandleMap<T: Send + Sync> {
- /// The dimensions of this handle-map
- dimensions: HandleMapDimensions,
-
- /// The individually-lockable "shards" of the handle-map,
- /// among which the keys will be roughly uniformly-distributed.
- handle_map_shards: Box<[HandleMapShard<T>]>,
-
- /// An atomically-incrementing counter which tracks the
- /// next handle ID which allocations will attempt to use.
- new_handle_id_counter: AtomicU64,
-
- /// An atomic integer roughly tracking the number of
- /// currently-outstanding allocated entries in this
- /// handle-map among all [`HandleMapShard`]s.
- outstanding_allocations_counter: AtomicU32,
-}
-
-impl<T: Send + Sync> HandleMap<T> {
- /// Creates a new handle-map with the given `HandleMapDimensions`.
- pub fn with_dimensions(dimensions: HandleMapDimensions) -> Self {
- let mut handle_map_shards = Vec::with_capacity(dimensions.num_shards as usize);
- for _ in 0..dimensions.num_shards {
- handle_map_shards.push(HandleMapShard::default());
- }
- let handle_map_shards = handle_map_shards.into_boxed_slice();
- Self {
- dimensions,
- handle_map_shards,
- new_handle_id_counter: AtomicU64::new(0),
- outstanding_allocations_counter: AtomicU32::new(0),
- }
- }
-}
-
-impl<T: Send + Sync> HandleMap<T> {
- /// Allocates a new object within the given handle-map, returning
- /// a handle to the location it was stored at. This operation
- /// may fail if attempting to allocate over the `dimensions.max_active_handles`
- /// limit imposed on the handle-map, in which case this method
- /// will return a `HandleMapFullError`.
- ///
- /// If you want the passed closure to be able to possibly fail, see
- /// [`Self::try_allocate`] instead.
- pub fn allocate(
- &self,
- initial_value_provider: impl FnOnce() -> T,
- ) -> Result<Handle, HandleMapFullError> {
- let wrapped_value_provider = move || Ok(initial_value_provider());
- self.try_allocate::<core::convert::Infallible>(wrapped_value_provider)
- .map_err(|e| match e {
- HandleMapTryAllocateError::ValueProviderFailed(never) => match never {},
- HandleMapTryAllocateError::HandleMapFull => HandleMapFullError,
- })
- }
-
- /// Attempts to allocate a new object within the given handle-map, returning
- /// a handle to the location it was stored at.
- ///
- /// This operation may fail if attempting to allocate over the `dimensions.max_active_handles`
- /// limit imposed on the handle-map, in which case this method will return a
- /// `HandleMapTryAllocateError::HandleMapFull` and the given `initial_value_provider` will not
- /// be run.
- ///
- /// The passed initial-value provider may also fail, in which case this will return the error
- /// wrapped in `HandleMapTryAllocateError::ValueProviderFailed`.
- ///
- /// If your initial-value provider is infallible, see [`Self::allocate`] instead.
- pub fn try_allocate<E: Debug>(
- &self,
- initial_value_provider: impl FnOnce() -> Result<T, E>,
- ) -> Result<Handle, HandleMapTryAllocateError<E>> {
- let mut initial_value_provider = initial_value_provider;
- loop {
- // Increment the new-handle-ID counter using relaxed memory ordering,
- // since the only invariant that we want to enforce is that concurrently-running
- // threads always get distinct new handle-ids.
- let new_handle_id = self.new_handle_id_counter.fetch_add(1, Ordering::Relaxed);
- let new_handle = Handle::from_id(new_handle_id);
- let shard_index = new_handle.get_shard_index(self.dimensions.num_shards);
-
- // Now, check the shard to see if we can actually allocate into it.
- #[allow(clippy::expect_used)]
- let shard_allocate_result = self
- .handle_map_shards
- .get(shard_index)
- .expect("Shard index is always within range")
- .try_allocate(
- new_handle,
- initial_value_provider,
- &self.outstanding_allocations_counter,
- self.dimensions.max_active_handles,
- );
- match shard_allocate_result {
- Ok(_) => {
- return Ok(new_handle);
- }
- Err(ShardAllocationError::ValueProviderFailed(e)) => {
- return Err(HandleMapTryAllocateError::ValueProviderFailed(e))
- }
- Err(ShardAllocationError::ExceedsAllocationLimit) => {
- return Err(HandleMapTryAllocateError::HandleMapFull);
- }
- Err(ShardAllocationError::EntryOccupied(thrown_back_provider)) => {
- // We need to do the whole thing again with a new ID
- initial_value_provider = thrown_back_provider;
- }
- }
- }
- }
-
- /// Gets a read-only reference to an object within the given handle-map,
- /// if the given handle is present. Otherwise, returns [`HandleNotPresentError`].
- pub fn get(&self, handle: Handle) -> Result<ObjectReadGuardImpl<T>, HandleNotPresentError> {
- let shard_index = handle.get_shard_index(self.dimensions.num_shards);
- #[allow(clippy::expect_used)]
- self.handle_map_shards
- .get(shard_index)
- .expect("shard index is always within range")
- .get(handle)
- }
-
- /// Gets a read+write reference to an object within the given handle-map,
- /// if the given handle is present. Otherwise, returns [`HandleNotPresentError`].
- pub fn get_mut(
- &self,
- handle: Handle,
- ) -> Result<ObjectReadWriteGuardImpl<T>, HandleNotPresentError> {
- let shard_index = handle.get_shard_index(self.dimensions.num_shards);
- #[allow(clippy::expect_used)]
- self.handle_map_shards
- .get(shard_index)
- .expect("shard_index is always in range")
- .get_mut(handle)
- }
-
- /// Removes the object pointed to by the given handle in
- /// the handle-map, returning the removed object if it
- /// exists. Otherwise, returns [`HandleNotPresentError`].
- pub fn deallocate(&self, handle: Handle) -> Result<T, HandleNotPresentError> {
- let shard_index = handle.get_shard_index(self.dimensions.num_shards);
- #[allow(clippy::expect_used)]
- self.handle_map_shards
- .get(shard_index)
- .expect("shard index is always in range")
- .deallocate(handle, &self.outstanding_allocations_counter)
- }
-
- /// Gets the actual number of elements stored in the entire map.
- pub fn get_current_allocation_count(&self) -> u32 {
- self.outstanding_allocations_counter.load(Ordering::Relaxed)
- }
-
- /// Sets the new-handle-id counter to the given value.
- /// Only suitable for tests.
- #[cfg(test)]
- pub(crate) fn set_new_handle_id_counter(&mut self, value: u64) {
- self.new_handle_id_counter = AtomicU64::new(value);
- }
-}
+pub use handle::Handle;
+pub use map::{
+ HandleMap, HandleMapDimensions, HandleMapFullError, HandleMapTryAllocateError,
+ HandleNotPresentError,
+};
+pub use owned_handle::OwnedHandle;
/// Externally-facing trait for things which behave like handle-map handles
/// with a globally-defined handle-map for the type.
@@ -295,11 +60,14 @@
) -> Result<Self, HandleMapFullError>;
/// Gets a RAII read-guard on the contents behind this handle.
- fn get(&self) -> Result<ObjectReadGuardImpl<Self::Object>, HandleNotPresentError>;
+ fn get(&self) -> Result<impl Deref<Target = Self::Object> + '_, HandleNotPresentError>;
/// Gets a RAII read-write guard on the contents behind this handle.
- fn get_mut(&self) -> Result<ObjectReadWriteGuardImpl<Self::Object>, HandleNotPresentError>;
+ fn get_mut(&self) -> Result<impl DerefMut<Target = Self::Object> + '_, HandleNotPresentError>;
/// Deallocates the contents behind this handle.
fn deallocate(self) -> Result<Self::Object, HandleNotPresentError>;
}
+
+#[cfg(test)]
+mod tests;
diff --git a/common/handle_map/src/map.rs b/common/handle_map/src/map.rs
new file mode 100644
index 0000000..e7dbaeb
--- /dev/null
+++ b/common/handle_map/src/map.rs
@@ -0,0 +1,283 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use crate::guard::{Downcast, LookupByHandle, ReadGuard, ReadWriteGuard};
+use crate::shard::{
+ BoxedAnyProvider, HandleMapShard, ShardAllocationError, ShardValue, ValueProvider,
+};
+use crate::Handle;
+
+use core::any::Any;
+use core::convert::Infallible;
+use core::fmt::Debug;
+use core::marker::PhantomData;
+use core::ops::{Deref, DerefMut};
+use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
+
+/// Error raised when attempting to allocate into a full handle-map.
+#[derive(Debug)]
+pub struct HandleMapFullError;
+
+/// Error raised when the entry for a given [`Handle`] doesn't exist.
+#[derive(Debug)]
+pub struct HandleNotPresentError;
+
+/// Errors which may be raised while attempting to allocate
+/// a handle from contents given by a (fallible) value-provider.
+#[derive(Debug)]
+pub enum HandleMapTryAllocateError<E: Debug> {
+ /// The call to the value-provider for the allocation failed.
+ ValueProviderFailed(E),
+ /// We couldn't reserve a spot for the allocation, because
+ /// the handle-map was full.
+ HandleMapFull,
+}
+
+/// FFI-transmissible structure expressing the dimensions
+/// (max # of allocatable slots, number of shards) of a handle-map
+/// to be used upon initialization.
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct HandleMapDimensions {
+ /// The number of shards which are employed
+ /// by the associated handle-map.
+ pub num_shards: u8,
+ /// The maximum number of active handles which may be
+ /// stored within the associated handle-map.
+ pub max_active_handles: u32,
+}
+
+/// A thread-safe mapping from "handle"s [like pointers, but safer]
+/// to underlying structures, supporting allocations, reads, writes,
+/// and deallocations of objects behind handles.
+pub struct HandleMap<T> {
+ /// Internal type-erased implementation. This allows most of the code to be shared for
+ /// code-size reasons.
+ erased: ErasedHandleMap,
+ /// Keep `T` around so that we can downcast values from the erased implementation.
+ _t: PhantomData<T>,
+}
+
+impl<T: Any + Send + Sync + 'static> HandleMap<T> {
+ /// Creates a new handle-map with the given `HandleMapDimensions`.
+ pub fn with_dimensions(dimensions: HandleMapDimensions) -> Self {
+ Self {
+ erased: ErasedHandleMap::with_dimensions(dimensions),
+ _t: PhantomData,
+ }
+ }
+
+ /// Allocates a new object within the given handle-map, returning
+ /// a handle to the location it was stored at. This operation
+ /// may fail if attempting to allocate over the `dimensions.max_active_handles`
+ /// limit imposed on the handle-map, in which case this method
+ /// will return a `HandleMapFullError`.
+ ///
+ /// If you want the passed closure to be able to possibly fail, see
+ /// [`Self::try_allocate`] instead.
+ pub fn allocate(
+ &self,
+ initial_value_provider: impl FnOnce() -> T,
+ ) -> Result<Handle, HandleMapFullError> {
+ let value_provider =
+ BoxedAnyProvider(move || Ok::<_, Infallible>(initial_value_provider()));
+ self.erased
+ .try_allocate_impl(value_provider)
+ .map_err(|e| match e {
+ HandleMapTryAllocateError::ValueProviderFailed(never) => match never {},
+ HandleMapTryAllocateError::HandleMapFull => HandleMapFullError,
+ })
+ }
+
+ /// Attempts to allocate a new object within the given handle-map, returning
+ /// a handle to the location it was stored at.
+ ///
+ /// This operation may fail if attempting to allocate over the `dimensions.max_active_handles`
+ /// limit imposed on the handle-map, in which case this method will return a
+ /// `HandleMapTryAllocateError::HandleMapFull` and the given `initial_value_provider` will not
+ /// be run.
+ ///
+ /// The passed initial-value provider may also fail, in which case this will return the error
+ /// wrapped in `HandleMapTryAllocateError::ValueProviderFailed`.
+ ///
+ /// If your initial-value provider is infallible, see [`Self::allocate`] instead.
+ pub fn try_allocate<E: Debug>(
+ &self,
+ initial_value_provider: impl FnOnce() -> Result<T, E>,
+ ) -> Result<Handle, HandleMapTryAllocateError<E>> {
+ let value_provider = BoxedAnyProvider(initial_value_provider);
+ self.erased.try_allocate_impl(value_provider)
+ }
+
+ /// Gets a read-only reference to an object within the given handle-map,
+ /// if the given handle is present. Otherwise, returns [`HandleNotPresentError`].
+ pub fn get(
+ &self,
+ handle: Handle,
+ ) -> Result<impl Deref<Target = T> + '_, HandleNotPresentError> {
+ self.erased
+ .get(handle)
+ .map(|read_guard| read_guard.map(Downcast::<T>::new()))
+ }
+
+ /// Gets a read+write reference to an object within the given handle-map,
+ /// if the given handle is present. Otherwise, returns [`HandleNotPresentError`].
+ pub fn get_mut(
+ &self,
+ handle: Handle,
+ ) -> Result<impl DerefMut<Target = T> + '_, HandleNotPresentError> {
+ self.erased
+ .get_mut(handle)
+ .map(|read_write_guard| read_write_guard.map(Downcast::<T>::new()))
+ }
+
+ /// Removes the object pointed to by the given handle in
+ /// the handle-map, returning the removed object if it
+ /// exists. Otherwise, returns [`HandleNotPresentError`].
+ pub fn deallocate(&self, handle: Handle) -> Result<T, HandleNotPresentError> {
+ // Values should always match T
+ #[allow(clippy::expect_used)]
+ let downcast = |boxed: ShardValue| {
+ *boxed
+ .downcast::<T>()
+ .expect("Handle map value has invalid type")
+ };
+ self.erased.deallocate(handle).map(downcast)
+ }
+
+ /// Gets the actual number of elements stored in the entire map.
+ pub fn get_current_allocation_count(&self) -> u32 {
+ self.erased.get_current_allocation_count()
+ }
+
+ /// Sets the new-handle-id counter to the given value.
+ /// Only suitable for tests.
+ #[cfg(test)]
+ pub(crate) fn set_new_handle_id_counter(&mut self, value: u64) {
+ self.erased.set_new_handle_id_counter(value)
+ }
+}
+
+/// Internal type-erased handle map implementation.
+struct ErasedHandleMap {
+ /// The dimensions of this handle-map
+ dimensions: HandleMapDimensions,
+
+ /// The individually-lockable "shards" of the handle-map,
+ /// among which the keys will be roughly uniformly-distributed.
+ handle_map_shards: Box<[HandleMapShard]>,
+
+ /// An atomically-incrementing counter which tracks the
+ /// next handle ID which allocations will attempt to use.
+ new_handle_id_counter: AtomicU64,
+
+ /// An atomic integer roughly tracking the number of
+ /// currently-outstanding allocated entries in this
+ /// handle-map among all [`HandleMapShard`]s.
+ outstanding_allocations_counter: AtomicU32,
+}
+
+impl ErasedHandleMap {
+ /// Creates a new handle-map with the given `HandleMapDimensions`.
+ fn with_dimensions(dimensions: HandleMapDimensions) -> Self {
+ let mut handle_map_shards = Vec::with_capacity(dimensions.num_shards as usize);
+ for _ in 0..dimensions.num_shards {
+ handle_map_shards.push(HandleMapShard::default());
+ }
+ let handle_map_shards = handle_map_shards.into_boxed_slice();
+ Self {
+ dimensions,
+ handle_map_shards,
+ new_handle_id_counter: AtomicU64::new(0),
+ outstanding_allocations_counter: AtomicU32::new(0),
+ }
+ }
+
+ /// Try allocate using a type erased ValueProvider.
+ fn try_allocate_impl<E: Debug>(
+ &self,
+ mut value_provider: impl ValueProvider<E>,
+ ) -> Result<Handle, HandleMapTryAllocateError<E>> {
+ loop {
+ // Increment the new-handle-ID counter using relaxed memory ordering,
+ // since the only invariant that we want to enforce is that concurrently-running
+ // threads always get distinct new handle-ids.
+ let new_handle_id = self.new_handle_id_counter.fetch_add(1, Ordering::Relaxed);
+ let new_handle = Handle::from_id(new_handle_id);
+
+ let shard_allocate_result = self.get_shard_from_handle(new_handle).try_allocate(
+ new_handle,
+ value_provider,
+ &self.outstanding_allocations_counter,
+ self.dimensions.max_active_handles,
+ );
+ match shard_allocate_result {
+ Ok(_) => {
+ return Ok(new_handle);
+ }
+ Err(ShardAllocationError::ValueProviderFailed(e)) => {
+ return Err(HandleMapTryAllocateError::ValueProviderFailed(e))
+ }
+ Err(ShardAllocationError::ExceedsAllocationLimit) => {
+ return Err(HandleMapTryAllocateError::HandleMapFull);
+ }
+ Err(ShardAllocationError::EntryOccupied(thrown_back_provider)) => {
+ // We need to do the whole thing again with a new ID
+ value_provider = thrown_back_provider;
+ }
+ }
+ }
+ }
+
+ fn get(&self, handle: Handle) -> Result<ReadGuard<LookupByHandle>, HandleNotPresentError> {
+ self.get_shard_from_handle(handle).get(handle)
+ }
+
+ fn get_mut(
+ &self,
+ handle: Handle,
+ ) -> Result<ReadWriteGuard<LookupByHandle>, HandleNotPresentError> {
+ self.get_shard_from_handle(handle).get_mut(handle)
+ }
+
+ /// Removes the object pointed to by the given handle in
+ /// the handle-map, returning the removed object if it
+ /// exists. Otherwise, returns [`HandleNotPresentError`].
+ fn deallocate(&self, handle: Handle) -> Result<ShardValue, HandleNotPresentError> {
+ self.get_shard_from_handle(handle)
+ .deallocate(handle, &self.outstanding_allocations_counter)
+ }
+
+ /// Lookup the shard containing the given handle.
+ fn get_shard_from_handle(&self, handle: Handle) -> &HandleMapShard {
+ let shard_index = handle.get_shard_index(self.dimensions.num_shards);
+ #[allow(clippy::expect_used)]
+ self.handle_map_shards
+ .get(shard_index)
+ // This should never occur due to `get_shard_index` respecting the given `num_shards`.
+ .expect("shard_index is always in range")
+ }
+
+ /// Gets the actual number of elements stored in the entire map.
+ pub fn get_current_allocation_count(&self) -> u32 {
+ self.outstanding_allocations_counter.load(Ordering::Relaxed)
+ }
+
+ /// Sets the new-handle-id counter to the given value.
+ /// Only suitable for tests.
+ #[cfg(test)]
+ pub(crate) fn set_new_handle_id_counter(&mut self, value: u64) {
+ self.new_handle_id_counter = AtomicU64::new(value);
+ }
+}
diff --git a/common/handle_map/src/owned_handle.rs b/common/handle_map/src/owned_handle.rs
new file mode 100644
index 0000000..a36bb25
--- /dev/null
+++ b/common/handle_map/src/owned_handle.rs
@@ -0,0 +1,128 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use crate::{HandleLike, HandleNotPresentError};
+use core::{
+ marker::Copy,
+ ops::{Deref, DerefMut},
+};
+
+/// An owned handle that should be deallocated when dropped.
+pub struct OwnedHandle<H: HandleLike + Copy>(H);
+
+impl<H: HandleLike + Copy> Deref for OwnedHandle<H> {
+ type Target = H;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<H: HandleLike + Copy> DerefMut for OwnedHandle<H> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl<H: HandleLike + Copy> OwnedHandle<H> {
+ /// Create an owned handle from an existing handle
+ pub fn new(handle: H) -> Self {
+ Self(handle)
+ }
+
+ /// Convert from an owned handle back to shared handle.
+ pub fn into_shared(self) -> H {
+ let skip_drop = core::mem::ManuallyDrop::new(self);
+ skip_drop.0
+ }
+
+ /// Deallocate the inner value
+ pub fn deallocate(self) -> Result<<H as HandleLike>::Object, HandleNotPresentError> {
+ self.into_shared().deallocate()
+ }
+}
+
+impl<H: HandleLike + Copy> Drop for OwnedHandle<H> {
+ fn drop(&mut self) {
+ let _ = self.0.deallocate();
+ }
+}
+
+#[cfg(test)]
+#[allow(dead_code)]
+#[allow(clippy::unwrap_used)]
+mod tests {
+ use super::*;
+ use crate::{declare_handle_map, HandleLike, HandleMapDimensions, HandleNotPresentError};
+
+ #[repr(C)]
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ struct MyHandle {
+ handle_id: u64,
+ }
+
+ struct MyValue {
+ name: String,
+ }
+
+ static DIMENSIONS: HandleMapDimensions = HandleMapDimensions {
+ num_shards: 2,
+ max_active_handles: u32::MAX - 1,
+ };
+
+ declare_handle_map!(DIMENSIONS, MyHandle, MyValue);
+
+ #[test]
+ fn owned_handle_will_free() {
+ let h = MyHandle::allocate(|| MyValue {
+ name: "Hello".to_owned(),
+ })
+ .unwrap();
+
+ {
+ let _owned = OwnedHandle::new(h);
+ }
+
+ assert!(matches!(h.get(), Err(HandleNotPresentError)));
+ }
+
+ #[test]
+ fn owned_handle_deallocate_will_free() {
+ let h = MyHandle::allocate(|| MyValue {
+ name: "Hello".to_owned(),
+ })
+ .unwrap();
+
+ let owned = OwnedHandle::new(h);
+ let _ = owned.deallocate();
+
+ assert!(matches!(h.get(), Err(HandleNotPresentError)));
+ }
+
+ #[test]
+ fn owned_handle_into_shared_will_not_free() {
+ let h = MyHandle::allocate(|| MyValue {
+ name: "Hello".to_owned(),
+ })
+ .unwrap();
+
+ let h2 = {
+ let owned = OwnedHandle::new(h);
+ owned.into_shared()
+ };
+
+ assert!(!matches!(h.get(), Err(HandleNotPresentError)));
+ assert!(!matches!(h2.get(), Err(HandleNotPresentError)));
+ }
+}
diff --git a/common/handle_map/src/shard.rs b/common/handle_map/src/shard.rs
index c7cf509..653ff4d 100644
--- a/common/handle_map/src/shard.rs
+++ b/common/handle_map/src/shard.rs
@@ -12,32 +12,55 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use core::any::Any;
use core::ops::{Deref, DerefMut};
-use lock_adapter::stdlib::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use lock_adapter::RwLock as _;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
-use std::marker::PhantomData;
use std::sync::atomic::{AtomicU32, Ordering};
use crate::guard::{
- ObjectReadGuardImpl, ObjectReadGuardMapping, ObjectReadWriteGuardImpl,
- ObjectReadWriteGuardMapping,
+ LookupByHandle, RawReadGuard, RawReadWriteGuard, ReadGuard, ReadWriteGuard, RwLock,
};
use crate::{Handle, HandleNotPresentError};
// Bunch o' type aliases to make talking about them much easier in the shard code.
-type ShardMapType<T> = HashMap<Handle, T>;
-type ShardReadWriteLock<T> = RwLock<ShardMapType<T>>;
-type ShardReadGuard<'a, T> = RwLockReadGuard<'a, ShardMapType<T>>;
-type ShardReadWriteGuard<'a, T> = RwLockWriteGuard<'a, ShardMapType<T>>;
+pub type ShardValue = Box<dyn Any + Send + Sync + 'static>;
+pub type ShardMap = HashMap<Handle, ShardValue>;
+
+type ShardReadWriteLock = RwLock<ShardMap>;
+type ShardReadGuard<'a> = RawReadGuard<'a, ShardMap>;
+type ShardReadWriteGuard<'a> = RawReadWriteGuard<'a, ShardMap>;
+
+pub(crate) trait ValueProvider<E> {
+ fn provide_value(self) -> Result<ShardValue, E>;
+}
+
+impl<E, F: FnOnce() -> Result<ShardValue, E>> ValueProvider<E> for F {
+ fn provide_value(self) -> Result<ShardValue, E> {
+ self()
+ }
+}
+
+pub(crate) struct BoxedAnyProvider<F>(pub F);
+
+impl<T: Any + Send + Sync + 'static, E, F: FnOnce() -> Result<T, E>> ValueProvider<E>
+ for BoxedAnyProvider<F>
+{
+ fn provide_value(self) -> Result<ShardValue, E> {
+ match self.0() {
+ Ok(value) => Ok(Box::new(value)),
+ Err(e) => Err(e),
+ }
+ }
+}
/// Internal error enum for failed allocations into a given shard.
-pub(crate) enum ShardAllocationError<T, E, F: FnOnce() -> Result<T, E>> {
+pub(crate) enum ShardAllocationError<E, P: ValueProvider<E>> {
/// Error for when the entry for the handle is occupied,
/// in which case we spit out the object-provider to try again
/// with a new handle-id.
- EntryOccupied(F),
+ EntryOccupied(P),
/// Error for when we would exceed the maximum number of allocations.
ExceedsAllocationLimit,
/// Error for when the initial value-provider call failed.
@@ -46,33 +69,26 @@
/// An individual handle-map shard, which is ultimately
/// just a hash-map behind a lock.
-pub(crate) struct HandleMapShard<T: Send + Sync> {
- data: RwLock<ShardMapType<T>>,
+pub(crate) struct HandleMapShard {
+ data: ShardReadWriteLock,
}
-impl<T: Send + Sync> Default for HandleMapShard<T> {
+impl Default for HandleMapShard {
fn default() -> Self {
Self {
- data: RwLock::new(HashMap::new()),
+ data: ShardReadWriteLock::new(HashMap::new()),
}
}
}
-impl<T: Send + Sync> HandleMapShard<T> {
- pub fn get(&self, handle: Handle) -> Result<ObjectReadGuardImpl<T>, HandleNotPresentError> {
- let map_read_guard = ShardReadWriteLock::<T>::read(&self.data);
+impl HandleMapShard {
+ pub fn get(&self, handle: Handle) -> Result<ReadGuard<LookupByHandle>, HandleNotPresentError> {
+ let map_read_guard = ShardReadWriteLock::read(&self.data);
let read_only_map_ref = map_read_guard.deref();
if read_only_map_ref.contains_key(&handle) {
- let object_read_guard = ShardReadGuard::<T>::map(
- map_read_guard,
- ObjectReadGuardMapping {
- handle,
- _marker: PhantomData,
- },
- );
- Ok(ObjectReadGuardImpl {
- guard: object_read_guard,
- })
+ let object_read_guard =
+ ShardReadGuard::map(map_read_guard, LookupByHandle::new(handle));
+ Ok(object_read_guard)
} else {
// Auto-drop the read guard, and return an error
Err(HandleNotPresentError)
@@ -83,7 +99,7 @@
fn get_read_write_guard_if_entry_exists(
&self,
handle: Handle,
- ) -> Result<ShardReadWriteGuard<T>, HandleNotPresentError> {
+ ) -> Result<ShardReadWriteGuard, HandleNotPresentError> {
let contains_key = {
let map_ref = self.data.read();
map_ref.contains_key(&handle)
@@ -104,26 +120,19 @@
pub fn get_mut(
&self,
handle: Handle,
- ) -> Result<ObjectReadWriteGuardImpl<T>, HandleNotPresentError> {
+ ) -> Result<ReadWriteGuard<LookupByHandle>, HandleNotPresentError> {
let map_read_write_guard = self.get_read_write_guard_if_entry_exists(handle)?;
// Expose only the pointed-to object with a mapped read-write guard
- let object_read_write_guard = ShardReadWriteGuard::<T>::map(
- map_read_write_guard,
- ObjectReadWriteGuardMapping {
- handle,
- _marker: PhantomData,
- },
- );
- Ok(ObjectReadWriteGuardImpl {
- guard: object_read_write_guard,
- })
+ let object_read_write_guard =
+ ShardReadWriteGuard::map(map_read_write_guard, LookupByHandle::new(handle));
+ Ok(object_read_write_guard)
}
pub fn deallocate(
&self,
handle: Handle,
outstanding_allocations_counter: &AtomicU32,
- ) -> Result<T, HandleNotPresentError> {
+ ) -> Result<ShardValue, HandleNotPresentError> {
let mut map_read_write_guard = self.get_read_write_guard_if_entry_exists(handle)?;
// We don't need to worry about double-decrements, since the above call
// got us an upgradable read guard for our read, which means it's the only
@@ -142,16 +151,13 @@
Ok(removed_object)
}
- pub fn try_allocate<E, F>(
+ pub fn try_allocate<E, P: ValueProvider<E>>(
&self,
handle: Handle,
- object_provider: F,
+ object_provider: P,
outstanding_allocations_counter: &AtomicU32,
max_active_handles: u32,
- ) -> Result<(), ShardAllocationError<T, E, F>>
- where
- F: FnOnce() -> Result<T, E>,
- {
+ ) -> Result<(), ShardAllocationError<E, P>> {
let mut read_write_guard = self.data.write();
match read_write_guard.entry(handle) {
Occupied(_) => {
@@ -181,7 +187,7 @@
Ok(_) => {
// We're good to actually allocate,
// so attempt to call the value-provider.
- match object_provider() {
+ match object_provider.provide_value() {
Ok(object) => {
// Successfully obtained the initial value,
// so insert it into the vacant entry.
diff --git a/common/handle_map/src/tests.rs b/common/handle_map/src/tests.rs
index 658b55d..31a7bc8 100644
--- a/common/handle_map/src/tests.rs
+++ b/common/handle_map/src/tests.rs
@@ -39,7 +39,7 @@
max_active_handles: MAX_ACTIVE_HANDLES,
};
-fn build_handle_map<T: Send + Sync>() -> HandleMap<T> {
+fn build_handle_map<T: Send + Sync + 'static>() -> HandleMap<T> {
HandleMap::with_dimensions(DEFAULT_DIMENSIONS)
}
diff --git a/common/lock_adapter/src/lib.rs b/common/lock_adapter/src/lib.rs
index 1c77ee8..59cdcb6 100644
--- a/common/lock_adapter/src/lib.rs
+++ b/common/lock_adapter/src/lib.rs
@@ -33,6 +33,9 @@
#[cfg(feature = "std")]
pub mod stdlib;
+/// Helpers for projecting derived data from RAII guard objects.
+pub mod mapped_guard;
+
/// A trait for mutex implementations that doesn't support poisoning. If the thread panicked while
/// holding the mutex, the data will be released normally (as spin::Mutex would).
pub trait NoPoisonMutex<T> {
diff --git a/common/lock_adapter/src/mapped_guard.rs b/common/lock_adapter/src/mapped_guard.rs
new file mode 100644
index 0000000..685fab2
--- /dev/null
+++ b/common/lock_adapter/src/mapped_guard.rs
@@ -0,0 +1,110 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use core::ops::{Deref, DerefMut};
+
+/// An RAII read lock guard which can point to a subfield of the protected data.
+pub struct MappedGuard<G: Deref, M: RwMapping<Arg = G::Target>> {
+ guard: G,
+ mapping: M,
+}
+
+impl<G: Deref, M: RwMapping<Arg = G::Target>> MappedGuard<G, M> {
+ /// Create a new MappedGuard from a guard and a mapping.
+ pub fn new(guard: G, mapping: M) -> Self {
+ Self { guard, mapping }
+ }
+
+ /// Un-map this guard returning the bare guard.
+ pub fn unmap(self) -> G {
+ self.guard
+ }
+
+ /// Apply another mapping to the guard. This returns an instance that applies the current
+ /// mapping and then the given mapping.
+ pub fn map<R>(
+ self,
+ m: impl RwMapping<Arg = M::Ret, Ret = R>,
+ ) -> MappedGuard<G, impl RwMapping<Arg = G::Target, Ret = R>>
+ where
+ G::Target: Sized,
+ M::Ret: 'static,
+ {
+ MappedGuard {
+ guard: self.guard,
+ mapping: compose(self.mapping, m),
+ }
+ }
+}
+
+impl<G: Deref, M: RwMapping<Arg = G::Target>> Deref for MappedGuard<G, M> {
+ type Target = M::Ret;
+
+ fn deref(&self) -> &Self::Target {
+ self.mapping.map(&*self.guard)
+ }
+}
+
+impl<G: DerefMut, M: RwMapping<Arg = G::Target>> DerefMut for MappedGuard<G, M> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.mapping.map_mut(&mut *self.guard)
+ }
+}
+
+/// Mapping functions which define how to map from one locked data type to a component of that locked data
+pub trait RwMapping {
+ /// The original locked data type
+ type Arg;
+ /// The returned mapped locked data type which is a component of the original locked data
+ type Ret;
+ /// Maps from Arg into Ret
+ fn map<'a>(&self, arg: &'a Self::Arg) -> &'a Self::Ret;
+ /// Mutably maps from Arg into Ret
+ fn map_mut<'a>(&self, arg: &'a mut Self::Arg) -> &'a mut Self::Ret;
+}
+
+/// Compose two RwMapping instances into a new RwMapping
+fn compose<T, U, R>(
+ a: impl RwMapping<Arg = T, Ret = U>,
+ b: impl RwMapping<Arg = U, Ret = R>,
+) -> impl RwMapping<Arg = T, Ret = R>
+where
+ U: 'static,
+{
+ struct Composed<A, B>(A, B);
+
+ impl<T, U, R, A, B> RwMapping for Composed<A, B>
+ where
+ A: RwMapping<Arg = T, Ret = U>,
+ B: RwMapping<Arg = U, Ret = R>,
+ U: 'static,
+ {
+ type Arg = T;
+ type Ret = R;
+
+ fn map<'a>(&self, arg: &'a Self::Arg) -> &'a Self::Ret {
+ let u = self.0.map(arg);
+ let r = self.1.map(u);
+ r
+ }
+
+ fn map_mut<'a>(&self, arg: &'a mut Self::Arg) -> &'a mut Self::Ret {
+ let u = self.0.map_mut(arg);
+ let r = self.1.map_mut(u);
+ r
+ }
+ }
+
+ Composed(a, b)
+}
diff --git a/common/lock_adapter/src/spin.rs b/common/lock_adapter/src/spin.rs
index e894cef..a8040c7 100644
--- a/common/lock_adapter/src/spin.rs
+++ b/common/lock_adapter/src/spin.rs
@@ -18,7 +18,10 @@
pub struct Mutex<T>(spin::Mutex<T>);
impl<T> NoPoisonMutex<T> for Mutex<T> {
- type MutexGuard<'a> = spin::MutexGuard<'a, T> where T: 'a;
+ type MutexGuard<'a>
+ = spin::MutexGuard<'a, T>
+ where
+ T: 'a;
fn lock(&self) -> Self::MutexGuard<'_> {
self.0.lock()
@@ -47,8 +50,14 @@
}
impl<T> crate::RwLock<T> for RwLock<T> {
- type RwLockReadGuard<'a> = spin::RwLockReadGuard<'a, T> where T: 'a;
- type RwLockWriteGuard<'a> = spin::RwLockWriteGuard<'a, T> where T: 'a;
+ type RwLockReadGuard<'a>
+ = spin::RwLockReadGuard<'a, T>
+ where
+ T: 'a;
+ type RwLockWriteGuard<'a>
+ = spin::RwLockWriteGuard<'a, T>
+ where
+ T: 'a;
fn read(&self) -> Self::RwLockReadGuard<'_> {
self.0.read()
diff --git a/common/lock_adapter/src/stdlib.rs b/common/lock_adapter/src/stdlib.rs
index c950d85..36dbfa7 100644
--- a/common/lock_adapter/src/stdlib.rs
+++ b/common/lock_adapter/src/stdlib.rs
@@ -12,14 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::mapped_guard::{MappedGuard, RwMapping};
use crate::NoPoisonMutex;
-use std::ops::{Deref, DerefMut};
+use core::ops::{Deref, DerefMut};
/// A mutual exclusion primitive useful for protecting shared data
pub struct Mutex<T>(std::sync::Mutex<T>);
impl<T> NoPoisonMutex<T> for Mutex<T> {
- type MutexGuard<'a> = std::sync::MutexGuard<'a, T> where T: 'a;
+ type MutexGuard<'a>
+ = std::sync::MutexGuard<'a, T>
+ where
+ T: 'a;
fn lock(&self) -> Self::MutexGuard<'_> {
self.0.lock().unwrap_or_else(|poison| poison.into_inner())
@@ -52,9 +56,15 @@
}
impl<T> crate::RwLock<T> for RwLock<T> {
- type RwLockReadGuard<'a> = RwLockReadGuard<'a, T> where T: 'a;
+ type RwLockReadGuard<'a>
+ = RwLockReadGuard<'a, T>
+ where
+ T: 'a;
- type RwLockWriteGuard<'a> = RwLockWriteGuard<'a, T> where T: 'a;
+ type RwLockWriteGuard<'a>
+ = RwLockWriteGuard<'a, T>
+ where
+ T: 'a;
fn read(&self) -> Self::RwLockReadGuard<'_> {
RwLockReadGuard(self.0.read().unwrap_or_else(|e| e.into_inner()))
@@ -70,15 +80,15 @@
impl<'a, T> RwLockReadGuard<'a, T> {
/// Make a new MappedRwLockReadGuard for a component of the locked data.
- pub fn map<U, M>(s: Self, mapping: M) -> MappedRwLockReadGuard<'a, T, U, M>
+ pub fn map<M>(s: Self, mapping: M) -> MappedRwLockReadGuard<'a, M>
where
- M: RwMapping<Arg = T, Ret = U>,
+ M: RwMapping<Arg = T>,
{
- MappedRwLockReadGuard { mapping, guard: s }
+ MappedRwLockReadGuard::new(s, mapping)
}
}
-impl<'a, T> Deref for RwLockReadGuard<'a, T> {
+impl<T> Deref for RwLockReadGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
@@ -86,40 +96,20 @@
}
}
-/// An RAII read lock guard returned by RwLockReadGuard::map, which can point to a subfield of the protected data.
-pub struct MappedRwLockReadGuard<'a, T, U, M>
-where
- M: RwMapping<Arg = T, Ret = U>,
-{
- mapping: M,
- guard: RwLockReadGuard<'a, T>,
-}
-
-impl<'a, T, U, M> Deref for MappedRwLockReadGuard<'a, T, U, M>
-where
- M: RwMapping<Arg = T, Ret = U>,
-{
- type Target = U;
-
- fn deref(&self) -> &Self::Target {
- self.mapping.map(&*self.guard)
- }
-}
-
/// RAII structure used to release the exclusive write access of a lock when dropped.
pub struct RwLockWriteGuard<'a, T>(std::sync::RwLockWriteGuard<'a, T>);
impl<'a, T> RwLockWriteGuard<'a, T> {
/// Make a new MappedRwLockWriteGuard for a component of the locked data.
- pub fn map<U, M>(s: Self, mapping: M) -> MappedRwLockWriteGuard<'a, T, U, M>
+ pub fn map<M>(s: Self, mapping: M) -> MappedRwLockWriteGuard<'a, M>
where
- M: RwMapping<Arg = T, Ret = U>,
+ M: RwMapping<Arg = T>,
{
- MappedRwLockWriteGuard { mapping, guard: s }
+ MappedRwLockWriteGuard::new(s, mapping)
}
}
-impl<'a, T> Deref for RwLockWriteGuard<'a, T> {
+impl<T> Deref for RwLockWriteGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
@@ -127,49 +117,15 @@
}
}
-impl<'a, T> DerefMut for RwLockWriteGuard<'a, T> {
+impl<T> DerefMut for RwLockWriteGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
}
}
+/// An RAII read lock guard returned by RwLockReadGuard::map, which can point to a subfield of the protected data.
+pub type MappedRwLockReadGuard<'a, M> = MappedGuard<RwLockReadGuard<'a, <M as RwMapping>::Arg>, M>;
+
/// An RAII read lock guard returned by RwLockWriteGuard::map, which can point to a subfield of the protected data.
-pub struct MappedRwLockWriteGuard<'a, T, U, M>
-where
- M: RwMapping<Arg = T, Ret = U>,
-{
- mapping: M,
- guard: RwLockWriteGuard<'a, T>,
-}
-
-impl<'a, P, T, M> Deref for MappedRwLockWriteGuard<'a, P, T, M>
-where
- M: RwMapping<Arg = P, Ret = T>,
-{
- type Target = T;
-
- fn deref(&self) -> &Self::Target {
- self.mapping.map(&*self.guard)
- }
-}
-
-impl<'a, P, T, M> DerefMut for MappedRwLockWriteGuard<'a, P, T, M>
-where
- M: RwMapping<Arg = P, Ret = T>,
-{
- fn deref_mut(&mut self) -> &mut Self::Target {
- self.mapping.map_mut(&mut *self.guard)
- }
-}
-
-/// Mapping functions which define how to map from one locked data type to a component of that locked data
-pub trait RwMapping {
- /// The original locked data type
- type Arg;
- /// The returned mapped locked data type which is a component of the original locked data
- type Ret;
- /// Maps from Arg into Ret
- fn map<'a>(&self, arg: &'a Self::Arg) -> &'a Self::Ret;
- /// Mutably maps from Arg into Ret
- fn map_mut<'a>(&self, arg: &'a mut Self::Arg) -> &'a mut Self::Ret;
-}
+pub type MappedRwLockWriteGuard<'a, M> =
+ MappedGuard<RwLockWriteGuard<'a, <M as RwMapping>::Arg>, M>;
diff --git a/common/pourover/src/desc.rs b/common/pourover/src/desc.rs
index 26bada6..15c479a 100644
--- a/common/pourover/src/desc.rs
+++ b/common/pourover/src/desc.rs
@@ -140,7 +140,7 @@
}
// Implement AsRef so that we can use this type as `Desc::Output` in [`ClassDesc`].
-impl<'lock> AsRef<JClass<'static>> for CachedClass<'lock> {
+impl AsRef<JClass<'static>> for CachedClass<'_> {
fn as_ref(&self) -> &JClass<'static> {
// `unwrap` is valid since we checked for `Some` in the constructor.
#[allow(clippy::expect_used)]
@@ -236,7 +236,7 @@
///
/// This returns the correct id. It is the same id obtained from the JNI. This id can be a pointer
/// in some JVM implementations. See trait [`MemberId`].
-unsafe impl<'cls, 'local, Id: MemberId> Desc<'local, Id> for &MemberDesc<'cls, Id> {
+unsafe impl<'local, Id: MemberId> Desc<'local, Id> for &MemberDesc<'_, Id> {
type Output = Id;
fn lookup(self, env: &mut JNIEnv<'local>) -> jni::errors::Result<Self::Output> {
diff --git a/common/pourover/src/exception.rs b/common/pourover/src/exception.rs
index 24e7962..e83e3df 100644
--- a/common/pourover/src/exception.rs
+++ b/common/pourover/src/exception.rs
@@ -39,7 +39,7 @@
JniError(jni::errors::Error),
}
-impl<'local> ThrowableJniError<'local> {
+impl ThrowableJniError<'_> {
/// Throws the error as a Java exception.
///
/// If the error is a `JavaThrowable` or a `JavaException`, it will be thrown on the given
@@ -138,7 +138,7 @@
fn unwrap_or_throw_with_default(self, env: &mut JNIEnv<'_>, default: impl FnOnce() -> T) -> T;
}
-impl<'local, T> ThrowableJniResultExt<T> for Result<T, ThrowableJniError<'local>> {
+impl<T> ThrowableJniResultExt<T> for Result<T, ThrowableJniError<'_>> {
fn unwrap_or_throw_with_default(self, env: &mut JNIEnv<'_>, default: impl FnOnce() -> T) -> T {
match self {
Ok(v) => v,
diff --git a/common/pourover/tests/call_method_integration.rs b/common/pourover/tests/call_method_integration.rs
index 5de2547..0e1622e 100644
--- a/common/pourover/tests/call_method_integration.rs
+++ b/common/pourover/tests/call_method_integration.rs
@@ -12,10 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#![allow(missing_docs)]
+
use jni::{objects::JObject, JavaVM};
use std::error::Error;
mod common;
+
use common::foo_class::*;
#[test]
diff --git a/common/pourover/tests/common/mod.rs b/common/pourover/tests/common/mod.rs
index 1dad720..e43fdd8 100644
--- a/common/pourover/tests/common/mod.rs
+++ b/common/pourover/tests/common/mod.rs
@@ -16,6 +16,6 @@
//! being compiled as an integration test.
// Not every item is used in every test
-#![allow(dead_code)]
+#![allow(dead_code, missing_docs)]
pub mod foo_class;
diff --git a/common/pourover/tests/desc_integration.rs b/common/pourover/tests/desc_integration.rs
index dd330c3..588ffc4 100644
--- a/common/pourover/tests/desc_integration.rs
+++ b/common/pourover/tests/desc_integration.rs
@@ -13,6 +13,7 @@
// limitations under the License.
#![allow(unsafe_code)]
+#![allow(missing_docs)]
use jni::{
descriptors::Desc,
diff --git a/common/pourover/tests/exception_integration.rs b/common/pourover/tests/exception_integration.rs
index 129d2e6..563b27d 100644
--- a/common/pourover/tests/exception_integration.rs
+++ b/common/pourover/tests/exception_integration.rs
@@ -12,7 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(unsafe_code, clippy::unwrap_used, clippy::expect_used, clippy::panic)]
+#![allow(
+ unsafe_code,
+ clippy::unwrap_used,
+ clippy::expect_used,
+ clippy::panic,
+ missing_docs
+)]
use jni::{
descriptors::Desc,
@@ -27,6 +33,7 @@
use std::error::Error;
mod common;
+
use common::foo_class::*;
#[pourover::jni_method(package = "com.example", class = "Foo")]
@@ -44,9 +51,9 @@
}
#[pourover::jni_method(
- package = "com.example",
- class = "Foo",
- panic_returns = JObject::null().into(),
+package = "com.example",
+class = "Foo",
+panic_returns = JObject::null().into(),
)]
extern "system" fn nativeReturnsObject<'local>(
_env: JNIEnv<'local>,
diff --git a/common/pourover/tests/jni_method_integration.rs b/common/pourover/tests/jni_method_integration.rs
index 87cb80d..99a095e 100644
--- a/common/pourover/tests/jni_method_integration.rs
+++ b/common/pourover/tests/jni_method_integration.rs
@@ -12,7 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(unsafe_code, clippy::unwrap_used, clippy::expect_used, clippy::panic)]
+#![allow(
+ unsafe_code,
+ clippy::unwrap_used,
+ clippy::expect_used,
+ clippy::panic,
+ missing_docs
+)]
use jni::{
descriptors::Desc,
@@ -27,15 +33,16 @@
};
mod common;
+
use common::foo_class::*;
static NATIVE_METHOD_CALLED: AtomicBool = AtomicBool::new(false);
static TEST_PANIC: AtomicBool = AtomicBool::new(false);
#[pourover::jni_method(
- package = "com.example",
- class = "Foo",
- panic_returns = -1
+package = "com.example",
+class = "Foo",
+panic_returns = - 1
)]
extern "system" fn nativeReturnsInt<'local>(
mut env: JNIEnv<'local>,
@@ -54,9 +61,9 @@
}
#[pourover::jni_method(
- package = "com.example",
- class = "Foo",
- panic_returns = JObject::null().into(),
+package = "com.example",
+class = "Foo",
+panic_returns = JObject::null().into(),
)]
extern "system" fn nativeReturnsObject<'local>(
env: JNIEnv<'local>,
diff --git a/common/pourover_macro/src/call_method/codegen.rs b/common/pourover_macro/src/call_method/codegen.rs
index bce3976..dcf0973 100644
--- a/common/pourover_macro/src/call_method/codegen.rs
+++ b/common/pourover_macro/src/call_method/codegen.rs
@@ -284,7 +284,7 @@
if let NonArray::Primitive(p) = ty {
if depth.get() == 1 {
let prim_type = prim_to_sys_type(p);
- type_name = parse_quote![::jni::objects::JPrimitiveArray<'_, #prim_type>]
+ type_name = parse_quote![&::jni::objects::JPrimitiveArray<'_, #prim_type>]
} else {
type_name = parse_quote![&::jni::objects::JObjectArray<'_>];
}
diff --git a/common/pourover_macro/src/type_parser.rs b/common/pourover_macro/src/type_parser.rs
index 9a120d2..6e879eb 100644
--- a/common/pourover_macro/src/type_parser.rs
+++ b/common/pourover_macro/src/type_parser.rs
@@ -64,7 +64,7 @@
}
}
-impl<'a> fmt::Display for JavaType<'a> {
+impl fmt::Display for JavaType<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
JavaType::Array { depth, ty } => {
@@ -84,7 +84,7 @@
}
}
-impl<'a> From<Primitive> for JavaType<'a> {
+impl From<Primitive> for JavaType<'_> {
fn from(prim: Primitive) -> Self {
JavaType::NonArray(NonArray::Primitive(prim))
}
@@ -132,7 +132,7 @@
}
}
-impl<'a> fmt::Display for NonArray<'a> {
+impl fmt::Display for NonArray<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
NonArray::Primitive(p) => write!(f, "{p}"),
@@ -141,7 +141,7 @@
}
}
-impl<'a> From<Primitive> for NonArray<'a> {
+impl From<Primitive> for NonArray<'_> {
fn from(p: Primitive) -> Self {
Self::Primitive(p)
}
diff --git a/common/rust-toolchain.toml b/common/rust-toolchain.toml
new file mode 100644
index 0000000..0193dee
--- /dev/null
+++ b/common/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "1.83.0"
diff --git a/nearby/.gitignore b/nearby/.gitignore
index 49cb45e..526b951 100644
--- a/nearby/.gitignore
+++ b/nearby/.gitignore
@@ -4,3 +4,6 @@
/auth_token.txt
.DS_Store
presence/cmake-build-debug
+build/
+project.xcworkspace/
+xcuserdata/
diff --git a/nearby/CMakeLists.txt b/nearby/CMakeLists.txt
index 240cc3c..d493688 100644
--- a/nearby/CMakeLists.txt
+++ b/nearby/CMakeLists.txt
@@ -45,7 +45,7 @@
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
- GIT_TAG v1.14.0
+ GIT_TAG v1.15.2
)
FetchContent_MakeAvailable(googletest)
enable_testing()
diff --git a/nearby/Cargo.toml b/nearby/Cargo.toml
index 11aecd8..2b1bab1 100644
--- a/nearby/Cargo.toml
+++ b/nearby/Cargo.toml
@@ -26,8 +26,8 @@
"presence/np_adv/fuzz",
"presence/np_adv_dynamic",
"presence/np_c_ffi",
- "presence/np_ed25519",
"presence/np_ffi_core",
+ "presence/np_ffi_core/fuzz",
"presence/np_hkdf",
"presence/np_java_ffi",
"presence/rand_ext",
@@ -40,9 +40,7 @@
default-members = ["build_scripts"]
# TODO: remove boringssl once we figure out a better plan for integrating the build system
-exclude = [
- "crypto/crypto_provider_boringssl",
-]
+exclude = ["crypto/crypto_provider_boringssl"]
resolver = "2"
[workspace.lints.rust]
@@ -54,7 +52,11 @@
unused_extern_crates = "deny"
unused_import_braces = "deny"
unused_results = "deny"
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)', 'cfg(blaze)'] }
+unexpected_cfgs = { level = "warn", check-cfg = [
+ 'cfg(fuzzing)',
+ 'cfg(rust_analyzer)',
+ 'cfg(blaze)',
+] }
[workspace.lints.clippy]
expect_used = "deny"
@@ -66,12 +68,21 @@
# local crates
array_ref = { path = "presence/array_ref" }
array_view = { path = "presence/array_view" }
+ble = { path = "medium/ble" }
+common = { path = "common" }
+credential_proto = { path = "identity/credential_proto" }
+identity_common = { path = "identity/common" }
+identity_manager = { path = "identity/identity_manager" }
+identity_provider = { path = "identity/identity_provider" }
+keystore = { path = "identity/keystore" }
crypto_provider = { path = "crypto/crypto_provider", default-features = false }
crypto_provider_default = { path = "crypto/crypto_provider_default", default-features = false }
crypto_provider_boringssl = { path = "crypto/crypto_provider_boringssl" }
crypto_provider_rustcrypto = { path = "crypto/crypto_provider_rustcrypto" }
crypto_provider_stubs = { path = "crypto/crypto_provider_stubs" }
crypto_provider_test = { path = "crypto/crypto_provider_test" }
+device_info = { path = "device_info" }
+nc = { path = "connections/nc" }
rand_ext = { path = "presence/rand_ext" }
test_helper = { path = "presence/test_helper" }
ukey2_connections = { path = "connections/ukey2/ukey2_connections" }
@@ -84,8 +95,8 @@
ldt_tbc = { path = "presence/ldt_tbc" }
np_adv = { path = "presence/np_adv" }
np_adv_dynamic = { path = "presence/np_adv_dynamic" }
-np_ed25519 = { path = "presence/np_ed25519" }
np_ffi_core = { path = "presence/np_ffi_core", default-features = false }
+np_ffi_core_fuzz = { path = "presence/np_ffi_core/fuzz" }
np_java_ffi = { path = "presence/np_java_ffi" }
sink = { path = "presence/sink" }
test_vector_hkdf = { path = "presence/test_vector_hkdf" }
@@ -95,6 +106,12 @@
lock_adapter = { path = "../common/lock_adapter" }
pourover = { path = "../common/pourover" }
pourover_macro = { path = "../common/pourover_macro" }
+storage = { path = "../common/storage", default-features = false }
+
+#phenotype flags
+flagging = { path = "../common/flagging" }
+ph_flag_source = { path = "../common/1p_internal/ph_flag_source", default-features = false }
+ph_client = { path = "../common/1p_internal/ph_client" }
# from crates.io
rand = { version = "0.8.5", default-features = false }
@@ -102,7 +119,7 @@
rand_pcg = "0.3.1"
sha2 = { version = "0.10.8", default-features = false }
aes = "0.8.3"
-arbitrary = "1.3.2"
+arbitrary = { version = "1.3.2", features = ["derive"] }
cbc = { version = "0.1.2", features = ["block-padding"] }
ctr = "0.9.2"
hkdf = "0.12.3"
@@ -116,54 +133,105 @@
serde_json = { version = "1.0.108", features = [
"alloc",
], default-features = false }
-base64 = "0.21.5"
+base64 = "0.22.1"
x25519-dalek = { version = "2.0.0", default-features = false }
subtle = { version = "2.5.0", default-features = false }
rand_chacha = { version = "0.3.1", default-features = false }
p256 = { version = "0.13.2", default-features = false }
sec1 = "0.7.3"
-protobuf = "=3.2.0"
-protobuf-codegen = "=3.2.0"
-reqwest = { version = "0.11.22", default-features = false, features = ["blocking", "rustls-tls"] }
+protobuf = "=3.7.2"
+protobuf-codegen = "=3.7.2"
+reqwest = { version = "0.11.22", default-features = false, features = [
+ "blocking",
+ "rustls-tls",
+] }
jni = "0.21.1"
lock_api = "0.4.11"
spin = { version = "0.9.8", features = ["once", "lock_api", "rwlock"] }
anyhow = "1.0.75"
log = "0.4.20"
-env_logger = "0.10.1"
+env_logger = "0.11.7"
+oslog = "0.2.0"
criterion = { version = "0.5.1", features = ["html_reports"] }
clap = { version = "4.5.13", features = ["derive"] }
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
hex-literal = "0.4.1"
cfg-if = "1.0.0"
blake2 = "0.10.6"
+hashbrown = "0.15"
hdrhistogram = "7.5.4"
regex = "1.10.2"
-tokio = { version = "1.35.0", features = ["full"] }
+tokio = { version = "1.35.0", features = [
+ "macros",
+ "rt",
+ "sync",
+ "test-util",
+ "time",
+] }
+tokio-stream = { version = "0.1.17" }
xts-mode = "0.5.1"
rstest = { version = "0.18.2", default-features = false }
rstest_reuse = "0.6.0"
wycheproof = "0.5.1"
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
tempfile = "3.8.1"
-thiserror = "1.0.51"
+thiserror = { version = "2.0.4", default-features = false }
tinyvec = { version = "1.6.0", features = ["rustc_1_55"] }
mlua = "0.9.2"
-strum = { version = "0.25.0", default-features = false }
-strum_macros = { version = "0.25.3", default-features = false }
+strum = { version = "0.27.1", default-features = false }
+strum_macros = { version = "0.27.1", default-features = false }
owo-colors = "3.5.0"
-rhai = { version = "1.16.3", features = ["sync"] }
-cbindgen = "0.26.0"
+rhai = { version = "1.20.0", features = ["sync"] }
+cbindgen = "0.27.0"
syn = { version = "2.0", features = ["full"] }
proc-macro2 = "1.0"
quote = "1.0"
+byteorder = "1.5.0"
itertools = { version = "0.13.0", default-features = false }
quickcheck = "1.0.3"
proptest = "1.4.0"
proptest-arbitrary-interop = { git = "https://github.com/brson/proptest-arbitrary-interop.git", branch = "incorrect-format" }
libfuzzer-sys = "0.4.7"
-derive_fuzztest = "0.1.1"
-derive_fuzztest_macro = "0.1.1"
+derive_fuzztest = "0.1.4"
+derive_fuzztest_macro = "0.1.4"
+async-trait = "0.1.83"
+directories = "5.0.1"
+uuid = { version = "1.11.0", features = ["v4"] }
+mockall = "0.13.1"
+uniffi = "0.29.3"
+async-stream = "0.3.6"
+prost = "0.14.1"
+ascii85 = "0.2.1"
+futures = "0.3.31"
+windows = { version = "0.61.1", features = [
+ "Devices_Bluetooth",
+ "Devices_Bluetooth_Advertisement",
+ "Devices_Radios",
+ "Foundation",
+ "Foundation_Collections",
+ "Storage_Streams",
+ "Win32_UI_WindowsAndMessaging",
+ "Win32_Graphics_Gdi",
+ "Win32_Foundation",
+ "Win32_System_LibraryLoader",
+ "Win32_System_RemoteDesktop",
+] }
+windows-core = "0.61.0"
+
+dispatch2 = { version = "0.3.0" }
+objc2 = { version = "0.6.1" }
+objc2-foundation = { version = "0.3.1", features = [
+ "NSArray",
+ "NSData",
+ "NSDictionary",
+ "NSEnumerator",
+ "NSError",
+ "NSObject",
+ "NSString",
+ "NSUUID",
+ "NSValue",
+] }
+objc2-core-bluetooth = { version = "0.3.1" }
[workspace.package]
version = "0.1.0"
diff --git a/nearby/README.md b/nearby/README.md
index f951a8b..eeb9ba1 100644
--- a/nearby/README.md
+++ b/nearby/README.md
@@ -25,6 +25,28 @@
cargo install --locked cargo-deny
cargo install cargo-fuzz
```
+### Protobuf
+
+On Linux, using apt or apt-get, for example:
+
+```
+apt install -y protobuf-compiler
+protoc --version # Ensure compiler version is 3+
+```
+
+On MacOS, using Homebrew:
+
+```
+brew install protobuf
+protoc --version # Ensure compiler version is 3+
+```
+
+On Windows, using Winget:
+
+```
+> winget install protobuf
+> protoc --version # Ensure compiler version is 3+
+```
### Setting up a Docker dev environment
@@ -43,7 +65,7 @@
system dependencies needed to start building and running all of the artifacts in
the codebase.
-Running the image runs `cargo run -- check-everything` to verify all of the targets can
+Running the image runs `cargo run -- run-default-checks` to verify all of the targets can
successfully build and all of the tests pass in your new container environment.
To open a bash shell from the container environment:
@@ -64,7 +86,7 @@
Check everything:
```
-cargo run -- check-everything
+cargo run -- run-default-checks
```
Build everything:
diff --git a/nearby/build_scripts/Cargo.toml b/nearby/build_scripts/Cargo.toml
index 5349e9c..72c41d0 100644
--- a/nearby/build_scripts/Cargo.toml
+++ b/nearby/build_scripts/Cargo.toml
@@ -27,4 +27,10 @@
xshell = "0.2.6"
[dev-dependencies]
+env_logger.workspace = true
+log.workspace = true
tempfile.workspace = true
+
+[[bin]]
+name = "build_scripts"
+doc = false
diff --git a/nearby/build_scripts/README.md b/nearby/build_scripts/README.md
new file mode 100644
index 0000000..af74db4
--- /dev/null
+++ b/nearby/build_scripts/README.md
@@ -0,0 +1,26 @@
+# build\_scripts User Guide
+
+This project contains the scripts that are required to build, lint, test, and run projects in this repository. The scripts are also used by the CI workflows and define what gets run under CI.
+
+Generally, the scripts are run with the following command template: `cargo run -p build_scripts -- $build_args...`. The `$build_args` are the arguments passed to the `build_scripts` binary.
+
+The list of supported actions can be seen using: `cargo run -p build_scripts -- --help`
+
+## Modifying build\_scripts
+
+### Adding top-level actions
+
+The top level of the tool is defined using [proc-macros][clap_derive] from the [`clap`][clap] crate. Our top level actions are defined in `enum SubCommand` in `main.rs`.
+
+1. Create a function like `fn my_build_action(root: &Path)` that performs the action. The `root` argument will be the path to the root directory of the project.
+1. Add an entry to `SubCommand` in `main.rs`. The doc comment will be the `--help` info. See the [clap documentation][clap_derive] on how to define additional arguments or flags.
+1. Create a handler in `fn main()` that matches the new subcommand and calls the function defined in step 1.
+1. Add any additional cleanup required by the action to `fn clean_everything(root: &Path)` in `main.rs`.
+1. (*optional*) Consider adding the action to CI by adding a call to the function from `fn verify_ci` in `main.rs`.
+
+[clap]: https://docs.rs/clap
+[clap_derive]: https://docs.rs/clap/latest/clap/_derive/index.html
+
+## Motivation
+
+Cargo is not a suitable tool for managing our various build configurations nor is it meant to build non-Rust libraries. Since our repository contains binaries requiring various feature-sets and libraries in many non-Rust languages, we needed a common place to define how the project is built. This package is that common place. Additionally, by writing this in Rust we can run these scripts on multiple platforms without the need for additional external dependencies.
diff --git a/nearby/build_scripts/src/crypto_ffi.rs b/nearby/build_scripts/src/crypto_ffi.rs
index b1e08e9..6ef5350 100644
--- a/nearby/build_scripts/src/crypto_ffi.rs
+++ b/nearby/build_scripts/src/crypto_ffi.rs
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use anyhow::anyhow;
-use cmd_runner::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
-use semver::{Version, VersionReq};
use std::{env, fs, path::Path};
-use crate::CargoOptions;
+use anyhow::anyhow;
+use semver::{Version, VersionReq};
+
+use cmd_runner::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
+
+use crate::{CargoOptions, ToolChain};
pub fn build_boringssl(root: &Path) -> anyhow::Result<()> {
let bindgen_version_req = VersionReq::parse(">=0.69.4")?;
@@ -72,13 +74,14 @@
run_cmd_shell(&bssl_dir, format!("cargo check {locked_arg}"))?;
run_cmd_shell(&bssl_dir, "cargo fmt --check")?;
run_cmd_shell(&bssl_dir, "cargo clippy --all-targets")?;
- run_cmd_shell(&bssl_dir, cargo_options.test("check_boringssl", ""))?;
+ run_cmd_shell(&bssl_dir, cargo_options.test("check_boringssl", "", None))?;
run_cmd_shell(&bssl_dir, "cargo doc --no-deps")?;
run_cmd_shell(
root,
cargo_options.test(
"check_boringssl_ukey2",
"-p ukey2_connections -p ukey2_rs --no-default-features --features test_boringssl",
+ None,
),
)?;
Ok(())
@@ -90,8 +93,11 @@
build_boringssl_at_latest(root)?;
let bssl_dir = root.join("crypto/crypto_provider_boringssl");
- run_cmd_shell(&bssl_dir, "cargo check")?;
- run_cmd_shell(&bssl_dir, cargo_options.test("check_boringssl_latest", ""))?;
+ run_cmd_shell(&bssl_dir, "cargo +nightly check")?;
+ run_cmd_shell(
+ &bssl_dir,
+ cargo_options.test("check_boringssl_latest", "", Some(ToolChain::Nightly)),
+ )?;
Ok(())
}
@@ -105,7 +111,7 @@
)?;
run_cmd_shell(
&root.parent().unwrap().join("third_party/boringssl"),
- "git checkout origin/master",
+ "git checkout origin/main",
)?;
build_boringssl(root)?;
Ok(())
diff --git a/nearby/build_scripts/src/ffi.rs b/nearby/build_scripts/src/ffi.rs
index 3427747..4bc2513 100644
--- a/nearby/build_scripts/src/ffi.rs
+++ b/nearby/build_scripts/src/ffi.rs
@@ -119,3 +119,8 @@
run_cmd_shell_with_color::<YellowStderr>(&test_dir, "ctest")?;
Ok(())
}
+
+pub(crate) fn generate_test_data(root: &path::Path) -> anyhow::Result<()> {
+ run_cmd_shell(root, "cargo run -p np_adv -Falloc,devtools --example generate_test_data")?;
+ Ok(())
+}
diff --git a/nearby/build_scripts/src/jni.rs b/nearby/build_scripts/src/jni.rs
index ddea7d2..75cf1ff 100644
--- a/nearby/build_scripts/src/jni.rs
+++ b/nearby/build_scripts/src/jni.rs
@@ -30,11 +30,9 @@
}
pub fn run_np_java_ffi_tests(root: &path::Path) -> anyhow::Result<()> {
- run_cmd_shell(
- root,
- "cargo build -p np_java_ffi -F crypto_provider_default/rustcrypto -F testing",
- )?;
+ run_cmd_shell(root, "cargo build -p np_java_ffi -F np_ffi_core/rustcrypto -F testing")?;
let np_java_path = root.to_path_buf().join("presence/np_java_ffi");
run_cmd_shell(&np_java_path, "./gradlew :test --info --rerun")?;
+ run_cmd_shell(&np_java_path, "./gradlew :proguardedTest --info --rerun")?;
Ok(())
}
diff --git a/nearby/build_scripts/src/license.rs b/nearby/build_scripts/src/license.rs
index 2229d04..a566cb3 100644
--- a/nearby/build_scripts/src/license.rs
+++ b/nearby/build_scripts/src/license.rs
@@ -57,6 +57,12 @@
"**/MODULE.bazel",
"**/WORKSPACE",
"**/.bazelrc",
+ "**/*.lib",
+ "**/*.dll",
+ "**/*.a",
+ "**/*.so",
+ "**/*.dylib",
+ "**/MANIFEST.MF",
],
};
diff --git a/nearby/build_scripts/src/main.rs b/nearby/build_scripts/src/main.rs
index 63ab0e9..f81cc6c 100644
--- a/nearby/build_scripts/src/main.rs
+++ b/nearby/build_scripts/src/main.rs
@@ -14,13 +14,15 @@
extern crate core;
+use std::{env, ffi::OsString, path};
+
use anyhow::anyhow;
use clap::Parser as _;
-use cmd_runner::{license_checker::LicenseSubcommand, run_cmd, run_cmd_shell, YellowStderr};
use env_logger::Env;
-use license::LICENSE_CHECKER;
use log::info;
-use std::{env, ffi::OsString, path};
+
+use cmd_runner::{license_checker::LicenseSubcommand, run_cmd, run_cmd_shell, YellowStderr};
+use license::LICENSE_CHECKER;
mod crypto_ffi;
mod ffi;
@@ -68,11 +70,14 @@
Subcommand::CheckUkey2Ffi(ref options) => ffi::check_ukey2_cmake(root_dir, options, false)?,
Subcommand::RunUkey2JniTests => jni::run_ukey2_jni_tests(root_dir)?,
Subcommand::RunNpJavaFfiTests => jni::run_np_java_ffi_tests(root_dir)?,
+ Subcommand::RunDctJavaFfiTests => jni::run_dct_java_ffi_tests(root_dir)?,
+ Subcommand::RunDctIosJavaFfiTests => jni::run_dct_ios_java_ffi_tests(root_dir)?,
Subcommand::CheckLdtCmake(ref options) => ffi::check_ldt_cmake(root_dir, options, false)?,
Subcommand::CheckNpFfiCmake(ref options) => {
ffi::check_np_ffi_cmake(root_dir, options, false)?
}
Subcommand::RunLdtKotlinTests => jni::run_ldt_kotlin_tests(root_dir)?,
+ Subcommand::GenerateTestData => ffi::generate_test_data(root_dir)?,
}
Ok(())
@@ -120,7 +125,15 @@
let root_str =
glob::Pattern::escape(root.to_str().expect("Non-unicode paths are not supported"));
let search = format!("{}/**/*.java", root_str);
- let java_files: Vec<_> = glob::glob(&search).unwrap().filter_map(Result::ok).collect();
+ let java_files: Vec<_> = glob::glob(&search)
+ .unwrap()
+ .filter_map(Result::ok)
+ .filter(|p| {
+ !p.starts_with(format!(
+ "{root_str}/connections/ukey2/ukey2_jni/java/build/jmh-generated-sources/"
+ ))
+ })
+ .collect();
for file_set in java_files.chunks(100) {
let mut args = fmt_command[1..].to_vec();
@@ -139,23 +152,42 @@
// ensure formatting is correct (Check for it first because it is fast compared to running tests)
check_format(root, &options.formatter_options)?;
+ // BEGIN-EXTERNAL
+ // let workspace_features = &[];
+ // END-EXTERNAL
+
for cargo_cmd in [
// make sure everything compiles
- "cargo check --workspace --all-targets --quiet",
+ &format!("cargo check --workspace --all-targets --quiet --features={}", workspace_features.join(",")),
+ "cargo check -p np_adv -Falloc,devtools --example generate_test_data",
// run all the tests
- &options.cargo_options.test("check_workspace", "--workspace --quiet"),
+ &options.cargo_options.test(
+ "check_workspace",
+ format!("--workspace --quiet --features={}", workspace_features.join(",")),
+ None
+ ),
// Test ukey2 builds with different crypto providers
&options.cargo_options.test(
"check_workspace_ukey2",
"-p ukey2_connections -p ukey2_rs --no-default-features --features test_rustcrypto",
+ None,
+ ),
+ &options.cargo_options.test(
+ "check_workspace_nocrypto",
+ "-p np_ffi_core -F nocrypto -F testing",
+ None,
),
// ensure the docs are valid (cross-references to other code, etc)
- concat!(
- "RUSTDOCFLAGS='--deny warnings' ",
- "cargo doc --quiet --workspace --no-deps --document-private-items ",
- "--target-dir target/dist_docs",
+ &format!(
+ "RUSTDOCFLAGS='--deny warnings' \
+ cargo doc --quiet --workspace --no-deps --document-private-items --features={} \
+ --exclude crypto_provider_test --target-dir target/dist_docs",
+ workspace_features.join(",")
),
- "cargo clippy --all-targets --workspace -- --deny warnings",
+ &format!(
+ "cargo clippy --all-targets --workspace --features={} -- --deny warnings",
+ workspace_features.join(",")
+ ),
"cargo deny --workspace check",
] {
run_cmd_shell(root, cargo_cmd)?;
@@ -172,12 +204,12 @@
check_workspace(root, check_options)?;
ffi::check_all_ffi(root, &check_options.cargo_options, false)?;
jni::run_np_java_ffi_tests(root)?;
+ jni::run_dct_java_ffi_tests(root)?;
jni::run_ldt_kotlin_tests(root)?;
jni::run_ukey2_jni_tests(root)?;
} else if cfg!(windows) {
license::LICENSE_CHECKER.check(root)?;
check_workspace(root, check_options)?;
- ffi::check_all_ffi(root, &check_options.cargo_options, false)?;
} else {
panic!("Unsupported OS for running CI!")
}
@@ -190,7 +222,7 @@
Ok(())
}
-/// Runs default checks that are suiable for verifying a local change.
+/// Runs default checks that are suitable for verifying a local change.
pub fn run_default_checks(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> {
license::LICENSE_CHECKER.check(root)?;
check_workspace(root, check_options)?;
@@ -208,6 +240,7 @@
// Test below requires Java SE 9, but on Windows we only have Java SE 8 installed
jni::run_np_java_ffi_tests(root)?;
}
+ jni::run_dct_java_ffi_tests(root)?;
jni::run_ldt_kotlin_tests(root)?;
jni::run_ukey2_jni_tests(root)?;
Ok(())
@@ -278,6 +311,12 @@
RunUkey2JniTests,
/// Checks the build of the np_java_ffi wrapper and runs tests
RunNpJavaFfiTests,
+ /// Checks the build of the dct_java_ffi wrapper and runs tests
+ RunDctJavaFfiTests,
+ /// Checks the build of the dct_java_ffi wrapper and runs tests
+ RunDctIosJavaFfiTests,
+ /// Generate FFI test data
+ GenerateTestData,
}
#[derive(clap::Args, Debug, Clone, Default)]
@@ -302,11 +341,19 @@
coverage: bool,
}
+#[derive(PartialEq, Eq)]
+pub enum ToolChain {
+ Nightly,
+}
+
impl CargoOptions {
/// Run `cargo test` or `cargo llvm-cov` depending on the configured options.
- pub fn test(&self, tag: &str, args: impl AsRef<str>) -> String {
+ pub fn test(&self, tag: &str, args: impl AsRef<str>, toolchain: Option<ToolChain>) -> String {
format!(
- "cargo {subcommand} {locked} {args} {cov_args} -- --color=always",
+ "cargo {toolchain} {subcommand} {locked} {args} {cov_args} -- --color=always",
+ toolchain = toolchain
+ .map(|t| if t == ToolChain::Nightly { "+nightly" } else { "" })
+ .unwrap_or(""),
subcommand = if self.coverage { "llvm-cov" } else { "test" },
locked = if self.locked { "--locked" } else { "" },
args = args.as_ref(),
diff --git a/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs b/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs
index 95d3b92..2475c8e 100644
--- a/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs
+++ b/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs
@@ -368,6 +368,10 @@
pub fn completed_handshake(&self) -> &CompletedHandshake {
&self.completed_handshake
}
+
+ pub fn into_completed_handshake(self) -> CompletedHandshake {
+ self.completed_handshake
+ }
}
pub struct Ukey2ClientStage1<C: CryptoProvider> {
@@ -591,6 +595,10 @@
pub fn completed_handshake(&self) -> &CompletedHandshake {
&self.completed_handshake
}
+
+ pub fn into_completed_handshake(self) -> CompletedHandshake {
+ self.completed_handshake
+ }
}
#[allow(clippy::enum_variant_names)]
diff --git a/nearby/connections/ukey2/ukey2/tests/tests.rs b/nearby/connections/ukey2/ukey2/tests/tests.rs
index d0acacf..febfe6f 100644
--- a/nearby/connections/ukey2/ukey2/tests/tests.rs
+++ b/nearby/connections/ukey2/ukey2/tests/tests.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::unwrap_used)]
+#![allow(clippy::unwrap_used, missing_docs)]
use crypto_provider_default::CryptoProviderImpl;
use rand::{rngs::StdRng, SeedableRng};
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_bindings.h b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_bindings.h
index e19aa68..87473d0 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_bindings.h
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_bindings.h
@@ -52,11 +52,16 @@
CD2DRestoreConnectionContextV1Status status;
} CD2DRestoreConnectionContextV1Result;
+typedef enum {
+ AES_CBC_HMAC_SHA256 = 0,
+ AES_GCM_256 = 1,
+} Ukey2HandshakeAlgorithm;
+
// Create a new ResponderD2DHandshakeContext
-Ukey2HandshakeContextHandle responder_new();
+Ukey2HandshakeContextHandle responder_new(Ukey2HandshakeAlgorithm algorithms[], uint32_t size);
// Create a new InitiatorD2DHandshakeContext
-Ukey2HandshakeContextHandle initiator_new();
+Ukey2HandshakeContextHandle initiator_new(Ukey2HandshakeAlgorithm algorithms[], uint32_t size);
// Utilities
void rust_dealloc_ffi_byte_array(RustFFIByteArray array);
@@ -70,6 +75,7 @@
Ukey2HandshakeContextHandle handle);
RustFFIByteArray get_verification_string(Ukey2HandshakeContextHandle handle,
size_t output_length);
+Ukey2HandshakeAlgorithm get_next_protocol(Ukey2HandshakeContextHandle handle);
// D2DConnectionContextV1 methods
RustFFIByteArray encode_message_to_peer(Ukey2ConnectionContextHandle handle,
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h
index 457286d..7f56a42 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h
@@ -14,6 +14,7 @@
#pragma once
#include <string>
+#include <vector>
#include "ukey2_bindings.h"
@@ -74,12 +75,21 @@
// a D2DConnectionContextV1.
class Ukey2Handshake {
public:
- // Creates a Ukey2Handshake instance for the responder.
+ // Creates a Ukey2Handshake instance for the responder, defaulting to the AES-CBC-HMAC_SHA256 implementation.
static Ukey2Handshake ForResponder();
- // Creates a Ukey2Handshake instance for the initiator.
+ // Creates a Ukey2Handshake instance for the responder with the specified cryptography
+ // algorithms.
+ static Ukey2Handshake ForResponder(std::vector<Ukey2HandshakeAlgorithm> algorithms);
+ // Creates a Ukey2Handshake instance for the initiator, defaulting to the AES-CBC-HMAC_SHA256 implementation.
static Ukey2Handshake ForInitiator();
+ // Creates a Ukey2Handshake instance for the initiator with the specified cryptography
+ // algorithms.
+ static Ukey2Handshake ForInitiator(std::vector<Ukey2HandshakeAlgorithm> algorithms);
// Returns true if the handshake is complete, false otherwise.
bool IsHandshakeComplete();
+ // Returns the next_protocol string in use for this handshake. Should only be called if the
+ // handshake is completed, otherwise will always return AES-CBC-HMAC_SHA256.
+ Ukey2HandshakeAlgorithm GetNextProtocol();
// Returns raw byte data with the message to send over the wire.
std::string GetNextHandshakeMessage();
// Parses the raw handshake message received over the wire.
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_glue.cc b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_glue.cc
index 354112c..326cbb8 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_glue.cc
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_glue.cc
@@ -29,11 +29,19 @@
// Implementation of functions
rust::Ukey2Handshake rust::Ukey2Handshake::ForInitiator() {
- return Ukey2Handshake(initiator_new());
+ return ForInitiator({Ukey2HandshakeAlgorithm::AES_CBC_HMAC_SHA256});
+}
+
+rust::Ukey2Handshake rust::Ukey2Handshake::ForInitiator(std::vector<Ukey2HandshakeAlgorithm> algorithms) {
+ return Ukey2Handshake(initiator_new(algorithms.data(), algorithms.size()));
}
rust::Ukey2Handshake rust::Ukey2Handshake::ForResponder() {
- return Ukey2Handshake(responder_new());
+ return ForResponder({Ukey2HandshakeAlgorithm::AES_CBC_HMAC_SHA256});
+}
+
+rust::Ukey2Handshake rust::Ukey2Handshake::ForResponder(std::vector<Ukey2HandshakeAlgorithm> algorithms) {
+ return Ukey2Handshake(responder_new(algorithms.data(), algorithms.size()));
}
bool rust::Ukey2Handshake::IsHandshakeComplete() {
@@ -76,6 +84,13 @@
return ret;
}
+Ukey2HandshakeAlgorithm rust::Ukey2Handshake::GetNextProtocol() {
+ if (IsHandshakeComplete()) {
+ return get_next_protocol(handle_);
+ }
+ return Ukey2HandshakeAlgorithm::AES_CBC_HMAC_SHA256;
+}
+
rust::D2DConnectionContextV1 rust::Ukey2Handshake::ToConnectionContext() {
assert(IsHandshakeComplete());
return D2DConnectionContextV1(to_connection_context(handle_));
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc
index 1a23278..2b9d611 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc
@@ -15,11 +15,26 @@
#include <string>
#include "gtest/gtest.h"
+#include "ukey2_bindings.h"
#include "ukey2_ffi.h"
namespace rust {
namespace {
+// Converts the FFI byte array to a string and frees the underlying memory.
+std::string ConvertFFIArrayToString(RustFFIByteArray array) {
+ std::string ret = std::string((const char*) array.handle, array.len);
+ rust_dealloc_ffi_byte_array(array);
+ return ret;
+}
+
+CFFIByteArray ConvertStringToByteArray(const std::string& str) {
+ return CFFIByteArray {
+ .handle = (uint8_t*)str.data(),
+ .len = str.length(),
+ };
+}
+
void RunHandshake(Ukey2Handshake initiator_handle,
Ukey2Handshake responder_handle) {
ParseResult parse_result = responder_handle.ParseHandshakeMessage(
@@ -31,6 +46,8 @@
parse_result = responder_handle.ParseHandshakeMessage(
initiator_handle.GetNextHandshakeMessage());
ASSERT_TRUE(parse_result.success);
+ ASSERT_TRUE(responder_handle.IsHandshakeComplete());
+ ASSERT_TRUE(initiator_handle.IsHandshakeComplete());
}
TEST(Ukey2RustTest, HandshakeStartsIncomplete) {
@@ -44,16 +61,12 @@
Ukey2Handshake responder_handle = Ukey2Handshake::ForResponder();
Ukey2Handshake initiator_handle = Ukey2Handshake::ForInitiator();
RunHandshake(initiator_handle, responder_handle);
- EXPECT_TRUE(responder_handle.IsHandshakeComplete());
- EXPECT_TRUE(initiator_handle.IsHandshakeComplete());
}
TEST(Ukey2RustTest, CanSendReceiveMessage) {
Ukey2Handshake responder_handle = Ukey2Handshake::ForResponder();
Ukey2Handshake initiator_handle = Ukey2Handshake::ForInitiator();
RunHandshake(initiator_handle, responder_handle);
- ASSERT_TRUE(responder_handle.IsHandshakeComplete());
- ASSERT_TRUE(initiator_handle.IsHandshakeComplete());
D2DConnectionContextV1 responder_connection =
responder_handle.ToConnectionContext();
D2DConnectionContextV1 initiator_connection =
@@ -70,8 +83,6 @@
Ukey2Handshake responder_handle = Ukey2Handshake::ForResponder();
Ukey2Handshake initiator_handle = Ukey2Handshake::ForInitiator();
RunHandshake(initiator_handle, responder_handle);
- ASSERT_TRUE(responder_handle.IsHandshakeComplete());
- ASSERT_TRUE(initiator_handle.IsHandshakeComplete());
D2DConnectionContextV1 responder_connection =
responder_handle.ToConnectionContext();
D2DConnectionContextV1 initiator_connection =
@@ -87,5 +98,81 @@
EXPECT_EQ("hello world", decoded);
}
+TEST(Ukey2RustTest, CanDoGcmRoundTrip) {
+ Ukey2Handshake responder_handle = Ukey2Handshake::ForResponder({Ukey2HandshakeAlgorithm::AES_GCM_256});
+ Ukey2Handshake initiator_handle = Ukey2Handshake::ForInitiator({Ukey2HandshakeAlgorithm::AES_GCM_256});
+ RunHandshake(initiator_handle, responder_handle);
+ EXPECT_EQ(responder_handle.GetNextProtocol(), Ukey2HandshakeAlgorithm::AES_GCM_256);
+ EXPECT_EQ(initiator_handle.GetNextProtocol(), Ukey2HandshakeAlgorithm::AES_GCM_256);
+ D2DConnectionContextV1 responder_connection =
+ responder_handle.ToConnectionContext();
+ D2DConnectionContextV1 initiator_connection =
+ initiator_handle.ToConnectionContext();
+ std::string message = "hello world";
+ auto encoded = responder_connection.EncodeMessageToPeer(message, "assocdata");
+ ASSERT_NE(encoded, "");
+ auto decoded =
+ initiator_connection.DecodeMessageFromPeer(encoded, "assocdata");
+ EXPECT_EQ(message, decoded);
+}
+
+TEST(Ukey2RustTest, EmptyVectorInitializesCbc) {
+ Ukey2Handshake responder_handle = Ukey2Handshake::ForResponder({});
+ Ukey2Handshake initiator_handle = Ukey2Handshake::ForInitiator({});
+ RunHandshake(initiator_handle, responder_handle);
+ EXPECT_EQ(responder_handle.GetNextProtocol(), Ukey2HandshakeAlgorithm::AES_CBC_HMAC_SHA256);
+ EXPECT_EQ(initiator_handle.GetNextProtocol(), Ukey2HandshakeAlgorithm::AES_CBC_HMAC_SHA256);
+ D2DConnectionContextV1 responder_connection =
+ responder_handle.ToConnectionContext();
+ D2DConnectionContextV1 initiator_connection =
+ initiator_handle.ToConnectionContext();
+ std::string message = "hello world";
+ auto encoded = responder_connection.EncodeMessageToPeer(message, "assocdata");
+ ASSERT_NE(encoded, "");
+ auto decoded =
+ initiator_connection.DecodeMessageFromPeer(encoded, "assocdata");
+ EXPECT_EQ(message, decoded);
+}
+
+TEST(Ukey2RustTest, RepeatedProtocolsInitializesOne) {
+ Ukey2Handshake responder_handle = Ukey2Handshake::ForResponder({Ukey2HandshakeAlgorithm::AES_GCM_256, Ukey2HandshakeAlgorithm::AES_GCM_256});
+ Ukey2Handshake initiator_handle = Ukey2Handshake::ForInitiator({Ukey2HandshakeAlgorithm::AES_GCM_256, Ukey2HandshakeAlgorithm::AES_GCM_256});
+ RunHandshake(initiator_handle, responder_handle);
+ EXPECT_EQ(responder_handle.GetNextProtocol(), Ukey2HandshakeAlgorithm::AES_GCM_256);
+ EXPECT_EQ(initiator_handle.GetNextProtocol(), Ukey2HandshakeAlgorithm::AES_GCM_256);
+ D2DConnectionContextV1 responder_connection =
+ responder_handle.ToConnectionContext();
+ D2DConnectionContextV1 initiator_connection =
+ initiator_handle.ToConnectionContext();
+ std::string message = "hello world";
+ auto encoded = responder_connection.EncodeMessageToPeer(message, "assocdata");
+ ASSERT_NE(encoded, "");
+ auto decoded =
+ initiator_connection.DecodeMessageFromPeer(encoded, "assocdata");
+ EXPECT_EQ(message, decoded);
+}
+
+TEST(Ukey2BindingsTest, NullptrInitializesCbc) {
+ uint64_t responder_handle = responder_new(/*algorithms= */nullptr, /*size= */0);
+ uint64_t initiator_handle = initiator_new(/*algorithms= */nullptr, /*size= */0);
+ // Client init generation and parse.
+ std::string client_init_msg = ConvertFFIArrayToString(get_next_handshake_message(initiator_handle));
+ CMessageParseResult result = parse_handshake_message(responder_handle, ConvertStringToByteArray(client_init_msg));
+ ASSERT_TRUE(result.success);
+ // Server init generation and parse.
+ std::string server_init_msg = ConvertFFIArrayToString(get_next_handshake_message(responder_handle));
+ result = parse_handshake_message(initiator_handle, ConvertStringToByteArray(server_init_msg));
+ ASSERT_TRUE(result.success);
+ // Client finish generation and parse.
+ std::string client_finish_msg = ConvertFFIArrayToString(get_next_handshake_message(initiator_handle));
+ result = parse_handshake_message(responder_handle, ConvertStringToByteArray(client_finish_msg));
+ ASSERT_TRUE(result.success);
+ // Confirm handshake complete.
+ EXPECT_TRUE(is_handshake_complete(responder_handle));
+ EXPECT_TRUE(is_handshake_complete(initiator_handle));
+ EXPECT_EQ(get_next_protocol(initiator_handle), Ukey2HandshakeAlgorithm::AES_CBC_HMAC_SHA256);
+ EXPECT_EQ(get_next_protocol(initiator_handle), Ukey2HandshakeAlgorithm::AES_CBC_HMAC_SHA256);
+}
+
} // namespace
} // namespace rust
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs b/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs
index 77d9b59..be24c66 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs
+++ b/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use std::ptr::null_mut;
use crypto_provider_default::CryptoProviderImpl as CryptoProvider;
@@ -158,7 +158,7 @@
.get(&handle)
.map(|h| {
let auth_vec = h
- .to_completed_handshake()
+ .completed_handshake()
.unwrap()
.auth_string::<CryptoProvider>()
.derive_vec(length)
@@ -183,25 +183,91 @@
insert_conn_gen_handle(ctx)
}
-// Responder-specific functions
+#[repr(C)]
+pub enum Ukey2HandshakeAlgorithm {
+ AesCbcHmacSha256,
+ AesGcm256,
+}
+
#[no_mangle]
-pub extern "C" fn responder_new() -> u64 {
+pub extern "C" fn get_next_protocol(handle: u64) -> Ukey2HandshakeAlgorithm {
+ let next_protocol = HANDLE_MAPPING.lock().get(&handle).map(|instance| {
+ instance
+ .completed_handshake()
+ .map_or(NextProtocol::Aes256CbcHmacSha256, |ch| ch.next_protocol)
+ });
+ match next_protocol {
+ Some(protocol) => match protocol {
+ NextProtocol::Aes256GcmSiv => Ukey2HandshakeAlgorithm::AesGcm256,
+ NextProtocol::Aes256CbcHmacSha256 => Ukey2HandshakeAlgorithm::AesCbcHmacSha256,
+ },
+ None => Ukey2HandshakeAlgorithm::AesCbcHmacSha256,
+ }
+}
+
+// Responder-specific functions
+/// Creates a new responder-side UKEY2 handshake with the specified cryptographic protocols.
+/// # Safety
+/// The C++ layer wraps the size and pointer in a vector (so the data is never mismatched),
+/// and this pointer and size data does not outlive this function, and is therefore safe to
+/// pass on the stack.
+#[no_mangle]
+pub unsafe extern "C" fn responder_new(
+ algorithms: *mut Ukey2HandshakeAlgorithm,
+ algorithms_size: u32,
+) -> u64 {
+ let algo_slice = if algorithms.is_null() || algorithms_size == 0 {
+ &[Ukey2HandshakeAlgorithm::AesCbcHmacSha256]
+ } else {
+ std::slice::from_raw_parts(algorithms, algorithms_size as usize)
+ };
let ctx = Box::new(ServerD2DHandshakeContext::<CryptoProvider>::new(
HandshakeImplementation::PublicKeyInProtobuf,
- &[NextProtocol::Aes256CbcHmacSha256],
+ &(Vec::from_iter(
+ algo_slice
+ .iter()
+ .map(|algo| match algo {
+ Ukey2HandshakeAlgorithm::AesCbcHmacSha256 => NextProtocol::Aes256CbcHmacSha256,
+ Ukey2HandshakeAlgorithm::AesGcm256 => NextProtocol::Aes256GcmSiv,
+ })
+ .collect::<HashSet<_>>()
+ .iter()
+ .cloned(),
+ )),
));
insert_gen_handle(ctx)
}
// Initiator-specific functions
+/// Creates a new initiator-side UKEY2 handshake with the specified cryptographic protocols.
/// # Safety
-/// We treat next_protocol as data, not as executable memory.
+/// The C++ layer wraps the size and pointer in a vector (so the data is never mismatched),
+/// and this pointer and size data does not outlive this function, and is therefore safe to
+/// pass on the stack.
#[no_mangle]
-pub extern "C" fn initiator_new() -> u64 {
+pub unsafe extern "C" fn initiator_new(
+ algorithms: *mut Ukey2HandshakeAlgorithm,
+ algorithms_size: u32,
+) -> u64 {
+ let algo_slice = if algorithms.is_null() || algorithms_size == 0 {
+ &[Ukey2HandshakeAlgorithm::AesCbcHmacSha256]
+ } else {
+ std::slice::from_raw_parts(algorithms, algorithms_size as usize)
+ };
let ctx = Box::new(InitiatorD2DHandshakeContext::<CryptoProvider>::new(
HandshakeImplementation::PublicKeyInProtobuf,
- vec![NextProtocol::Aes256CbcHmacSha256],
+ Vec::from_iter(
+ algo_slice
+ .iter()
+ .map(|algo| match algo {
+ Ukey2HandshakeAlgorithm::AesCbcHmacSha256 => NextProtocol::Aes256CbcHmacSha256,
+ Ukey2HandshakeAlgorithm::AesGcm256 => NextProtocol::Aes256GcmSiv,
+ })
+ .collect::<HashSet<_>>()
+ .iter()
+ .cloned(),
+ ),
));
insert_gen_handle(ctx)
}
@@ -229,7 +295,7 @@
let ret = CONNECTION_HANDLE_MAPPING
.lock()
.get_mut(&handle)
- .map(|c| c.encode_message_to_peer::<CryptoProvider, _>(msg, associated_data));
+ .map(|c| c.encode_message_to_peer::<CryptoProvider>(msg, associated_data));
if let Some(msg) = ret {
RustFFIByteArray::from_vec(msg)
} else {
@@ -259,7 +325,7 @@
.lock()
.get_mut(&handle)
.unwrap()
- .decode_message_from_peer::<CryptoProvider, _>(msg, associated_data);
+ .decode_message_from_peer::<CryptoProvider>(msg, associated_data);
if let Ok(decoded) = ret {
RustFFIByteArray::from_vec(decoded)
} else {
@@ -279,12 +345,12 @@
#[no_mangle]
pub extern "C" fn get_sequence_number_for_encoding(handle: u64) -> i32 {
- CONNECTION_HANDLE_MAPPING.lock().get(&handle).unwrap().get_sequence_number_for_encoding()
+ CONNECTION_HANDLE_MAPPING.lock().get(&handle).unwrap().encode_seq_num()
}
#[no_mangle]
pub extern "C" fn get_sequence_number_for_decoding(handle: u64) -> i32 {
- CONNECTION_HANDLE_MAPPING.lock().get(&handle).unwrap().get_sequence_number_for_decoding()
+ CONNECTION_HANDLE_MAPPING.lock().get(&handle).unwrap().decode_seq_num()
}
#[no_mangle]
diff --git a/nearby/connections/ukey2/ukey2_connections/Cargo.toml b/nearby/connections/ukey2/ukey2_connections/Cargo.toml
index ff2ee44..a1efc27 100644
--- a/nearby/connections/ukey2/ukey2_connections/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_connections/Cargo.toml
@@ -21,9 +21,10 @@
ukey2_proto.workspace = true
nom = { version = "7.1.3", features = ["alloc"] }
bytes = "1.7.1"
-criterion.workspace = true
+thiserror.workspace = true
[dev-dependencies]
+criterion.workspace = true
crypto_provider_default.workspace = true
# This would only be used when the feature "test_rustcrypto" is set, but optional dev-dependencies
# are not allowed ¯\_(ツ)_/¯
diff --git a/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs b/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
index 8c55d23..91940d8 100644
--- a/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
+++ b/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
@@ -80,10 +80,10 @@
|b| {
b.iter(|| {
let msg = initiator_ctx
- .encode_message_to_peer::<CryptoProviderImpl, &[u8]>(&plaintext, None);
+ .encode_message_to_peer::<CryptoProviderImpl>(&plaintext, None::<&[u8]>);
black_box(
server_ctx
- .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(&msg, None),
+ .decode_message_from_peer::<CryptoProviderImpl>(&msg, None::<&[u8]>),
)
})
},
@@ -108,10 +108,10 @@
|b| {
b.iter(|| {
let msg = initiator_ctx
- .encode_message_to_peer::<CryptoProviderImpl, &[u8]>(&plaintext, None);
+ .encode_message_to_peer::<CryptoProviderImpl>(&plaintext, None::<&[u8]>);
black_box(
server_ctx
- .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(&msg, None),
+ .decode_message_from_peer::<CryptoProviderImpl>(&msg, None::<&[u8]>),
)
})
},
diff --git a/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml b/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml
index 6ba66f5..890d7b5 100644
--- a/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml
@@ -20,7 +20,10 @@
libfuzzer-sys.workspace = true
[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+unexpected_cfgs = { level = "warn", check-cfg = [
+ 'cfg(fuzzing)',
+ 'cfg(rust_analyzer)',
+] }
[[bin]]
name = "fuzz_connection"
diff --git a/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_connection.rs b/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_connection.rs
index 8a02bc0..402a80d 100644
--- a/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_connection.rs
+++ b/nearby/connections/ukey2/ukey2_connections/fuzz/fuzz_targets/fuzz_connection.rs
@@ -82,35 +82,29 @@
match sender {
Type::SentByInitiator => {
let ciphertext = initiator_connection
- .encode_message_to_peer::<RustCrypto, _>(&payload, associated_data.as_ref());
+ .encode_message_to_peer::<RustCrypto>(&payload, associated_data.as_ref());
let decoded = server_connection
- .decode_message_from_peer::<RustCrypto, _>(
- &ciphertext,
- associated_data.as_ref(),
- )
+ .decode_message_from_peer::<RustCrypto>(&ciphertext, associated_data.as_ref())
.unwrap();
assert_eq!(decoded, payload);
}
Type::SentByServer => {
let ciphertext = server_connection
- .encode_message_to_peer::<RustCrypto, _>(&payload, associated_data.as_ref());
+ .encode_message_to_peer::<RustCrypto>(&payload, associated_data.as_ref());
let decoded = initiator_connection
- .decode_message_from_peer::<RustCrypto, _>(
- &ciphertext,
- associated_data.as_ref(),
- )
+ .decode_message_from_peer::<RustCrypto>(&ciphertext, associated_data.as_ref())
.unwrap();
assert_eq!(decoded, payload);
}
Type::ReceivedByInitiator => {
// Both Ok and Err results are possible here since the input is Arbitrary payload
let _unused_result = initiator_connection
- .decode_message_from_peer::<RustCrypto, _>(&payload, associated_data.as_ref());
+ .decode_message_from_peer::<RustCrypto>(&payload, associated_data.as_ref());
}
Type::ReceivedByServer => {
// Both Ok and Err results are possible here since the input is Arbitrary payload
let _unused_result = server_connection
- .decode_message_from_peer::<RustCrypto, _>(&payload, associated_data.as_ref());
+ .decode_message_from_peer::<RustCrypto>(&payload, associated_data.as_ref());
}
}
}
diff --git a/nearby/connections/ukey2/ukey2_connections/src/d2d_connection_context_v1.rs b/nearby/connections/ukey2/ukey2_connections/src/d2d_connection_context_v1.rs
index 22def10..99832f3 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/d2d_connection_context_v1.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/d2d_connection_context_v1.rs
@@ -102,26 +102,6 @@
buf
}
-/// Implementation of the UKEY2 connection protocol, also known as version 1 of the D2D protocol. In
-/// this version, communication is fully duplex, as separate keys and sequence numbers are used for
-/// encoding and decoding.
-#[derive(Debug)]
-pub struct D2DConnectionContextV1<R = rand::rngs::StdRng>
-where
- R: rand::Rng + rand::SeedableRng + rand::CryptoRng,
-{
- decode_sequence_num: i32,
- encode_sequence_num: i32,
- encode_key: Aes256Key,
- decode_key: Aes256Key,
- encryption_key: Aes256Key,
- decryption_key: Aes256Key,
- signing_key: Aes256Key,
- verify_key: Aes256Key,
- rng: R,
- protocol: NextProtocol,
-}
-
/// Error type for [`decode_message_from_peer`][D2DConnectionContextV1::decode_message_from_peer].
#[derive(Debug)]
pub enum DecodeError {
@@ -152,6 +132,16 @@
}
}
+/// Implementation of the UKEY2 connection protocol, also known as version 1 of the D2D protocol. In
+/// this version, communication is fully duplex, as separate keys and sequence numbers are used for
+/// encoding and decoding.
+#[derive(Debug)]
+pub struct D2DConnectionContextV1<R = rand::rngs::StdRng> {
+ rx_connection_context: RxConnectionContext,
+ tx_connection_context: TxConnectionContext<R>,
+ protocol: NextProtocol,
+}
+
impl D2DConnectionContextV1<rand::rngs::StdRng> {
pub fn from_saved_session<C: CryptoProvider>(session: &[u8]) -> Result<Self, DeserializeError> {
Self::from_saved_session_with_rng::<C>(session, rand::rngs::StdRng::from_entropy())
@@ -160,7 +150,7 @@
impl<R> D2DConnectionContextV1<R>
where
- R: rand::Rng + rand::SeedableRng + rand::CryptoRng,
+ R: rand::Rng + rand::CryptoRng,
{
pub fn new<C: CryptoProvider>(
decode_sequence_num: i32,
@@ -175,20 +165,26 @@
let signing_key = derive_aes256_key::<C>(&encode_key, b"SIG:1");
let verify_key = derive_aes256_key::<C>(&decode_key, b"SIG:1");
D2DConnectionContextV1 {
- decode_sequence_num,
- encode_sequence_num,
- encode_key,
- decode_key,
- encryption_key,
- decryption_key,
- signing_key,
- verify_key,
- rng,
+ rx_connection_context: RxConnectionContext {
+ decode_sequence_num,
+ decode_key,
+ decryption_key,
+ verify_key,
+ protocol,
+ },
+ tx_connection_context: TxConnectionContext {
+ encode_sequence_num,
+ encode_key,
+ encryption_key,
+ signing_key,
+ protocol,
+ rng,
+ },
protocol,
}
}
- pub(crate) fn from_initiator_handshake<C: CryptoProvider>(
+ pub fn from_initiator_handshake<C: CryptoProvider>(
handshake: &CompletedHandshake,
rng: R,
) -> Self {
@@ -204,7 +200,7 @@
)
}
- pub(crate) fn from_responder_handshake<C: CryptoProvider>(
+ pub fn from_responder_handshake<C: CryptoProvider>(
handshake: &CompletedHandshake,
rng: R,
) -> Self {
@@ -240,10 +236,10 @@
pub fn save_session(&self) -> Vec<u8> {
let mut ret: Vec<u8> = vec![];
ret.push(PROTOCOL_VERSION);
- ret.put_i32(self.encode_sequence_num);
- ret.put_i32(self.decode_sequence_num);
- ret.extend_from_slice(self.encode_key.as_slice());
- ret.extend_from_slice(self.decode_key.as_slice());
+ ret.put_i32(self.tx_connection_context.encode_sequence_num);
+ ret.put_i32(self.rx_connection_context.decode_sequence_num);
+ ret.extend_from_slice(self.tx_connection_context.encode_key.as_slice());
+ ret.extend_from_slice(self.rx_connection_context.decode_key.as_slice());
if self.protocol == NextProtocol::Aes256GcmSiv {
ret.extend_from_slice(&EncScheme::AES_256_GCM_SIV.value().to_be_bytes())
}
@@ -310,15 +306,186 @@
/// the calculation of the signature for this message. Note that the *size* (length in
/// bytes) of the associated data will be sent in the *UNENCRYPTED* header information,
/// even if you are using encryption.
- pub fn encode_message_to_peer<C: CryptoProvider, A: AsRef<[u8]>>(
+ pub fn encode_message_to_peer<C: CryptoProvider>(
&mut self,
payload: &[u8],
- associated_data: Option<A>,
+ associated_data: Option<impl AsRef<[u8]>>,
) -> Vec<u8> {
- self.increment_encode_sequence_number();
+ self.tx_connection_context.encode_message_to_peer::<C>(payload, associated_data)
+ }
+
+ /// Once `InitiatorHello` and `ResponderHello` (and payload) are exchanged, use this method to
+ /// decrypt and verify a message received from the other device. Both initiator and responder
+ /// devices can use this message.
+ ///
+ /// * `message` - the message that should be encrypted.
+ /// * `associated_data` - Optional associated data that must match what the sender provided. See
+ /// the documentation on [`encode_message_to_peer`][Self::encode_message_to_peer].
+ pub fn decode_message_from_peer<C: CryptoProvider>(
+ &mut self,
+ message: &[u8],
+ associated_data: Option<impl AsRef<[u8]>>,
+ ) -> Result<Vec<u8>, DecodeError> {
+ self.rx_connection_context.decode_message_from_peer::<C>(message, associated_data)
+ }
+
+ /// Returns the last sequence number used to encode a message.
+ pub fn encode_seq_num(&self) -> i32 {
+ self.tx_connection_context.encode_seq_num()
+ }
+
+ /// Returns the last sequence number used to decode a message.
+ pub fn decode_seq_num(&self) -> i32 {
+ self.rx_connection_context.decode_seq_num()
+ }
+
+ /// Returns a cryptographic digest (SHA256) of the session keys prepended by the SHA256 hash
+ /// of the ASCII string "D2D". Since the server and client share the same session keys, the
+ /// resulting session unique is also the same.
+ pub fn get_session_unique<C: CryptoProvider>(&self) -> Vec<u8> {
+ let encode_key_hash =
+ java_utils::hash_code(self.tx_connection_context.encode_key.as_slice());
+ let decode_key_hash =
+ java_utils::hash_code(self.rx_connection_context.decode_key.as_slice());
+ let first_key_bytes = if encode_key_hash < decode_key_hash {
+ self.tx_connection_context.encode_key.as_slice()
+ } else {
+ self.rx_connection_context.decode_key.as_slice()
+ };
+ let second_key_bytes =
+ if first_key_bytes == self.tx_connection_context.encode_key.as_slice() {
+ self.rx_connection_context.decode_key.as_slice()
+ } else {
+ self.tx_connection_context.encode_key.as_slice()
+ };
+ C::Sha256::sha256(&[&SESSION_UNIQUE_SALT, first_key_bytes, second_key_bytes].concat())
+ .to_vec()
+ }
+
+ /// Split into a [RxConnectionContext] and [TxConnectionContext] so that each may be owned
+ /// independently.
+ ///
+ /// This makes certain ownership patterns more convenient.
+ pub fn into_split_contexts(self) -> (RxConnectionContext, TxConnectionContext<R>) {
+ (self.rx_connection_context, self.tx_connection_context)
+ }
+}
+
+/// The rx (receive) side of a [D2DConnectionContextV1].
+#[derive(Debug)]
+pub struct RxConnectionContext {
+ decode_sequence_num: i32,
+ decode_key: Aes256Key,
+ decryption_key: Aes256Key,
+ verify_key: Aes256Key,
+ protocol: NextProtocol,
+}
+
+impl RxConnectionContext {
+ /// See [D2DConnectionContextV1::decode_message_from_peer].
+ pub fn decode_message_from_peer<C: CryptoProvider>(
+ &mut self,
+ message: &[u8],
+ associated_data: Option<impl AsRef<[u8]>>,
+ ) -> Result<Vec<u8>, DecodeError> {
+ // first confirm that the payload MAC matches the header_and_body
+ let message = SecureMessage::parse_from_bytes(message).map_err(|_| DecodeError::BadData)?;
+ let header_and_body = message.header_and_body.ok_or(DecodeError::BadData)?;
+ match self.protocol {
+ NextProtocol::Aes256CbcHmacSha256 => {
+ let payload_mac: [u8; 32] = message
+ .signature
+ .and_then(|signature| signature.try_into().ok())
+ .ok_or(DecodeError::BadData)?;
+ let mut hmac = C::HmacSha256::new_from_slice(&self.verify_key).unwrap();
+ hmac.update(&header_and_body);
+ if let Some(associated_data) = associated_data.as_ref() {
+ hmac.update(associated_data.as_ref())
+ }
+ hmac.verify(payload_mac).map_err(|_| DecodeError::BadData)?;
+ }
+ NextProtocol::Aes256GcmSiv => {} // No need to check signature on an AEAD cipher.
+ }
+
+ let payload =
+ HeaderAndBody::parse_from_bytes(&header_and_body).map_err(|_| DecodeError::BadData)?;
+ let decrypted = match self.protocol {
+ NextProtocol::Aes256GcmSiv => {
+ let nonce: <<C as CryptoProvider>::Aes256GcmSiv as Aead>::Nonce = payload
+ .header
+ .as_ref()
+ .and_then(|header| header.nonce().try_into().ok())
+ .ok_or(DecodeError::BadData)?;
+ crypto_utils::decrypt_gcm_siv::<C::Aes256GcmSiv>(
+ &self.decryption_key,
+ &payload.body.unwrap_or_default(),
+ associated_data.as_ref().map_or(&[], AsRef::as_ref),
+ &nonce,
+ )
+ .map_err(|_| DecodeError::BadData)?
+ }
+ NextProtocol::Aes256CbcHmacSha256 => {
+ let associated_data_len =
+ payload.header.as_ref().and_then(|header| header.associated_data_length);
+ if associated_data_len != associated_data.map(|ad| ad.as_ref().len() as u32) {
+ return Err(DecodeError::BadData);
+ }
+ let iv: AesCbcIv = payload
+ .header
+ .as_ref()
+ .and_then(|header| header.iv().try_into().ok())
+ .ok_or(DecodeError::BadData)?;
+ crypto_utils::decrypt_cbc::<C::AesCbcPkcs7Padded>(
+ &self.decryption_key,
+ &payload.body.unwrap_or_default(),
+ &iv,
+ )
+ .map_err(|_| DecodeError::BadData)?
+ }
+ };
+ let d2d_message = unwrap_device_to_device_message(&decrypted)?;
+ if d2d_message.sequence_num != self.decode_seq_num() + 1 {
+ return Err(DecodeError::BadSequenceNumber);
+ }
+ self.increment_decode_seq_num();
+ Ok(d2d_message.message)
+ }
+
+ fn increment_decode_seq_num(&mut self) {
+ self.decode_sequence_num += 1;
+ }
+
+ /// See [D2DConnectionContextV1::decode_seq_num]
+ pub fn decode_seq_num(&self) -> i32 {
+ self.decode_sequence_num
+ }
+}
+
+/// The tx (send) side of a [D2DConnectionContextV1].
+#[derive(Debug)]
+pub struct TxConnectionContext<R> {
+ encode_sequence_num: i32,
+ encode_key: Aes256Key,
+ encryption_key: Aes256Key,
+ signing_key: Aes256Key,
+ protocol: NextProtocol,
+ rng: R,
+}
+
+impl<R> TxConnectionContext<R>
+where
+ R: rand::Rng + rand::CryptoRng,
+{
+ /// See [D2DConnectionContextV1::encode_message_to_peer].
+ pub fn encode_message_to_peer<C: CryptoProvider>(
+ &mut self,
+ payload: &[u8],
+ associated_data: Option<impl AsRef<[u8]>>,
+ ) -> Vec<u8> {
+ self.increment_encode_seq_num();
let message = create_device_to_device_message(RustDeviceToDeviceMessage {
message: payload.to_vec(),
- sequence_num: self.get_sequence_number_for_encoding(),
+ sequence_num: self.encode_seq_num(),
});
let metadata = GcmMetadata {
type_: Some(Type::DEVICE_TO_DEVICE_MESSAGE.into()),
@@ -400,116 +567,12 @@
secure_message.write_to_bytes().unwrap()
}
- /// Once `InitiatorHello` and `ResponderHello` (and payload) are exchanged, use this method to
- /// decrypt and verify a message received from the other device. Both initiator and responder
- /// devices can use this message.
- ///
- /// * `message` - the message that should be encrypted.
- /// * `associated_data` - Optional associated data that must match what the sender provided. See
- /// the documentation on [`encode_message_to_peer`][Self::encode_message_to_peer].
- pub fn decode_message_from_peer<C: CryptoProvider, A: AsRef<[u8]>>(
- &mut self,
- payload: &[u8],
- associated_data: Option<A>,
- ) -> Result<Vec<u8>, DecodeError> {
- // first confirm that the payload MAC matches the header_and_body
- let message = SecureMessage::parse_from_bytes(payload).map_err(|_| DecodeError::BadData)?;
- let header_and_body = message.header_and_body.ok_or(DecodeError::BadData)?;
- match self.protocol {
- NextProtocol::Aes256CbcHmacSha256 => {
- let payload_mac: [u8; 32] = message
- .signature
- .and_then(|signature| signature.try_into().ok())
- .ok_or(DecodeError::BadData)?;
- let mut hmac = C::HmacSha256::new_from_slice(&self.verify_key).unwrap();
- hmac.update(&header_and_body);
- if let Some(associated_data) = associated_data.as_ref() {
- hmac.update(associated_data.as_ref())
- }
- hmac.verify(payload_mac).map_err(|_| DecodeError::BadData)?;
- }
- NextProtocol::Aes256GcmSiv => {} // No need to check signature on an AEAD cipher.
- }
-
- let payload =
- HeaderAndBody::parse_from_bytes(&header_and_body).map_err(|_| DecodeError::BadData)?;
- let decrypted = match self.protocol {
- NextProtocol::Aes256GcmSiv => {
- let nonce: <<C as CryptoProvider>::Aes256GcmSiv as Aead>::Nonce = payload
- .header
- .as_ref()
- .and_then(|header| header.nonce().try_into().ok())
- .ok_or(DecodeError::BadData)?;
- crypto_utils::decrypt_gcm_siv::<C::Aes256GcmSiv>(
- &self.decryption_key,
- &payload.body.unwrap_or_default(),
- associated_data.as_ref().map_or(&[], AsRef::as_ref),
- &nonce,
- )
- .map_err(|_| DecodeError::BadData)?
- }
- NextProtocol::Aes256CbcHmacSha256 => {
- let associated_data_len =
- payload.header.as_ref().and_then(|header| header.associated_data_length);
- if associated_data_len != associated_data.map(|ad| ad.as_ref().len() as u32) {
- return Err(DecodeError::BadData);
- }
- let iv: AesCbcIv = payload
- .header
- .as_ref()
- .and_then(|header| header.iv().try_into().ok())
- .ok_or(DecodeError::BadData)?;
- crypto_utils::decrypt_cbc::<C::AesCbcPkcs7Padded>(
- &self.decryption_key,
- &payload.body.unwrap_or_default(),
- &iv,
- )
- .map_err(|_| DecodeError::BadData)?
- }
- };
- let d2d_message = unwrap_device_to_device_message(&decrypted)?;
- if d2d_message.sequence_num != self.get_sequence_number_for_decoding() + 1 {
- return Err(DecodeError::BadSequenceNumber);
- }
- self.increment_decode_sequence_number();
- Ok(d2d_message.message)
- }
-
- fn increment_encode_sequence_number(&mut self) {
+ fn increment_encode_seq_num(&mut self) {
self.encode_sequence_num += 1;
}
- fn increment_decode_sequence_number(&mut self) {
- self.decode_sequence_num += 1;
- }
-
- /// Returns the last sequence number used to encode a message.
- pub fn get_sequence_number_for_encoding(&self) -> i32 {
+ /// See [D2DConnectionContextV1::encode_seq_num]
+ pub fn encode_seq_num(&self) -> i32 {
self.encode_sequence_num
}
-
- /// Returns the last sequence number used to decode a message.
- pub fn get_sequence_number_for_decoding(&self) -> i32 {
- self.decode_sequence_num
- }
-
- /// Returns a cryptographic digest (SHA256) of the session keys prepended by the SHA256 hash
- /// of the ASCII string "D2D". Since the server and client share the same session keys, the
- /// resulting session unique is also the same.
- pub fn get_session_unique<C: CryptoProvider>(&self) -> Vec<u8> {
- let encode_key_hash = java_utils::hash_code(self.encode_key.as_slice());
- let decode_key_hash = java_utils::hash_code(self.decode_key.as_slice());
- let first_key_bytes = if encode_key_hash < decode_key_hash {
- self.encode_key.as_slice()
- } else {
- self.decode_key.as_slice()
- };
- let second_key_bytes = if first_key_bytes == self.encode_key.as_slice() {
- self.decode_key.as_slice()
- } else {
- self.encode_key.as_slice()
- };
- C::Sha256::sha256(&[&SESSION_UNIQUE_SALT, first_key_bytes, second_key_bytes].concat())
- .to_vec()
- }
}
diff --git a/nearby/connections/ukey2/ukey2_connections/src/d2d_handshake_context.rs b/nearby/connections/ukey2/ukey2_connections/src/d2d_handshake_context.rs
index 5e7262b..343a013 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/d2d_handshake_context.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/d2d_handshake_context.rs
@@ -22,18 +22,22 @@
Ukey2ClientStage1, Ukey2Server, Ukey2ServerStage1, Ukey2ServerStage2,
};
-#[derive(Debug)]
+#[derive(Debug, thiserror::Error)]
pub enum HandshakeError {
+ #[error("Handshake not complete")]
HandshakeNotComplete,
}
-#[derive(Debug)]
+#[derive(Debug, thiserror::Error)]
pub enum HandleMessageError {
/// The supplied message was not applicable for the current state
+ #[error("Invalid state")]
InvalidState,
/// Handling the message produced an error that should be sent to the other party
+ #[error("Error message for other party")]
ErrorMessage(Vec<u8>),
/// Bad message
+ #[error("Bad message")]
BadMessage,
}
@@ -68,7 +72,7 @@
/// if [`is_handshake_complete`][Self::is_handshake_complete] returns true. Before trusting the
/// connection, callers should check that `to_completed_handshake().auth_string()` matches on
/// the client and server sides first. See the documentation for
- /// [`to_completed_handshake`][Self::to_completed_handshake].
+ /// [`completed_handshake`][Self::completed_handshake].
fn to_connection_context(&mut self) -> Result<D2DConnectionContextV1<R>, HandshakeError>;
/// Returns the [`CompletedHandshake`] using the results from this handshake context. May only
@@ -78,7 +82,11 @@
/// trying to create a connection context. This authentication string verification needs to be
/// done out-of-band, either by displaying the string to the user, or verified by some other
/// secure means.
- fn to_completed_handshake(&self) -> Result<&CompletedHandshake, HandshakeError>;
+ fn completed_handshake(&self) -> Result<&CompletedHandshake, HandshakeError>;
+
+ /// Like [Self::completed_handshake], but consumes `self` so it can return the handshake
+ /// rather than a reference to it.
+ fn into_completed_handshake(self) -> Result<CompletedHandshake, HandshakeError>;
}
enum InitiatorState<C: CryptoProvider> {
@@ -161,11 +169,11 @@
// Since self.rng is expected to be a seeded PRNG, not an OsRng directly, from_rng
// should never fail. https://rust-random.github.io/book/guide-err.html
let rng = R::from_rng(&mut self.rng).unwrap();
- self.to_completed_handshake()
+ self.completed_handshake()
.map(|h| D2DConnectionContextV1::from_initiator_handshake::<C>(h, rng))
}
- fn to_completed_handshake(&self) -> Result<&CompletedHandshake, HandshakeError> {
+ fn completed_handshake(&self) -> Result<&CompletedHandshake, HandshakeError> {
match &self.state {
InitiatorState::Stage1(_) | InitiatorState::Invalid => {
Err(HandshakeError::HandshakeNotComplete)
@@ -173,6 +181,15 @@
InitiatorState::Complete(c) => Ok(c.completed_handshake()),
}
}
+
+ fn into_completed_handshake(self) -> Result<CompletedHandshake, HandshakeError> {
+ match self.state {
+ InitiatorState::Stage1(_) | InitiatorState::Invalid => {
+ Err(HandshakeError::HandshakeNotComplete)
+ }
+ InitiatorState::Complete(c) => Ok(c.into_completed_handshake()),
+ }
+ }
}
enum ServerState<C: CryptoProvider> {
@@ -233,13 +250,10 @@
}
fn get_next_handshake_message(&self) -> Option<Vec<u8>> {
- let next_msg = match &self.state {
- ServerState::Stage1(_) => None,
+ match &self.state {
ServerState::Stage2(s) => Some(s.server_init_msg().to_vec()),
- ServerState::Complete(_) => None,
- ServerState::Invalid => None,
- }?;
- Some(next_msg)
+ ServerState::Stage1(_) | ServerState::Complete(_) | ServerState::Invalid => None,
+ }
}
fn handle_handshake_message(&mut self, message: &[u8]) -> Result<(), HandleMessageError> {
@@ -264,7 +278,15 @@
}
}
- fn to_completed_handshake(&self) -> Result<&CompletedHandshake, HandshakeError> {
+ fn to_connection_context(&mut self) -> Result<D2DConnectionContextV1<R>, HandshakeError> {
+ // Since self.rng is expected to be a seeded PRNG, not an OsRng directly, from_rng
+ // should never fail. https://rust-random.github.io/book/guide-err.html
+ let rng = R::from_rng(&mut self.rng).unwrap();
+ self.completed_handshake()
+ .map(|h| D2DConnectionContextV1::from_responder_handshake::<C>(h, rng))
+ }
+
+ fn completed_handshake(&self) -> Result<&CompletedHandshake, HandshakeError> {
match &self.state {
ServerState::Stage1(_) | ServerState::Stage2(_) | ServerState::Invalid => {
Err(HandshakeError::HandshakeNotComplete)
@@ -273,11 +295,12 @@
}
}
- fn to_connection_context(&mut self) -> Result<D2DConnectionContextV1<R>, HandshakeError> {
- // Since self.rng is expected to be a seeded PRNG, not an OsRng directly, from_rng
- // should never fail. https://rust-random.github.io/book/guide-err.html
- let rng = R::from_rng(&mut self.rng).unwrap();
- self.to_completed_handshake()
- .map(|h| D2DConnectionContextV1::from_responder_handshake::<C>(h, rng))
+ fn into_completed_handshake(self) -> Result<CompletedHandshake, HandshakeError> {
+ match self.state {
+ ServerState::Stage1(_) | ServerState::Stage2(_) | ServerState::Invalid => {
+ Err(HandshakeError::HandshakeNotComplete)
+ }
+ ServerState::Complete(s) => Ok(s.into_completed_handshake()),
+ }
}
}
diff --git a/nearby/connections/ukey2/ukey2_connections/src/lib.rs b/nearby/connections/ukey2/ukey2_connections/src/lib.rs
index eb8a55b..6f88375 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/lib.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/lib.rs
@@ -36,7 +36,8 @@
mod tests;
pub use d2d_connection_context_v1::{
- Aes256Key, D2DConnectionContextV1, DecodeError, DeserializeError,
+ Aes256Key, D2DConnectionContextV1, DecodeError, DeserializeError, RxConnectionContext,
+ TxConnectionContext,
};
pub use d2d_handshake_context::{
D2DHandshakeContext, HandleMessageError, HandshakeError, InitiatorD2DHandshakeContext,
diff --git a/nearby/connections/ukey2/ukey2_connections/src/tests.rs b/nearby/connections/ukey2/ukey2_connections/src/tests.rs
index 9f0b13e..6e89644 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/tests.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/tests.rs
@@ -151,7 +151,7 @@
vec![NextProtocol::Aes256CbcHmacSha256],
);
let encoded =
- init_conn_ctx.encode_message_to_peer::<RustCryptoImpl<MockRng>, &[u8]>(message, None);
+ init_conn_ctx.encode_message_to_peer::<RustCryptoImpl<MockRng>>(message, None::<&[u8]>);
// Expected values extracted from the results of the current implementation.
// This test makes sure that we don't accidentally change the encryption logic that
// causes incompatibility between versions.
@@ -166,7 +166,7 @@
]
);
let decoded = server_conn_ctx
- .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(&encoded, None)
+ .decode_message_from_peer::<CryptoProviderImpl>(&encoded, None::<&[u8]>)
.unwrap();
assert_eq!(message, &decoded[..]);
}
@@ -175,9 +175,10 @@
fn send_receive_message() {
let message = b"Hello World!";
let (mut init_conn_ctx, mut server_conn_ctx) = run_cbc_handshake();
- let encoded = init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl, &[u8]>(message, None);
+ let encoded =
+ init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl>(message, None::<&[u8]>);
let decoded = server_conn_ctx
- .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
+ .decode_message_from_peer::<CryptoProviderImpl>(encoded.as_slice(), None::<&[u8]>);
assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
}
@@ -185,9 +186,10 @@
fn send_receive_message_gcm() {
let message = b"Hello World!";
let (mut init_conn_ctx, mut server_conn_ctx) = run_gcm_handshake();
- let encoded = init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl, &[u8]>(message, None);
+ let encoded =
+ init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl>(message, None::<&[u8]>);
let decoded = server_conn_ctx
- .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
+ .decode_message_from_peer::<CryptoProviderImpl>(encoded.as_slice(), None::<&[u8]>);
assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
}
@@ -196,18 +198,18 @@
let message = b"Hello World!";
let (mut init_conn_ctx, mut server_conn_ctx) = run_cbc_handshake();
let encoded = init_conn_ctx
- .encode_message_to_peer::<CryptoProviderImpl, _>(message, Some(b"associated data"));
- let decoded = server_conn_ctx.decode_message_from_peer::<CryptoProviderImpl, _>(
+ .encode_message_to_peer::<CryptoProviderImpl>(message, Some(b"associated data"));
+ let decoded = server_conn_ctx.decode_message_from_peer::<CryptoProviderImpl>(
encoded.as_slice(),
Some(b"associated data"),
);
assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
// Make sure decode fails with missing associated data.
let decoded = server_conn_ctx
- .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
+ .decode_message_from_peer::<CryptoProviderImpl>(encoded.as_slice(), None::<&[u8]>);
assert!(decoded.is_err());
// Make sure decode fails with different associated data.
- let decoded = server_conn_ctx.decode_message_from_peer::<CryptoProviderImpl, _>(
+ let decoded = server_conn_ctx.decode_message_from_peer::<CryptoProviderImpl>(
encoded.as_slice(),
Some(b"assoc1ated data"),
);
@@ -227,9 +229,9 @@
.expect("failed to restore server session");
let message = b"Hello World!";
let encoded =
- init_restored_ctx.encode_message_to_peer::<CryptoProviderImpl, &[u8]>(message, None);
+ init_restored_ctx.encode_message_to_peer::<CryptoProviderImpl>(message, None::<&[u8]>);
let decoded = server_restored_ctx
- .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
+ .decode_message_from_peer::<CryptoProviderImpl>(encoded.as_slice(), None::<&[u8]>);
assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
}
@@ -266,15 +268,16 @@
let init_session = init_conn_ctx.get_session_unique::<CryptoProviderImpl>();
let server_session = server_conn_ctx.get_session_unique::<CryptoProviderImpl>();
let message = b"Hello World!";
- let encoded = init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl, &[u8]>(message, None);
+ let encoded =
+ init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl>(message, None::<&[u8]>);
let decoded = server_conn_ctx
- .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
+ .decode_message_from_peer::<CryptoProviderImpl>(encoded.as_slice(), None::<&[u8]>);
assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
let init_session_after = init_conn_ctx.get_session_unique::<CryptoProviderImpl>();
let server_session_after = server_conn_ctx.get_session_unique::<CryptoProviderImpl>();
let bad_server_ctx = D2DConnectionContextV1::new::<CryptoProviderImpl>(
- server_conn_ctx.get_sequence_number_for_decoding(),
- server_conn_ctx.get_sequence_number_for_encoding(),
+ server_conn_ctx.decode_seq_num(),
+ server_conn_ctx.encode_seq_num(),
Aes256Key::default(),
Aes256Key::default(),
StdRng::from_entropy(),
diff --git a/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts b/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts
index 78b0ebc..b42401d 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts
+++ b/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts
@@ -26,7 +26,7 @@
plugins {
// Apply the java-library plugin for API and implementation separation.
`java-library`
- kotlin("jvm") version "1.8.0"
+ kotlin("jvm") version "1.9.24"
id("me.champeau.jmh") version "0.7.1"
}
@@ -48,7 +48,7 @@
}
kotlin {
- jvmToolchain(17)
+ jvmToolchain(11)
}
tasks.jmh {
diff --git a/nearby/connections/ukey2/ukey2_jni/java/gradle/wrapper/gradle-wrapper.properties b/nearby/connections/ukey2/ukey2_jni/java/gradle/wrapper/gradle-wrapper.properties
index f398c33..171d876 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/gradle/wrapper/gradle-wrapper.properties
+++ b/nearby/connections/ukey2/ukey2_jni/java/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/nearby/connections/ukey2/ukey2_jni/src/lib.rs b/nearby/connections/ukey2/ukey2_jni/src/lib.rs
index e10f723..28804c8 100644
--- a/nearby/connections/ukey2/ukey2_jni/src/lib.rs
+++ b/nearby/connections/ukey2/ukey2_jni/src/lib.rs
@@ -260,7 +260,7 @@
) -> jbyteArray {
let empty_array = env.new_byte_array(0).unwrap();
let result = if let Some(ctx) = HANDLE_MAPPING.lock().get_mut(&(context_handle as u64)) {
- ctx.to_completed_handshake()
+ ctx.completed_handshake()
.map_err(|_| JniError::HandshakeError(HandshakeError::HandshakeNotComplete))
.map(|h| h.auth_string::<CryptoProvider>().derive_vec(length as usize).unwrap())
} else {
@@ -338,7 +338,7 @@
let result = if let Some(ctx) =
CONNECTION_HANDLE_MAPPING.lock().get_mut(&(context_handle as u64))
{
- Ok(ctx.encode_message_to_peer::<CryptoProvider, _>(
+ Ok(ctx.encode_message_to_peer::<CryptoProvider>(
env.convert_byte_array(unsafe { JByteArray::from_raw(payload) }).unwrap().as_slice(),
if associated_data.is_null() {
None
@@ -380,7 +380,7 @@
let result = if let Some(ctx) =
CONNECTION_HANDLE_MAPPING.lock().get_mut(&(context_handle as u64))
{
- ctx.decode_message_from_peer::<CryptoProvider, _>(
+ ctx.decode_message_from_peer::<CryptoProvider>(
env.convert_byte_array(unsafe { JByteArray::from_raw(message) }).unwrap().as_slice(),
if associated_data.is_null() {
None
@@ -424,7 +424,7 @@
context_handle: jlong,
) -> jint {
if let Some(ctx) = CONNECTION_HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
- ctx.get_sequence_number_for_encoding()
+ ctx.encode_seq_num()
} else {
env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
.expect("failed to find error class");
@@ -440,7 +440,7 @@
context_handle: jlong,
) -> jint {
if let Some(ctx) = CONNECTION_HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
- ctx.get_sequence_number_for_decoding()
+ ctx.decode_seq_num()
} else {
env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
.expect("failed to find error class");
diff --git a/nearby/connections/ukey2/ukey2_proto/Cargo.toml b/nearby/connections/ukey2/ukey2_proto/Cargo.toml
index e7292da..b910bfc 100644
--- a/nearby/connections/ukey2/ukey2_proto/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_proto/Cargo.toml
@@ -12,6 +12,7 @@
[features]
default = ["cargo"]
cargo = []
+soong = []
[dev-dependencies]
diff = "0.1.13"
diff --git a/nearby/connections/ukey2/ukey2_proto/src/lib.rs b/nearby/connections/ukey2/ukey2_proto/src/lib.rs
index 1f3e807..178f1d8 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/lib.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/lib.rs
@@ -17,7 +17,10 @@
include!(concat!(env!("OUT_DIR"), "/proto/mod.rs"));
}
-#[cfg(not(feature = "cargo"))]
+#[cfg(feature = "soong")]
+pub use ukey2_all_proto;
+
+#[cfg(all(not(feature = "cargo"), not(feature = "soong")))]
pub mod ukey2_all_proto {
pub mod device_to_device_messages;
pub mod securegcm;
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs
index ee43758..6c0665f 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.19.1
+// This file is generated by rust-protobuf 3.7.2. Do not edit
+// .proto file is parsed by protoc 27.3
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
@@ -23,7 +23,6 @@
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
-#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
@@ -38,10 +37,10 @@
/// Generated files are compatible only with the same version
/// of protobuf runtime.
-const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
+const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_7_2;
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.DeviceToDeviceMessage)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct DeviceToDeviceMessage {
// message fields
// @@protoc_insertion_point(field:securegcm.DeviceToDeviceMessage.message)
@@ -198,8 +197,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.InitiatorHello)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct InitiatorHello {
// message fields
// @@protoc_insertion_point(field:securegcm.InitiatorHello.public_dh_key)
@@ -326,8 +325,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.ResponderHello)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct ResponderHello {
// message fields
// @@protoc_insertion_point(field:securegcm.ResponderHello.public_dh_key)
@@ -454,8 +453,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.EcPoint)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct EcPoint {
// message fields
// @@protoc_insertion_point(field:securegcm.EcPoint.curve)
@@ -673,8 +672,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.SpakeHandshakeMessage)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct SpakeHandshakeMessage {
// message fields
// @@protoc_insertion_point(field:securegcm.SpakeHandshakeMessage.flow_number)
@@ -920,6 +919,13 @@
}
}
+ fn from_str(str: &str) -> ::std::option::Option<Curve> {
+ match str {
+ "ED_25519" => ::std::option::Option::Some(Curve::ED_25519),
+ _ => ::std::option::Option::None
+ }
+ }
+
const VALUES: &'static [Curve] = &[
Curve::ED_25519,
];
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs
index e394495..f92d6be 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.19.1
+// This file is generated by rust-protobuf 3.7.2. Do not edit
+// .proto file is parsed by protoc 27.3
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
@@ -23,7 +23,6 @@
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
-#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
@@ -38,10 +37,10 @@
/// Generated files are compatible only with the same version
/// of protobuf runtime.
-const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
+const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_7_2;
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.GcmMetadata)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct GcmMetadata {
// message fields
// @@protoc_insertion_point(field:securegcm.GcmMetadata.type)
@@ -253,6 +252,28 @@
}
}
+ fn from_str(str: &str) -> ::std::option::Option<Type> {
+ match str {
+ "ENROLLMENT" => ::std::option::Option::Some(Type::ENROLLMENT),
+ "TICKLE" => ::std::option::Option::Some(Type::TICKLE),
+ "TX_REQUEST" => ::std::option::Option::Some(Type::TX_REQUEST),
+ "TX_REPLY" => ::std::option::Option::Some(Type::TX_REPLY),
+ "TX_SYNC_REQUEST" => ::std::option::Option::Some(Type::TX_SYNC_REQUEST),
+ "TX_SYNC_RESPONSE" => ::std::option::Option::Some(Type::TX_SYNC_RESPONSE),
+ "TX_PING" => ::std::option::Option::Some(Type::TX_PING),
+ "DEVICE_INFO_UPDATE" => ::std::option::Option::Some(Type::DEVICE_INFO_UPDATE),
+ "TX_CANCEL_REQUEST" => ::std::option::Option::Some(Type::TX_CANCEL_REQUEST),
+ "PROXIMITYAUTH_PAIRING" => ::std::option::Option::Some(Type::PROXIMITYAUTH_PAIRING),
+ "GCMV1_IDENTITY_ASSERTION" => ::std::option::Option::Some(Type::GCMV1_IDENTITY_ASSERTION),
+ "DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD" => ::std::option::Option::Some(Type::DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD),
+ "DEVICE_TO_DEVICE_MESSAGE" => ::std::option::Option::Some(Type::DEVICE_TO_DEVICE_MESSAGE),
+ "DEVICE_PROXIMITY_CALLBACK" => ::std::option::Option::Some(Type::DEVICE_PROXIMITY_CALLBACK),
+ "UNLOCK_KEY_SIGNED_CHALLENGE" => ::std::option::Option::Some(Type::UNLOCK_KEY_SIGNED_CHALLENGE),
+ "LOGIN_NOTIFICATION" => ::std::option::Option::Some(Type::LOGIN_NOTIFICATION),
+ _ => ::std::option::Option::None
+ }
+ }
+
const VALUES: &'static [Type] = &[
Type::ENROLLMENT,
Type::TICKLE,
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs
index cafd900..8db57f0 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.19.1
+// This file is generated by rust-protobuf 3.7.2. Do not edit
+// .proto file is parsed by protoc 27.3
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
@@ -23,7 +23,6 @@
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
-#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
@@ -38,10 +37,10 @@
/// Generated files are compatible only with the same version
/// of protobuf runtime.
-const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
+const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_7_2;
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securemessage.SecureMessage)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct SecureMessage {
// message fields
// @@protoc_insertion_point(field:securemessage.SecureMessage.header_and_body)
@@ -221,8 +220,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securemessage.Header)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct Header {
// message fields
// @@protoc_insertion_point(field:securemessage.Header.signature_scheme)
@@ -651,8 +650,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securemessage.HeaderAndBody)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct HeaderAndBody {
// message fields
// @@protoc_insertion_point(field:securemessage.HeaderAndBody.header)
@@ -802,8 +801,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securemessage.EcP256PublicKey)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct EcP256PublicKey {
// message fields
// @@protoc_insertion_point(field:securemessage.EcP256PublicKey.x)
@@ -983,8 +982,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securemessage.SimpleRsaPublicKey)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct SimpleRsaPublicKey {
// message fields
// @@protoc_insertion_point(field:securemessage.SimpleRsaPublicKey.n)
@@ -1144,8 +1143,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securemessage.DhPublicKey)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct DhPublicKey {
// message fields
// @@protoc_insertion_point(field:securemessage.DhPublicKey.y)
@@ -1273,8 +1272,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securemessage.GenericPublicKey)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct GenericPublicKey {
// message fields
// @@protoc_insertion_point(field:securemessage.GenericPublicKey.type)
@@ -1475,6 +1474,16 @@
}
}
+ fn from_str(str: &str) -> ::std::option::Option<SigScheme> {
+ match str {
+ "HMAC_SHA256" => ::std::option::Option::Some(SigScheme::HMAC_SHA256),
+ "ECDSA_P256_SHA256" => ::std::option::Option::Some(SigScheme::ECDSA_P256_SHA256),
+ "RSA2048_SHA256" => ::std::option::Option::Some(SigScheme::RSA2048_SHA256),
+ "AEAD" => ::std::option::Option::Some(SigScheme::AEAD),
+ _ => ::std::option::Option::None
+ }
+ }
+
const VALUES: &'static [SigScheme] = &[
SigScheme::HMAC_SHA256,
SigScheme::ECDSA_P256_SHA256,
@@ -1518,6 +1527,15 @@
}
}
+ fn from_str(str: &str) -> ::std::option::Option<EncScheme> {
+ match str {
+ "NONE" => ::std::option::Option::Some(EncScheme::NONE),
+ "AES_256_CBC" => ::std::option::Option::Some(EncScheme::AES_256_CBC),
+ "AES_256_GCM_SIV" => ::std::option::Option::Some(EncScheme::AES_256_GCM_SIV),
+ _ => ::std::option::Option::None
+ }
+ }
+
const VALUES: &'static [EncScheme] = &[
EncScheme::NONE,
EncScheme::AES_256_CBC,
@@ -1560,6 +1578,15 @@
}
}
+ fn from_str(str: &str) -> ::std::option::Option<PublicKeyType> {
+ match str {
+ "EC_P256" => ::std::option::Option::Some(PublicKeyType::EC_P256),
+ "RSA2048" => ::std::option::Option::Some(PublicKeyType::RSA2048),
+ "DH2048_MODP" => ::std::option::Option::Some(PublicKeyType::DH2048_MODP),
+ _ => ::std::option::Option::None
+ }
+ }
+
const VALUES: &'static [PublicKeyType] = &[
PublicKeyType::EC_P256,
PublicKeyType::RSA2048,
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs
index 648f2ac..10c930d 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.19.1
+// This file is generated by rust-protobuf 3.7.2. Do not edit
+// .proto file is parsed by protoc 27.3
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
@@ -23,7 +23,6 @@
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
-#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
@@ -38,10 +37,10 @@
/// Generated files are compatible only with the same version
/// of protobuf runtime.
-const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
+const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_7_2;
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.Ukey2Message)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct Ukey2Message {
// message fields
// @@protoc_insertion_point(field:securegcm.Ukey2Message.message_type)
@@ -236,6 +235,17 @@
}
}
+ fn from_str(str: &str) -> ::std::option::Option<Type> {
+ match str {
+ "UNKNOWN_DO_NOT_USE" => ::std::option::Option::Some(Type::UNKNOWN_DO_NOT_USE),
+ "ALERT" => ::std::option::Option::Some(Type::ALERT),
+ "CLIENT_INIT" => ::std::option::Option::Some(Type::CLIENT_INIT),
+ "SERVER_INIT" => ::std::option::Option::Some(Type::SERVER_INIT),
+ "CLIENT_FINISH" => ::std::option::Option::Some(Type::CLIENT_FINISH),
+ _ => ::std::option::Option::None
+ }
+ }
+
const VALUES: &'static [Type] = &[
Type::UNKNOWN_DO_NOT_USE,
Type::ALERT,
@@ -253,8 +263,8 @@
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.Ukey2Alert)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct Ukey2Alert {
// message fields
// @@protoc_insertion_point(field:securegcm.Ukey2Alert.type)
@@ -464,6 +474,22 @@
}
}
+ fn from_str(str: &str) -> ::std::option::Option<AlertType> {
+ match str {
+ "BAD_MESSAGE" => ::std::option::Option::Some(AlertType::BAD_MESSAGE),
+ "BAD_MESSAGE_TYPE" => ::std::option::Option::Some(AlertType::BAD_MESSAGE_TYPE),
+ "INCORRECT_MESSAGE" => ::std::option::Option::Some(AlertType::INCORRECT_MESSAGE),
+ "BAD_MESSAGE_DATA" => ::std::option::Option::Some(AlertType::BAD_MESSAGE_DATA),
+ "BAD_VERSION" => ::std::option::Option::Some(AlertType::BAD_VERSION),
+ "BAD_RANDOM" => ::std::option::Option::Some(AlertType::BAD_RANDOM),
+ "BAD_HANDSHAKE_CIPHER" => ::std::option::Option::Some(AlertType::BAD_HANDSHAKE_CIPHER),
+ "BAD_NEXT_PROTOCOL" => ::std::option::Option::Some(AlertType::BAD_NEXT_PROTOCOL),
+ "BAD_PUBLIC_KEY" => ::std::option::Option::Some(AlertType::BAD_PUBLIC_KEY),
+ "INTERNAL_ERROR" => ::std::option::Option::Some(AlertType::INTERNAL_ERROR),
+ _ => ::std::option::Option::None
+ }
+ }
+
const VALUES: &'static [AlertType] = &[
AlertType::BAD_MESSAGE,
AlertType::BAD_MESSAGE_TYPE,
@@ -487,8 +513,8 @@
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.Ukey2ClientInit)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct Ukey2ClientInit {
// message fields
// @@protoc_insertion_point(field:securegcm.Ukey2ClientInit.version)
@@ -723,8 +749,8 @@
/// Nested message and enums of message `Ukey2ClientInit`
pub mod ukey2client_init {
- #[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.Ukey2ClientInit.CipherCommitment)
+ #[derive(PartialEq,Clone,Default,Debug)]
pub struct CipherCommitment {
// message fields
// @@protoc_insertion_point(field:securegcm.Ukey2ClientInit.CipherCommitment.handshake_cipher)
@@ -885,8 +911,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.Ukey2ServerInit)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct Ukey2ServerInit {
// message fields
// @@protoc_insertion_point(field:securegcm.Ukey2ServerInit.version)
@@ -1176,8 +1202,8 @@
}
}
-#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:securegcm.Ukey2ClientFinished)
+#[derive(PartialEq,Clone,Default,Debug)]
pub struct Ukey2ClientFinished {
// message fields
// @@protoc_insertion_point(field:securegcm.Ukey2ClientFinished.public_key)
@@ -1329,6 +1355,15 @@
}
}
+ fn from_str(str: &str) -> ::std::option::Option<Ukey2HandshakeCipher> {
+ match str {
+ "RESERVED" => ::std::option::Option::Some(Ukey2HandshakeCipher::RESERVED),
+ "P256_SHA512" => ::std::option::Option::Some(Ukey2HandshakeCipher::P256_SHA512),
+ "CURVE25519_SHA512" => ::std::option::Option::Some(Ukey2HandshakeCipher::CURVE25519_SHA512),
+ _ => ::std::option::Option::None
+ }
+ }
+
const VALUES: &'static [Ukey2HandshakeCipher] = &[
Ukey2HandshakeCipher::RESERVED,
Ukey2HandshakeCipher::P256_SHA512,
diff --git a/nearby/connections/ukey2/ukey2_shell/src/main.rs b/nearby/connections/ukey2/ukey2_shell/src/main.rs
index c50ee93..57aa62a 100644
--- a/nearby/connections/ukey2/ukey2_shell/src/main.rs
+++ b/nearby/connections/ukey2/ukey2_shell/src/main.rs
@@ -115,11 +115,11 @@
let (cmd, payload) = (&input[0..idx], &input[idx + 1..]);
if cmd == b"encrypt" {
let result =
- connection_ctx.encode_message_to_peer::<RustCrypto, &[u8]>(payload, None);
+ connection_ctx.encode_message_to_peer::<RustCrypto>(payload, None::<&[u8]>);
write_frame(result);
} else if cmd == b"decrypt" {
let result =
- connection_ctx.decode_message_from_peer::<RustCrypto, &[u8]>(payload, None);
+ connection_ctx.decode_message_from_peer::<RustCrypto>(payload, None::<&[u8]>);
if result.is_err() {
println!("failed to decode payload");
return false;
@@ -148,7 +148,7 @@
write_frame(initiator_ctx.get_next_handshake_message().unwrap_or_default());
// confirm auth str
let auth_str = initiator_ctx
- .to_completed_handshake()
+ .completed_handshake()
.ok()
.and_then(|h| h.auth_string::<RustCrypto>().derive_vec(self.verification_string_length))
.unwrap_or_else(|| vec![0; self.verification_string_length]);
@@ -178,7 +178,7 @@
.expect("Failed to handle message");
// confirm auth str
let auth_str = server_ctx
- .to_completed_handshake()
+ .completed_handshake()
.ok()
.and_then(|h| h.auth_string::<RustCrypto>().derive_vec(self.verification_string_length))
.unwrap_or_else(|| vec![0; self.verification_string_length]);
diff --git a/nearby/crypto/crypto_provider/benches/constant_time_eq_bench.rs b/nearby/crypto/crypto_provider/benches/constant_time_eq_bench.rs
index 3859616..161c21f 100644
--- a/nearby/crypto/crypto_provider/benches/constant_time_eq_bench.rs
+++ b/nearby/crypto/crypto_provider/benches/constant_time_eq_bench.rs
@@ -35,7 +35,7 @@
const TEST_LEN: usize = 1000;
for i in (0..=TEST_LEN).step_by(100) {
let _ = group.bench_function(
- &format!(
+ format!(
"constant_time_eq impl {} differ by {:04} bytes",
std::any::type_name::<C>(),
i,
diff --git a/nearby/crypto/crypto_provider/src/aes/mod.rs b/nearby/crypto/crypto_provider/src/aes/mod.rs
index b3dbd6a..66b50bf 100644
--- a/nearby/crypto/crypto_provider/src/aes/mod.rs
+++ b/nearby/crypto/crypto_provider/src/aes/mod.rs
@@ -16,7 +16,7 @@
//!
//! The design is an attempt to make it easy to provide implementations that are both idiomatic
//! Rust (e.g. RustCrypto) as well as FFI-backed (e.g. boringSSL and other C impls).
-use core::{array, fmt};
+use core::array;
pub mod ctr;
@@ -70,7 +70,7 @@
///
/// This is broken out as a separate type to allow the `fmt::Debug` requirement needed for
/// `expect()`.
- type TryFromError: fmt::Debug;
+ type TryFromError: core::fmt::Debug;
/// The byte array type the key can be represented with
type Array;
@@ -128,6 +128,12 @@
key: [u8; 32],
}
+impl core::fmt::Debug for Aes256Key {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
+ f.debug_struct("Aes256Key").finish()
+ }
+}
+
impl AesKey for Aes256Key {
type TryFromError = array::TryFromSliceError;
type Array = [u8; 32];
diff --git a/nearby/crypto/crypto_provider/src/ecdsa.rs b/nearby/crypto/crypto_provider/src/ecdsa.rs
new file mode 100644
index 0000000..2e22e99
--- /dev/null
+++ b/nearby/crypto/crypto_provider/src/ecdsa.rs
@@ -0,0 +1,231 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+use tinyvec::ArrayVec;
+
+/// The maximum length of a DER-encoded ecdsa `Signature`, in bytes.
+pub const SIGNATURE_MAX_LENGTH: usize = 72;
+
+/// The length of an ecdsa `PrivateKey`, in bytes.
+pub const PRIVATE_KEY_LENGTH: usize = 32;
+
+/// The length of a sec1 ecdsa `PublicKey`, in bytes.
+pub const PUBLIC_KEY_LENGTH: usize = 65;
+
+/// A byte buffer the size of a P256 ecdsa `Signature`.
+pub type RawP256Signature = ArrayVec<[u8; SIGNATURE_MAX_LENGTH]>;
+
+/// A byte buffer the size of a ecdsa `PublicKey`.
+pub type RawP256PublicKey = [u8; PUBLIC_KEY_LENGTH];
+
+/// A byte buffer the size of a ecdsa `PrivateKey`.
+pub type RawP256PrivateKey = [u8; PRIVATE_KEY_LENGTH];
+
+/// A permission token which may be supplied to methods which allow
+/// converting private keys to/from raw bytes.
+///
+/// In general, operations of this kind should only be done in
+/// development-tools, tests, or in credential storage layers
+/// to prevent accidental exposure of the private key.
+pub struct RawPrivateKeyPermit {
+ _marker: (),
+}
+
+impl RawPrivateKeyPermit {
+ /// Allows for internal construction of a private key permit to
+ /// allow accessing a raw private key.
+ fn new() -> Self {
+ Self { _marker: () }
+ }
+}
+
+/// This implementation is behind a feature flag to allow auditing of uses for
+/// constructing a private key from raw bytes.
+#[cfg(feature = "raw_private_key_permit")]
+impl core::default::Default for RawPrivateKeyPermit {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// A crypto-provider-independent representation of the private
+/// key of a P256 ECDSA key-pair, kept in such a way that
+/// it does not permit de-structuring it into raw bytes,
+/// nor constructing one from raw bytes.
+pub struct P256PrivateKey {
+ key: RawP256PrivateKey,
+}
+
+impl P256PrivateKey {
+ /// This must be kept private in order to ensure safety of the private key material.
+ /// [Self::from_raw_private_key] adds a [RawPrivateKeyPermit] to allow auditing of usages to
+ /// ensure that proper precautions are taken when handling the raw key material.
+ fn new(key: RawP256PrivateKey) -> Self {
+ Self { key }
+ }
+
+ /// Derives the public key corresponding to this private key.
+ pub fn derive_public_key<E: P256EcdsaProvider>(&self) -> impl PublicKey {
+ let key_pair = E::P256KeyPair::from_private_key(self);
+ key_pair.public_key()
+ }
+
+ /// Sign the given message and return a digital signature
+ pub fn sign<E: P256EcdsaProvider>(&self, msg: &[u8]) -> impl Signature {
+ let key_pair = E::P256KeyPair::from_private_key(self);
+ key_pair.sign(msg)
+ }
+
+ /// Generate an ecdsa private key from a CSPRNG
+ /// generate is not available in `no-std`.
+ #[cfg(feature = "std")]
+ pub fn generate<E: P256EcdsaProvider>() -> Self {
+ let key_pair = E::P256KeyPair::generate();
+ (&key_pair).into()
+ }
+
+ /// Returns the raw bytes of this private key.
+ /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
+ pub fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawP256PrivateKey {
+ self.key
+ }
+
+ /// Constructs a private key from the raw bytes of the key.
+ /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
+ pub fn from_raw_private_key(wrapped: RawP256PrivateKey, _permit: &RawPrivateKeyPermit) -> Self {
+ P256PrivateKey::new(wrapped)
+ }
+}
+
+/// Error returned when bad bytes are used to instantiate a private key.
+#[derive(Debug)]
+pub struct InvalidPrivateKeyBytes;
+
+/// error returned when bad bytes are provided to generate keypair
+#[derive(Debug)]
+pub struct InvalidPublicKeyBytes;
+
+/// Error returned invalid bytes are used to instantiate a signature.
+#[derive(Debug)]
+pub struct InvalidSignatureBytes;
+
+/// Error returned if the verification on the signature + message fails
+#[derive(Debug)]
+pub struct VerifyError;
+
+/// Collection of types used to provide an implementation of ecdsa.
+pub trait P256EcdsaProvider {
+ /// The internal representation of a keypair which includes both public and secret halves of an asymmetric key.
+ type P256KeyPair: KeyPair<
+ P256PublicKey = Self::P256PublicKey,
+ P256Signature = Self::P256Signature,
+ >;
+ /// The internal representation of an ecdsa public key, used when verifying a message
+ type P256PublicKey: PublicKey<Signature = Self::P256Signature>;
+ /// The internal representation of an ecdsa signature which is the result of signing a message
+ type P256Signature: Signature;
+}
+
+/// The keypair which includes both public and secret halves of an asymmetric key.
+/// This is different from [Self::P256PublicKey] as it includes operations to create a raw private
+/// key as well. The private key operations are contained in this trait, and the public key can be
+/// extracted from the private key to be used for verification. This trait is what providers
+/// implement to add ECDSA-P256 functionality.
+pub trait KeyPair: Sized {
+ /// The ecdsa public key, used when verifying a message
+ type P256PublicKey: PublicKey;
+
+ /// The ecdsa signature returned when signing a message
+ type P256Signature: Signature;
+
+ /// Returns the private key bytes of the `KeyPair`.
+ /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
+ fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawP256PrivateKey;
+
+ /// Builds a key-pair from a `RawPrivateKey` array of bytes.
+ /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
+ fn from_raw_private_key(bytes: &RawP256PrivateKey, _permit: &RawPrivateKeyPermit) -> Self
+ where
+ Self: Sized;
+
+ /// Sign the given message and return a digital signature
+ fn sign(&self, msg: &[u8]) -> Self::P256Signature;
+
+ /// Generate an ecdsa keypair from a CSPRNG
+ /// generate is not available in `no-std`
+ #[cfg(feature = "std")]
+ fn generate() -> Self;
+
+ /// Builds a key-pair from a [`P256PrivateKey`], given in an opaque form.
+ fn from_private_key(private_key: &P256PrivateKey) -> Self
+ where
+ Self: Sized,
+ {
+ // We're okay to reach in and construct an instance from
+ // the bytes of the private key, since the way that they
+ // were originally expressed would still require a valid
+ // [`RawPrivateKeyPermit`] to access them.
+ let raw_private_key = &private_key.key;
+ Self::from_raw_private_key(raw_private_key, &RawPrivateKeyPermit::new())
+ }
+
+ /// Getter function for the Public Key of the key pair
+ fn public_key(&self) -> Self::P256PublicKey;
+}
+
+impl<K> From<&K> for P256PrivateKey
+where
+ K: KeyPair,
+{
+ fn from(value: &K) -> Self {
+ // We're okay to reach in and grab the bytes of the private key,
+ // since the way that we're exposing it would require a valid
+ // [`RawPrivateKeyPermit`] to extract them again.
+ let wrapped = value.raw_private_key(&RawPrivateKeyPermit::new());
+ P256PrivateKey::new(wrapped)
+ }
+}
+
+/// An ecdsa signature
+pub trait Signature: Sized {
+ /// Create a new signature from a fixed size byte array. This represents a container for the
+ /// byte serialization of an ECDSA signature, and does not necessarily represent well-formed
+ /// field or curve elements.
+ ///
+ /// Signature verification libraries are expected to reject invalid field
+ /// elements at the time a signature is verified (not constructed).
+ fn from_bytes(bytes: &RawP256Signature) -> Result<Self, InvalidSignatureBytes>;
+
+ /// Returns a slice of the signature bytes
+ fn to_bytes(&self) -> RawP256Signature;
+}
+
+/// An ecdsa public key.
+pub trait PublicKey {
+ /// The signature type being used by verify
+ type Signature: Signature;
+
+ /// Builds this public key from an array of bytes in
+ /// the SEC1 format.
+ fn from_bytes(bytes: &RawP256PublicKey) -> Result<Self, InvalidPublicKeyBytes>
+ where
+ Self: Sized;
+
+ /// Yields the bytes of the public key in a SEC1 format.
+ fn to_bytes(&self) -> RawP256PublicKey;
+
+ /// Succeeds if the signature was a valid signature created by this Keypair on the prehashed_message.
+ fn verify_strict(&self, message: &[u8], signature: &Self::Signature)
+ -> Result<(), VerifyError>;
+}
diff --git a/nearby/crypto/crypto_provider/src/lib.rs b/nearby/crypto/crypto_provider/src/lib.rs
index 214175d..4866157 100644
--- a/nearby/crypto/crypto_provider/src/lib.rs
+++ b/nearby/crypto/crypto_provider/src/lib.rs
@@ -47,6 +47,9 @@
/// mod containing traits for ed25519 key generation, signing, and verification
pub mod ed25519;
+/// mod containing ecdsa trait.
+pub mod ecdsa;
+
pub use tinyvec;
/// Uber crypto trait which defines the traits for all crypto primitives as associated types
@@ -88,6 +91,8 @@
type Aes128Gcm: aead::AesGcm + aead::AeadInit<Aes128Key>;
/// The trait defining AES-256-GCM, an AEAD with a key size of 32 bytes.
type Aes256Gcm: aead::AesGcm + aead::AeadInit<Aes256Key>;
+ /// The trait defining ECDSA-P256.
+ type EcdsaP256: ecdsa::P256EcdsaProvider;
/// The cryptographically secure random number generator
type CryptoRng: CryptoRng;
diff --git a/nearby/crypto/crypto_provider_boringssl/src/ecdsa.rs b/nearby/crypto/crypto_provider_boringssl/src/ecdsa.rs
new file mode 100644
index 0000000..7fea3a2
--- /dev/null
+++ b/nearby/crypto/crypto_provider_boringssl/src/ecdsa.rs
@@ -0,0 +1,144 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+//
+//
+
+use crypto_provider::ecdsa::{
+ InvalidPublicKeyBytes, P256EcdsaProvider, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH,
+ SIGNATURE_MAX_LENGTH,
+};
+use crypto_provider::tinyvec::ArrayVec;
+
+pub struct P256Ecdsa;
+
+impl P256EcdsaProvider for P256Ecdsa {
+ type P256KeyPair = P256EcdsaKeyPair;
+
+ type P256PublicKey = P256EcdsaPublicKey;
+
+ type P256Signature = P256EcdsaSignature;
+}
+
+pub struct P256EcdsaKeyPair(bssl_crypto::ecdsa::PrivateKey<bssl_crypto::ec::P256>);
+
+impl crypto_provider::ecdsa::KeyPair for P256EcdsaKeyPair {
+ type P256PublicKey = P256EcdsaPublicKey;
+
+ type P256Signature = P256EcdsaSignature;
+
+ fn raw_private_key(
+ &self,
+ _permit: &crypto_provider::ecdsa::RawPrivateKeyPermit,
+ ) -> crypto_provider::ecdsa::RawP256PrivateKey {
+ let mut bytes = [0u8; PRIVATE_KEY_LENGTH];
+ bytes.copy_from_slice(self.0.to_big_endian().as_ref());
+ bytes
+ }
+
+ #[allow(clippy::unwrap_used)]
+ fn from_raw_private_key(
+ bytes: &crypto_provider::ecdsa::RawP256PrivateKey,
+ _permit: &crypto_provider::ecdsa::RawPrivateKeyPermit,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ // Unwrap is safe here because the private key bytes can be fully random
+ // (no curve-related constraints), and when we instantiate the raw private
+ // key, we check the size, so this should never fail.
+ let inner_key =
+ bssl_crypto::ecdsa::PrivateKey::<bssl_crypto::ec::P256>::from_big_endian(bytes)
+ .unwrap();
+ Self(inner_key)
+ }
+
+ #[allow(clippy::unwrap_used)]
+ fn sign(&self, msg: &[u8]) -> Self::P256Signature {
+ // Signing is an infallible operation, and we have a variable allocated length,
+ // so this should be fine.
+ P256EcdsaSignature(self.0.sign(msg).as_slice().try_into().unwrap())
+ }
+
+ fn generate() -> Self {
+ let inner_key = bssl_crypto::ecdsa::PrivateKey::<bssl_crypto::ec::P256>::generate();
+ Self(inner_key)
+ }
+
+ fn public_key(&self) -> Self::P256PublicKey {
+ P256EcdsaPublicKey(self.0.to_public_key())
+ }
+}
+
+pub struct P256EcdsaPublicKey(bssl_crypto::ecdsa::PublicKey<bssl_crypto::ec::P256>);
+
+impl crypto_provider::ecdsa::PublicKey for P256EcdsaPublicKey {
+ type Signature = P256EcdsaSignature;
+
+ fn from_bytes(
+ bytes: &crypto_provider::ecdsa::RawP256PublicKey,
+ ) -> Result<Self, crypto_provider::ecdsa::InvalidPublicKeyBytes>
+ where
+ Self: Sized,
+ {
+ let inner_key =
+ bssl_crypto::ecdsa::PublicKey::<bssl_crypto::ec::P256>::from_x962_uncompressed(bytes)
+ .ok_or(InvalidPublicKeyBytes)?;
+ Ok(Self(inner_key))
+ }
+
+ fn to_bytes(&self) -> crypto_provider::ecdsa::RawP256PublicKey {
+ let mut bytes = [0u8; PUBLIC_KEY_LENGTH];
+ bytes.copy_from_slice(self.0.to_x962_uncompressed().as_ref());
+ bytes
+ }
+
+ fn verify_strict(
+ &self,
+ message: &[u8],
+ signature: &Self::Signature,
+ ) -> Result<(), crypto_provider::ecdsa::VerifyError> {
+ self.0.verify(message, &signature.0).map_err(|_| crypto_provider::ecdsa::VerifyError)
+ }
+}
+
+pub struct P256EcdsaSignature(ArrayVec<[u8; SIGNATURE_MAX_LENGTH]>);
+
+impl crypto_provider::ecdsa::Signature for P256EcdsaSignature {
+ fn from_bytes(
+ bytes: &crypto_provider::ecdsa::RawP256Signature,
+ ) -> Result<Self, crypto_provider::ecdsa::InvalidSignatureBytes> {
+ Ok(Self(*bytes))
+ }
+
+ fn to_bytes(&self) -> crypto_provider::ecdsa::RawP256Signature {
+ self.0
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::P256Ecdsa;
+ use crypto_provider_test::ecdsa::*;
+
+ #[test]
+ fn p256_ecdsa_tests() {
+ run_wycheproof_test_vectors::<P256Ecdsa>(EcdsaTestName::EcdsaSecp256r1Sha256);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/lib.rs b/nearby/crypto/crypto_provider_boringssl/src/lib.rs
index 560169a..7b6e1d5 100644
--- a/nearby/crypto/crypto_provider_boringssl/src/lib.rs
+++ b/nearby/crypto/crypto_provider_boringssl/src/lib.rs
@@ -50,6 +50,9 @@
/// Implementations of crypto_provider::sha2 traits backed by BoringSSL
mod sha2;
+/// Implementation of P256-ecdsa.
+mod ecdsa;
+
/// The BoringSSL backed struct which implements CryptoProvider
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct Boringssl;
@@ -73,6 +76,7 @@
type Aes256GcmSiv = aead::aes_gcm_siv::AesGcmSiv256;
type Aes128Gcm = aead::aes_gcm::AesGcm128;
type Aes256Gcm = aead::aes_gcm::AesGcm256;
+ type EcdsaP256 = ecdsa::P256Ecdsa;
type CryptoRng = BoringSslRng;
fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
diff --git a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
index c26c7db..b0fbfa5 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
+++ b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
@@ -13,15 +13,16 @@
aes-gcm-siv = { version = "0.11.1", features = [
"aes",
], default-features = false }
-aes-gcm = { version = "0.10.3", features = [
- "aes",
-], default-features = false }
+aes-gcm = { version = "0.10.3", features = ["aes"], default-features = false }
crypto_provider = { workspace = true }
hmac.workspace = true
hkdf.workspace = true
sha2.workspace = true
x25519-dalek.workspace = true
-p256 = { workspace = true, features = ["ecdh"], default-features = false }
+p256 = { workspace = true, features = [
+ "ecdh",
+ "ecdsa",
+], default-features = false }
sec1.workspace = true
ed25519-dalek = { workspace = true, default-features = false, features = [
"rand_core",
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/ecdsa.rs b/nearby/crypto/crypto_provider_rustcrypto/src/ecdsa.rs
new file mode 100644
index 0000000..1c44f67
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/ecdsa.rs
@@ -0,0 +1,145 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+//
+//
+
+use crypto_provider::ecdsa::{
+ InvalidSignatureBytes, P256EcdsaProvider, VerifyError, PUBLIC_KEY_LENGTH,
+};
+use p256::ecdsa::signature::{Signer, Verifier};
+
+pub struct P256Ecdsa;
+
+impl P256EcdsaProvider for P256Ecdsa {
+ type P256KeyPair = P256SigningKey;
+
+ type P256PublicKey = P256VerifyingKey;
+
+ type P256Signature = P256Signature;
+}
+
+pub struct P256SigningKey(p256::ecdsa::SigningKey);
+
+impl crypto_provider::ecdsa::KeyPair for P256SigningKey {
+ type P256PublicKey = P256VerifyingKey;
+
+ type P256Signature = P256Signature;
+
+ fn raw_private_key(
+ &self,
+ _permit: &crypto_provider::ecdsa::RawPrivateKeyPermit,
+ ) -> crypto_provider::ecdsa::RawP256PrivateKey {
+ self.0.to_bytes().into()
+ }
+
+ #[allow(clippy::unwrap_used)]
+ fn from_raw_private_key(
+ bytes: &crypto_provider::ecdsa::RawP256PrivateKey,
+ _permit: &crypto_provider::ecdsa::RawPrivateKeyPermit,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ // Unwrap is safe here, as the bytes for a private key can be anything.
+ // We size check the raw bytes beforehand, so there is no failure.
+ let signing_key = p256::ecdsa::SigningKey::from_slice(bytes).unwrap();
+ Self(signing_key)
+ }
+
+ fn sign(&self, msg: &[u8]) -> Self::P256Signature {
+ P256Signature(self.0.sign(msg))
+ }
+
+ #[cfg(feature = "std")]
+ fn generate() -> Self {
+ let mut csprng = rand::rngs::ThreadRng::default();
+ Self(p256::ecdsa::SigningKey::random(&mut csprng))
+ }
+
+ #[allow(clippy::clone_on_copy)]
+ fn public_key(&self) -> Self::P256PublicKey {
+ P256VerifyingKey(*self.0.verifying_key())
+ }
+}
+
+pub struct P256VerifyingKey(p256::ecdsa::VerifyingKey);
+
+impl crypto_provider::ecdsa::PublicKey for P256VerifyingKey {
+ type Signature = P256Signature;
+
+ #[allow(clippy::unwrap_used)]
+ fn from_bytes(
+ bytes: &crypto_provider::ecdsa::RawP256PublicKey,
+ ) -> Result<Self, crypto_provider::ecdsa::InvalidPublicKeyBytes>
+ where
+ Self: Sized,
+ {
+ let encoded_point = p256::EncodedPoint::from_bytes(bytes)
+ .map_err(|_| crypto_provider::ecdsa::InvalidPublicKeyBytes)?;
+ // Unwrap is safe here, as we've verified during construction that the public key is
+ // of the correct size and that it is a point on the curve, so this cannot fail.
+ let verifying_key = p256::ecdsa::VerifyingKey::from_encoded_point(&encoded_point).unwrap();
+ Ok(Self(verifying_key))
+ }
+
+ fn to_bytes(&self) -> crypto_provider::ecdsa::RawP256PublicKey {
+ let mut public_key = [0u8; PUBLIC_KEY_LENGTH];
+ let point = self.0.to_encoded_point(false);
+ public_key.copy_from_slice(point.as_bytes());
+ public_key
+ }
+
+ fn verify_strict(
+ &self,
+ message: &[u8],
+ signature: &Self::Signature,
+ ) -> Result<(), VerifyError> {
+ self.0.verify(message, &signature.0).map_err(|_| VerifyError)
+ }
+}
+
+pub struct P256Signature(p256::ecdsa::Signature);
+
+impl crypto_provider::ecdsa::Signature for P256Signature {
+ fn from_bytes(
+ bytes: &crypto_provider::ecdsa::RawP256Signature,
+ ) -> Result<Self, InvalidSignatureBytes> {
+ p256::ecdsa::Signature::from_der(bytes).map(Self).map_err(|_| InvalidSignatureBytes)
+ }
+
+ #[allow(clippy::unwrap_used)]
+ fn to_bytes(&self) -> crypto_provider::ecdsa::RawP256Signature {
+ // Unwrap is safe here as the maximum size of a DER-serialized key is 72 bytes.
+ // Since we check the size of the signature on initialization from bytes, we don't
+ // need a check here and this cannot fail.
+ self.0.to_der().as_bytes().try_into().unwrap()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::P256Ecdsa;
+ use crypto_provider_test::ecdsa::*;
+
+ #[test]
+ fn p256_ecdsa_tests() {
+ run_wycheproof_test_vectors::<P256Ecdsa>(EcdsaTestName::EcdsaSecp256r1Sha256);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
index 97cf6a7..59e8743 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
@@ -30,6 +30,8 @@
mod aead;
/// Contains the RustCrypto backed AES impl for CryptoProvider
pub mod aes_cp;
+/// Contains the RustCrypto backed ECDSA P256 impl for CryptoProvider
+mod ecdsa;
/// Contains the RustCrypto backed impl for ed25519 key generation, signing, and verification
mod ed25519;
/// Contains the RustCrypto backed hkdf impl for CryptoProvider
@@ -90,6 +92,7 @@
type Aes256GcmSiv = aead::aes_gcm_siv::AesGcmSiv<aes::Aes256>;
type Aes128Gcm = aead::aes_gcm::AesGcm<aes::Aes128>;
type Aes256Gcm = aead::aes_gcm::AesGcm<aes::Aes256>;
+ type EcdsaP256 = ecdsa::P256Ecdsa;
type CryptoRng = RcRng<R>;
fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
diff --git a/nearby/crypto/crypto_provider_stubs/src/lib.rs b/nearby/crypto/crypto_provider_stubs/src/lib.rs
index 35bc274..c93ba76 100644
--- a/nearby/crypto/crypto_provider_stubs/src/lib.rs
+++ b/nearby/crypto/crypto_provider_stubs/src/lib.rs
@@ -20,14 +20,14 @@
use std::fmt::Debug;
-use crypto_provider::aead::AeadInit;
use crypto_provider::{
- aead::{Aead, AeadError, AesGcm, AesGcmSiv},
+ aead::{Aead, AeadError, AeadInit, AesGcm, AesGcmSiv},
aes::{
cbc::{AesCbcIv, AesCbcPkcs7Padded, DecryptionError, EncryptionError},
ctr::{AesCtr, NonceAndCounter},
Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher,
},
+ ecdsa::{self, InvalidSignatureBytes, P256EcdsaProvider},
ed25519::{
self, Ed25519Provider, InvalidPublicKeyBytes, KeyPairImpl, RawPrivateKey,
RawPrivateKeyPermit, RawPublicKey, RawSignature, SignatureError, SignatureImpl,
@@ -62,6 +62,7 @@
type Aes256GcmSiv = Aes256Stubs;
type Aes128Gcm = Aes128Stubs;
type Aes256Gcm = Aes256Stubs;
+ type EcdsaP256 = P256EcdsaStubs;
type CryptoRng = ();
fn constant_time_eq(_a: &[u8], _b: &[u8]) -> bool {
@@ -469,6 +470,87 @@
impl AesGcm for Aes256Stubs {}
+pub struct P256EcdsaStubs;
+
+impl P256EcdsaProvider for P256EcdsaStubs {
+ type P256KeyPair = P256EcdsaKeyPairStubs;
+
+ type P256PublicKey = P256EcdsaPublicKeyStubs;
+
+ type P256Signature = P256EcdsaSignatureStubs;
+}
+
+pub struct P256EcdsaKeyPairStubs;
+
+impl ecdsa::KeyPair for P256EcdsaKeyPairStubs {
+ type P256PublicKey = P256EcdsaPublicKeyStubs;
+
+ type P256Signature = P256EcdsaSignatureStubs;
+
+ fn raw_private_key(&self, _permit: &ecdsa::RawPrivateKeyPermit) -> ecdsa::RawP256PrivateKey {
+ unimplemented!()
+ }
+
+ fn from_raw_private_key(
+ bytes: &ecdsa::RawP256PrivateKey,
+ _permit: &ecdsa::RawPrivateKeyPermit,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ unimplemented!()
+ }
+
+ fn sign(&self, msg: &[u8]) -> Self::P256Signature {
+ unimplemented!()
+ }
+
+ fn generate() -> Self {
+ unimplemented!()
+ }
+
+ fn public_key(&self) -> Self::P256PublicKey {
+ unimplemented!()
+ }
+}
+
+pub struct P256EcdsaPublicKeyStubs;
+
+impl ecdsa::PublicKey for P256EcdsaPublicKeyStubs {
+ type Signature = P256EcdsaSignatureStubs;
+
+ fn from_bytes(bytes: &ecdsa::RawP256PublicKey) -> Result<Self, ecdsa::InvalidPublicKeyBytes>
+ where
+ Self: Sized,
+ {
+ unimplemented!()
+ }
+
+ fn to_bytes(&self) -> ecdsa::RawP256PublicKey {
+ unimplemented!()
+ }
+
+ fn verify_strict(
+ &self,
+ message: &[u8],
+ signature: &Self::Signature,
+ ) -> Result<(), ecdsa::VerifyError> {
+ unimplemented!()
+ }
+}
+
+pub struct P256EcdsaSignatureStubs;
+
+impl ecdsa::Signature for P256EcdsaSignatureStubs {
+ fn from_bytes(bytes: &ecdsa::RawP256Signature) -> Result<Self, InvalidSignatureBytes> {
+ unimplemented!()
+ }
+
+ fn to_bytes(&self) -> ecdsa::RawP256Signature {
+ unimplemented!()
+ }
+}
+
pub struct Ed25519Stubs;
impl Ed25519Provider for Ed25519Stubs {
diff --git a/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml b/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml
index d8dbd6f..ec3516e 100644
--- a/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml
+++ b/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml
@@ -17,7 +17,10 @@
libfuzzer-sys.workspace = true
[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+unexpected_cfgs = { level = "warn", check-cfg = [
+ 'cfg(fuzzing)',
+ 'cfg(rust_analyzer)',
+] }
[features]
default = ["crypto_provider_default/default"]
diff --git a/nearby/crypto/crypto_provider_test/src/ecdsa.rs b/nearby/crypto/crypto_provider_test/src/ecdsa.rs
new file mode 100644
index 0000000..265a889
--- /dev/null
+++ b/nearby/crypto/crypto_provider_test/src/ecdsa.rs
@@ -0,0 +1,75 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+extern crate alloc;
+extern crate std;
+
+use crypto_provider::ecdsa::{P256EcdsaProvider, PublicKey, RawP256Signature, Signature};
+pub use wycheproof::ecdsa::TestName as EcdsaTestName;
+use wycheproof::TestResult;
+
+/// Runs set of ECDSA wycheproof test vectors against a provided ecdsa implementation
+/// Tests vectors from Project Wycheproof: <https://github.com/google/wycheproof>.
+/// # Arguments
+/// - test_name - The test name to load vectors from. Should correspond to the `EcdsaCurve`.
+pub fn run_wycheproof_test_vectors<E>(test_name: wycheproof::ecdsa::TestName)
+where
+ E: P256EcdsaProvider,
+{
+ let test_set =
+ wycheproof::ecdsa::TestSet::load(test_name).expect("should be able to load test set");
+
+ for test_group in test_set.test_groups {
+ let public_key = test_group.key.key;
+ for test in test_group.tests {
+ let tc_id = test.tc_id;
+ let comment = test.comment;
+ let sig = test.sig;
+ let msg = test.msg;
+ let valid = match test.result {
+ TestResult::Invalid => false,
+ TestResult::Valid | TestResult::Acceptable => true,
+ };
+ let result =
+ run_wycheproof_test::<E>(public_key.as_slice(), sig.as_slice(), msg.as_slice());
+ if valid {
+ if let Err(desc) = result {
+ panic!(
+ "\n\
+ Failed test {}: {}\n\
+ msg:\t{:?}\n\
+ sig:\t{:?}\n\
+ comment:\t{:?}\n",
+ tc_id, desc, msg, sig, comment,
+ );
+ }
+ } else {
+ assert!(result.is_err())
+ }
+ }
+ }
+}
+
+fn run_wycheproof_test<E>(pub_key: &[u8], sig: &[u8], msg: &[u8]) -> Result<(), &'static str>
+where
+ E: P256EcdsaProvider,
+{
+ let pub_key = E::P256PublicKey::from_bytes(pub_key.try_into().unwrap())
+ .map_err(|_| "Invalid public key bytes")?;
+
+ let raw_sig: RawP256Signature = sig.try_into().map_err(|_| "Invalid length signature")?;
+ let signature = E::P256Signature::from_bytes(&raw_sig).map_err(|_| "Invalid signature")?;
+
+ pub_key.verify_strict(msg, &signature).map_err(|_| "Signature verification failed")
+}
diff --git a/nearby/crypto/crypto_provider_test/src/lib.rs b/nearby/crypto/crypto_provider_test/src/lib.rs
index 6ba6413..36c81b7 100644
--- a/nearby/crypto/crypto_provider_test/src/lib.rs
+++ b/nearby/crypto/crypto_provider_test/src/lib.rs
@@ -25,6 +25,7 @@
pub mod aead;
pub mod aes;
+pub mod ecdsa;
pub mod ed25519;
pub mod elliptic_curve;
pub mod hkdf;
diff --git a/nearby/deny.toml b/nearby/deny.toml
index bc7f4c5..20f50aa 100644
--- a/nearby/deny.toml
+++ b/nearby/deny.toml
@@ -25,8 +25,11 @@
# comment explaining why we have to ignore it
# "RUSTSEC-FOO",
- # Need a new release of cbindgen: https://github.com/mozilla/cbindgen/issues/983
- "RUSTSEC-2021-0145",
+ # instant is a dependency of rhai which is only used in the CLI for testing purposes
+ # at this time it's okay that this is un-maintained
+ "RUSTSEC-2024-0384",
+ # Rust protobuf team is still using paste.
+ "RUSTSEC-2024-0436",
]
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
# lower than the range specified will be ignored. Note that ignored advisories
@@ -57,11 +60,12 @@
"Apache-2.0 WITH LLVM-exception",
"BSD-3-Clause",
"BSD-2-Clause",
- "ISC",
- "Unicode-DFS-2016",
- "OpenSSL",
+ "Unicode-3.0",
"Unlicense",
- "CC0-1.0"
+ "CC0-1.0",
+ "ISC",
+ "MPL-2.0",
+ "Zlib",
]
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the
@@ -76,17 +80,20 @@
# "Reciprocal" licensed crate pulled directly from crates.io without modifications
- # Important: Update https://third-party-mirror.googlesource.com/webpki-roots/ if you update this version
- { allow = ["MPL-2.0"], name = "webpki-roots", version = "0.25.2" },
- # "Reciprocal" licensed crate pulled directly from crates.io without modifications
# Important: Update https://third-party-mirror.googlesource.com/option-ext/ if you update this version
- { allow = ["MPL-2.0"], name = "option-ext", version = "0.2.0" },
+ { allow = [
+ "MPL-2.0",
+ ], name = "option-ext", version = "0.2.0" },
# "Reciprocal" licensed crate pulled directly from crates.io without modifications
# Important: Update https://third-party-mirror.googlesource.com/smartstring/ if you update this version
- { allow = ["MPL-2.0"], name = "smartstring", version = "1.0.1" },
+ { allow = [
+ "MPL-2.0",
+ ], name = "smartstring", version = "1.0.1" },
# "Reciprocal" licensed crate pulled directly from crates.io without modifications
# Important: Update https://third-party-mirror.googlesource.com/cbindgen/ if you update this version
- { allow = ["MPL-2.0"], name = "cbindgen", version = "0.26.0" },
+ { allow = [
+ "MPL-2.0",
+ ], name = "cbindgen", version = "0.27.0" },
]
# Some crates don't have (easily) machine readable licensing information,
@@ -115,7 +122,7 @@
expression = "MIT AND ISC AND OpenSSL"
license-files = [
# Each entry is a crate relative path, and the (opaque) hash of its contents
- { path = "LICENSE", hash = 0xbd0eed23 }
+ { path = "LICENSE", hash = 0xbd0eed23 },
]
[licenses.private]
diff --git a/nearby/presence/ldt/benches/ldt_scan.rs b/nearby/presence/ldt/benches/ldt_scan.rs
index f5fa359..92cb67e 100644
--- a/nearby/presence/ldt/benches/ldt_scan.rs
+++ b/nearby/presence/ldt/benches/ldt_scan.rs
@@ -175,6 +175,7 @@
/// A wrapper that lets us avoid percolating the need to specify a bogus and type-confused padder
/// for ciphers that don't use one.
struct LdtScanCipher<const B: usize, T: TweakableBlockCipher<B>, M: Mix, P: Padder<B, T>> {
+ #[allow(dead_code)]
ldt_enc: LdtEncryptCipher<B, T, M>,
ldt_dec: LdtDecryptCipher<B, T, M>,
padder: P,
diff --git a/nearby/presence/ldt/fuzz/Cargo.toml b/nearby/presence/ldt/fuzz/Cargo.toml
index 14659e8..efffaee 100644
--- a/nearby/presence/ldt/fuzz/Cargo.toml
+++ b/nearby/presence/ldt/fuzz/Cargo.toml
@@ -19,7 +19,10 @@
libfuzzer-sys.workspace = true
[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+unexpected_cfgs = { level = "warn", check-cfg = [
+ 'cfg(fuzzing)',
+ 'cfg(rust_analyzer)',
+] }
[[bin]]
name = "ldt_roundtrip"
diff --git a/nearby/presence/ldt/src/lib.rs b/nearby/presence/ldt/src/lib.rs
index 2b4a962..23c38a5 100644
--- a/nearby/presence/ldt/src/lib.rs
+++ b/nearby/presence/ldt/src/lib.rs
@@ -93,7 +93,7 @@
impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtDecryptCipher<B, T, M> {
/// Create an [LdtDecryptCipher] with the provided Tweakable block cipher and Mix function
-
+ ///
/// Decrypt `data` in place, performing the pad operation with `padder`.
///
/// Unless you have particular padding needs, use [DefaultPadder].
diff --git a/nearby/presence/ldt/tests/ldt_roundtrip.rs b/nearby/presence/ldt/tests/ldt_roundtrip.rs
index fabddf0..a3e1426 100644
--- a/nearby/presence/ldt/tests/ldt_roundtrip.rs
+++ b/nearby/presence/ldt/tests/ldt_roundtrip.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::unwrap_used)]
+#![allow(clippy::unwrap_used, missing_docs)]
use crypto_provider::aes::BLOCK_SIZE;
use crypto_provider::{CryptoProvider, CryptoRng};
diff --git a/nearby/presence/ldt/tests/ldt_test_vectors.rs b/nearby/presence/ldt/tests/ldt_test_vectors.rs
index 1e8a5e1..d072fa5 100644
--- a/nearby/presence/ldt/tests/ldt_test_vectors.rs
+++ b/nearby/presence/ldt/tests/ldt_test_vectors.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::unwrap_used, clippy::indexing_slicing)]
+#![allow(clippy::unwrap_used, clippy::indexing_slicing, missing_docs)]
use anyhow::anyhow;
use crypto_provider_default::CryptoProviderImpl;
diff --git a/nearby/presence/ldt/tests/tests.rs b/nearby/presence/ldt/tests/tests.rs
index e68273f..ad4bf32 100644
--- a/nearby/presence/ldt/tests/tests.rs
+++ b/nearby/presence/ldt/tests/tests.rs
@@ -11,6 +11,7 @@
// WITHOUT 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(missing_docs)]
extern crate alloc;
diff --git a/nearby/presence/ldt_np_adv/fuzz/Cargo.toml b/nearby/presence/ldt_np_adv/fuzz/Cargo.toml
index 544b257..39e0ee5 100644
--- a/nearby/presence/ldt_np_adv/fuzz/Cargo.toml
+++ b/nearby/presence/ldt_np_adv/fuzz/Cargo.toml
@@ -21,7 +21,10 @@
libfuzzer-sys.workspace = true
[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+unexpected_cfgs = { level = "warn", check-cfg = [
+ 'cfg(fuzzing)',
+ 'cfg(rust_analyzer)',
+] }
[[bin]]
name = "ldt_np_roundtrip"
diff --git a/nearby/presence/ldt_np_adv_ffi/c/benchmarks/ldt_benchmarks.cc b/nearby/presence/ldt_np_adv_ffi/c/benchmarks/ldt_benchmarks.cc
index 2559954..fb6686f 100644
--- a/nearby/presence/ldt_np_adv_ffi/c/benchmarks/ldt_benchmarks.cc
+++ b/nearby/presence/ldt_np_adv_ffi/c/benchmarks/ldt_benchmarks.cc
@@ -14,6 +14,7 @@
#include "np_ldt.h"
+#include <cstdlib>
#include <ctime>
#include "benchmark/benchmark.h"
diff --git a/nearby/presence/ldt_np_jni/java/LdtNpJni/build.gradle.kts b/nearby/presence/ldt_np_jni/java/LdtNpJni/build.gradle.kts
index 0f0991e..ad52c1c 100644
--- a/nearby/presence/ldt_np_jni/java/LdtNpJni/build.gradle.kts
+++ b/nearby/presence/ldt_np_jni/java/LdtNpJni/build.gradle.kts
@@ -19,15 +19,13 @@
kotlin("jvm") version "1.9.0"
}
-kotlin {
- jvmToolchain {
- languageVersion.set(JavaLanguageVersion.of("17"))
- }
-}
-
group = "com.google.android.gms.nearby.presence.hazmat"
version = "1.0-SNAPSHOT"
+kotlin {
+ jvmToolchain(11)
+}
+
repositories {
mavenCentral()
google()
diff --git a/nearby/presence/ldt_np_jni/java/LdtNpJni/gradle/wrapper/gradle-wrapper.properties b/nearby/presence/ldt_np_jni/java/LdtNpJni/gradle/wrapper/gradle-wrapper.properties
index 070cb70..19cfad9 100644
--- a/nearby/presence/ldt_np_jni/java/LdtNpJni/gradle/wrapper/gradle-wrapper.properties
+++ b/nearby/presence/ldt_np_jni/java/LdtNpJni/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/nearby/presence/np_adv/Cargo.toml b/nearby/presence/np_adv/Cargo.toml
index 4b79c97..7793ba3 100644
--- a/nearby/presence/np_adv/Cargo.toml
+++ b/nearby/presence/np_adv/Cargo.toml
@@ -13,7 +13,6 @@
ldt_np_adv.workspace = true
ldt.workspace = true
np_hkdf.workspace = true
-np_ed25519.workspace = true
xts_aes.workspace = true
crypto_provider.workspace = true
strum.workspace = true
@@ -23,6 +22,7 @@
sink.workspace = true
tinyvec.workspace = true
itertools = { workspace = true, default-features = false }
+thiserror.workspace = true
[features]
default = ["alloc"]
@@ -43,14 +43,20 @@
test_vector_hkdf.workspace = true
criterion.workspace = true
crypto_provider_default = { workspace = true, features = ["std", "rustcrypto"] }
-np_ed25519 = { workspace = true, features = ["std"] }
sink = { workspace = true, features = ["std"] }
+# examples/test_data_gen
+clap.workspace = true
+
[[bench]]
name = "deser_adv"
harness = false
required-features = ["alloc", "devtools"]
+[[example]]
+name = "generate_test_data"
+required-features = ["alloc", "devtools"]
+
[[test]]
name = "examples_v0"
path = "tests/examples_v0.rs"
diff --git a/nearby/presence/np_adv/benches/deser_adv.rs b/nearby/presence/np_adv/benches/deser_adv.rs
index 0cc553b..73316a4 100644
--- a/nearby/presence/np_adv/benches/deser_adv.rs
+++ b/nearby/presence/np_adv/benches/deser_adv.rs
@@ -22,13 +22,12 @@
)]
use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
-use crypto_provider::{ed25519, CryptoProvider, CryptoRng};
+use crypto_provider::{CryptoProvider, CryptoRng};
use crypto_provider_default::CryptoProviderImpl;
use ldt_np_adv::{V0IdentityToken, V0Salt, V0_IDENTITY_TOKEN_LEN};
use np_adv::credential::matched::EmptyMatchedCredential;
use np_adv::deserialization_arena;
-use np_adv::extended::serialize::AdvertisementType;
use np_adv::extended::V1IdentityToken;
use np_adv::legacy::serialize::UnencryptedEncoder;
use np_adv::{
@@ -39,7 +38,7 @@
deserialize::VerificationMode,
serialize::{
AdvBuilder as ExtendedAdvBuilder, MicEncryptedSectionEncoder, SectionBuilder,
- SectionEncoder, SignedEncryptedSectionEncoder, UnencryptedSectionEncoder,
+ SectionEncoder, UnencryptedSectionEncoder,
},
},
legacy::{
@@ -56,7 +55,7 @@
let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
for crypto_type in CryptoMaterialType::iter() {
- for &identity_type in &[VerificationMode::Mic, VerificationMode::Signature] {
+ for &identity_type in &[VerificationMode::Mic] {
for &num_identities in &[10, 100, 1000] {
for &num_sections in &[1, 2] {
// measure worst-case performance -- the correct identities will be the last
@@ -70,14 +69,13 @@
.map(|_| V1Identity::random::<CryptoProviderImpl>(&mut crypto_rng))
.collect::<Vec<_>>();
- let mut adv_builder = ExtendedAdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = ExtendedAdvBuilder::new();
// take the first n identities, one section per identity
for identity in identities.iter().take(num_sections) {
let broadcast_cm = V1BroadcastCredential::new(
identity.key_seed,
identity.identity_token,
- identity.private_key.clone(),
);
match identity_type {
VerificationMode::Mic => {
@@ -91,17 +89,6 @@
add_des(&mut sb);
sb.add_to_advertisement::<CryptoProviderImpl>();
}
- VerificationMode::Signature => {
- let mut sb = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
- &mut crypto_rng,
- &broadcast_cm,
- ))
- .unwrap();
-
- add_des(&mut sb);
- sb.add_to_advertisement::<CryptoProviderImpl>();
- }
}
}
@@ -122,7 +109,7 @@
pub fn deser_adv_v1_plaintext(c: &mut Criterion) {
c.bench_function("Deser V1 plaintext: sections=1", |b| {
- let mut adv_builder = ExtendedAdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = ExtendedAdvBuilder::new();
let mut sb = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
@@ -330,8 +317,8 @@
fn add_des<I: SectionEncoder>(
sb: &mut SectionBuilder<&mut np_adv::extended::serialize::AdvBuilder, I>,
) {
- sb.add_de_res(|_| TxPower::try_from(17).map(TxPowerDataElement::from)).unwrap();
- sb.add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &[0; 10])).unwrap();
+ sb.add_de(&TxPowerDataElement::from(TxPower::try_from(17).unwrap())).unwrap();
+ sb.add_de(&GenericDataElement::try_from(100_u16.into(), &[0; 10]).unwrap()).unwrap();
}
criterion_group!(
benches,
@@ -368,17 +355,12 @@
struct V1Identity {
key_seed: [u8; 32],
identity_token: V1IdentityToken,
- private_key: ed25519::PrivateKey,
}
impl V1Identity {
/// Generate a new identity with random crypto material
fn random<C: CryptoProvider>(rng: &mut C::CryptoRng) -> Self {
- Self {
- key_seed: rng.gen(),
- identity_token: rng.gen(),
- private_key: ed25519::PrivateKey::generate::<C::Ed25519>(),
- }
+ Self { key_seed: rng.gen(), identity_token: rng.gen() }
}
/// Convert this `V1Identity` into a `V1DiscoveryCredential`.
fn into_discovery_credential<C: CryptoProvider>(self) -> V1DiscoveryCredential {
@@ -392,10 +374,6 @@
hkdf.v1_mic_extended_salt_keys()
.identity_token_hmac_key()
.calculate_hmac::<C>(self.identity_token.as_slice()),
- hkdf.v1_signature_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<C>(self.identity_token.as_slice()),
- self.private_key.derive_public_key::<C::Ed25519>(),
)
}
}
diff --git a/nearby/presence/np_adv/examples/fmt/mod.rs b/nearby/presence/np_adv/examples/fmt/mod.rs
new file mode 100644
index 0000000..a4846c5
--- /dev/null
+++ b/nearby/presence/np_adv/examples/fmt/mod.rs
@@ -0,0 +1,84 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+pub fn cpp_bytes(bytes: &[u8]) -> impl std::fmt::Display + '_ {
+ display_bytes("0x", bytes)
+}
+
+pub fn java_bytes(bytes: &[u8]) -> impl std::fmt::Display + '_ {
+ display_bytes("(byte) 0x", bytes)
+}
+
+pub fn display_bytes<'a>(prefix: &'a str, bytes: &'a [u8]) -> impl std::fmt::Display + 'a {
+ use std::fmt;
+
+ struct DisplayBytes<'a> {
+ prefix: &'a str,
+ bytes: &'a [u8],
+ }
+
+ impl fmt::Display for DisplayBytes<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let prefix = self.prefix;
+
+ write!(f, "{{")?;
+
+ let mut first = true;
+ for byte in self.bytes {
+ if !first {
+ write!(f, ", ")?;
+ } else {
+ first = false;
+ }
+ write!(f, "{prefix}{byte:02X}")?;
+ }
+
+ write!(f, "}}")?;
+ Ok(())
+ }
+ }
+
+ DisplayBytes { prefix, bytes }
+}
+
+pub fn java_static<'a>(name: &'a str, bytes: &'a [u8]) -> impl std::fmt::Display + 'a {
+ use std::fmt;
+
+ struct JavaStatic<'a>(&'a str, &'a [u8]);
+
+ impl fmt::Display for JavaStatic<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let JavaStatic(name, bytes) = *self;
+ write!(f, "public static final byte[] {name} = {};", java_bytes(bytes))
+ }
+ }
+
+ JavaStatic(name, bytes)
+}
+
+pub fn cpp_static<'a>(name: &'a str, bytes: &'a [u8]) -> impl std::fmt::Display + 'a {
+ use std::fmt;
+
+ struct CppStatic<'a>(&'a str, &'a [u8]);
+
+ impl fmt::Display for CppStatic<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let CppStatic(name, bytes) = *self;
+ let len = bytes.len();
+ write!(f, "constexpr std::array<uint8_t, {len}> {name} = {};", cpp_bytes(bytes))
+ }
+ }
+
+ CppStatic(name, bytes)
+}
diff --git a/nearby/presence/np_adv/examples/generate_test_data.rs b/nearby/presence/np_adv/examples/generate_test_data.rs
new file mode 100644
index 0000000..5cc9e8c
--- /dev/null
+++ b/nearby/presence/np_adv/examples/generate_test_data.rs
@@ -0,0 +1,195 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+#![allow(dead_code)]
+#![allow(missing_docs)]
+#![allow(clippy::unwrap_used)]
+#![allow(clippy::expect_used)]
+#![allow(clippy::indexing_slicing)]
+#![allow(clippy::panic)]
+
+use clap::Parser;
+use crypto_provider::{CryptoProvider, CryptoRng};
+use crypto_provider_default::CryptoProviderImpl;
+use np_adv::credential::{
+ book::CredentialBookBuilder,
+ matched::{MatchedCredential, MetadataMatchedCredential, WithMatchedCredential},
+ v1::{V1BroadcastCredential, V1DiscoveryCredential, V1},
+ MatchableCredential,
+};
+use np_adv::extended::deserialize::{Section, V1DeserializedSection};
+use np_adv::extended::serialize::{AdvBuilder, MicEncryptedSectionEncoder};
+use np_adv::extended::{data_elements::TxPowerDataElement, V1IdentityToken};
+use np_adv::shared_data::TxPower;
+use np_adv::{deserialization_arena, deserialize_advertisement};
+use np_hkdf::DerivedSectionKeys;
+use serde::{Deserialize, Serialize};
+
+mod fmt;
+
+#[derive(Parser, Debug)]
+struct Args {}
+
+fn main() {
+ let args = Args::parse();
+ generate_data(&args);
+}
+
+fn generate_data(_args: &Args) {
+ // identity material
+ let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
+
+ let identity_token = V1IdentityToken::from(rng.gen::<[u8; 16]>());
+ let key_seed = rng.gen();
+ let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+
+ let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token);
+
+ // Serialize and encrypt some identity metadata (sender-side)
+ let sender_metadata = IdentityMetadata {
+ uuid: "378845e1-2616-420d-86f5-674177a7504d".to_string(),
+ display_name: "Alice".to_string(),
+ location: "Wonderland".to_string(),
+ };
+ let sender_metadata_bytes = sender_metadata.to_bytes();
+ let encrypted_sender_metadata = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
+ V1,
+ CryptoProviderImpl,
+ >(&hkdf, identity_token, &sender_metadata_bytes);
+
+ // prepare advertisement
+ let mut adv_builder = AdvBuilder::new();
+
+ let mut section_builder = adv_builder
+ .section_builder(MicEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
+ &mut rng,
+ &broadcast_cred,
+ ))
+ .unwrap();
+ section_builder.add_de(&TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
+ section_builder.add_to_advertisement::<CryptoProviderImpl>();
+ let adv = adv_builder.into_advertisement();
+
+ let discovery_credential = V1DiscoveryCredential::new(
+ key_seed,
+ hkdf.v1_mic_short_salt_keys()
+ .identity_token_hmac_key()
+ .calculate_hmac::<CryptoProviderImpl>(identity_token.bytes()),
+ hkdf.v1_mic_extended_salt_keys()
+ .identity_token_hmac_key()
+ .calculate_hmac::<CryptoProviderImpl>(identity_token.bytes()),
+ );
+
+ let credentials: [MatchableCredential<V1, MetadataMatchedCredential<_>>; 1] =
+ [MatchableCredential {
+ discovery_credential,
+ match_data: encrypted_sender_metadata.clone(),
+ }];
+ let cred_book = CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(
+ &[],
+ &credentials,
+ );
+
+ let arena = deserialization_arena!();
+ let contents =
+ deserialize_advertisement::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book)
+ .expect("Should be a valid advertisement")
+ .into_v1()
+ .expect("Should be V1");
+
+ let sections = contents.sections().collect::<Vec<_>>();
+ let matched: &WithMatchedCredential<_, _> = match §ions[0] {
+ V1DeserializedSection::Decrypted(d) => d,
+ _ => panic!("this is a ciphertext adv"),
+ };
+ let section = matched.contents();
+ let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
+ let de = &data_elements[0];
+
+ println!("\nC++ test data (nearby/presence/np_cpp_ffi/shared/shared_test_util.h):\n");
+ println!("{}", fmt::cpp_static("V1AdvEncryptedBytes", adv.as_slice()));
+ println!("{}", fmt::cpp_static("V1AdvKeySeed", &key_seed));
+ println!(
+ "{}",
+ fmt::cpp_static(
+ "V1AdvExpectedMicShortSaltIdentityTokenHmac",
+ &credentials[0].discovery_credential.expected_mic_short_salt_identity_token_hmac
+ )
+ );
+ println!(
+ "{}",
+ fmt::cpp_static(
+ "V1AdvExpectedMicExtendedSaltIdentityTokenHmac",
+ &credentials[0].discovery_credential.expected_mic_extended_salt_identity_token_hmac
+ )
+ );
+ println!(
+ "inline std::vector<uint8_t> V1AdvEncryptedMetadata = {};",
+ fmt::cpp_bytes(&encrypted_sender_metadata.fetch_encrypted_metadata().unwrap())
+ );
+ println!(
+ "inline std::string ExpectedV1DecryptedMetadata = {:?};",
+ std::str::from_utf8(&sender_metadata_bytes).unwrap()
+ );
+ let derived_salt = de.salt().unwrap().derive::<16, CryptoProviderImpl>().unwrap();
+ println!("{}", fmt::cpp_static("ExpectedV1DerivedSalt", &derived_salt));
+
+ println!("\nJava test data (nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java)\n");
+ println!("{}", fmt::java_static("V1_KEY_SEED", &key_seed));
+ println!("{}", fmt::java_static("V1_IDENTITY_TOKEN", identity_token.bytes()));
+ println!(
+ "{}",
+ fmt::java_static(
+ "V1_MIC_SHORT_HMAC",
+ &credentials[0].discovery_credential.expected_mic_short_salt_identity_token_hmac
+ )
+ );
+ println!(
+ "{}",
+ fmt::java_static(
+ "V1_MIC_LONG_HMAC",
+ &credentials[0].discovery_credential.expected_mic_extended_salt_identity_token_hmac
+ )
+ );
+ println!("{}", fmt::java_static("V1_ALICE_METADATA", &sender_metadata_bytes));
+ println!(
+ "{}",
+ fmt::java_static(
+ "V1_ENCRYPTED_ALICE_METADATA",
+ &encrypted_sender_metadata.fetch_encrypted_metadata().unwrap()
+ )
+ );
+ println!("{}", fmt::java_static("V1_PRIVATE", adv.as_slice()));
+}
+
+/// Sample contents for some encrypted identity metadata
+/// which consists of a UUID together with a display name
+/// and a general location.
+#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
+struct IdentityMetadata {
+ uuid: String,
+ display_name: String,
+ location: String,
+}
+
+impl IdentityMetadata {
+ /// Serialize this identity metadata to a json byte-string.
+ fn to_bytes(&self) -> Vec<u8> {
+ serde_json::to_vec(self).expect("Identity metadata serialization is infallible")
+ }
+ /// Attempt to deserialize identity metadata from a json byte-string.
+ fn try_from_bytes(serialized: &[u8]) -> Option<Self> {
+ serde_json::from_slice(serialized).ok()
+ }
+}
diff --git a/nearby/presence/np_adv/fuzz/Cargo.toml b/nearby/presence/np_adv/fuzz/Cargo.toml
index d65d5b6..03389ca 100644
--- a/nearby/presence/np_adv/fuzz/Cargo.toml
+++ b/nearby/presence/np_adv/fuzz/Cargo.toml
@@ -8,6 +8,10 @@
[package.metadata]
cargo-fuzz = true
+[lints.rust.unexpected_cfgs]
+level = "warn"
+check-cfg = ["cfg(fuzzing)", "cfg(rust_analyzer)"]
+
[dependencies]
arbitrary = { workspace = true, features = ["derive"] }
derive_fuzztest.workspace = true
@@ -30,3 +34,6 @@
name = "actions_de_roundtrip"
doc = false
+[[bin]]
+name = "actions_bitmap_encoder"
+doc = false
diff --git a/nearby/presence/np_adv/fuzz/src/bin/actions_bitmap_encoder.rs b/nearby/presence/np_adv/fuzz/src/bin/actions_bitmap_encoder.rs
new file mode 100644
index 0000000..6e307c4
--- /dev/null
+++ b/nearby/presence/np_adv/fuzz/src/bin/actions_bitmap_encoder.rs
@@ -0,0 +1,24 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Fuzz test for encoding actions from the FFI-friendly bitmap format to a DE
+
+#![cfg_attr(fuzzing, no_main)]
+
+use np_adv::extended::data_elements::ActionsDataElement;
+
+#[derive_fuzztest::fuzztest]
+fn encode_actions_bitmap(input: [u8; 256]) {
+ let _maybe_actions_de = ActionsDataElement::try_from_actions_bitmap(&input);
+}
diff --git a/nearby/presence/np_adv/fuzz/src/bin/actions_de_deser.rs b/nearby/presence/np_adv/fuzz/src/bin/actions_de_deser.rs
index de06305..db4abd6 100644
--- a/nearby/presence/np_adv/fuzz/src/bin/actions_de_deser.rs
+++ b/nearby/presence/np_adv/fuzz/src/bin/actions_de_deser.rs
@@ -18,7 +18,7 @@
use arbitrary::Unstructured;
use np_adv::extended::data_elements::DeserializedActionsDE;
-use np_adv::extended::deserialize::data_element::DataElement;
+use np_adv::extended::deserialize::data_element::DeserializedDataElement;
#[derive(arbitrary::Arbitrary, Clone, Debug)]
struct ActionsDeserFuzzInput {
@@ -33,9 +33,11 @@
#[derive_fuzztest::fuzztest]
fn deserialize_actions_de(input: ActionsDeserFuzzInput) {
- let de = DataElement::new_for_testing(0.into(), 6_u32.into(), &input.data[..input.de_len]);
- let actions_de = DeserializedActionsDE::try_from(&de).map(|actions| {
- // collect actions to trigger iterator parsing logic
- let action_ids = actions.collect_action_ids();
- });
+ let actions_de =
+ DeserializedActionsDE::try_deserialize(None, &input.data[..input.de_len]).map(|actions| {
+ // collect actions to trigger iterator parsing logic
+ let action_ids = actions.collect_action_ids();
+ // Also show that it can be formatted into a bitmap without panicking.
+ let action_ids_bitmap = actions.action_ids_bitmap();
+ });
}
diff --git a/nearby/presence/np_adv/fuzz/src/bin/actions_de_encoder.rs b/nearby/presence/np_adv/fuzz/src/bin/actions_de_encoder.rs
index 9108169..d96008e 100644
--- a/nearby/presence/np_adv/fuzz/src/bin/actions_de_encoder.rs
+++ b/nearby/presence/np_adv/fuzz/src/bin/actions_de_encoder.rs
@@ -20,8 +20,10 @@
use np_adv::ArrayVec;
use np_adv_fuzz::FuzzInput;
+// Note: The corresponding "deserialize"-type test is part of
+// actions_de_deser.rs.
#[derive_fuzztest::fuzztest]
-fn deserialize_actions_de(input: FuzzInput) {
+fn serialize_actions_de(input: FuzzInput) {
let mut actions = ArrayVec::new();
actions.extend_from_slice(&input.data[..input.count]);
let actions_de = ActionsDataElement::try_from_actions(actions);
diff --git a/nearby/presence/np_adv/fuzz/src/bin/actions_de_roundtrip.rs b/nearby/presence/np_adv/fuzz/src/bin/actions_de_roundtrip.rs
index 7976c5f..6202c99 100644
--- a/nearby/presence/np_adv/fuzz/src/bin/actions_de_roundtrip.rs
+++ b/nearby/presence/np_adv/fuzz/src/bin/actions_de_roundtrip.rs
@@ -20,10 +20,10 @@
use np_adv::credential::book::CredentialBookBuilder;
use np_adv::credential::matched::EmptyMatchedCredential;
use np_adv::extended::data_elements::{ActionsDataElement, DeserializedActionsDE};
+use np_adv::extended::de_type::HasDEType;
+use np_adv::extended::deserialize::data_element::DeserializedDataElement;
use np_adv::extended::deserialize::{Section, V1DeserializedSection};
-use np_adv::extended::serialize::{
- AdvBuilder, AdvertisementType, SingleTypeDataElement, UnencryptedSectionEncoder,
-};
+use np_adv::extended::serialize::{AdvBuilder, UnencryptedSectionEncoder};
use np_adv::{deserialization_arena, deserialize_advertisement, ArrayVec};
use np_adv_fuzz::FuzzInput;
@@ -35,9 +35,9 @@
Ok(de) => de,
Err(_) => return,
};
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
- section_builder.add_de(|_salt| actions_de).unwrap();
+ section_builder.add_de(&actions_de).unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
let arena = deserialization_arena!();
let adv = adv_builder.into_advertisement();
@@ -62,8 +62,8 @@
assert_eq!(1, data_elements.len());
let de = &data_elements[0];
assert_eq!(ActionsDataElement::DE_TYPE, de.de_type());
- let actions_de =
- DeserializedActionsDE::try_from(de).expect("Should succeed since this de is an actions de");
+ let actions_de = DeserializedActionsDE::try_deserialize(None, de.contents())
+ .expect("Should succeed since this de is an actions de");
let decoded_actions = actions_de
.collect_action_ids()
.iter()
diff --git a/nearby/presence/np_adv/resources/test/mic-extended-salt-encrypted-test-vectors.json b/nearby/presence/np_adv/resources/test/mic-extended-salt-encrypted-test-vectors.json
index 52ff336..05ce6d9 100644
--- a/nearby/presence/np_adv/resources/test/mic-extended-salt-encrypted-test-vectors.json
+++ b/nearby/presence/np_adv/resources/test/mic-extended-salt-encrypted-test-vectors.json
@@ -4,8 +4,8 @@
"aes_key": "93FD082896AFAD5C02272FCAC7A4B19B",
"data_elements": [
{
- "contents": "F111383178463B",
- "de_type": 965
+ "contents": "938EF51180242B59D35CAD0DA09ED9FAA4D809BC",
+ "de_type": 47
},
{
"contents": "77A7CA333048119F23A460ABA89494CF7075CB3CC00E",
@@ -16,18 +16,18 @@
"de_type": 596
},
{
- "contents": "7B25D72AF20B8B53495E79E004108C7651C5F9CF07BDCD784874",
- "de_type": 969
+ "contents": "F111383178463B",
+ "de_type": 965
},
{
- "contents": "938EF51180242B59D35CAD0DA09ED9FAA4D809BC",
- "de_type": 47
+ "contents": "7B25D72AF20B8B53495E79E004108C7651C5F9CF07BDCD784874",
+ "de_type": 969
}
],
- "encoded_section": "02E628D03BE58B76606FD929A1D1F814D1163B0AE8EFD98D12D07B1F0D8D0203296CC45F4BB29B1B55859205A94440A19A7D13551244694A00388C0DF6B25405C6D2F6E15D8D1E4383D8D6A28BE510CB8941A7E44040B656C77670B9DE3612BD51E7FC7200C5CA9D4014BD784335CC4C377156D3E8B8750CDEAF086D82715EFCDB87F02C7F96D8EAF7E011169F2A",
+ "encoded_section": "02E628D03BE58B76606FD929A1D1F814D1810D680AA4024B89C147F3CFA89D111D6C511113EB8E2F4C6EA7F07D580F091A77A645C6C9DDB34E285F853162CB5FF3F4C9A23E35F13E25A0FC5B081B3D07057ECF8B6CA366F4B8DFA4A00C4CDEF96F96820048437ECFAF621DF2279CC7B049F41C206B87DBD48F4D3E7DC15C9607DD4823151EF553324931F8031BE5",
"identity_token": "92220A2275B7CB8D5B8F7FDEE137C385",
"key_seed": "74BD3E08D69FBF2DC5431C5D2CC66BF83D602B54FA10334916F9457DD7F309DA",
- "nonce": "C2B4EB521EA983D74D009C90",
+ "nonce": "8DDB143415C9E1682BB41E4F",
"section_mic_hmac_key": "D1A28568BDFFFFB2D23AD8FAA174F72993AC0B5B23ED27CB27E7D99760AE368B",
"section_salt": "E628D03BE58B76606FD929A1D1F814D1"
},
@@ -35,10 +35,10 @@
"adv_header_byte": "20",
"aes_key": "D371D308EB2FE0705ACBECFA55123A55",
"data_elements": [],
- "encoded_section": "02074BA9EC8D17A3258489F865AA8B5AF679E71F22BA9938EDB211EA8163236EA810390E1A6CAE374F5446C039F9A031CE38",
+ "encoded_section": "02074BA9EC8D17A3258489F865AA8B5AF62B5BEDCA083116D24B53499E00C32BD010F8BFCF2A4B88556F8A79A443F34268CE",
"identity_token": "1F01AB90BD3A83FE37E74A301D415475",
"key_seed": "1104D617C7F480B1B5875CE6711FF91BC76FB208F8CD7D9F9A08AA9DB0FBC5A3",
- "nonce": "4FEC96E42BE9C8CD180003A3",
+ "nonce": "7E310C56B5CE9C4BA8E4C994",
"section_mic_hmac_key": "A5E871DC448A3B0EFD26DFEBA9EED9FC74EE02481C6704FE906D7A82E2BBCD29",
"section_salt": "074BA9EC8D17A3258489F865AA8B5AF6"
},
@@ -46,10 +46,10 @@
"adv_header_byte": "20",
"aes_key": "97F371754AFE9C6F5438F467D077CEA8",
"data_elements": [],
- "encoded_section": "02973AC46A20846531A220E935400EDDFEC74BA44660727C7B33874948C21A903010ED5DE380C975B9216B743277D73628A8",
+ "encoded_section": "02973AC46A20846531A220E935400EDDFE98A51EC60D18ABBB0B0C61274FE40A1A1023B3356116212F5DE6FA5E9EBACD1BB4",
"identity_token": "3ECB6505ECA3F0644780E4F848B8C914",
"key_seed": "777BA0008B38A5AF3F3CE15B1DB83B2612E312739447D525885DC0123FD347B5",
- "nonce": "A848DAB9F824A480E1ACC319",
+ "nonce": "94414E7CD43DEA45847C7005",
"section_mic_hmac_key": "FBAF9AF0C9E256671A99C74F3FDB9C17803ED334F5BB24C72FA056E05E3FFF12",
"section_salt": "973AC46A20846531A220E935400EDDFE"
},
@@ -66,10 +66,10 @@
"de_type": 945
}
],
- "encoded_section": "02ED1132C6CB2146F95306E0095791FB969C04AD11D18479F8A868273B5A3C3E1833592069C4077690D4A48D8F5F55C27FF3565B3D859F44E6D4DC439A38BC6063D55F20EA3494469131156AC1568BA18A8AE6322B",
+ "encoded_section": "02ED1132C6CB2146F95306E0095791FB969D1EFF37905E4C06A3DBF0D20E2E39AD33AED329067BC7AD0C29EA1DA83C3BB905C8D0B29057E2194F10DB478EBABCAFF04D74518E59D66150071C355C09893886F33ED9",
"identity_token": "932D597EA1265715083C01111B233551",
"key_seed": "5B1F90B019BC982CA431E8CE303F506E192913E9E0D9E7AF75936F1A8336E29E",
- "nonce": "CE5FAF170ABFEE4B7D446D23",
+ "nonce": "02D6D86872F5247F2091BF29",
"section_mic_hmac_key": "A876F7A6153B0A4E0C00E367D2A62C2E69B8B172AACD2F88F5AEA1CE1992BB32",
"section_salt": "ED1132C6CB2146F95306E0095791FB96"
},
@@ -82,22 +82,22 @@
"de_type": 149
},
{
- "contents": "5912AE51A1D62035C323468AF0198079E1A7110FDBA42D",
- "de_type": 664
- },
- {
"contents": "091D6CE6C774BDF76F52CF04688503B8496460F3E90D0880",
"de_type": 305
},
{
"contents": "248EC62B07E7904634AD309D8238B1514FCEF2D86601BB2ACA027F14",
"de_type": 507
+ },
+ {
+ "contents": "5912AE51A1D62035C323468AF0198079E1A7110FDBA42D",
+ "de_type": 664
}
],
- "encoded_section": "026A74D621344D87428CEE5060FFDB570C73D57CE4A2CFF3EB2C14839E619A1D967170CAFCA1BAF48A2E45501A6453450187A1D4854E4B238A56DB9E26F231288701D10E43D8468A1E661AB447C32E366315B6BC1073EE143D7C02458C56030516A3DB91E367C941689CAFD6FA9252FF37B88EE0ED4D99EB9313B7BE72F5931ED45525D49C1E068C96D133078E33B466EECEC2",
+ "encoded_section": "026A74D621344D87428CEE5060FFDB570CE3C7F158AE3C4F0981AC488ACC7BF5B571DB5D7DB02E0C5C341B92F13919C04C493BA4E9686FAA074220CBEB046F16572CA7B40B801F967B9FFE64D05F526EA7774F06772DD9660BBEBE1B8E7AD5A0955D5D40C07558B38587D989F9E3370CCCD4426A2C5B944400546CB6A6BB36FA142927F275BA740CA093640F3878EDE98A03AF",
"identity_token": "19263E5F61578937D8D7EFC85325267B",
"key_seed": "8578CDF5097D9F1C253B4DDA8471A03AF7F26A01FAE138D2F3D350FBB752FF24",
- "nonce": "0C6C9E6B32B490B080285EDB",
+ "nonce": "3E329806EC2E4418F46877AD",
"section_mic_hmac_key": "953DBE0C875F66FFFA680AEA984DFF74C906D326B58B19A804F20BD104B236A3",
"section_salt": "6A74D621344D87428CEE5060FFDB570C"
},
@@ -106,22 +106,22 @@
"aes_key": "DCE5AF2A5A7AB0A6F2308EF445084D6A",
"data_elements": [
{
- "contents": "44986ED23F155AC13CB849AD9C4FD3",
- "de_type": 520
+ "contents": "",
+ "de_type": 192
},
{
"contents": "9AE30C51E00E42BB",
"de_type": 294
},
{
- "contents": "",
- "de_type": 192
+ "contents": "44986ED23F155AC13CB849AD9C4FD3",
+ "de_type": 520
}
],
- "encoded_section": "02384716FD70BCC1D2B3572C678ACDCB1CCB3ED7DAD80CEB08CD5C6E324C5BF51830123D1A83775462CC0BD667255BD88D21D8A972AF982CFCCB54DE20E1F38F8A7F6E2E525B13AC0AA5282ECB34FD2AA8C2",
+ "encoded_section": "02384716FD70BCC1D2B3572C678ACDCB1C628F55AB4A059E4C8EDC3AC5FB7D856030919F339EF0C362CB8ABEAF11B078BBFA71BF10DBEE786BD3605E35219E22484F00DF510AF0B32B6EDB893760A8E686CA",
"identity_token": "1831C66E762DD247D3D0A3F7CD278B6D",
"key_seed": "2168D48C66B609FA91D4A98D35F0941AB227F1D05CB42C81E6C3773DBCCE8556",
- "nonce": "6BE8AB33FEA8DFCAF84ECC84",
+ "nonce": "161B8CDEFC4FCE9703DD8B3A",
"section_mic_hmac_key": "A62AAB51DDFA839AF5FA651C807D7148269148D0926E1A86D108796B98706601",
"section_salt": "384716FD70BCC1D2B3572C678ACDCB1C"
},
@@ -130,6 +130,10 @@
"aes_key": "A220AB81C5F6D95C89F75244513A865C",
"data_elements": [
{
+ "contents": "CBAD44D6954386F14343090074EF8B4BF900F75B810B",
+ "de_type": 209
+ },
+ {
"contents": "6045FC019235",
"de_type": 587
},
@@ -138,22 +142,18 @@
"de_type": 720
},
{
- "contents": "335D2813B9BB0B3D302B",
- "de_type": 978
- },
- {
- "contents": "CBAD44D6954386F14343090074EF8B4BF900F75B810B",
- "de_type": 209
- },
- {
"contents": "A5",
"de_type": 818
+ },
+ {
+ "contents": "335D2813B9BB0B3D302B",
+ "de_type": 978
}
],
- "encoded_section": "025676256DEEE42A0A9FB667E01DE178058CA5C182B845F41E66D5A78E96C550B1620460461E74C4B4FAA6277A598899798E8E07B73E6B99BBBD4F22767E7B9C751B4AE7FD09529BFC16BFF03E02662E3D2F6AAFB8429B233260311E995B59CB3757479A4F5B1F167E0E461BA882F9F1A95D8B7B5A65014DA76C3796BFAB028974E9F487",
+ "encoded_section": "025676256DEEE42A0A9FB667E01DE178051C64E1E8C22770E3371C2AF56E53049C62DD72B989805575ACED47CD7E158E0F7D335D9571A7A3C5B16263E6F56AA5F4EAFE5E7C744AA111477344E82BB845D59518ADED9348038DB8B8216FBDA04C591F06E50F9A9DE1284E58B8BBDEB85DE63A380FD1CFA0296D02D8FC11C251361F2E65B4",
"identity_token": "E98107D0B6209C2F99E458FF9DDA5EDD",
"key_seed": "67397E84E19D943457D649FC1C85177CA2283A285DEC02F77DAE3223822F10C6",
- "nonce": "84C350AAA3DBEFE6C4AEE93D",
+ "nonce": "932FC786F2B3E5C7F20E5DA0",
"section_mic_hmac_key": "CAC3BB4657D8FE9840CE0B2339AE0F7416D583748B89D950BDE6DECA04EC5C5C",
"section_salt": "5676256DEEE42A0A9FB667E01DE17805"
},
@@ -161,10 +161,10 @@
"adv_header_byte": "20",
"aes_key": "8183EC3E35044B898637AC452F1344F8",
"data_elements": [],
- "encoded_section": "026167137CA22AF2130EA98F1046B4063D4D2353C7595F0631AC75DC9B450BEFEA106A6C243686C795EE3D7B68717556E523",
+ "encoded_section": "026167137CA22AF2130EA98F1046B4063D82A4E779B9D5E8B6F339D82DD0C7CB58103A6115FD8CED2A8E06025FA6D7C2FCBD",
"identity_token": "E0855E9D123AD57CD4BAA1619A33CB8F",
"key_seed": "1572B2064990B9BA7EA8FD487BE6580BED3AD5332CA54605D801D82C25813372",
- "nonce": "1AFDA6FFD7B3CF5FC5C2844E",
+ "nonce": "76EF4B7E8F5A5A0CF983A1C4",
"section_mic_hmac_key": "E6F8DF148FC91C004791D9CB5E6881CA536E14CB366F6B2D73C1C3995161A74D",
"section_salt": "6167137CA22AF2130EA98F1046B4063D"
},
@@ -173,22 +173,22 @@
"aes_key": "083CFF217DC4F7DFFA8B3989A602A800",
"data_elements": [
{
- "contents": "F01E2E1B19F7F410819FD81F2B22F7065E3164E1A54ACFF05B73D011",
- "de_type": 886
+ "contents": "",
+ "de_type": 230
},
{
"contents": "87A24D8B5990862B291D9CDF44A7CF15",
"de_type": 363
},
{
- "contents": "",
- "de_type": 230
+ "contents": "F01E2E1B19F7F410819FD81F2B22F7065E3164E1A54ACFF05B73D011",
+ "de_type": 886
}
],
- "encoded_section": "026DC137DC284DA96BE876644C985CFB021DFDAB61844B115F94E05FA2D4EAD52345AA96C988FB3376D05D29BDA4D027AAFE41CF5E10625A61B12A000FCA96AD5D5BAE78AA3A8C186DCF5729744F572FF4370291D35642D5088995AD034E28CC61D05876729AB1",
+ "encoded_section": "026DC137DC284DA96BE876644C985CFB02C88731E868A1CA66CD26CC9533724EC1453761F0C0F22DBB1BA2C2C99E568E76CB0538F31AF5B30FBDB4BBF2358B4CDCBF24F826FE0F17F6AFE1BAE6A8656E887B1155CD66F85D84406BDF77BC8798EDD9D64B5B2FD8",
"identity_token": "7310CA7BB155529509A89AC74042075E",
"key_seed": "5DC834BD1930C71E749AEEE8E3518EBC09C9B860B6F6372D58A1BD141E89B08F",
- "nonce": "4410414C0013373CBE93D03E",
+ "nonce": "52D6A4A6B432C07B30AC72A0",
"section_mic_hmac_key": "B9B9B858E9859BA09D8A1F17B0E6AC0C5F6286C708C95552A464924B629C7C10",
"section_salt": "6DC137DC284DA96BE876644C985CFB02"
},
@@ -196,10 +196,10 @@
"adv_header_byte": "20",
"aes_key": "B44C62C6931281637F75DE6F1D3F39B1",
"data_elements": [],
- "encoded_section": "02048517CD42EC7AF6C9FB66E2F3078ACFA38819E6A4E0B9BD8859750BADB4232910C0BE5A04C215798BC885D7781C579B72",
+ "encoded_section": "02048517CD42EC7AF6C9FB66E2F3078ACF67F30ADD32EEDB5D0EF0FA1FA4D74C141093A182FCA3D1FB702AEAB247E2029CBD",
"identity_token": "D9EB6B7007B30C0EF6CF01A7910D8B11",
"key_seed": "15F39B4B7DA56E28820DE711A002EFE525A5E32605234508F57E31A12C520938",
- "nonce": "EEABEA7D9A551E134F3BB00E",
+ "nonce": "85CDE9EA7865C23FEED315A0",
"section_mic_hmac_key": "0C9AFE67BD257E145EDC49C851378382C4899F18B61ED1A9D1488D63D9A7AE1B",
"section_salt": "048517CD42EC7AF6C9FB66E2F3078ACF"
},
@@ -208,6 +208,10 @@
"aes_key": "187638BAC5FEA85287840A933BF6BC8F",
"data_elements": [
{
+ "contents": "3D75D65255A28AFA0FFAC86128AC",
+ "de_type": 568
+ },
+ {
"contents": "15CAF364A09A674E23B3724F87",
"de_type": 655
},
@@ -216,18 +220,14 @@
"de_type": 663
},
{
- "contents": "3D75D65255A28AFA0FFAC86128AC",
- "de_type": 568
- },
- {
"contents": "8F87B0032134C16C3A5C55E944216C6CBA1FCA9699",
"de_type": 919
}
],
- "encoded_section": "021131C64A1BDAC5CD00E80724986AB4FFD8B57A83029720FCBDD1CB945FA6A0E95D43C3FEC6573FC13757D4EDF3D85AD806A456778D52E013B1B418BE71E73CC08857EEF334BC2DBC1AE87C04D8D5FC35874B09964455EF5B444D0569F53D2EA3F442011BB799A6646E266EEDB93BB2295E58C17BCE62C63DF2A988773A94",
+ "encoded_section": "021131C64A1BDAC5CD00E80724986AB4FF72F7761F335C0A8D6492072011FEE1D35D27B3A1D096AB462D5774A10C403BFCBE3A892D0FCC04AD0ECDB04903F62B7F8D91C6A36FF96904258F95B14627970430628B7AC1F3DE5520B86E4756B15F4ACD9158630E7261240FEC545A084673F17E5D8BFA743E8A213655CC8A76F8",
"identity_token": "33C178C3119530767A131BA20E39C282",
"key_seed": "B1D18E5E497268A55170AE018FE33B3085490E7FA03A04AF02A6853BAA9C700F",
- "nonce": "19A21AE5D8370686F9D64523",
+ "nonce": "857E607F32D7E3EB454B1B73",
"section_mic_hmac_key": "F751860DCF60C03436AA4782865E8C89C33C1851352793CE41E3E08B5585866F",
"section_salt": "1131C64A1BDAC5CD00E80724986AB4FF"
},
@@ -240,10 +240,10 @@
"de_type": 272
}
],
- "encoded_section": "0284E383F03BCFBA802956AC1D331D49148DBDAE82CC80EF8E854495252475C3491BC6A881642FDDA6C4B8403F32D4E3EBC65F5702BA53D740EA6A01C2",
+ "encoded_section": "0284E383F03BCFBA802956AC1D331D4914F9D98E83FB7D7D4C62A8C97F6F51C4721B4A4BA5BBB0BB008A26088C30DB3558BAB2F56B830BECF7F5151ADF",
"identity_token": "2AD4A3ED2DB4D967EAC7F080F4CF40C8",
"key_seed": "19F9E4A47586993C7A276A929D00A0FFC15FE2F4020CF6B05B04C4F4D0C6DD1A",
- "nonce": "F807F49730187CB3ADE6199D",
+ "nonce": "2FF2983256F39AEF0969A9C2",
"section_mic_hmac_key": "1576D69950F0F1E079F379F3ADB914775C4AD5AD22CD61561F8B9372225E8019",
"section_salt": "84E383F03BCFBA802956AC1D331D4914"
},
@@ -260,10 +260,10 @@
"de_type": 582
}
],
- "encoded_section": "02ED47A961048AF2432C532A68C85D8774E661489D120680C88F13AAEEBB7FB3492F43D8628F8F4F0908A22FFA780DDD956F89A5050DBB98E4F52C25FF30349A76D9B9E39D7FEABA2E63CBAA742B683B65",
+ "encoded_section": "02ED47A961048AF2432C532A68C85D877474E6F8E2CFDC5A41D426DD997DAFBB7A2F31861469D5E54F29F051D081BB62F99957460C89D8522045E2A7710F3C602D7ED322E4D89A66C766DE666A3BF8A663",
"identity_token": "82A145333F4DEE9F069AADF41C54F072",
"key_seed": "DE758EEEE4750E04730AA94B4F238B6CD535F551C2BA67446A254314D600ED28",
- "nonce": "4241309102A36EEB9CECAD67",
+ "nonce": "3EFF20D6818AE42E8FA2A674",
"section_mic_hmac_key": "4C1043B09127BBD4807B84CB8095EB362229FD36D9392553B988807B8EA386D0",
"section_salt": "ED47A961048AF2432C532A68C85D8774"
},
@@ -276,10 +276,10 @@
"de_type": 389
}
],
- "encoded_section": "0288E7C4BD0D3B41077F9D17F9CDFAD09AC4BB564892C8E1CAFC76393E1BCBFF0720893D1D78C7F60C4A4379DA5631EB6410BBF5B9D2E695C95895F1386B3533A72F",
+ "encoded_section": "0288E7C4BD0D3B41077F9D17F9CDFAD09ACCDB7E4CDA659E68595C9B89D5B8E0A22072A5415D083EC0F769B41BE30060115EFECEE838B8DF8A5309BDAA99D18CFA22",
"identity_token": "98AC76B171BBA2D6F74D2EB070CCB2A5",
"key_seed": "289A2476986D21D1D4461D43370BFB00BAA75DB8129366D1B287E1DF2346D686",
- "nonce": "5AA5D7324173A3A8B25CFD2E",
+ "nonce": "4CFE10A009E5198109E28D63",
"section_mic_hmac_key": "613B7BEC61CA3F4917327E9F57265497A2DA3FB4A8D1A2E64F450E70BF4C2A55",
"section_salt": "88E7C4BD0D3B41077F9D17F9CDFAD09A"
},
@@ -292,10 +292,10 @@
"de_type": 600
}
],
- "encoded_section": "025588191AB27D12C53FBB0AC6514C35B6A213087FD19806FD6344B4BB336E49962992FB90F69A43E3D3281CC6E20822037F382FB9D98F25653F9FA6984E51DEBAEA3061605A4614434689",
+ "encoded_section": "025588191AB27D12C53FBB0AC6514C35B6E830A513819FEAA85A389FBCC50FF9DB2995C6EFBBFED2FA8A87F90F47E202B976AF897C2B2440A763F88D5D7EC6A4B70DB98B431147A0D223BE",
"identity_token": "A0A4A57454A31A1B4CE36D757ECD18AA",
"key_seed": "AB60BD9D7B2DBE281E8A6EB916B2042923046CF68F36CDA67F27E117AD4A7CC2",
- "nonce": "82E43E99AD73B19CFD430B95",
+ "nonce": "24621952E3CB925DC189A614",
"section_mic_hmac_key": "8F4D7D6A549F07659A3FC656056C17916AD3A4ADAC5DA504388BFEF875990157",
"section_salt": "5588191AB27D12C53FBB0AC6514C35B6"
},
@@ -304,22 +304,22 @@
"aes_key": "4B9221D8B63D26A9711B130E1C0535EE",
"data_elements": [
{
- "contents": "ED6EBC",
- "de_type": 591
- },
- {
"contents": "1D8DDBD8DF0C950711662DA284BC23FA6C6FDF479636",
"de_type": 29
},
{
+ "contents": "ED6EBC",
+ "de_type": 591
+ },
+ {
"contents": "7AD06A9E86A0A2DF",
"de_type": 596
}
],
- "encoded_section": "027EF3DDC32AD8B67DEB708CB96FACF7E87C2ED0A5F105F30E74CC01BA5995023B39908CD7C3A28D416AD43B60A822D3A6781CEA5EAD3A26EEE188FEDBCC4B86207D626E3A7C5177E7EBB46D720404463C45A0832B3B146BF9E842",
+ "encoded_section": "027EF3DDC32AD8B67DEB708CB96FACF7E8F3B20B5C947D3D5DB7C798EA8344A3EF39625503E9EF00272137DF1791181D424A99F4D8E12DEEB3946E9E642FB53A168D107803A72C545AF8B1DF6F33BFE95E65F8E976A0D7282C371F",
"identity_token": "46470C0BA32542A5ED33D66AE1481EE6",
"key_seed": "7397B457C7BACC338D5C6134346FAF2D3164EE0E53FB48C18EAB31257483E83C",
- "nonce": "BF76260300391F69E456FE0F",
+ "nonce": "80507A85583D26817E6FEDF0",
"section_mic_hmac_key": "7056AD67950509C88DFEECEBE92D739F5C702C566F1854EE785EAC1509C26815",
"section_salt": "7EF3DDC32AD8B67DEB708CB96FACF7E8"
},
@@ -328,30 +328,30 @@
"aes_key": "1E806EFC9EDD141577721BB5E61CA53F",
"data_elements": [
{
- "contents": "57A7035EA380C98F071876E21F4CEAEA67E964A732568F5D10AF",
- "de_type": 518
- },
- {
- "contents": "913B76C6EA7BE3C1FAF1C2E1CD4F929F4F56565BCC1113674C17",
- "de_type": 898
- },
- {
"contents": "",
"de_type": 415
},
{
+ "contents": "57A7035EA380C98F071876E21F4CEAEA67E964A732568F5D10AF",
+ "de_type": 518
+ },
+ {
"contents": "5D48E33377BFA3527720447FDC7F8DA97DFC44DC4A",
"de_type": 588
},
{
+ "contents": "913B76C6EA7BE3C1FAF1C2E1CD4F929F4F56565BCC1113674C17",
+ "de_type": 898
+ },
+ {
"contents": "A3",
"de_type": 908
}
],
- "encoded_section": "026567A1F3D63165D9D7A854C9642CE61B9DE8B6E101BE92EC9ED692E04BF86E866946345059A1F4F9C54A625AC3EC77B55C37C774CD11AF71385AFB813F8DFECDB95CA83929CB8309AE5206F125E22E25368DA9580EAAA50F61EAAF37FA6A61D44CA946D3BE28D9E298858E09DF3D50CEF793CA14D94A733C1FE663A8ED047D4718336108C1154EBCE52E",
+ "encoded_section": "026567A1F3D63165D9D7A854C9642CE61BA6406E935611C20294BF9C49139D708C69DB3EDC8906DD2FDB1B670DC6F947298948D9123AACBD24C374A80010D9AC58C47A48112818C0F0EFDC1CD30544BAF43139D1263DC59FD76410EF3345E221F0DA8C6C775985B167A62C115DBA6AD97AD23D2243EBA8A7225952DE25E52A06C6817DA9C3ABB84AA871E3",
"identity_token": "DE24F284089324DCB3AF76801C2F94B6",
"key_seed": "6555B34DD6A332E6F727381BCE1A2B9FBDEC1B5367367BBCFCB5071BCF8178DF",
- "nonce": "F423CF36EF58853645712DF1",
+ "nonce": "ECF6EBAB32BAB916658BC0DC",
"section_mic_hmac_key": "54C323CCC04E240721D7F77C1205F216D251C79E41FCB3695EAB28C6C5423C40",
"section_salt": "6567A1F3D63165D9D7A854C9642CE61B"
},
@@ -368,18 +368,18 @@
"de_type": 110
},
{
- "contents": "BDFDCF83C918D94DDE25E6A13E0586CD83EFC76F5C43AE46CFFE",
- "de_type": 674
- },
- {
"contents": "F2C0052877838299",
"de_type": 273
+ },
+ {
+ "contents": "BDFDCF83C918D94DDE25E6A13E0586CD83EFC76F5C43AE46CFFE",
+ "de_type": 674
}
],
- "encoded_section": "0282028A9CDE5CFB8B7B85C55034433DD7A9C6DC3CEEC3F54854F91708D8DAD5B458C396ADD9D43DF02B09513E3E428A0B91BC838C3D10B5D755ACFF0BA217A77117A96C3E2D5849F2861E7DC257A3450D740424901564F7F25EA0E7E239E628C8E22381D9B0FA86896E0A562DDCB26E1E6C4EB2DB7D4559CEA1",
+ "encoded_section": "0282028A9CDE5CFB8B7B85C55034433DD7B62AEFA5F7AB29B1ED82CD2ADD56881F58B5AE70A4F495EEF52E6924FF815F1C96E88C9C428CAE000BD469ECB63CD662E4A49F5277763712E2DFFF38F24A2EFAC5B862E5772375ECFA65F3DE5464F605AA20BDF7D64880F2B8C2E398DD3F9F36A45DA7D5C23276D969",
"identity_token": "15A7903360B4430A13965FC72FA2B9D2",
"key_seed": "E06BFE99B917D1C1B38AB13D3F6A2163AF91507531E86E5F6F32D7116D94AA03",
- "nonce": "F1728A657AD70C28875CBC1E",
+ "nonce": "35AB729E621EA290DF818D6B",
"section_mic_hmac_key": "3AEAC66D3852FEB86D7D1FCBEA0A9D6EA47EFE74BF6F2CB6E861BB1ABE8E3657",
"section_salt": "82028A9CDE5CFB8B7B85C55034433DD7"
},
@@ -388,10 +388,6 @@
"aes_key": "C606686684F622AEC70CAA4F201F2EF3",
"data_elements": [
{
- "contents": "4FA0D0",
- "de_type": 599
- },
- {
"contents": "B3E804B287672E412D53B1183CEC5D",
"de_type": 174
},
@@ -400,14 +396,18 @@
"de_type": 461
},
{
+ "contents": "4FA0D0",
+ "de_type": 599
+ },
+ {
"contents": "C378C50F",
"de_type": 800
}
],
- "encoded_section": "0222C5A6BC119ACEB0EC9EDCA6D57639B767DE04C14B5F0B4DEE8B49E4DE3AC7CD47CDEFFB4ECFDF012DED1CB340D2E3D98D7DE9970BB9F48ADC6998B8A52B1CC674048CB3C58752BAFD16B73DC21D36973DF5F18BA6027AD6060AB0EB78862BA8D7BED26968C3629F",
+ "encoded_section": "0222C5A6BC119ACEB0EC9EDCA6D57639B7838DCC2A5707256A3C37BCCE47718C4A476606558F66F18ADDA82A14341749A35EA279495EDCE2F7E71CEEFDB7F0FC1459AA1548ECBBB9086E93EEA3E3EDC6AA0F01C528B1AE2E605CBF882460663099F512C0DF784D0318",
"identity_token": "59BA27AA8A76A50F12A5D2BC8EDA7635",
"key_seed": "CC49C0BC358FFB2BF90A62F446709CA6A6FD1CE686EB7D4EB37FF620AB5866E1",
- "nonce": "D222455382EF2556A27EF3B3",
+ "nonce": "0691DE8FD873847EEC770AF5",
"section_mic_hmac_key": "507CDE4A24AAAD3B43BE6AAAAE73CA5C5F59D011AD220F1272E7FF4D43747F22",
"section_salt": "22C5A6BC119ACEB0EC9EDCA6D57639B7"
},
@@ -415,10 +415,10 @@
"adv_header_byte": "20",
"aes_key": "A33086631E13B0A0226E32EF4D4CCDBD",
"data_elements": [],
- "encoded_section": "0228770D81B47DD0E4F188546B893534900F41411BF870D5E9458D457698D1F74710D4A0F4340600E0E9285E5E6F3351E3CA",
+ "encoded_section": "0228770D81B47DD0E4F188546B893534903127796A1C6A14E6E84CA3EF1AD56F7110FC3875749DAD6995606291950CAE4990",
"identity_token": "92A3CA4B2F9F59AB64EFBBFE25121C7E",
"key_seed": "D6AB21137556AF94989CF92E53261B867163C16147313EC5275B8E1DD5073BE1",
- "nonce": "59480DC604E5C6AA30BFEF23",
+ "nonce": "43929FF1A733D5F331B7585B",
"section_mic_hmac_key": "9B12B9904F7ACF8E22828B994212CABE3FC8B85F6121D1E5D47951473A0379DD",
"section_salt": "28770D81B47DD0E4F188546B89353490"
},
@@ -427,30 +427,30 @@
"aes_key": "05099C0809FFCE0781D65DB0E2F6B265",
"data_elements": [
{
- "contents": "3B",
- "de_type": 789
- },
- {
- "contents": "2626C80C748B72F0A32FC66788960604A3FAED3070AB9114B8E358106CBC",
- "de_type": 671
+ "contents": "",
+ "de_type": 145
},
{
"contents": "09F36841",
"de_type": 406
},
{
- "contents": "",
- "de_type": 145
- },
- {
"contents": "BCBA4F2899DE",
"de_type": 446
+ },
+ {
+ "contents": "2626C80C748B72F0A32FC66788960604A3FAED3070AB9114B8E358106CBC",
+ "de_type": 671
+ },
+ {
+ "contents": "3B",
+ "de_type": 789
}
],
- "encoded_section": "0227A3A0FE2CD4B612A4DD0355C51E1C2D53FA09EAB78824316A73D15FC3238396482A461EEBF605D7B33293B3D196BF25B1667EA66BB514A7F7EF27648FB8048B94FA70B2D4EA36BCD86D913ACA072E6D89CF3F5490550820D362E1A8B395B539B466D74A9592AA8ECE",
+ "encoded_section": "0227A3A0FE2CD4B612A4DD0355C51E1C2DA549D34409F3E6278AE9DBBDF7926BF84831F70C46BA3626FC43665414B5D35D2CCA700A9EDDD585BBF13AEA969CF7EC526AC6570381F33CCC0661B63610DE987FF55B9A2E4E664C3DC13AC6C610D9048B23EC6D043B7AB4A0",
"identity_token": "C76668A2A2FA840BC9C89353B8540B43",
"key_seed": "162E48714870CDB8C0BE54F698A9010FFBC9532C15C8C2DD130D579AB4AE4512",
- "nonce": "08860371E5C35E5F8B076FB5",
+ "nonce": "38A5ED39047B0C0B125E6AD3",
"section_mic_hmac_key": "DF7611BF559EA9C2D8A0A5E7032D4813971952E0A8701C4F55652939CA1A47BF",
"section_salt": "27A3A0FE2CD4B612A4DD0355C51E1C2D"
},
@@ -458,10 +458,10 @@
"adv_header_byte": "20",
"aes_key": "B5B046CFE2E170E4E29529FC53380945",
"data_elements": [],
- "encoded_section": "0209539A273A8888A0158CB6D08FE4CC1C0839E0605B3EAC31AC2855629A66D6F2106532ECC714BF797E4A08D85E49AE31DC",
+ "encoded_section": "0209539A273A8888A0158CB6D08FE4CC1C233636E399DEA8C28C19FC1E2045F75810BEDB20E5FF5AECF9E754F02A91F3C53E",
"identity_token": "A9BF53E9D87F9E63328C211778CEB74B",
"key_seed": "AEF3279A8EEC857AB241D4B6678BAC2CC4EAA9CABF2A4C229E9FE17C44D725E8",
- "nonce": "025E4CF04B1E5B5A77BFCA51",
+ "nonce": "86C3A91026BB64BF6D242FD9",
"section_mic_hmac_key": "967C2FBC5F186228F868E5BEA0482273B1686A3411687FE6123FA533BB4D7FC4",
"section_salt": "09539A273A8888A0158CB6D08FE4CC1C"
},
@@ -469,10 +469,10 @@
"adv_header_byte": "20",
"aes_key": "5126EA9102AD6B5ED537A4D06C62A735",
"data_elements": [],
- "encoded_section": "02FA35178CEF220907A4C1C1EF1AB507C7D294D3B20708B7D18524BF185DA511A510052AA4B0C8474901B9B7E8D7B64AC9E7",
+ "encoded_section": "02FA35178CEF220907A4C1C1EF1AB507C70E0236B7C636FD566856422451128D1F10596B64FB5C8C4E964C3BFC4C25B0040A",
"identity_token": "027C381EF363ED32EA4CECA28DAD7FA8",
"key_seed": "6C43637A9F73217C7EC4D4754A0B292230B96368E7D4A70FD3C42B3F42E11685",
- "nonce": "3A6CD0CDD1DF482C7A7B44D7",
+ "nonce": "83397983A62AEE8831A32AA7",
"section_mic_hmac_key": "E2F8A00B2F7AC93D7BA123A5855124052A4077FE7CC331DB260AB2D284A87E1A",
"section_salt": "FA35178CEF220907A4C1C1EF1AB507C7"
},
@@ -481,22 +481,22 @@
"aes_key": "6451E8C836D286C2C658118FF5E0F808",
"data_elements": [
{
+ "contents": "A0151F3798A7E3E871",
+ "de_type": 81
+ },
+ {
"contents": "7E07F945516A13D0910FBBB4C05258A4B21405268DF1DD3DA8EF",
"de_type": 175
},
{
"contents": "86D01ACD89762B2F",
"de_type": 691
- },
- {
- "contents": "A0151F3798A7E3E871",
- "de_type": 81
}
],
- "encoded_section": "0278DBC03386503100B99EEDFDAA3A54AA59606C74808EA12DF14116C0AAF2551143D8F46AFA3CDFE853CDB017AAA6AFDACBD1B5FD0088A709C4B9F007DD53269BBF736DFF095E0C7AD6087B9B8947B5F6D22C5ABC75278BAF2ABDD8D326B3D07C96C370F1",
+ "encoded_section": "0278DBC03386503100B99EEDFDAA3A54AAD394C57258726071583E06AFAAF65FD843672F332AE260F6D2AAA068CAAAF9AFF97DD4E761CA62AC5FBD1449645AEA90A5C1B98078282E842CB5A7D8F0585BF1DBA64846E7FA3E88259DCFF7E16B38D8287529F5",
"identity_token": "5A1729E7B2B2193C67112CD8DEC59637",
"key_seed": "82158E1C98CAE04C9FF662087015A804F181B25565FC29BB0CCEB32AD29AF8A6",
- "nonce": "1C4B30EFE7A7EE25837487DD",
+ "nonce": "7D50A5BB3443683A76247D3B",
"section_mic_hmac_key": "3A3FEBA1F8EC810C8ADA1EB14859C5CC3B1A391B0ABEF72BAB65DC1EF1AD7E43",
"section_salt": "78DBC03386503100B99EEDFDAA3A54AA"
},
@@ -504,10 +504,10 @@
"adv_header_byte": "20",
"aes_key": "37CED522A58075CD5910D4AD5C755AFF",
"data_elements": [],
- "encoded_section": "029134AAA791A8F2F363752DE40D98CAD079A38F6AA2E33B1F368213CA503B1EBF108276BDB43AEFB7AF93C1B94EBC08EAF1",
+ "encoded_section": "029134AAA791A8F2F363752DE40D98CAD000E13B076720B28437674B7F79285DBA10A7E2E0FFC3EAFEC0C9BAA810328B6FF9",
"identity_token": "542BC2A7B465A7069300EA500112834B",
"key_seed": "B6DE0DBCA9A2C1290749B479AEBFF54549CC733CA19439E66A8EC38A81AEEFA1",
- "nonce": "D8FB3E5CBEECB405135DE22B",
+ "nonce": "8E5363189BB47D2864CA13C3",
"section_mic_hmac_key": "311EE9A96B22421A80A8813DBD29ED80FE7AC3212A64988033105047A72A90E6",
"section_salt": "9134AAA791A8F2F363752DE40D98CAD0"
},
@@ -516,10 +516,6 @@
"aes_key": "D03161023435EBFCAF2F2D0ADAE25EFC",
"data_elements": [
{
- "contents": "28",
- "de_type": 471
- },
- {
"contents": "6536FA2872CDAF623C96BCEFF2471CB4E036039A4BE890ACB57FB1EA68",
"de_type": 72
},
@@ -528,14 +524,18 @@
"de_type": 118
},
{
+ "contents": "28",
+ "de_type": 471
+ },
+ {
"contents": "1D4CDFC938C8BE1CEE8BC23DA0D65E51121C1CE238E37259EE389D12",
"de_type": 778
}
],
- "encoded_section": "02A17062B53D66D4533A53A453F2485BE62F35088593326E7742857102AD4D29E45FD71D6D2423810C4B94EE6C28BCB8D171D49564294B141921978D180CA20F1798397775BF0E1F4182C915E186E768837E8E7779611BF91FD12C2E9E8608BE33C4AA6E7664C477D7186E03151F5692C1D61E246BE67263333765EF7573769AAF",
+ "encoded_section": "02A17062B53D66D4533A53A453F2485BE6F2BC711D3EDC795216CBABBD177C08D55F256E4922CA3472F0CCAB0E8AC41847748C3EE21A4296E6BD88E2B75DE17EB71ED78816E129C8F07138DB241FBDDFA8ED326E577294099F6D2DA1C0B85BEBFB42E02CC87575A46F85660B117AC809E807A26F3BDCD90B9A600317FA26F6496C",
"identity_token": "DBB90C1A48327EF7A00F4B43D68A1F81",
"key_seed": "92750637170C948A4C8F8DBEC0C99DFA3B3F62D666E78860A0E66090E9EEAF46",
- "nonce": "90059268A0BAAB4B5C27102D",
+ "nonce": "7E297CF559E117E7DC980882",
"section_mic_hmac_key": "D894D0DBC6434DC2E305B9DBAE11164339617951C9503578183E2243A89A0A62",
"section_salt": "A17062B53D66D4533A53A453F2485BE6"
},
@@ -552,10 +552,10 @@
"de_type": 745
}
],
- "encoded_section": "02C9525C2999AA4C2313D3A88E0DD65385C468B7D341E9C2DA03D43E31514EAE512BB2AD526CF2AE44A758B4DEF16471C615F34A79E7CB8E4294BD91EA791BDA401DD3F4A6BA80EF87187D037E",
+ "encoded_section": "02C9525C2999AA4C2313D3A88E0DD65385640A3DB5BD2D722E28E39B0A4EE6A2842B7D144CFFA4AC1674EBB6B0661388EED23168F3BBC532444213CFECEFC91BD58CF5259A0A1BE04491C6ACC7",
"identity_token": "67B6209E52C3003631906D5B6F8F99E2",
"key_seed": "E8BBEFAA58A804851A6A5FEEF442A5B42E1790D624E171D82666F37D98F479AD",
- "nonce": "13ACC6C2DEB7C80286605695",
+ "nonce": "46F22155A4F8C0E30987FDA1",
"section_mic_hmac_key": "2C5777BA0FC851E2168A7F6A065542A454A00FDBC37796DD2C0F61B8F0F479DB",
"section_salt": "C9525C2999AA4C2313D3A88E0DD65385"
},
@@ -568,10 +568,10 @@
"de_type": 232
}
],
- "encoded_section": "02CCC6C1F682DC2B26C2F0EBD88DF8CD10B46827F6998B5BCD033810162169C9E6177E21F144FBF887F2D62C9384E5C67E9C293E808D79710B",
+ "encoded_section": "02CCC6C1F682DC2B26C2F0EBD88DF8CD1092291E748721229EC5A2757E1646F8491702678CD109CB08C9889B287B855E8A9C2628DC35CB6C11",
"identity_token": "84A064CB500C36450216D83A3893987A",
"key_seed": "BD2E624D9CFE4D8BFFD5F64E71D91E7E6C355207AAC713DCD27E762E43F93778",
- "nonce": "2CD2585EC8A6FFB46BDD67B7",
+ "nonce": "3ED04F786496FCD3F1D98071",
"section_mic_hmac_key": "CBA836A7141228C2F80DD3CBA933639E4901D26EA5D596DF73749070389E72FE",
"section_salt": "CCC6C1F682DC2B26C2F0EBD88DF8CD10"
},
@@ -588,10 +588,10 @@
"de_type": 492
}
],
- "encoded_section": "02E13EE1C144ACAAC46546A7FC73BC517AEC2793AF4C8289C1179DB702134B875F279099ADA5B9F9D20CF79506163A1BB5B7CB158AB8FEB2D5BED7A9DC2737819FB7B5DC2475404D58",
+ "encoded_section": "02E13EE1C144ACAAC46546A7FC73BC517AFCEA4D8215565571659B8FF2495ACA2A277AFD446A73708A0A88DF79C1823A07F7F0EE51129B429EEAE5B2C9D2FFF797B039EA9DF89948DB",
"identity_token": "8206FA4E58FBB7DB6CCDC6A8E17D215A",
"key_seed": "9BBCA7052AAB7FFE1E37DCE21853BDC901CA146141C567A77E042D1DB8E7677C",
- "nonce": "60FE311C1DE98B1F0CB8F645",
+ "nonce": "999BBA892A9C08BC7B7AD3B5",
"section_mic_hmac_key": "9AF2DB9C6C964A6B120625992CBFE9810428FB44F41EA85EE8C98FAF048B45C3",
"section_salt": "E13EE1C144ACAAC46546A7FC73BC517A"
},
@@ -604,10 +604,10 @@
"de_type": 698
}
],
- "encoded_section": "025368A3DEB0ABAFA3684502DBF3F2D2F1A4919DA6DFA241CA937D946397B2FAD624E77A62AC2FC3FFEACDED692D3C146025FD5E9A540D93A6BB85443C5958087A248897A165",
+ "encoded_section": "025368A3DEB0ABAFA3684502DBF3F2D2F1FD471CE0B11E23B2A265633D1DD772AC24D30CD194896CA9682AE602BD5EDC55338FD9DFE307589320059328B139A2B98188C94205",
"identity_token": "2F46468244F32581D94A0E134A269C27",
"key_seed": "8C34898BE3791991680388FDAE3F11539734BD838C00F4D721D886031B1CD8D1",
- "nonce": "158DA837D312152F80C64662",
+ "nonce": "D9B22B06AA550C9B6AAB174A",
"section_mic_hmac_key": "62BCFEF7FABECF3EF0B822A4970BF3444DB880A02805943EB055FE7E3E5BBFF7",
"section_salt": "5368A3DEB0ABAFA3684502DBF3F2D2F1"
},
@@ -615,10 +615,10 @@
"adv_header_byte": "20",
"aes_key": "6FF3996ADE933F9445D5D9B3BA72ECED",
"data_elements": [],
- "encoded_section": "02DFD2A8ACFCB1979E11868B2EB2A148861E7D3C74802325EA20998C2CF3013BA51070A82DE70003EBD48675EFDA6C8A1040",
+ "encoded_section": "02DFD2A8ACFCB1979E11868B2EB2A14886397EB5FA16DE03CDCCA26A26C389BBFE10AEB0660AC8C3D95688954032CEFDD18B",
"identity_token": "573B7D3AF2FD3EDDDA487D8254537EAC",
"key_seed": "45B309037CCE98B8DADF3DF7818D3A3B842E292E9F8C54E82F10C4B91EE601DA",
- "nonce": "9FFEC13ECBB99430B280A426",
+ "nonce": "8044ABD3D0D46FD3628C9015",
"section_mic_hmac_key": "4C699D0E312750AA09EFCA9A4C8C4E7E032BC4D5398A6F72F4FB6AFD22D64110",
"section_salt": "DFD2A8ACFCB1979E11868B2EB2A14886"
},
@@ -627,22 +627,22 @@
"aes_key": "95D640F8FEEF0F476D173FB35C70E3A8",
"data_elements": [
{
- "contents": "0E32AADB46",
- "de_type": 405
- },
- {
"contents": "DD700C351B",
"de_type": 316
},
{
+ "contents": "0E32AADB46",
+ "de_type": 405
+ },
+ {
"contents": "C58F70E4A3F3EDBD5E8E2F0A",
"de_type": 651
}
],
- "encoded_section": "029855CCB82C1B5CB8DBD66839B011C07894F5F607E6B8C7FE9C8841A25BB06AD92F451F1DAD781A11061A62ADB2B7F8E951759290C76BCF3497C6323C597BFDB3DB01BDE012660251994A30CEB69F3E76",
+ "encoded_section": "029855CCB82C1B5CB8DBD66839B011C078A98106D7B7B517BCE3F9C907213AEC362FDE9B1633B1AC01A21E74960B81496032F5E5EFB0FC0EA23451DA50409B411DA9D171BD37BBB8F6FC403AA26BE27F1A",
"identity_token": "4D475A274742151475B341C48194D9F5",
"key_seed": "4DCF489FA193E73A86A4EB4C3268CEE5B950A1957C0CA006F3422A15A11DD205",
- "nonce": "1BC76946373179415F596851",
+ "nonce": "A21A6E2C322AA1CDEAAD0BB6",
"section_mic_hmac_key": "3381BD66395B21BCFD1C73E775C967784C3F3DE77F2FC1EBE5D2CF5EEACB91C3",
"section_salt": "9855CCB82C1B5CB8DBD66839B011C078"
},
@@ -651,26 +651,26 @@
"aes_key": "31B7CC1805DAD1ED55DFCBA3ABD6C01A",
"data_elements": [
{
- "contents": "D5D8A14C3B06",
- "de_type": 534
- },
- {
- "contents": "4D974CBC01B6728564EB30C3C780500EC117E0B2667EC576E10D7DF5",
- "de_type": 364
+ "contents": "F1A1CA7D01BCF394",
+ "de_type": 115
},
{
"contents": "79CC9D9954D0C77577859D94D862FA",
"de_type": 131
},
{
- "contents": "F1A1CA7D01BCF394",
- "de_type": 115
+ "contents": "4D974CBC01B6728564EB30C3C780500EC117E0B2667EC576E10D7DF5",
+ "de_type": 364
+ },
+ {
+ "contents": "D5D8A14C3B06",
+ "de_type": 534
}
],
- "encoded_section": "0220E344A3BD5D875DA30DF57C04B32022F56AE8CF9A5F02F774BD2483829674CA544027FC5D3CDE71FD19D2AE4861CB74ADE4484C3F758870EB49C211D07B2A0B1DBCAB684203713EC5ADC830B6FB75C927813EFC51BAFED6ECDF8322B679FB9190DAD7AA7915BDE58E399FE58066EBAB338B8F1AE1",
+ "encoded_section": "0220E344A3BD5D875DA30DF57C04B32022E45BF9E79E06ECFFF32F25AD8B3E217E54216A5576963A2E91F51020747FA0C5E5FDB28FAC1DAFB5CE78EC1EE174DD7A5393E646F1FAF05FFD1742B9944E6D21BC01B72DF92C5C8BD25162296CB06BE8B3D20DE8811795B80EB2F0977D6A5167579A117A86",
"identity_token": "4AC470B5857B8F029D2F8E4B634765F2",
"key_seed": "41F01CFE759E12F979BB0A6B23DBF53D408761D4F667CAB71042C9789588CC44",
- "nonce": "8BACC88ED412BB6E927D153B",
+ "nonce": "A5444700D6204D05AAE55A13",
"section_mic_hmac_key": "E7B1D619DF9082D0A9328111D0D72FD515B276BA4954B697B942363DBD12120D",
"section_salt": "20E344A3BD5D875DA30DF57C04B32022"
},
@@ -679,22 +679,22 @@
"aes_key": "D75007BB49FEF17E0CF767B19412BA83",
"data_elements": [
{
- "contents": "483FF7B534C3DC8427",
- "de_type": 702
- },
- {
"contents": "",
"de_type": 310
},
{
"contents": "02826430E4AD154BAD4FC491CFFEC8",
"de_type": 638
+ },
+ {
+ "contents": "483FF7B534C3DC8427",
+ "de_type": 702
}
],
- "encoded_section": "026B334E9E52C272A60F1CD9AB613752273F973DF5218C53E2B5FC7487309A978F31870C486D424DF9B79D0D68C6594ACC1E30F8F4D7F3FC7E559FDBEFF1B88A849FB996E466D2F725D8C76BFC705F34227E67",
+ "encoded_section": "026B334E9E52C272A60F1CD9AB61375227B970AD70B3052D31630C95E039FC1F4031B8DB54B7171592576912B10C25B83D469931D3DD42821174D3DF901A0ABA1DAA242897C1A216EF1FBE8104688ECDF01369",
"identity_token": "60040508990303A048FACF72038CE464",
"key_seed": "98DF428BC184564FB1DCA947106B8F89CBB668C74A9CB48C0721430BD2047A1A",
- "nonce": "C4CC65DCE3FA0CC241D9AA35",
+ "nonce": "3A0C1B68E3F4B05C3724868E",
"section_mic_hmac_key": "49AB3D69719CCE05B5A246DCF61AAB29376178213347CA7F318F9661A21CC63E",
"section_salt": "6B334E9E52C272A60F1CD9AB61375227"
},
@@ -707,10 +707,10 @@
"de_type": 900
}
],
- "encoded_section": "027EA134E36AA6249D20357948A07A39430106C70B56C2DDD6649EA769968F03AE283A99A8441AC04033B08878A90E7684242045AC3A191170C5ACBB55B2218679CF770A384C7FB67E06",
+ "encoded_section": "027EA134E36AA6249D20357948A07A39435B9F7C339FF62D7ACFBB9CAB71430F8B28959D7284E9D6C5AA0A51E87163652FEA7C29B02611D7EBDC043967CEBD0062FE45EB0CC0A59DB0A1",
"identity_token": "7FCFA0BC8F7C8C85EF59ADB9D250F8F5",
"key_seed": "7032B5E67A8BEF82845ADF25B6152283D544435CDCABFCA0750BCB81A84934A8",
- "nonce": "018137DF8171014B5AA4F794",
+ "nonce": "DD2CABC763518974772680DB",
"section_mic_hmac_key": "F2571523E2DC1E97A4A1D95D74967F22268C47733385C3241FAB1653A1F61BCA",
"section_salt": "7EA134E36AA6249D20357948A07A3943"
},
@@ -719,6 +719,10 @@
"aes_key": "135CFF48972D3892BD5236733CBDE72F",
"data_elements": [
{
+ "contents": "ADF4960FABA6559565B659F87C13DD9C0838CCF7881CDC",
+ "de_type": 133
+ },
+ {
"contents": "13E83591B1805E6EF828EBAB8189A0F7F47E65F6EA9B8EDD",
"de_type": 325
},
@@ -727,18 +731,14 @@
"de_type": 514
},
{
- "contents": "ADF4960FABA6559565B659F87C13DD9C0838CCF7881CDC",
- "de_type": 133
- },
- {
"contents": "F4884DC85E0B24372426BC917E7824B9CCC6C682619428170A2EF989CF",
"de_type": 863
}
],
- "encoded_section": "029EE7BC1BBBE9DABEF0A56E7E11D1A11D432AFF55DAB52B42407E950ECD6FB8D174564E551C9E68F5C5F229808F63E587225B49D682D481A708DBDCF8DC53D55AEB7250AF51464C4C8EDF689933DD8414BAD3BFA8C633B7914A67A55D90598A8070EC256500F60B92F22B2E0A08749AD01AD7AAFEEC9C547993E7D8F5495E1D9B0CA185F41DAE0F48E4E953530CF8A8EF77A80CDC11",
+ "encoded_section": "029EE7BC1BBBE9DABEF0A56E7E11D1A11D3A25625D5C140F6737F443037658DC3B74B188587F1E1ED233695C11C1329EE657175E265D4DF4887D0523CD48289052DE06145A83CA28C6B5522360B4ECAF00C08E0481A02E0912745239870D15ACC1355CDD065CBF9B576DA7E878925433EF8B7C894BEFCEB5FBC01B1BFBDE6D5EB784FB2498591ED090C8B865991942C93CBB45EA8902",
"identity_token": "24DB26605841C2C1F2FF4EAFA8C9A3B6",
"key_seed": "B80CEE8A5289B0E88AEE0FD08789C7D762F4DFF8D0FC5D79CC9332F24F7AB889",
- "nonce": "20979F413869AE8D0069CEA6",
+ "nonce": "2B05360DCF03071EEB3F7517",
"section_mic_hmac_key": "F57425B28C88DD3C938D7C7F9E2E2DEDE7883C21116B6C2AECD9659F508172EB",
"section_salt": "9EE7BC1BBBE9DABEF0A56E7E11D1A11D"
},
@@ -751,10 +751,10 @@
"de_type": 465
}
],
- "encoded_section": "02AD0144308355660F11BC1522BB4040343B6EE18AFFEF9B71D3239005448AD9FA2193AA48A6A9D558FCC66E02BD74A9A83891166A550882FDB1654D80C853A5F40E40",
+ "encoded_section": "02AD0144308355660F11BC1522BB404034782DB97851B63728F018F7D91223B52321D9BBD4CF9B8587EEC662BCE18CC74017A05DA87D2FAA7A68A86E2397CCFC368195",
"identity_token": "70AE495464B5C777F3B7533287CABFA0",
"key_seed": "703D9283FAFFA27816DFE81AB6FC0C86F70F9EE2A59F9AE9FA675BB94F4AFAF2",
- "nonce": "F29395253E56ED38E3AEE6C7",
+ "nonce": "A14790123B0C39A7F03558AA",
"section_mic_hmac_key": "62107DA82458F0FF6C1F510024186527BEC70BEB8E9E2BC63618656F1F10E27E",
"section_salt": "AD0144308355660F11BC1522BB404034"
},
@@ -763,22 +763,22 @@
"aes_key": "744AC27B83E2766619EC0FA0A3B7145F",
"data_elements": [
{
+ "contents": "7B302C10AB20F7E646A1DF783ABFDD4A568C9C1C17E26F8A",
+ "de_type": 111
+ },
+ {
"contents": "8809E7D36FF481A81F7CFD46325C0E4999977FF41E8D9E6D",
"de_type": 326
},
{
"contents": "662C4AAB4A9065207E",
"de_type": 794
- },
- {
- "contents": "7B302C10AB20F7E646A1DF783ABFDD4A568C9C1C17E26F8A",
- "de_type": 111
}
],
- "encoded_section": "02CB4B333C50936FA751BFD0BF7F69C50DFC0358422A96FC26DC3450BAF84BB28751B73C86769524A89061A0178DAF1B8BA70B97DA597B7B4536312CD3FE3302EEC5659948F0310B6A04EA623628523BA417256D89531912AB7E5F5DF54EF46DD764CF9B7E10303A4CD3CE36A96E5F46139A98",
+ "encoded_section": "02CB4B333C50936FA751BFD0BF7F69C50DA9FD2218EACEF33E822591A427BAAA9A51C96E425331925C49B2D24B050850C4194BC1B13885FF2F533D8D52C7B6550F91B6503EFEEB0F8AF004300D1071753A8F28CA6CDCEF1F29C9253E4830C94E31390EE4B84FAEB2DC609B1FEA49B7C5363956",
"identity_token": "A8ED77B3381D625297D75804FE02F5F1",
"key_seed": "94DA586A9221F4376146845409E3CD6C6464BD5313D34E2E6AAC864BE7088C20",
- "nonce": "54278CC24894F36FD3FA5AF6",
+ "nonce": "6CED86E210694D640CB74AB9",
"section_mic_hmac_key": "FACFF42071ECFC827096DCC9464F0FD7EE42772DE4D3CB9E33AE208A206D8145",
"section_salt": "CB4B333C50936FA751BFD0BF7F69C50D"
},
@@ -787,22 +787,22 @@
"aes_key": "8382239DC9F8AD351C6691B8765751A8",
"data_elements": [
{
- "contents": "731A9182CFCDCB4B5AFDC795BB7D773039",
- "de_type": 672
- },
- {
"contents": "AB559ED81984D1E11D3D77C65B1B068E",
"de_type": 34
},
{
"contents": "EC20C11534",
"de_type": 182
+ },
+ {
+ "contents": "731A9182CFCDCB4B5AFDC795BB7D773039",
+ "de_type": 672
}
],
- "encoded_section": "02574DD074E3030F3C440118AF057A6E81DE77D7C6A81CB708D46329C685DBF63A3E49CB49CE48208A0BC9BE897C615383859784266BEABD8739CFCEB412B7D160A078FC7DC9717CD450C0EC5CFE6FF9078EE887CF798B061A8DC8A0A4B81FA7",
+ "encoded_section": "02574DD074E3030F3C440118AF057A6E81062B491C9426F86B9EF20A17F6BE8E613E3AD0440288801189F40965CD0CEF21EDF91E2631A4B73EEF7F989BB565B0317BF6821160E353DD0D1F41A65D8F59E625419607F800AF991FC8303A637558",
"identity_token": "A23C1EC6DA331C8BFCC59AD45299A8AD",
"key_seed": "3FFFE6BC8E09AEC10917636F693B7B623B83B0F60CD0A53B6E671AFA06BA71D7",
- "nonce": "603365E941059E1DBA63216A",
+ "nonce": "E6BFBED7DC88D3E8A94F8E7B",
"section_mic_hmac_key": "0600965BADD5FE7A27707EC8DD96E50F8565A262FA2F006E9C34C32FAA442F21",
"section_salt": "574DD074E3030F3C440118AF057A6E81"
},
@@ -811,18 +811,18 @@
"aes_key": "0B146273164E3535D882394B7D8E8A10",
"data_elements": [
{
- "contents": "A6",
- "de_type": 435
- },
- {
"contents": "FCD83E31FC",
"de_type": 117
+ },
+ {
+ "contents": "A6",
+ "de_type": 435
}
],
- "encoded_section": "02DFD3246C71A4A97E4B1BA9D4F7AC34AACDAA84C85C205A6730B0C34D1925BB3D1BED7A91D44420270CFAD5FF0D68D5A081A74B5E6A9F825DAA528DD0",
+ "encoded_section": "02DFD3246C71A4A97E4B1BA9D4F7AC34AAEB545EE07BBBA6AC3BA7954BFCE803F21B98C2D7C38B5F8E1EA155D9AF8683876E10ECCE98B1A5D871B060E7",
"identity_token": "F188D1E3D8D9C0272F29936D3AA2C4C0",
"key_seed": "0454EA82B20D1CB3895AA549F07794CE16A417EBEA2D45CC3D7CEE88840D1DEE",
- "nonce": "4A37E74B7919321224347C0F",
+ "nonce": "562CD1C455B541CB088F4FF0",
"section_mic_hmac_key": "89FB1E3BB8673A6A39207460CC3EFB7BFEA89A274547F2C1DCD105AF454A48F7",
"section_salt": "DFD3246C71A4A97E4B1BA9D4F7AC34AA"
},
@@ -839,22 +839,22 @@
"de_type": 547
},
{
- "contents": "A74A12CFF895DE4FD969A2BFEE920D3A131220",
- "de_type": 954
+ "contents": "32EF1931690553C3B4AEDD035D12FB776F93BF91",
+ "de_type": 742
},
{
"contents": "C02CB796EE5010050633D340C8",
"de_type": 784
},
{
- "contents": "32EF1931690553C3B4AEDD035D12FB776F93BF91",
- "de_type": 742
+ "contents": "A74A12CFF895DE4FD969A2BFEE920D3A131220",
+ "de_type": 954
}
],
- "encoded_section": "02A0318DC4C04450120D114911CB0F21DA6895C9D7D2B7AF43E57F6DF8A5B85F297250D4B38EE384132B5026A691A5A848C1284550348231A6A97CF8D47CA5F1F71AC81B984264A05F794327D9C50A9F72A5A102CD566D023DFF1C4E116C82B83BBC8A0884611E37C6CD8581360318DE18DC880E2B00048F9C4308A81975B0B73F0A255F229D267F19CEDE7329D8180F71978672",
+ "encoded_section": "02A0318DC4C04450120D114911CB0F21DA8DAC1F7D13A7C2EC11AE95B0719EEF3572696521A3810B132BD9850720EBADAB387D9436AE86E21EC5396FF634BCFC39A8B128711D25F2D03CBC20B13A502482A58FABFAC6CB9E2673954102049C75AC514BA88DE6CF5475B9E1E9D2CD14FEA5537769BB7EA97D669AA3B36CFB828053608BDFE5C80E20F67C5486CA29ABD2B21B6EB0",
"identity_token": "E0D703959CE67BF41B6976FD455FCE5B",
"key_seed": "CB83F57DBD5CE9CA5F898CB4F5556A090479FD88C20F7EE18D632FAF178323A4",
- "nonce": "380124B5FF2BF739EE781770",
+ "nonce": "C436A8C763E9DF12EA6A1FBD",
"section_mic_hmac_key": "B044296E5AD11AE978E19E56856D6829769C3F38E488A3D741E9AA632BB32FF7",
"section_salt": "A0318DC4C04450120D114911CB0F21DA"
},
@@ -871,10 +871,10 @@
"de_type": 824
}
],
- "encoded_section": "0230ECD327F2101FD57C67F868BFE5591A9EA0510A3A1918D42719B7D937FC25EB347643A382CB1EEF1BE0D45278B0EF49241FD4AA53333E9B20D7DF545644C794C0C313FC9C2D22968DADB4406A9E092999EBFB7F92",
+ "encoded_section": "0230ECD327F2101FD57C67F868BFE5591A0645C6032FF4C4A8421C3C964724737F34112BB04D332E59D51997ACDC8DDEE97BA513D8ED19817AC385F96142F8FE27F9DC8FBEAFA3DA0FF16054659A3637EC7679D5123A",
"identity_token": "35AB8A4472A635C5EC6458F32686DBEB",
"key_seed": "99637210F99F390A997F576C706F9BC7C8581692E1285E29D1BCDE117CC0B428",
- "nonce": "10CCB0A8DE6C3AFC5E747D1C",
+ "nonce": "A79B02AD57FD56F90ACE4114",
"section_mic_hmac_key": "57447E9E35E7BF08280D4F92360E0141A766AA4CE818AAA45752563253DBD173",
"section_salt": "30ECD327F2101FD57C67F868BFE5591A"
},
@@ -882,10 +882,10 @@
"adv_header_byte": "20",
"aes_key": "73D9602DDB93D7699C8C046E0B9E602C",
"data_elements": [],
- "encoded_section": "02D74F68B471771AD2A4F8559305EC53902AEAF1CBF3D8C11992C5DE18AC41601310DD15D84C0CB36514110875BA8151D0EF",
+ "encoded_section": "02D74F68B471771AD2A4F8559305EC5390D3AEAA209F7446B71704FA4E447367C61009BF90944A0555DA54481419ED2A676B",
"identity_token": "00EE8F5898673EFCC8D7D593ACA886C2",
"key_seed": "6BCC9CE0FCED7111026D0ABAC01E65F4149D37F617683A58960B8F4AB3DA64B9",
- "nonce": "14B891CE7E628E756A830231",
+ "nonce": "C3FA51DD9707D93C39630D78",
"section_mic_hmac_key": "8A469C665BB030DFA05EBAEDEFCBA98BB35C9B9EBE41B1692B2D74ACB0973D58",
"section_salt": "D74F68B471771AD2A4F8559305EC5390"
},
@@ -894,22 +894,22 @@
"aes_key": "116268F2138D48B7377D1F9B993F112F",
"data_elements": [
{
- "contents": "142941",
- "de_type": 384
- },
- {
"contents": "4E6B16DA0CD13D6EC21E797862C3AFDD25BE",
"de_type": 254
},
{
+ "contents": "142941",
+ "de_type": 384
+ },
+ {
"contents": "B77B7519377B061318D90278B71FBA82C604D2DB5C69",
"de_type": 570
}
],
- "encoded_section": "02BE0BB3EBC88AD859D7B875008C0931365F394AB17B39FB727A93193C4FEAC4E844DF2573378CFFA4B15B0D082B3D59EAB8373E20E17079FE768CA1A2479D57E002BC914D4BC807673ACD10F31FF5F6A39183669EC4B8F593E6E086C4456A3A872F3F401844",
+ "encoded_section": "02BE0BB3EBC88AD859D7B875008C0931366922D8D0BF74497DC57F190F6D8AFEA644E4465696883584628DE0A01F51708906317DBED9B527C321A91FC73E6D8BA9B1B695381CB5842F7B75D506323369AB0DF15D0EB34A080E9D9B429B5335D3FCD01C0C9501",
"identity_token": "BEC77D2328CC57D22E0258715D0841E9",
"key_seed": "70FF80E5D4D65D6C8E4C68082D75ACD7B1EA9FD3C0F4AA154D4086D2D20D0A8D",
- "nonce": "DC2D9C34D25C7E6D95B384F6",
+ "nonce": "ECAEDDCF4BC1EFEFBC7F9258",
"section_mic_hmac_key": "2FEE78E42D658EAE17656D3815534D07B399660A2030795ADEF7FFF532321509",
"section_salt": "BE0BB3EBC88AD859D7B875008C093136"
},
@@ -922,10 +922,10 @@
"de_type": 589
}
],
- "encoded_section": "0263497BB609ACDC19D570B16320321281AF7072F3BB15F889145ED8A9AE6E9F6A25CC28D879CFDE5740FA13C8E044D88BB6CA7A7DD16174A3ED805757529717CB260449D92B67",
+ "encoded_section": "0263497BB609ACDC19D570B163203212815C493A8D46FB01C8EAB3B4DE51730D1925803974452855F1F36F7B5142CE1EB4C4EAE6CA39722493EDE82518884F3ADCCA7080320847",
"identity_token": "855F0220CB5E718D26321D4D7C7577C7",
"key_seed": "A1F0ABA90F81ED6943F95065A29589446498EC2F79520A56108539E3C30A19D4",
- "nonce": "39A3380C10BB5CF8E9435BAA",
+ "nonce": "AD656653F8B772A9C83B1003",
"section_mic_hmac_key": "6B654DBCC294323566E00D2A8C486A6F1ECB91FF949FA640B93D3B152F047230",
"section_salt": "63497BB609ACDC19D570B16320321281"
},
@@ -934,22 +934,22 @@
"aes_key": "B6E9D40607D8C39ECF7625E686CEF325",
"data_elements": [
{
- "contents": "2B2948F09099",
- "de_type": 544
+ "contents": "4E6F0748C486",
+ "de_type": 37
},
{
"contents": "E551587B",
"de_type": 375
},
{
- "contents": "4E6F0748C486",
- "de_type": 37
+ "contents": "2B2948F09099",
+ "de_type": 544
}
],
- "encoded_section": "0205FA3C47404A35D4FD5BF0F547164C81B299006BA6CBEFBD4E0E4A450CFFA1382856C3C42CDA3A189D6FA37FFE8CA32EA8548FE61DEFAC42D1532D3865A41D6F71DB37B9C1DB88B1A3",
+ "encoded_section": "0205FA3C47404A35D4FD5BF0F547164C817ADA8D65589E65D8642F81871ED783BC28196058F8C3ABDDA138311D37E7A3D732AD2F4D5B7F5B17701E8852C7A8E653A250D413F2A1E5EF7A",
"identity_token": "72A30E0B2EF76A0265690E2564D197F3",
"key_seed": "13F03C6648844D00F21EA2CF23AC0B8284D36465438DE45C81A59775BA116FCE",
- "nonce": "499227762DE0BC7B33BC7153",
+ "nonce": "6087E81DB2AD8458B86FE55A",
"section_mic_hmac_key": "8714D596BE3472DF951E6861CA718DF4C391976F932EEEF3F8B48F78C2AF7C40",
"section_salt": "05FA3C47404A35D4FD5BF0F547164C81"
},
@@ -958,22 +958,22 @@
"aes_key": "2293B6380F70640ABA8871ED56932E20",
"data_elements": [
{
+ "contents": "",
+ "de_type": 27
+ },
+ {
"contents": "0479AB07E83192319E104D967353472F1DC0F863CCCCE0BB99",
"de_type": 258
},
{
"contents": "A0AC8E8D1A916AA5AB9E021CF32A85288F238809CBAE59AE8A71",
"de_type": 672
- },
- {
- "contents": "",
- "de_type": 27
}
],
- "encoded_section": "0294E4D54F244E47A822FF413F6BC376B2B6162B1A181C00187B1ABDB43612153D4B8EA58CD0CFC7314AE91A06A4E35027FEB5EB8C9D93AB2872B2A3B561EDFD98F3A937890D95B5994729C4D04DB85258644B6E92E15E87C21CC5D567A005CF496490988A3AE18BAD1F233DE0",
+ "encoded_section": "0294E4D54F244E47A822FF413F6BC376B2D76FEEF5539EBA973BED654773428E814B1D5A4D0D5C50A07DB2647670496B45AD79AE8F786AB9CAF50D43F98AA9FC4D707B90D41052AB0E1A27A1980199B5D9E0C309AB2CCC0910DFD566FE07FBFA51ACFF9C09C41BC62218006A8F",
"identity_token": "3AA3E8B81C9B7D2D22CFD1E3FD7EA5D5",
"key_seed": "DBC016C10B947661DE37B57C0C33FFBA01B89FD64B9417DC635D9D19C44511A3",
- "nonce": "3F5472259721D69E0511C6F1",
+ "nonce": "2843109F0838BA8192D96328",
"section_mic_hmac_key": "1097F2D8149337A3927ECD7E4E0534AE66E7CECBA50D8C8699E2747AAEEB82DB",
"section_salt": "94E4D54F244E47A822FF413F6BC376B2"
},
@@ -990,22 +990,22 @@
"de_type": 123
},
{
+ "contents": "22C4B157B2334471749AB6",
+ "de_type": 320
+ },
+ {
"contents": "C5AC",
"de_type": 539
},
{
"contents": "98C2695950C4B5C2F3C69D91B019D5045198846A088508158C4835",
"de_type": 929
- },
- {
- "contents": "22C4B157B2334471749AB6",
- "de_type": 320
}
],
- "encoded_section": "028C8FFACE6BCDFE650833331F10BFDFB7B4E3B071CA7AC5CC1A3BAE79685825EA4FFA30EA2417529BFF840848F99C6A8243648D1F8244457FCA1F808E370B94D2F04BF78B7E51C23752D922268EB1443C0B08D3D2CEE883673ECE7AE5DD5EC5E99360901AFD815B2413BB071A62FF2960",
+ "encoded_section": "028C8FFACE6BCDFE650833331F10BFDFB715C72BF908A227D93D448B600067F3DF4F76562CB11161B2A45494F6D32168DE23457D1DED2DF482BB609D2DCD1C4BB9455EA1C32FC2E8A71AFCA00621FCBFBC458D76A8226445FD0A70226B05CBD6F4548CA690EE5E0E6C549892917A361A1F",
"identity_token": "A1D0AB03665B36F07643E7DEACE86D57",
"key_seed": "40A14BB11063D05593EB466ACA4C4DF438FFE2FDA5D874A4E5B3B4E79A6DC65B",
- "nonce": "484B3BAE18B85C1027094E93",
+ "nonce": "CAC0615ACD6F37B137DFA0D8",
"section_mic_hmac_key": "CE4B87F04EBF02FF12744FCAD9753239514C6E0856D6C2C6CA768315385A98FC",
"section_salt": "8C8FFACE6BCDFE650833331F10BFDFB7"
},
@@ -1022,10 +1022,10 @@
"de_type": 586
}
],
- "encoded_section": "022FCDCA777563F84DDE7D907A02D77407964B19DF4F96C67E2F5E3078AD2D094326EC3BA4DD06CBF9CD27A0996D83942964893D079B8CA07E07B72D80482F25156C66BC22D60047",
+ "encoded_section": "022FCDCA777563F84DDE7D907A02D774077B1E0A24826D181C391B861E7988F2CB26848A40C859144BA016A41F59AD3734F4BBA0D675E3E80C67BB13F948A5261ECDECC77E199403",
"identity_token": "A1D64E8B5EF18E775F30C87C7F41937F",
"key_seed": "CF5EA21DD3F05F95A8A1658B4904E63F7ECBBDF860F1D27B88E4B418E914D87F",
- "nonce": "C8C9C5FCF402ADFC23600F63",
+ "nonce": "084D0BF9FF0903FEE07D3EEB",
"section_mic_hmac_key": "9768775B2D00E70743DB112CFA2E4DB37DB7D9D469E594D364EE254533F2714E",
"section_salt": "2FCDCA777563F84DDE7D907A02D77407"
},
@@ -1034,22 +1034,22 @@
"aes_key": "841D9C330BD88576BB9159CFD67F912E",
"data_elements": [
{
- "contents": "815DBB833846588103164057A0156ADDC531E401D585BD5368B419",
- "de_type": 864
- },
- {
"contents": "2FA2A37BC6594C3B4A29A2BA077AB8F324F1C743354C2A1B97",
"de_type": 71
},
{
+ "contents": "815DBB833846588103164057A0156ADDC531E401D585BD5368B419",
+ "de_type": 864
+ },
+ {
"contents": "E91DCCA08179D3",
"de_type": 873
}
],
- "encoded_section": "02F49C8B85636DBD028678AA984B5A0F2643DBF77E8FC339E837757F1FE34403F353E16488648B5914A8EB8045BEC91AE34344DE4B90445DB2730AC1FADA1635D2DB87068BBA5ED44F6EEA57EAE2CA5B15BD5FDDBDC1C71C1AF39E58E2E2DDFD721C6E7B4CBBA4918FCC33688EC2119A6402854F69",
+ "encoded_section": "02F49C8B85636DBD028678AA984B5A0F26FD056CA7C65E4AE4D99DA7294FA383F153FA850943C17E72FABCF261A9DE719A5DC89FA8D74291FC32C30025C506E737B94FEE29CA97DDA6DCE7F11A345391A6D4ACB5549B3B363E6179AEE827260C60A15D97E34EAD0DF5F3995A22589001EEFCB60959",
"identity_token": "27A1DED5C2E9533BF3F99698E1E525BC",
"key_seed": "6B7032582260EC0DA57AA5AD575BEFEC7D28696DC6035472F5745873DCB3B840",
- "nonce": "6B048702497ECD3CADA72B3B",
+ "nonce": "A6B15EC5C82CB1406EE53264",
"section_mic_hmac_key": "80747614953138AF6109362B19BD2C97FB7F8A224C908CA870258F1CFD5D6196",
"section_salt": "F49C8B85636DBD028678AA984B5A0F26"
},
@@ -1062,10 +1062,10 @@
"de_type": 194
}
],
- "encoded_section": "02999AD00461B54D36BA7FC221F7F10D1239480EA7C91F1C200AC73639F608F5022706B4E495786CEDD18D582218A20C83534889C7026B633769D626E2BF60E33069B91D50A8CB1CEF",
+ "encoded_section": "02999AD00461B54D36BA7FC221F7F10D12FE19333F3600565C1B8F81B1621E7DDC27982182FC30226EB7E7980684912647DA4E826D5FB27ECBBAB23EE3538D3AFB6F1DAC2A377F3BF8",
"identity_token": "158D50035DE9F0289B63225B57629D58",
"key_seed": "CF8C5DDD57F9E7399E46F52E06906C39D6155AAB1BA940EDC3068F3642B0F3CA",
- "nonce": "7E6FEF58E278A361FA3F77BA",
+ "nonce": "9E18E48AFDCAEAD0CB6DAD13",
"section_mic_hmac_key": "561D62F4B1EC440C5A8C9511A0981C6A14F1A30EE5D156AC2460A25D9F2A095D",
"section_salt": "999AD00461B54D36BA7FC221F7F10D12"
},
@@ -1074,22 +1074,22 @@
"aes_key": "99305ED53E510A6A5620AF58B298A200",
"data_elements": [
{
- "contents": "BAE0D38B",
- "de_type": 354
- },
- {
"contents": "CEC61AB08C2D8EF32C869D2FE2",
"de_type": 67
},
{
+ "contents": "BAE0D38B",
+ "de_type": 354
+ },
+ {
"contents": "0EF4293C",
"de_type": 947
}
],
- "encoded_section": "02E368EE79CEE4FF33EA1BCF4950B8A8799DFA666B5562CA808D1919E6F24C5A3C2DB36770DB9C7514738035B233AB2025C66D50FA57008D76346564BD8D3BEDA68A676153B3638C47AE4ED7043A6F",
+ "encoded_section": "02E368EE79CEE4FF33EA1BCF4950B8A879D0EA5EC2E451675682F3399610786FE82D011263774A37DBABD732EA801C82A605DF96D93BBC3D3805B488F24D84E2F2F3F6A181EF15A170C5DBC9B2E314",
"identity_token": "9105B80384B6610035BC153B06A07D90",
"key_seed": "D72E99D75674D6C2F4E8EED8D64568DABC14FA7C55BF6F7E0B04302142474AF1",
- "nonce": "61E591B344D506E266FEB0E2",
+ "nonce": "D41B35F1B0D95356CA0666EA",
"section_mic_hmac_key": "501159272ABDB1D1BA4BE9311D77AC0559D63152DA5BD34A2820DA15A82AD037",
"section_salt": "E368EE79CEE4FF33EA1BCF4950B8A879"
},
@@ -1106,22 +1106,22 @@
"de_type": 159
},
{
+ "contents": "60A513C6D691D578B1B8E0D8678C4C93417CCC8772A98A744EC6C3",
+ "de_type": 305
+ },
+ {
"contents": "C94372A46C75",
"de_type": 471
},
{
"contents": "E212FDD45F7031CCF2F0",
"de_type": 609
- },
- {
- "contents": "60A513C6D691D578B1B8E0D8678C4C93417CCC8772A98A744EC6C3",
- "de_type": 305
}
],
- "encoded_section": "0239448EC52D1360B39AAF3D8D2ECBE21C8903D1847E324F6AA5575C8EE28D8E4157ADCE19A5988809B2C3396F2F55B8B1A6D7C14B509EAD06129426C91C94D7387DD790EF5EACA005EABAEAFF8D0E4E1A2286A3EBD393A0B0E2A0C73F900B32D82F28F3CF8441414116BC415BF3D652DC1F6ADCBCE89D5A5E",
+ "encoded_section": "0239448EC52D1360B39AAF3D8D2ECBE21CC994D512C2F1B6F8F21983695C8B47ED57E6AD25CC75BD5B3D035FFB95BBCEBBD2096A2029C07D72BDA4AAF853A531B1A683E2EB22316B5674648BBFE47607FDAC5B689C0CC2E525BF1D14351E9C66A6642A136D3B6A6B1188F2CF83F3769E3A51663F3515B2D922",
"identity_token": "EB2E94C1013BBCE935289A687EE855F4",
"key_seed": "CF638C9E9ADEED53473692F67FB230EFCA033A90FB31FE01640296AD33198E24",
- "nonce": "8A1421B5D302EABCB4E9AFDE",
+ "nonce": "BC9FB3D65AFAA36AD78F0128",
"section_mic_hmac_key": "C0F1CC05E31EDC758059D4FA1C6A539059A63E86ED3A81EFE5B49ED6504BC816",
"section_salt": "39448EC52D1360B39AAF3D8D2ECBE21C"
},
@@ -1138,18 +1138,18 @@
"de_type": 266
},
{
- "contents": "C025E0",
- "de_type": 749
- },
- {
"contents": "B668775936872A95200E2A1A544742E4B5DA218E66",
"de_type": 581
+ },
+ {
+ "contents": "C025E0",
+ "de_type": 749
}
],
- "encoded_section": "02605100404180967E0C35A27B303198AA0777DBC954ECAEC74857B598034CF02642E7BE5C315CFD1DCD1AB6164D1AC72EE44713D4332D0ACD1C0A588BB4DED8FBD841CC2D6EC37D6FF4DC1D9E3E1DBFFE520C351C868F4DACD799112FBE876903BD4DB4",
+ "encoded_section": "02605100404180967E0C35A27B303198AA03B722F2EA1AE59E0232EDDA267726B6428C78F7CD4AE0CDD337F974FB115829FD062B57AB00A2D2532A5149B42B8D0CBD75BE80B8A4E4194C370483ED93304399F74CA8680B360C06002FCBA7AEE0072457FB",
"identity_token": "681670B22B7549584CC281C7E9C7BDC1",
"key_seed": "5B8298AAA972242D14166249DAB29D642898C1EDBF4AEE35E5A28A523D79EF4A",
- "nonce": "EAE83D7AF44006D4DB2FBEF7",
+ "nonce": "860CE9B274CDBAC73BF31F33",
"section_mic_hmac_key": "D4810C45A1A468EFFC95E987192D01D0B2ECFD313E9852B1A3C914AEE1BAAB5D",
"section_salt": "605100404180967E0C35A27B303198AA"
},
@@ -1166,10 +1166,10 @@
"de_type": 824
}
],
- "encoded_section": "02BBDBD06DC3AB72F0082A6D0BC513951E56012BBCAAAC40D3F10B436E42DCDBD020E61B167FB0A77E527F3837E41D9AB666EDEF30B943B65984B33E7CCEBE0B4F79",
+ "encoded_section": "02BBDBD06DC3AB72F0082A6D0BC513951EA976D1F8D189ED568B0324F5B721E765205302754B928F1593E4E4885D45908244EF90B5F58BD2EF11244CEF2DEED9DCCD",
"identity_token": "21346A45B8489F23A9E63CB46B0AD4EE",
"key_seed": "D940EE0C157926DAB1DBF8020B86F06D152092B162CAF11C607B125A18FE58EC",
- "nonce": "5A5DBDBC22E9F0485B0F0C4B",
+ "nonce": "C49F2DDB2C41B10F095C692A",
"section_mic_hmac_key": "0BC02C5EB12F36E458141B6C48D553B9164D52F712BEDD2E0A7CB3FDB3C54862",
"section_salt": "BBDBD06DC3AB72F0082A6D0BC513951E"
},
@@ -1178,26 +1178,26 @@
"aes_key": "0E6F2D1663BA6A254DE665F436412DAB",
"data_elements": [
{
- "contents": "4E4693617BA9052ECC",
- "de_type": 692
- },
- {
- "contents": "",
- "de_type": 510
+ "contents": "72EC503A57",
+ "de_type": 283
},
{
"contents": "6DC4A08E16F5789C7F4CC0625715EC35E03DEC75153C98F9F3",
"de_type": 501
},
{
- "contents": "72EC503A57",
- "de_type": 283
+ "contents": "",
+ "de_type": 510
+ },
+ {
+ "contents": "4E4693617BA9052ECC",
+ "de_type": 692
}
],
- "encoded_section": "02184D45A5DFF4F98BBA2B9BCCD0E66F1E6E4F2095ABB97491594F1E2615C6EEEA434ACD59264403E3B7C932D33D7AF49E223255148D5B9176DA76830D451DC226F6290B659EFB946BE7CB2F323D1AC1ED48187ACA1C7A7A1AF00E47D0A09AEA3D5B1FEED1",
+ "encoded_section": "02184D45A5DFF4F98BBA2B9BCCD0E66F1E981280E38481F0B64030C534E8E2631A43A694F2E3DB274161DA216F67AD601612ADE45162EF0A22B43EFCAC9DBB5C7848DEE9D0D23439DC8F5D5B4A34F91FFAF34B7BD1E3FC6962AC5B7F3010225E06D62B14F5",
"identity_token": "EF610B037D7AA8DB3C45146D1CF5510C",
"key_seed": "33D5F107E69D63BB05BD8CDDE5BCA9749BC619217585D756B67D84889942B812",
- "nonce": "3600FCE45834326431FD54EA",
+ "nonce": "A3A3DFD3D78549C87149045B",
"section_mic_hmac_key": "36DC6B1B43D34B4A36A28DD14996986E4F3B14D346A24B1C7D5055202E1232C4",
"section_salt": "184D45A5DFF4F98BBA2B9BCCD0E66F1E"
},
@@ -1210,10 +1210,10 @@
"de_type": 444
}
],
- "encoded_section": "02AC62D0F2D343B4B67F2541B51FAB36F7DE79C99A6FBB63335DD4A5DB2D418C2B253164CEFA3BCB42A517E89BCD1FB1E5C6F04FA0C99CCB247F03D8A9575F2154D07D25D551BF",
+ "encoded_section": "02AC62D0F2D343B4B67F2541B51FAB36F7C5EFDD497DF6CE4FDDD8C5EC150A330E259185C3825EE10158EF110D543B9AAEE80D665C70C51DE959B5D6B2305A026CE99060A38BBA",
"identity_token": "63E358995194244AD3281A35F844DEBF",
"key_seed": "AABF59ABBB17F47267FC66D84EADD558ED1AAFDEA87072FBAE1597E4F894CC2C",
- "nonce": "438DCBDC58F3DC62CF4DD8C0",
+ "nonce": "BDECFC497A2A3BA0D2F93B72",
"section_mic_hmac_key": "9C1F6AEEDBF0ED82872AEE17C94E67388D8DE56BF4E6D064B091F26314BC1C37",
"section_salt": "AC62D0F2D343B4B67F2541B51FAB36F7"
},
@@ -1222,26 +1222,26 @@
"aes_key": "CE4777B8675B646E84D6CBA4000FE4CD",
"data_elements": [
{
- "contents": "F1C005DE37",
- "de_type": 856
- },
- {
- "contents": "5CD278AE4F49F14B64AE6FFA980EDA5A30A96D674AE21E35EF1ACF",
- "de_type": 669
+ "contents": "E8EF398A871164F4AAC2512A73C2BBECD0ED64",
+ "de_type": 116
},
{
"contents": "2CF1F076D4D763145F",
"de_type": 339
},
{
- "contents": "E8EF398A871164F4AAC2512A73C2BBECD0ED64",
- "de_type": 116
+ "contents": "5CD278AE4F49F14B64AE6FFA980EDA5A30A96D674AE21E35EF1ACF",
+ "de_type": 669
+ },
+ {
+ "contents": "F1C005DE37",
+ "de_type": 856
}
],
- "encoded_section": "02777790C1F455A388A8A8908F2530F5823703F5594AF0E8BC06C2C683DB980BF2575C3006313D8F42FC9A6E4F133C88CAF4EE90C76199F59C6778C13865B9FA83AFD4C4E27F22E1857B682EE6401C973E67F6A04CB1FC1030861B77DABD0FD7018DBDD221F4CCC89BBA1423311439598E945890317E4DB25C",
+ "encoded_section": "02777790C1F455A388A8A8908F2530F5829CC17C442698FDBC57D6DE405B09C03A572236B9B842B332E1D0DDB09378E1AB75F080CD65927300A3D11218466CDA324BF3173FBFFEFAFFA20042FDC7B59574D6EC9400D768D8289D984A9A40B4A6A13510A750ACD2803A0C63D15902E8BB6C9D92B86DD7151C1F",
"identity_token": "98BE3C3FDFF640C7902485986A54CB7B",
"key_seed": "8AC62E22A13FD1D0BC83021FBE72E253187FD032D48150CE2E363F0DCA427A01",
- "nonce": "838BA90741E30DB71D749F04",
+ "nonce": "D29B3705C0352582A9288CA7",
"section_mic_hmac_key": "CA069B89338B5A7AB5B6385DF46AD1EDBEE95EE58763002DC70201E132F0B98D",
"section_salt": "777790C1F455A388A8A8908F2530F582"
},
@@ -1250,26 +1250,26 @@
"aes_key": "73204F77542E61479FFBB70F15BE9D82",
"data_elements": [
{
- "contents": "E731",
- "de_type": 228
+ "contents": "A488A172EFF5843E44EA9C",
+ "de_type": 28
},
{
- "contents": "795B467FE034E95A4441C2E6E67FBE2117483A61793D21748522A4882BF8",
- "de_type": 994
+ "contents": "E731",
+ "de_type": 228
},
{
"contents": "0532BB79831FFD7B67D9FC26C7C25759EBE3AB6B",
"de_type": 343
},
{
- "contents": "A488A172EFF5843E44EA9C",
- "de_type": 28
+ "contents": "795B467FE034E95A4441C2E6E67FBE2117483A61793D21748522A4882BF8",
+ "de_type": 994
}
],
- "encoded_section": "024E89F9D070337876390A8DC90F532057A9F37C117727BDBBD0521775EAC63B345A3E53A0483357B963123F26B640EADA4F0EF043D4B25D831100557C0DF475FFC0A6E721D016053596B52CB0404090F67FAF7565661416996E3EB43882485EFA6EB866520D4CBB482F26EAEE25053B69059BF757603E9514DF131B",
+ "encoded_section": "024E89F9D070337876390A8DC90F5320572336F6573FC05C089B3207B4F8B11BF45A9370767D13AE40384C1F1732168F6C03042DE57F3CF6C0F59C35B95556B4043E7920BDB5E86A2E55191CE4DAEA79221421983CAF6FDC5DBD61F0AEBCCCC61E4E4BAB650B0EA0EA5D4FC46F2A569629ED7FE78CFA56396F5906C4",
"identity_token": "49A94C68702A530BA761CF9A02D0D447",
"key_seed": "03BA9921A9EC9DE1B4C3975F758B63E9D5110DA1ED0C3926D9B047804C36C525",
- "nonce": "CA9A5E6F0D1FB66704D8835E",
+ "nonce": "4D3A297BA54ECB46DE5A8EB4",
"section_mic_hmac_key": "790DB507668CD2006E6C56455ACCFD851022A5FD00E5AD27DC3A7F011241EE47",
"section_salt": "4E89F9D070337876390A8DC90F532057"
},
@@ -1278,26 +1278,26 @@
"aes_key": "13A1E3A6D3075C5319C41E8467634AE0",
"data_elements": [
{
- "contents": "F98092C5EEE605C57E4F559DE1A39F6E097E90A6DF5ADD0516B1D0AE7C5E",
- "de_type": 586
- },
- {
"contents": "9894D7",
"de_type": 148
},
{
+ "contents": "",
+ "de_type": 382
+ },
+ {
"contents": "2F910D12E20F96F45D7E8D75D378C34ADC",
"de_type": 473
},
{
- "contents": "",
- "de_type": 382
+ "contents": "F98092C5EEE605C57E4F559DE1A39F6E097E90A6DF5ADD0516B1D0AE7C5E",
+ "de_type": 586
}
],
- "encoded_section": "0201E3D1E7546915A844FBAE96D804883DC7EE8DA279EF8B4A38498DBB4B05C1444E7A3076881D63895422DDC17BE94B3AB229D60FC56E3F7B356246DB710D92629B73ADD1F33F12518070CB4B6409C406DCCDE26A4AD9972FC8235F4912ED2538D0B9D6B8D736C9FA05BF569A9033C4",
+ "encoded_section": "0201E3D1E7546915A844FBAE96D804883D6F33E2C92C1DB4636FACF57522644A934ED1AE3D7635B801C2A3A791096CAB28A519F1BBD1CEF837ACFC509F79648F435678B34AC59BB4C209D6EA59AFECFA646B99428758F63780230C6F72598786BD1FC5A8F62E9538EC0010CFF3CA9094",
"identity_token": "39D0F77560A9F411D420421C51345467",
"key_seed": "D752C5EC1FD70E7443A37506DB53F6D9E1D521779526D7458CAA5E0239027498",
- "nonce": "14BBE83961C2E06DF151C3D5",
+ "nonce": "80A7F37CB66708B53CF3B979",
"section_mic_hmac_key": "290C81A58DFBE27E6FCB3DFE0BC632B9D929AC6A60527853230793FFFEE83969",
"section_salt": "01E3D1E7546915A844FBAE96D804883D"
},
@@ -1310,10 +1310,10 @@
"de_type": 183
}
],
- "encoded_section": "02006BF54BA34C5EEABE6733C40C6A4B9FEDB9B4127A2B99B33B12AA97FF6CB70D16AC7E7B3147A10EDB1F652024427E285A71D69A95BD2A",
+ "encoded_section": "02006BF54BA34C5EEABE6733C40C6A4B9FC87F590C3E74BA7761D713816BD0E8D316D9BCB4CC316A2077A1E5A81A96591F31079909FF94B7",
"identity_token": "5CE752DDBBDE278BB19C9C3B3ED426AA",
"key_seed": "649A30B204D8F4A1B64EB975ADB6A2B83A7B6BC69728489535090564F3ABA77A",
- "nonce": "454F3FBAA1278732E147A055",
+ "nonce": "12A72E1C4E47861EEED11CDE",
"section_mic_hmac_key": "99262C1952E0E08CAC8768A100996C6CF6F1519EC57F18A0B1D73F5B2F6426FB",
"section_salt": "006BF54BA34C5EEABE6733C40C6A4B9F"
},
@@ -1321,10 +1321,10 @@
"adv_header_byte": "20",
"aes_key": "C6F955057E1A4F1FFC1F2ABEA0B8FD33",
"data_elements": [],
- "encoded_section": "027518E350DA3DC2A90BEBE9C1446ADC7DC841ADD922FC0E770BFF64583C4D9E7610A97E13326A236796EC40EC1C636A3B65",
+ "encoded_section": "027518E350DA3DC2A90BEBE9C1446ADC7D92E04D27B29491C385633F714C65976F1052F4ECE7F158B945D7EAED231D9F48D4",
"identity_token": "A161A4CC8259EB0F707BF4DF7B3DC27A",
"key_seed": "E73209D0C0D85198651F3B798FC53A0927DED2EEC6CE5B8EFB83E50BEF351209",
- "nonce": "A5984AB79981B29B451A8BC8",
+ "nonce": "27D3BAF64E6958C3A6E1E243",
"section_mic_hmac_key": "A8AD4166470668BE0394BA7A2B9DD470F34C3BA85186BD1ECB8B00C4DD8C513C",
"section_salt": "7518E350DA3DC2A90BEBE9C1446ADC7D"
},
@@ -1337,10 +1337,10 @@
"de_type": 643
}
],
- "encoded_section": "02C99D27CDFF106A9BF60401B30A12B78CC29E229B704A39A08B30A358EC76C0B124735CC7DA14B34B5D303B34BCF804C0FF5E6FB3A56426766182C9DB7FB908D4AA1DCB08C0",
+ "encoded_section": "02C99D27CDFF106A9BF60401B30A12B78CCD2B778E74720728B0FB06CD7D72092F24355C92853DA70EC0EA475BD93EBF26199D28A2C5379DD220521CB83B231F66515D46A1C9",
"identity_token": "40CE8EF0C7FB9C8B822F760DACF25447",
"key_seed": "1928670C4B6F0DEBED5BAF7A97E950BBE94EC7D567A70CE967E4B2B212B5A7A9",
- "nonce": "E8D11B32953AD61E43FF147A",
+ "nonce": "B1C48D016F22F19BCD068E7B",
"section_mic_hmac_key": "4CFDEBF521FF7DF20B146DB5947E0939F80346BA991E4D06BCE88587B675052A",
"section_salt": "C99D27CDFF106A9BF60401B30A12B78C"
},
@@ -1349,18 +1349,18 @@
"aes_key": "1D8DA89F7A7D6CE5CB5AA18F77039AF1",
"data_elements": [
{
- "contents": "632D457E2AFD3A",
- "de_type": 945
- },
- {
"contents": "E4B2038436599A2A4888A76F09A99786DA51BC3F",
"de_type": 628
+ },
+ {
+ "contents": "632D457E2AFD3A",
+ "de_type": 945
}
],
- "encoded_section": "02660B19D834927254C3CA4C44B4E58F9592C282EAEEFC114E0CAB05CF072BF7E5310DC0238DF9009FA972F31FDB78172E2D8751ABFB1B8D2C5A1472CB8AB52F915318FBEB98357115D69CB19FA2B381E5EE03",
+ "encoded_section": "02660B19D834927254C3CA4C44B4E58F959BBEFAC252B43171D4633FD8C8D447F631ADD7D753C94FB6C36828E933F9536F68B14B86EC09A416A8B9CEE49E46494C5656B75DA41398D7CC1DF66CDB13AB903419",
"identity_token": "58CFED2666F58A35DB28BEFADE58C82A",
"key_seed": "4B54C23D5EE9BA26FAB0C9D819532B27104C3158DCF1204D2C90E493371465FF",
- "nonce": "BC35F77BE3787AE9D562921E",
+ "nonce": "696ABE133FC400897331B876",
"section_mic_hmac_key": "832DD5525C889D87C58DFC886FC275FD53EF23602257DBAAADD8260445190895",
"section_salt": "660B19D834927254C3CA4C44B4E58F95"
},
@@ -1373,18 +1373,18 @@
"de_type": 143
},
{
- "contents": "E84E413DC7B2226F720540790093",
- "de_type": 781
- },
- {
"contents": "FD05E7976720868B46D00A20A71E",
"de_type": 358
+ },
+ {
+ "contents": "E84E413DC7B2226F720540790093",
+ "de_type": 781
}
],
- "encoded_section": "022B54CE0520655913AD3C401F94530EE71BDFC769D515CB765DCA10AB115D32FD4F371FA81787527B9EE63C1D0B68DBFEC7C9651EEB12D6442CEC14CA7877CEE0261668C118A1A255A0994F5B8B7D86D5A6242B709350887160A90EF1DD0A3F0DBC402C8C114CD57EED600F0044C6E971",
+ "encoded_section": "022B54CE0520655913AD3C401F94530EE73BF705D17C000CECF05A66B9730AE9724FB8393CDAC00610A9A37C03160964F4B81047001FF91EE63E29EF22524E8163A52164EF917BF4DA19E26110FE5B3848048549A4A6E4BB34D6D19BEE48C7308CC01734A2913A63C4893EFAC35F89A6F9",
"identity_token": "8C50C0ACF8A65AEDEB6963051EBB25EC",
"key_seed": "E78E6D29382220CEE2E4C81341D26B5FA63132186B82A0F0EC2A723B2BB184C1",
- "nonce": "A3E85AD7D06053583B8A3734",
+ "nonce": "17552FF886F3E79295679494",
"section_mic_hmac_key": "E1CAD553106692395DDEB1B29597259001161AE17E76B7D1329891F98547002A",
"section_salt": "2B54CE0520655913AD3C401F94530EE7"
},
@@ -1393,18 +1393,18 @@
"aes_key": "4357834BD8534421FC328E433DC7B1B5",
"data_elements": [
{
- "contents": "A0B4D6535ADDE0",
- "de_type": 842
- },
- {
"contents": "82B652635D1D50ED2BA2B02EB2C724B314C1F528079AE302",
"de_type": 350
+ },
+ {
+ "contents": "A0B4D6535ADDE0",
+ "de_type": 842
}
],
- "encoded_section": "02258C3D33CE4C862F9B69FAAD6C1BFE1F1F8D746B94BD1AB65354B648AC0D054A35385EF82E5626CEB99335CF2637088410D4B278EC77451E3C9AE2DB438AF702EA1327B74E5B341CEE8D2D1DF16A482EE18DE4F8511B",
+ "encoded_section": "02258C3D33CE4C862F9B69FAAD6C1BFE1F8446B5B536ABAC51FDA5EF8760DADEA3357119DD45490C218D6767DA72524204F51ABAC46028D7361C02576638FF444264C731EB6F941622731B845422B21EE08B31AB68D95B",
"identity_token": "D53777A4358E0F2A2EC95AFA3D408870",
"key_seed": "A01B914623D5624DBBF6DBC8757BF955A979E32B187BC7F70E3EDBDB95C4716F",
- "nonce": "B55F4A96033F23DBB8BD3F5C",
+ "nonce": "18FF878C37EB05B269029989",
"section_mic_hmac_key": "13A8C92D1F018B4F892D459BBFFFBA84307867F39B300918B0D6518766347554",
"section_salt": "258C3D33CE4C862F9B69FAAD6C1BFE1F"
},
@@ -1413,30 +1413,30 @@
"aes_key": "326371E295F358C8F1237CC22F501666",
"data_elements": [
{
- "contents": "6C378A24DE51EE42",
- "de_type": 264
- },
- {
"contents": "248624F8FF242C90854382BB24",
"de_type": 248
},
{
+ "contents": "6C378A24DE51EE42",
+ "de_type": 264
+ },
+ {
"contents": "99135A2BC5D7D748EE152CE572FFC032207AA2AA605D",
"de_type": 514
},
{
- "contents": "1EA3BE72AE386B6F42",
- "de_type": 820
- },
- {
"contents": "94AC3E25F14015AC7C7AAC7E611A931FAF59A447F1D3AE",
"de_type": 708
+ },
+ {
+ "contents": "1EA3BE72AE386B6F42",
+ "de_type": 820
}
],
- "encoded_section": "023B3CCC62107DBA3672E653B736AC923AA99D7B440C792BCA1D85378A240F0D766A47C965608B564999D496CDA5185BA9180928BCA9C43D9CDE40A6F8B5E52141EE95718009505ADE7B0D5285867B634ADEF94FC38951BD92B98C4005CE71ADFEA83040140F00688181B3763809B99366E69077DF00ECF3CE124D629BB0AA5F49A2378DCB05EAE7F9D64B38",
+ "encoded_section": "023B3CCC62107DBA3672E653B736AC923A96B0C0E15996965E765F6132269344096A7D90EEA351BE868E561AD5EB39DC7DBE6D3CD24D8BD85D2442CEC60DEA28355F693229ACEB1F9ADB906522FF160B6BEAF54AF8CF4E1B3E5FE23D89188DF4CCB345C5B36DD50E3EDB0ADEC81E3397053A39893F3A57AF78F0B44BD3C7D02FDB527A5EA8D75E412AC7043B",
"identity_token": "53E15A1B5692E1BE92BD953C353F7B91",
"key_seed": "8DA28C3966FAA33B3C7302CD269126F56D184CFC10498ED7969A288AFB0DA616",
- "nonce": "F4839E1496794C1F01572669",
+ "nonce": "B46E5519CDCA07DF972FD6A4",
"section_mic_hmac_key": "B65A6625E49603D87BB9CDF3D5B0D4C2DCDB4536F45A953A67FE6F101C1AE798",
"section_salt": "3B3CCC62107DBA3672E653B736AC923A"
},
@@ -1449,26 +1449,26 @@
"de_type": 305
},
{
- "contents": "B72753",
- "de_type": 903
- },
- {
"contents": "6983195D8AA985C58DB6DA9ADABBB38DB3917430F36372",
"de_type": 540
},
{
- "contents": "BF901CD8",
- "de_type": 951
- },
- {
"contents": "A4",
"de_type": 859
+ },
+ {
+ "contents": "B72753",
+ "de_type": 903
+ },
+ {
+ "contents": "BF901CD8",
+ "de_type": 951
}
],
- "encoded_section": "021026C610CCE881C1A6126022EFE069A66FDB75AAE177D28EAD4AEC586AE134F23F54F49C0E7D888DB5DD850D204030D83C303EC74ABEA7876345F687BBAC70AC0F1C47D6FF493166B00D002A6E7B89CDB7E10E921F081D8C674D10E2D2633E92",
+ "encoded_section": "021026C610CCE881C1A6126022EFE069A68A1B0DE9529BB9EE5AC8083883BFA49A3F067AD67802B6F7B2B4FA1127466C6F369A2C904708ED433222294BB9FD24E4D915DAEC9026168782FA12757EC0C944AB5ED7C73169DE30B00BD16660B98A3C",
"identity_token": "A3D9812AA67DE7AFAFB41B411582B2F0",
"key_seed": "4A23659FB7CFA0A0E73DE712ADD01F8FA0E7215FF8CFBD35771236B60A387011",
- "nonce": "53E571E0DF89BF6C7216C7C2",
+ "nonce": "423AFB4AD38239E68D478712",
"section_mic_hmac_key": "5ACB14F2E5F318A1C9D769FFEF152293CF1A0A37B735153951DD236CA6DAD527",
"section_salt": "1026C610CCE881C1A6126022EFE069A6"
},
@@ -1476,10 +1476,10 @@
"adv_header_byte": "20",
"aes_key": "6AED67F14438D7D911EDB8D722F488BD",
"data_elements": [],
- "encoded_section": "02F4CA6005D5468E98560AC1A7EF01DEE90F3B25A4E8592F10463ED297782C65DF10AA8303AD3B3F647B0B35AB90E3751E3A",
+ "encoded_section": "02F4CA6005D5468E98560AC1A7EF01DEE9F2EC32FCC57496B60911526A9920AA4B10D98FD5D9E8EE35ABF35F9C53DC06445E",
"identity_token": "DB6F1DD46763D8721214EA515B47CFFA",
"key_seed": "F86B31559658B13CC4384D00DDA8CA7924F7F545DCB973E94AFA03D006B8E305",
- "nonce": "A7702BAF126B2CBAE121878E",
+ "nonce": "42AF00BEB0236D09608B4F79",
"section_mic_hmac_key": "1AE36FC285306CC764C77EA289920D5428518672A3B9A5941C02DC4D2C836231",
"section_salt": "F4CA6005D5468E98560AC1A7EF01DEE9"
},
@@ -1496,10 +1496,10 @@
"de_type": 830
}
],
- "encoded_section": "02C4D4D2A92767AA88EC70C16EF70254C5028B41E18F6CFE231D87F6DF5CCACB1E3A2A223140474CA5A48FC80DEF2A64251E63A8645D1B338045BE4F1755E31916DC8169AF62BD094E015DF416DCB929C6FE9419C0E553CCB48BC8B8",
+ "encoded_section": "02C4D4D2A92767AA88EC70C16EF70254C54C3CB46407AB7618E29FD8A1127FC1443AF23DFB2DC6B9206D5616AD8441EC7260D951F849DD6DD6D43693BBA8EEC1FCBC82BF69A4466E2782B4BA4C2C04349CEF2AAAC55D4F82F9CC249F",
"identity_token": "9BD9767523D8349176EB79FAA07CC03F",
"key_seed": "9D09EA97DF4EEC2BB30BF025A9E6B81BD3EEB19870BBA43A4EEE452066BA7F03",
- "nonce": "5B6638F881B5983B1A5E97C2",
+ "nonce": "7F38D84ADAA771FFC27AE62C",
"section_mic_hmac_key": "8F6F64C6F8626EA4F63A0B62B87E5A9F25A5ADE408E1E829E06B952BFC1FC419",
"section_salt": "C4D4D2A92767AA88EC70C16EF70254C5"
},
@@ -1507,10 +1507,10 @@
"adv_header_byte": "20",
"aes_key": "BBCA7CCF407D135B2E4E30F2F2D5BECF",
"data_elements": [],
- "encoded_section": "021AA76A19C77A439E19ADB69D70B0DB1C25F4EF40194EA5874083980925FE7A7710C4C42DA77C168CD699E7ACE28920571B",
+ "encoded_section": "021AA76A19C77A439E19ADB69D70B0DB1C8F91911C4FE808BEBF14883E4E2845A7106FF715C9B2869C58D723B3EFBB81ED3B",
"identity_token": "5350E2CA3B7927CADA9D6F2B02C28310",
"key_seed": "1B679F25EB8C387B01FFB315A1DB04D8981936963BFCAE20BF4F5524C1F762AE",
- "nonce": "182CB56F4E9A946E490A066B",
+ "nonce": "E725F32E784094DD8944D763",
"section_mic_hmac_key": "F7683F77117946A69929DBD6580EA3311C1DAF13C3755FDF052DCA96D0685970",
"section_salt": "1AA76A19C77A439E19ADB69D70B0DB1C"
},
@@ -1519,30 +1519,30 @@
"aes_key": "52C67744CF1B5AE97F091C915CFAA6A4",
"data_elements": [
{
- "contents": "0A882289B49B",
- "de_type": 860
- },
- {
- "contents": "38DB2A68327F6FFCDDCB2DEAE4B4D7C1C68FBD52F18711A8952AE1CE",
- "de_type": 586
+ "contents": "FF6658532AD2EECB814EB0A5BF41",
+ "de_type": 63
},
{
"contents": "E89FE85C63F175B22F6D373707D9C51F7D062B7278DFEB7F2699041DC4",
"de_type": 403
},
{
- "contents": "FF6658532AD2EECB814EB0A5BF41",
- "de_type": 63
+ "contents": "38DB2A68327F6FFCDDCB2DEAE4B4D7C1C68FBD52F18711A8952AE1CE",
+ "de_type": 586
+ },
+ {
+ "contents": "0A882289B49B",
+ "de_type": 860
},
{
"contents": "FBAA7E6F8C867A50DA6EB4F406B7FEEFC2378AC3CF45F1DEE9742FC9",
"de_type": 910
}
],
- "encoded_section": "0240A2448B3046FFE23A82D40AE027B410210C3D699F8057821BCB3C7465565CA087A7250E582F9AA365F5FC4E35F1F1340926427605DF90EED725463E57D16453CA4D3AC95BF35434676E3AA1616176E89F53AC9224003690DFEB64DB256D9DEFEDDB93D9393DEFA3A5BC664C8A234118E5548A6495334FC717CBC4571D2152B6D28FE4EECDE4F3C4179C62CB02BDE28EC7A55C029E3030A30407028FE1A4CE0925F431A2B14437E6",
+ "encoded_section": "0240A2448B3046FFE23A82D40AE027B410A46833AE73DF4FBEFC30968ADA60F584875C9F8511DA646CB3DD95EFDF53F5238FAE90FC9A18D516F8D1F40BCABEF85D33345A6F461773DF7A06F766592068DFADC3F780A150B4B666E4274B0706B6422D06DA1410755F3ACE41B3153B3C35E66AB18CF18556797CCAE896870714578DE09B72FB405DD505BA7A808466A7A1EE18E4722FB380EDC9755F29ACBDE3E39A462A055CFC9C4047",
"identity_token": "44CFC684E6C7050263E2CAB4D744A814",
"key_seed": "9A30AAA2B9A12B59236DB28F6761EFC40CCDDE59A07518BDB19D45BA2B72BF26",
- "nonce": "AD0E77BB4D951804E67A70B8",
+ "nonce": "5441DD796904403437FC61AB",
"section_mic_hmac_key": "CEB035A541C1D12C0F02B861402E73D21C8F4C2EC5C4C8DEFFD1623EC7FCBFDD",
"section_salt": "40A2448B3046FFE23A82D40AE027B410"
},
@@ -1551,8 +1551,8 @@
"aes_key": "23F75DC56095B764A3367B0BAC2CAB3A",
"data_elements": [
{
- "contents": "3ECF",
- "de_type": 825
+ "contents": "84E31D1747DA456817",
+ "de_type": 98
},
{
"contents": "A0A7F010524BB3B0CF95D62762830DD0192486B2C21E",
@@ -1563,14 +1563,14 @@
"de_type": 769
},
{
- "contents": "84E31D1747DA456817",
- "de_type": 98
+ "contents": "3ECF",
+ "de_type": 825
}
],
- "encoded_section": "02E1422169AFD9B4F1C931B65EF8BFCB907E66B43EB676DCF362B31F339BB43B1A58B6D6E192491D7C136397078953ABB335D77668F6EC744FD67142A16C9FCAE8761E4F8388E8B0A2DA4037AB767EDFF9A9A30E209A8D6E2E23A371C4085780A50EDDCD91B895442DB896B38455B9221B3096C69684C14599AC",
+ "encoded_section": "02E1422169AFD9B4F1C931B65EF8BFCB90BD28E613284C82757081D6B53C9FEBD65835B36C0F18A7506AFD7B3B16B0AEAFEB98E4874CFFFD0E4A767DA80D64B948A9230142ABC2CC06F656B834ABDBFDC6F7628EE982C3DA8DC4A3761F01CEF110C943B6EF997C64ACD79E364C489CEC355D1B965F7BCC637A9A",
"identity_token": "10D4BDF9C47D2AB3EC885FBD625C0246",
"key_seed": "1FE48B81802881434FD77BF3C21EC0E8858AF9164C27C74D0F4B362F0EB5C528",
- "nonce": "833F029D2BD54533FC3E70BC",
+ "nonce": "F5060B48D0F7D25968BA53C2",
"section_mic_hmac_key": "78F4C0AAA20F8DA2EFC963344E5926EE8A07494B48E778BC158B3997AB2B6285",
"section_salt": "E1422169AFD9B4F1C931B65EF8BFCB90"
},
@@ -1579,30 +1579,30 @@
"aes_key": "7617C16EFAEF5EDFB06625756362C2A0",
"data_elements": [
{
- "contents": "E5D673BAFF29A4D3C1",
- "de_type": 270
- },
- {
- "contents": "6A57381D",
- "de_type": 887
- },
- {
"contents": "0E556091078C6748C36BAF40BBB4E4BB",
"de_type": 205
},
{
+ "contents": "E5D673BAFF29A4D3C1",
+ "de_type": 270
+ },
+ {
"contents": "2055F5870F08F9CD3EFA17955C99F226404A",
"de_type": 383
},
{
"contents": "C8A4A9A3FA548102A1F96327A4287A70D500DE35862AEC8E1AC6F1DB84",
"de_type": 628
+ },
+ {
+ "contents": "6A57381D",
+ "de_type": 887
}
],
- "encoded_section": "0279C36005CB2893825C08AC9D00C707FABE695CE889681CF240A6D97597ED0E7E6B4B7EE58F722D9DEA51FF86226C332E4917FA9E0724B39DBC3C1C9425F4CAAF49019E646B21816BA6B92701E19118AA31321C1ACC9725D4B7AECE13F56161A978E8F646FBEF9A61498F657340BB21E8D96E8F1980F62FF69F92695C6164D59D271991E38B4367DED244F3C4",
+ "encoded_section": "0279C36005CB2893825C08AC9D00C707FA5954E376CF6171AF16B26695CF3CEEE66BB39C768D17E9BE2B53B49486E970653D29BB1F60322D7F62E4539180F8ED18497FF7CC2234DC0C699583FC44DD5B606DD74884C3EA2A50FE7DF680B223B2313CE785E7337DD9D8032D7426FF5C30F32A35D7FC846166A7FA160B7897C0EBBF6729C5C99E9F2B6992ECA3D1",
"identity_token": "F65402F8A50A61A802B8D5C12F7B6139",
"key_seed": "91E49925060895395298594A49F168FC765017D07C6E7A111FCED5C43A0C3A51",
- "nonce": "5E14CA25F6C86D3DAAAE3A82",
+ "nonce": "FCC9B9829D2B9764CBA7C887",
"section_mic_hmac_key": "F81460B1DA857EF01821651349218A0B6DA51DBD2338913621264D75C0EA0BCE",
"section_salt": "79C36005CB2893825C08AC9D00C707FA"
},
@@ -1611,6 +1611,10 @@
"aes_key": "7D5C69BCCFE498E5464BEDDEFA884421",
"data_elements": [
{
+ "contents": "7456300CB3DD5E",
+ "de_type": 241
+ },
+ {
"contents": "445E8055",
"de_type": 714
},
@@ -1621,16 +1625,12 @@
{
"contents": "72BC2F855131395E0C6C0E5814B02D406724A11179EC",
"de_type": 799
- },
- {
- "contents": "7456300CB3DD5E",
- "de_type": 241
}
],
- "encoded_section": "02FD4D5055F7192BE8BA25A2FA5385B5D524CDE482D669C0E529D5308E322AE8A14F3A9DDC777769014856F0A8E6A042F1C05FB685E2E14E6B63F18556036E4D68A37E9941D7BD4F583C469D5F37105A22BE25B994D2227206DFE0922276D6375D480C08DE38B82D8DE92B0ED8C7AA1A37",
+ "encoded_section": "02FD4D5055F7192BE8BA25A2FA5385B5D57C8E596E9E7C921BEAC64726606A2AF04FDFA05667C2170412239D9EB2ABE47400445F06AFE8B02260569620E87AF145EA06379E8D1E9B9EB3107EAD37928632B3285404B40AA745E2929810298D862D58D24A4AB52307028F78681556EF66D4",
"identity_token": "AE507BDB86B14FE8EB76147249EC5865",
"key_seed": "797EE27B2E559335E524718A4346DC8823367D14A7D7242B0645A103D1E73391",
- "nonce": "13623B08CBFD928C92254CFF",
+ "nonce": "1C84172E56910F18AC7DD184",
"section_mic_hmac_key": "409F3A5462F5B64E15C9264A981ECEDC489C5779AAF29CB5523714DBBB82A06E",
"section_salt": "FD4D5055F7192BE8BA25A2FA5385B5D5"
},
@@ -1638,10 +1638,10 @@
"adv_header_byte": "20",
"aes_key": "F5F75410EF7DC53152BD2130C6327171",
"data_elements": [],
- "encoded_section": "022252124D2BC9B6498FDFCCD0196CF4C56B993255169168AA337B00796E3C0DDA106B5F1F519ADC5898F7F5661895462E16",
+ "encoded_section": "022252124D2BC9B6498FDFCCD0196CF4C560A3A3443255D731C153368D9509786210179F2E5DD53062AC2FD89B5DD8CB8A65",
"identity_token": "E18F1D6F8CEB5CF84C941AA47C728D66",
"key_seed": "4A01C7DC09BD3999C74B95A6B50FC0CFAFEE1E36C260026D0DD46F21C56D82EF",
- "nonce": "497A0BDB69B6E6258A1941A3",
+ "nonce": "32CC8058EFD92868C065E919",
"section_mic_hmac_key": "B4D73916A49E25F62EC59C9E9B24A347EB1A5B95694681467B8FF6EC79A3EC27",
"section_salt": "2252124D2BC9B6498FDFCCD0196CF4C5"
},
@@ -1650,18 +1650,18 @@
"aes_key": "3A64FA90D74450D083302E2C059C9665",
"data_elements": [
{
- "contents": "CFC759543DAD41C009FC402A",
- "de_type": 988
- },
- {
"contents": "786F5232031993953C6A9A08C1976E8C30BFF38FA84531086FF19DFF",
"de_type": 846
+ },
+ {
+ "contents": "CFC759543DAD41C009FC402A",
+ "de_type": 988
}
],
- "encoded_section": "025F023F5D79C52E9DBAB9F0DA69237D642DA017E11913BBB351FAA7067EB3B06A3E8BFB15DE59E4CD1C3444935A180C26E58FCD08650970B216E1CEE4E8E96A4EFB59E03AB6409A5FBC07282CAF51181B8196D0D6DD95D424076EBA7F4C92AC",
+ "encoded_section": "025F023F5D79C52E9DBAB9F0DA69237D64398E6903F30B13E13D093382D3CF8EAD3EA8F85B91DC4BC07D91D327BA7B79C206100EFD2B04B679185B529D3B82DB84B5B698CF215FB49C393E13A9FE34AB670896D072FC20D185231BD53726B990",
"identity_token": "71E0154EC5B0D069EF2CBDBE4D4F64EC",
"key_seed": "076E48318DBCAE9AC52822CCB8C328E8E526278A881C3E777285843D900DC7C3",
- "nonce": "B81706D97EDB8A2F21130E10",
+ "nonce": "B0FD3C98969D09FD94AE1F0F",
"section_mic_hmac_key": "5F5F097D7C90D9DD993E554E3620ACB5E32EEED68D0A70C8D0A96B9E8FCFCE0C",
"section_salt": "5F023F5D79C52E9DBAB9F0DA69237D64"
},
@@ -1670,30 +1670,30 @@
"aes_key": "B443584BA0695A5DE100FC7E4471F096",
"data_elements": [
{
- "contents": "8D07D7C161B1E3B9AD44036C9152",
- "de_type": 568
+ "contents": "EAE8BF35970B3FA758ECD25FAFB52DAAE97E90AE207A5AE1F65EAD0D49",
+ "de_type": 240
+ },
+ {
+ "contents": "FAC026BE6D3446FD6C7EA221B4",
+ "de_type": 282
},
{
"contents": "241997237B",
"de_type": 304
},
{
- "contents": "EAE8BF35970B3FA758ECD25FAFB52DAAE97E90AE207A5AE1F65EAD0D49",
- "de_type": 240
+ "contents": "8D07D7C161B1E3B9AD44036C9152",
+ "de_type": 568
},
{
"contents": "C3BEE2960CA3C9BA732858B26600209CC17E277BB91578F7F992A7",
"de_type": 923
- },
- {
- "contents": "FAC026BE6D3446FD6C7EA221B4",
- "de_type": 282
}
],
- "encoded_section": "02AADF94195BB7269EEA6B86F55F8F625EBAB866B50BDE8C3963CEFD8AF4C6415F77869D51C5DF0D1BE02D052056B0A8EC667DF3E294C898501276BB22C0657468C4BA18A25F35C4C3EE4A233A77C5C3C77F23588264BDE91EE9D0B00735C5A5DFF02F97849972CE1FF1CC5DC22200D33DF6FD1957F9CFE6C42242B63563BEAA654E63F10611B4C43262949BA084C9ADD9DD34430D71A64D68",
+ "encoded_section": "02AADF94195BB7269EEA6B86F55F8F625E4B912D1AADA4E65028278092A2D2417777CFB48C8E8E1813DB119BD75BD93EC3DD165E2705BE0C150A42ABAD015ACB2761C1756B1B46E74A9DC3C192DF2F87FF76248EA626623D775AF45380FDE3F4166CE252D2DE991A5F745A3CE4BB6113E95B520C6C3EE3386A35858B9C660BF2508E2D3CA0FF5B96D23DFDA4FC9173A2B81FD6333C71977358",
"identity_token": "F73F1700F798A87559130AB945C74741",
"key_seed": "DAC1B91B10F16C4398EF7C8058ACCAEC2A9B30A0FB9AD66E121DD19CDAEB37A0",
- "nonce": "2EC6381E4B347BFED3E91754",
+ "nonce": "1BDABE75376431FA74073007",
"section_mic_hmac_key": "642792EDF30489843D0FE58973082D231FDC14BE72A4C007BBBBF85CBE8757A2",
"section_salt": "AADF94195BB7269EEA6B86F55F8F625E"
},
@@ -1702,6 +1702,10 @@
"aes_key": "83EC80F024DF21B65CFEFD1EF94740E2",
"data_elements": [
{
+ "contents": "5672B7B1AA5E483968D00E77AB12",
+ "de_type": 124
+ },
+ {
"contents": "27A69E731F",
"de_type": 287
},
@@ -1710,18 +1714,14 @@
"de_type": 576
},
{
- "contents": "5672B7B1AA5E483968D00E77AB12",
- "de_type": 124
- },
- {
"contents": "F46774",
"de_type": 705
}
],
- "encoded_section": "02D525060ACC8BA94C63CDDEC60BEDB5AD791F43370ED6BDE4CC9BAE70A03E4C9B3D33536D52A575D5C02DD67E91BED7C3DE179AFC1C035397388F48297764EA4E8C329557E6A71FE5A63379A8618777524C2AD19E0A9BF1F65F6BC54C076C",
+ "encoded_section": "02D525060ACC8BA94C63CDDEC60BEDB5ADCDC1FE8607D5F65092EDE44110258BB83D44738DEAB7CDA977D7DA3733635528C9AF4C73D859B3CF4041D08FD75CC269760FEF852A0E309A82DCB962519DE8408BD174C3E88064FD7AEE6E1B12E4",
"identity_token": "DCF0C0BC6CF0AED2BB98005F9BD314CC",
"key_seed": "40402744DE9F4976A2DC489058A6E3392FB0B9835DB8C7018E678B3406C4441D",
- "nonce": "02DA50D948182D528619E7AB",
+ "nonce": "98E36D6F90B865EF20649CED",
"section_mic_hmac_key": "035640B10657D96179E987095B1D01D9A98C6158A98726FDDFADFCA582268F9E",
"section_salt": "D525060ACC8BA94C63CDDEC60BEDB5AD"
},
@@ -1730,22 +1730,22 @@
"aes_key": "0418BBAF50BB908526F913583233E243",
"data_elements": [
{
- "contents": "F45CB09C4F9BCC64CEA8729CB757EBB30B9F35DDA00494D9DB2CFF53ACAE",
- "de_type": 909
- },
- {
"contents": "50F3A9F85CE86BB46570EC179649CACF30E68184C26F0EF029D88AF32869",
"de_type": 47
},
{
"contents": "036508751C50D9C40FF288AC9CC81B46D0CA17",
"de_type": 498
+ },
+ {
+ "contents": "F45CB09C4F9BCC64CEA8729CB757EBB30B9F35DDA00494D9DB2CFF53ACAE",
+ "de_type": 909
}
],
- "encoded_section": "02CCE7901E20E283CD0A9CC73E4EECE408A6D1C01EDC8574E8FF9F2D4E1F07CB7267AC537E5BC0F41CA06B85E63818B10F5B012DB59C0D127A1AF043750A04910CF3E8CFCD245190702461AF431A690891BA9B37146990A0A330674F9708D25B3730D5B23A4DBEDF46694CBF3D7AA4CE6C35C8CC833A22B82B74A3858DCBFC262C065EB5A5DBEB36B7",
+ "encoded_section": "02CCE7901E20E283CD0A9CC73E4EECE4083D5204521AB35FD8B31ABF69E9A08E6A67AA4EEA683DF7FF901D8A65248CB1490B0C48F4EE7C93983AD327E8957D89367C06B7AFD911C704228D85E9769039A1B2CAF6FBAD0BA9EE93BF4A7D5AF23BEB76E531C53E066483A501E9025CE01DF61E60F0843BC1753E6BD5D0175922A756BBC33BDE8F7FB1C2",
"identity_token": "F8D68732B8CB236625F2F9FE6EC1DC4B",
"key_seed": "A2BA315C4719A6F2F908D6662DBBCB072B283D419F9A56B79B2B6233E96C21C7",
- "nonce": "1B41FBADE98FA042E9FEC7C3",
+ "nonce": "9F6CCD8C9DBD0581B2D5A5D1",
"section_mic_hmac_key": "72F23D0F266F564D8D5A2545426A7CDDA3F7D20D43E372CB6EE46D85871537FA",
"section_salt": "CCE7901E20E283CD0A9CC73E4EECE408"
},
@@ -1762,10 +1762,10 @@
"de_type": 503
}
],
- "encoded_section": "020E4D5C6ED93B014055B8951F8FD220A57D827187D544B46DCD6ED9432B424F4122E01AC41591CB4DF89F5D00410412A7647452BF4AE9865BC9F337CBC12706F1881985",
+ "encoded_section": "020E4D5C6ED93B014055B8951F8FD220A597407FE316D4F31EFB1F31E20AF065462258FD8D26C33BAFB16D593BD1DBFA757D900EBEEC5775DB14CA849950E2EBA34F639D",
"identity_token": "F46324863C329E19C0522E7AFB0D9B82",
"key_seed": "43766BADDE1B80B6598CA446112A8FC98DD42EAB244B3DEBF08302D7BD4FD376",
- "nonce": "FC43822BD63D014A2ABA6479",
+ "nonce": "33316F0633C356FD60B97E5C",
"section_mic_hmac_key": "DF4154082EFCF4F670B87A36A31E095ED7B7AFBF8C324D9F8E0D29E1EEE8F7FE",
"section_salt": "0E4D5C6ED93B014055B8951F8FD220A5"
},
@@ -1773,10 +1773,10 @@
"adv_header_byte": "20",
"aes_key": "77F690701DDFD7AAEE15B393CA8E4113",
"data_elements": [],
- "encoded_section": "029E470E6DD0076A256D60553EC207DA37F38577FBF8CB639C6E8F84A4502D5CAD10B4F17F4720071345C051290A66EE1347",
+ "encoded_section": "029E470E6DD0076A256D60553EC207DA370BEE846E20317FFBBAC4794570AFC5FE1055F18451DC418C56327D2371768D4EC0",
"identity_token": "AEA2B76D803628E0426E64AB7477B395",
"key_seed": "13154557990A80FB24A70BCE09BBF62DAC2C95146B1163508D9CACEE89C75D96",
- "nonce": "6B54C468B6CD60C9BCF05C06",
+ "nonce": "3D49EC610B481B6608613141",
"section_mic_hmac_key": "7C56EF628CB49E0E3439DBE47034A4EE4D74CC431D919E7ED97F31B7C4680D00",
"section_salt": "9E470E6DD0076A256D60553EC207DA37"
},
@@ -1793,10 +1793,10 @@
"de_type": 920
}
],
- "encoded_section": "02ED8065C1778DDB635023F96FC9DB4B43C2FCFC10BD074146EED51E7F9A46C9C437F01690BF9BAB7C0A9964BFDAFF313CF0B46D6387B78922EC76F009B1C9D11C103F54DB4B6C2CC32CFCE97E2F3349B91AF70279128DDE2D",
+ "encoded_section": "02ED8065C1778DDB635023F96FC9DB4B4363CF88987DDC212FE7ABDDE4C63B7E3D37F189A85A72B221E4A839E3565D7D62FF2965C77D6808670B717C96DFD6AFF3CEBD5C77FEF31C2AA4E75AB0C26C8E88F496E0D9961128D5",
"identity_token": "0A257DF8A7692B3249407E160C037F32",
"key_seed": "3F64813E8A4B3DC815206ADADF36DB6D66B9BAD623636491CE3A47A85A80ACE1",
- "nonce": "E1AA1F6A438632C46B33AB73",
+ "nonce": "9536AD95206E88DA4E412004",
"section_mic_hmac_key": "A8A1D0BB14D04085F5340F1ED0AEE5C2201BD4EC668991370E804DA18EC94CB5",
"section_salt": "ED8065C1778DDB635023F96FC9DB4B43"
},
@@ -1813,10 +1813,10 @@
"de_type": 323
}
],
- "encoded_section": "022C1DC1FC780C25AA2E4852BA8CDB2BCB44DBC4820A019D95184F42A70E419861498AF4A6CD8AC3D9E110C0021461F9020233FBF843404099F378366DB902EB106947263B984BF7D40116604F55C42DB12F25C686B250E139DC1C994CDF5E7C1CF308144E294155D20AD3",
+ "encoded_section": "022C1DC1FC780C25AA2E4852BA8CDB2BCBCC6FF3A657F5C31865615CD13DD95379494F34BCA2CD4E4AED268F5176A62C4C20A78AFB7551CE89C444FF6D8774F8BB8E818B1D90ED5EB7A4F48C097A762554F50886C45F218F31EAC3EFECA4AFE0A244B25826C7A25BEE3357",
"identity_token": "776B3A62CB93BA94651552C97A2E4267",
"key_seed": "9509B69A6A2BB6CCBB1A1E7436E77AFC2D00A2FABD2A1665A7BA6FD6E33354B3",
- "nonce": "42DA14BD4FA470084787B4EB",
+ "nonce": "D533C1483769341E798F5F17",
"section_mic_hmac_key": "603743F9A5815E17229E364C363DEAB2ED6341B86E6D6A4F2F723629272CD0DD",
"section_salt": "2C1DC1FC780C25AA2E4852BA8CDB2BCB"
},
@@ -1825,8 +1825,8 @@
"aes_key": "B60EBD62633BB487ED9F7D9E426A0D96",
"data_elements": [
{
- "contents": "7A0AC4F78A416F5B0114535909A5307F",
- "de_type": 902
+ "contents": "CEDFEE9059F500C97D25C94F",
+ "de_type": 7
},
{
"contents": "EF581947C68D9E50AA05",
@@ -1834,21 +1834,21 @@
},
{
"contents": "FD",
- "de_type": 888
- },
- {
- "contents": "FD",
"de_type": 315
},
{
- "contents": "CEDFEE9059F500C97D25C94F",
- "de_type": 7
+ "contents": "FD",
+ "de_type": 888
+ },
+ {
+ "contents": "7A0AC4F78A416F5B0114535909A5307F",
+ "de_type": 902
}
],
- "encoded_section": "02DF453EF92630EB12DC02E3EDCA4A36A08BA4F34CBFA1B595CB3A6D01C58724D0455434E8D72455F125B81A186A41FA589EE9710ABD7F9F67E011912FD0DFB2337306AC59112298BA20EE5FE66227AB94034D13388BB4484B8A6587A31F0234024115F6D30CF9",
+ "encoded_section": "02DF453EF92630EB12DC02E3EDCA4A36A0B77C94E769E8C47223261980FB97B1E045F5C324253658B22ADB1844DEA65000C23795AADCA7D07BB39FDA5A8764750CC02A561E34DCE771C2F50F60415DF21A30097214CDA4463BE295A8DF6984AEA0D568DEB04E5B",
"identity_token": "A1C908312B87892BB9828075C672635F",
"key_seed": "6D32B114EF3DF0128A529FDF8B2E7EFBAFF451FAB30302DDD8BCDC811D0FF0AD",
- "nonce": "720138995818A3EE421768BF",
+ "nonce": "3D479E4969EE31BBCB0D3F49",
"section_mic_hmac_key": "9243F2687949CC4D05AEB4D8FB77890569AD61591E37FF206959B6CF98342BEB",
"section_salt": "DF453EF92630EB12DC02E3EDCA4A36A0"
},
@@ -1865,10 +1865,10 @@
"de_type": 238
}
],
- "encoded_section": "023AF28C09CAEC840269BF5DEEEB2AA95C60E64AF819E6A97B8ABE980FD7320593400018850FCA6227B727F1ECFEFC2B74B62F21E44B40B14500E9736646713FE6B65DBCEA9A39CA410D4F4E7C25FC753BF4E89064485C09E8B30D936CF3FC31E704",
+ "encoded_section": "023AF28C09CAEC840269BF5DEEEB2AA95CFD49346D385E95567069742E3A31E39C40C4A488D9FCEF4EC2F23E3823480DF41B878EFFD813BA59C999E882373CC027E632E7ED775C781F238C4AB18872A4D84C4B87A869BB5144402AC8D1031A69827F",
"identity_token": "0758CA3E92715E8088FEDB4D069661F7",
"key_seed": "46F7E0CC1B0AFF6971DE8E4AF1BEDA76D5FBBBC1673D67D5E3592207241B03CC",
- "nonce": "CD05581E1D32C9D6F251E1DA",
+ "nonce": "7A3D49949CAFB8D201A3B7D6",
"section_mic_hmac_key": "7EAE309E12347554A36BE7F5416BC98458A6BBE1CE11812AF7E1E0170942DE7D",
"section_salt": "3AF28C09CAEC840269BF5DEEEB2AA95C"
},
@@ -1877,26 +1877,26 @@
"aes_key": "094545346D6DA2AB87E9F21C22EBEB5A",
"data_elements": [
{
- "contents": "FBD5BF3B86FC4DF98299A04516E245CF69BD201E44111D9F58835F",
- "de_type": 962
+ "contents": "F86971896012BD",
+ "de_type": 517
},
{
"contents": "7A00781BDED3EB83ACB0F347A49F14D039",
"de_type": 611
},
{
- "contents": "42A41A496D6BE276833FFFFD2778E555A4ACC09190B60594CFAA1F",
- "de_type": 992
+ "contents": "FBD5BF3B86FC4DF98299A04516E245CF69BD201E44111D9F58835F",
+ "de_type": 962
},
{
- "contents": "F86971896012BD",
- "de_type": 517
+ "contents": "42A41A496D6BE276833FFFFD2778E555A4ACC09190B60594CFAA1F",
+ "de_type": 992
}
],
- "encoded_section": "02F8F5300B7A01F96A0B73610D4A82F877EC68C96FE249E235C18FC5856E98E4646AADE7D3002AA4F39AF7F2113392EE3C6521AAB6EDABC8A5C5CD33275355C434BA00554158FE2207C510267BB6A50B721D2BF1DB8A832CEE8C1D09CBE920E9B9AE68686B7393211E5CD0943C97A76CA8A0FD82B18CE06A7ED92FB378BE450B6930A6F241B1D2696E80B598",
+ "encoded_section": "02F8F5300B7A01F96A0B73610D4A82F877C0DFA3080811DD0C27B09B47AA8BEAAA6AFD3BA7010566CBA2383A571CD9FA395DB9AE7024504585CA07E077316A3101A5476FC5548C024D16603F9A568C97618FC21A755DD07623CDB8E86DBF45AEFA7EBB815E3577D06F20C8E56751FCA7E3A575072CAA6AA885C208A1D842518B84120FEFFBFBC5CB50D0E9EC",
"identity_token": "6F95D140C0F87F34DD4CE7CB17CE8609",
"key_seed": "0CCB26C7DB958F8435D66E11D6A68875DA25E7D12A11EAA58386A2290A42027E",
- "nonce": "6DD2B018A20FAA4E46703F7B",
+ "nonce": "1CF657E6F0DADEB81BC949EB",
"section_mic_hmac_key": "D5A0F725FE6E6D8BDC79C2C668196D3303D6465C92D478BC676F91C1A462E024",
"section_salt": "F8F5300B7A01F96A0B73610D4A82F877"
},
@@ -1913,10 +1913,10 @@
"de_type": 698
}
],
- "encoded_section": "02B14D7C2C925F3E24ACB1E4A5D4DA6F193FA87DDA8E2FEF20F01FFB5ABF3B8CFC18B5ABC3BB0FA0C46785729CFCB9192442D7DCF702EFC0304F",
+ "encoded_section": "02B14D7C2C925F3E24ACB1E4A5D4DA6F190818294E851823F88A1448E49798CE66183A67653B68C863320570A9C4D8D4ACE187EF97789FFF3561",
"identity_token": "9E6F8A6063A6BDD60D5954F592A3B8F2",
"key_seed": "C62E96131C7C8B805CC9FE6FD634CB9BF84BD4984529B30576B3F3856CF04E63",
- "nonce": "0FDA5313782FED9FF7E65C4C",
+ "nonce": "166BAAC81B2035D7F2568ACE",
"section_mic_hmac_key": "89023EED69C4E5DA5ACD11E15AA540323F2EA47A19E4937C19A10DB89BF9A735",
"section_salt": "B14D7C2C925F3E24ACB1E4A5D4DA6F19"
},
@@ -1925,26 +1925,26 @@
"aes_key": "B5CE72C63EFA9DD095716472FAEE0784",
"data_elements": [
{
- "contents": "DC06F9FBA5ED966657D9CFA8667DC006850CF89899DD",
- "de_type": 989
- },
- {
- "contents": "F68AA9DFC06B7EB7D9CBBF5EAFA968984A100C2B03C504A5E1D9C7",
- "de_type": 706
+ "contents": "5D1B64B0D53EDAE32FA989CCC38E1B2ACEAEE0930EDC",
+ "de_type": 221
},
{
"contents": "055D1DEDA05C238D7A31E5CCAE",
"de_type": 379
},
{
- "contents": "5D1B64B0D53EDAE32FA989CCC38E1B2ACEAEE0930EDC",
- "de_type": 221
+ "contents": "F68AA9DFC06B7EB7D9CBBF5EAFA968984A100C2B03C504A5E1D9C7",
+ "de_type": 706
+ },
+ {
+ "contents": "DC06F9FBA5ED966657D9CFA8667DC006850CF89899DD",
+ "de_type": 989
}
],
- "encoded_section": "02E1630DAC1DF049E06A3020858C0857589FF7482D737059F5C999422EC552C91670AA375473BE99ADE7BEBCE8F54E3EA98B2C36D02BAF87EAACB81D0C461C824138DFBBE8BDF53FA7E19F53BEEE1EC2E228956F551335C7BE1FA38CA5933DE6AF767650F6B00C4CE3F7FEC0ECEAAB12811D79317CE035BE9E5E19522A1E329CB0D9B6FD0275F619D95F0BFDF8F071C4FB0A",
+ "encoded_section": "02E1630DAC1DF049E06A3020858C085758B52E9B2B43347825D5A49F65633949C270F27FF513C7668B2F4A001DBB9E2521EF5AAC8CB9C007B16CDAC617923E9123457796D659E9B82EC7A16A3A88B8E94D00B0EED9A223C5E8BBCA745082487E98ED65578529D865A6DDE52A753543F6A5F98FED6DCF493843E8B3C44A5CB09EAD2C41177D9FED49635809AA5E6D83600F64",
"identity_token": "57EABE3C09AF926FCAC347ABFAD9E33B",
"key_seed": "D3E7B4BD3F00BB656B34B68A9B57D3499B1690439F52E39BF55145069B69E012",
- "nonce": "70AA23057C2F616829EB283B",
+ "nonce": "1E3A05E747CFA9137388F848",
"section_mic_hmac_key": "6DF923F1C439D6DCB1D17267F1DCF9F77974CA0CDF3815525FC480F56FBD46C7",
"section_salt": "E1630DAC1DF049E06A3020858C085758"
},
@@ -1953,30 +1953,30 @@
"aes_key": "E257258A3F83CEC8750E338556A3806F",
"data_elements": [
{
- "contents": "FC2AA7F38F27003ACE8033FABCE80A1A0A2855",
- "de_type": 534
- },
- {
"contents": "93CFA0A1BF2C1517B833EE189ADBE984348FE43D91B738D1D51B1A52",
"de_type": 317
},
{
- "contents": "4883BFDB2678",
- "de_type": 899
+ "contents": "37C02A83E008FA26",
+ "de_type": 532
+ },
+ {
+ "contents": "FC2AA7F38F27003ACE8033FABCE80A1A0A2855",
+ "de_type": 534
},
{
"contents": "EF90D2A4545867C3",
"de_type": 654
},
{
- "contents": "37C02A83E008FA26",
- "de_type": 532
+ "contents": "4883BFDB2678",
+ "de_type": 899
}
],
- "encoded_section": "0289F842808FD5738C338616A2560581A8A52F4A6E16A6AD73CAA41028724B501D64C5800CE30D76F8D3BAD6CD098CDCE3FEB9F4425CDC836605063FFC21BCDD041E0158EB7478416478361D4E2073A9E1C61EE770E179998E0211D0B225F1F59C2B9392F5843E3E4F99716B356E52081797EDA99115FC8A0012D55A0C3D119117D0D98F57AF",
+ "encoded_section": "0289F842808FD5738C338616A2560581A86D613F514836E661FFA78E2E9B1AEF5B64BEC3936EDAA9D5F676711C12A1B8BC9C235892B46E215D735A30F2D9402A6A69E3F8929EE1AEE43ABF5FB1DCEB49AC8F86EA8EB2EDE3E5449596CAA8C7D6F999E53B51BFD9C4DAE1B507D9BB8E71CFD5CD2B7C389984C84FC2A9C26CBDF05F980BA7C3A2",
"identity_token": "80FAE8E4817345A5115FE9556AA13FE2",
"key_seed": "C6044A22FEF3D9547667D2B2E2BDB95FE9E436B43F7137CB775B790BBF1C5914",
- "nonce": "DCB6A5793686750BFF4725CA",
+ "nonce": "F5311B953C948B4DFAF64D57",
"section_mic_hmac_key": "B2A815D44780CBD0317F016087313B40970A52496DAA6B665EDEEBE1287BC499",
"section_salt": "89F842808FD5738C338616A2560581A8"
},
@@ -1985,26 +1985,26 @@
"aes_key": "C92370BF646709CC74E15195B1ABABD1",
"data_elements": [
{
- "contents": "BCBFD4CA484D111FC650E199022CEDB4472F4D9F59C27551835889B9",
- "de_type": 89
- },
- {
"contents": "479B03",
"de_type": 87
},
{
- "contents": "7491B3382FB4A3",
- "de_type": 857
+ "contents": "BCBFD4CA484D111FC650E199022CEDB4472F4D9F59C27551835889B9",
+ "de_type": 89
},
{
"contents": "28006920EB7BE921EE08CA9336FF3707D9EF2A7D46FD3CB35978CFCD0C72",
"de_type": 714
+ },
+ {
+ "contents": "7491B3382FB4A3",
+ "de_type": 857
}
],
- "encoded_section": "02E87E2FF9D4E8BED3A34DE8708A523395A9FDFF7E97F5E5222F1C4B3AA8AAF6845EA536945D4DFA8BFEDDC75C93B34610D1621CCE80013B78B90E0E007523EA372201FA31AFC18C88A789792AE6E37CC5F054D82642578AC1DAC9EC7A79931FAE1F296A85C216E879CB49D5438B4D9F7342F63576831F37CBA052BEF9674439",
+ "encoded_section": "02E87E2FF9D4E8BED3A34DE8708A523395C11AA3285669D08A4BDDA9F6C3ABB8EE5E07B8BF888880D4B28F91DA12F56C9E05899C26B751870E7FB477C69EDDB725362F9A46ACFBAC37FB66CA39D95EC076888E070C6EE444DC5D1B76E14FBE2BA17F21DB32708931DD014A65850706EFE2729B743B801B9B0A3986983FBF5C83",
"identity_token": "08ACA51C40D663F21A4CF2BFDAD1E502",
"key_seed": "D4B0FE67999329E23C3217DB7A469FC35280B5978CE80E532C08B59BCEE4EF00",
- "nonce": "8FCF4745A019605307496CCF",
+ "nonce": "BCE65B4113ABE4B3A6EE47CE",
"section_mic_hmac_key": "E2CD200FD23CE0BCDB1CB503048CE71B4E7E3F150D5658F8234B6C6D28893749",
"section_salt": "E87E2FF9D4E8BED3A34DE8708A523395"
},
@@ -2021,10 +2021,10 @@
"de_type": 969
}
],
- "encoded_section": "02E44ED613C98F0DC2AE5C4323042CD36D21B0A7C29E85FB08B05F62E9EA5FE3C231E9F3572CBD15F02D0A232F930664DE8A8D93CD63BF008526F7F60015C1BCB75F2BC2D12AC600CE2E0F87AA96C8E54BF99F",
+ "encoded_section": "02E44ED613C98F0DC2AE5C4323042CD36DA3E24B77F4F999DB0EF602E3A236191E313D000C2D2C5954A8DAC563E136702A3CD517B533539BF268DF09AF2A753D99E817597C24CA7FEB27E2B63B695DD460B817",
"identity_token": "19D4435134937780CED35A279D095109",
"key_seed": "6C41D17E9C408D509BEC4EA6716F010BC72B801F9545BDF8F9DB9539AC6273C6",
- "nonce": "189F567A1F62F04507652C14",
+ "nonce": "D17849ED325F860E4303CFC9",
"section_mic_hmac_key": "D1B6432B61BB4272E325A6979882FE9BED562061A383C648C3294B19B707169C",
"section_salt": "E44ED613C98F0DC2AE5C4323042CD36D"
},
@@ -2033,10 +2033,6 @@
"aes_key": "5533BA11358651AC5301BCC7DFE8AC6E",
"data_elements": [
{
- "contents": "B775D5A14D08158FF431E168F10515145B07",
- "de_type": 798
- },
- {
"contents": "2FE686DF80A9EBFE2405D84EAB72016B882AB50A11CE9EE5ECC5",
"de_type": 430
},
@@ -2045,18 +2041,22 @@
"de_type": 482
},
{
- "contents": "C8EAFF62E466C78817A3747427C22E68750507B744A6",
- "de_type": 823
- },
- {
"contents": "C964A89C33D058E2D533",
"de_type": 773
+ },
+ {
+ "contents": "B775D5A14D08158FF431E168F10515145B07",
+ "de_type": 798
+ },
+ {
+ "contents": "C8EAFF62E466C78817A3747427C22E68750507B744A6",
+ "de_type": 823
}
],
- "encoded_section": "02A25AF6F97A49FD5AD7A58AC7C618CADCE311355595A5534B1F7F0BAADF291C8A7AB1895AF21700990CD60C3B451431E563A6D614CA7E04A84935698C0401CF79D74AA6DD6B65E23E6B0FDAA670D41B995EE23576190458B823A743CC915471252B601682AD93237C3B986070F377F4E1254953760337B10F55882E805E44447AE30E32DD4111B1BE46735F9409A8F57BFD89C9CBC8389EB5DC220B",
+ "encoded_section": "02A25AF6F97A49FD5AD7A58AC7C618CADCF8C4BF4337E66115C801D9AB4D5817F77AB4B82B9907FDEB2D47B102A7DAA35E2AC226E32B146DDEF8A2701D5EC83727F76E446021A22BE441C8CC2BEBF2DBD2808D81EC825742907A973D9E911F13F33BCEA93EDB911124CAFDB274A6BC2B2B4289208E0D4CED224E4A239C2C134C7628628F29644F397298B0BDFFAC6B0EBE242A4A29D508662625E802",
"identity_token": "F8378536329D2B2E962DE8AC894BA112",
"key_seed": "195197E02A7D9C2F04073A07987A7FBA10B4EAF66D69FAB54015A68960933535",
- "nonce": "E3647946D8FC39BCBF6CB625",
+ "nonce": "1E59F0B612B773819A3A9C9F",
"section_mic_hmac_key": "1E35AFE30548CE05848D3E362CD077E2DB826E10B217D21C3026CD2D91F1CAFC",
"section_salt": "A25AF6F97A49FD5AD7A58AC7C618CADC"
},
@@ -2069,26 +2069,26 @@
"de_type": 93
},
{
- "contents": "AD7FA1E4C83E494CADC0E105",
- "de_type": 591
+ "contents": "0C4AD34F70D189FB9B6B93A52E0804BE81CAA18CDD",
+ "de_type": 272
},
{
"contents": "8085D6",
"de_type": 425
},
{
- "contents": "267DB04BA141F8A0B9D16F98F815AA051AC5A2DD18AE2CBB5C",
- "de_type": 719
+ "contents": "AD7FA1E4C83E494CADC0E105",
+ "de_type": 591
},
{
- "contents": "0C4AD34F70D189FB9B6B93A52E0804BE81CAA18CDD",
- "de_type": 272
+ "contents": "267DB04BA141F8A0B9D16F98F815AA051AC5A2DD18AE2CBB5C",
+ "de_type": 719
}
],
- "encoded_section": "025723B7208ADF96D9B8BC420F33486A6C8B36F43A0470FE4B458949F34055F1E75BAB75AA88F163CC5E6DCE56928779AB957A749284BD8B7B84527BF9AD52C4A08EF70D4CD2B76BAE77533DF9239474DCF758373A9D0EB08BD383A582626E70F39871439F29B764B9348F6B9984421D2E7F6A13C62B7164CC9219958E",
+ "encoded_section": "025723B7208ADF96D9B8BC420F33486A6C3FD07FDA261D9EB9FCC222340F1FBEF45B7BA3395296175DB7F16D828CB07E715E68860C6736C557A9D5DACFD23CF46D507A4DEED8269F3A8A62A0A7432F69CC36DA392EA76CF3E26AADB40249D8C3A5609D1D22FC117A50F9EC803C45006A820913A735294E5A858F4BACE2",
"identity_token": "655638465094190FE9A77463BFC9E126",
"key_seed": "AE9604009D3FD6E950A44CC767AE10C22A6744CCF2CD8E0C76E76385E6081DD5",
- "nonce": "228A038D1F89336A39CD594D",
+ "nonce": "292EED5471687D875789448A",
"section_mic_hmac_key": "ECB8CB36E3E179D9FD78006D46C85058F1A2BAB87014F58D792382331B7FC062",
"section_salt": "5723B7208ADF96D9B8BC420F33486A6C"
},
@@ -2097,22 +2097,22 @@
"aes_key": "489C9B6D1094A881909199D2F70129E9",
"data_elements": [
{
- "contents": "A50C355E004E703AB3E11199F9F7042FB8B297A8EF63EA692AE83EFCFD10",
- "de_type": 636
- },
- {
"contents": "58069F92B86136E4DAFADE70D940D3C997D95F94",
"de_type": 434
},
{
"contents": "FE0781315E780B0C45D4B4B1",
"de_type": 577
+ },
+ {
+ "contents": "A50C355E004E703AB3E11199F9F7042FB8B297A8EF63EA692AE83EFCFD10",
+ "de_type": 636
}
],
- "encoded_section": "0271B85F63AC3C61F03F39EF0E169D1D82FCF69C3E0CE5EE7830BAF677CAB9A146578963EA76613CC2A8B79EAD6AC940394D4BB937993A3C29BB3566FA8B23B9F6C035CDF9CA49FBAAF3B9D32E7D177EEE6D5E30D64DA436E0A6F3822B94C8F9DC84F77D0546A0728F6D4A0C0886DAE3C50D217601E697BB48",
+ "encoded_section": "0271B85F63AC3C61F03F39EF0E169D1D82A91A45CDB5B94EF9A28CBE962ACE8641575EE913B9E5AB0439CA8807EF6CA09ACB8BFA27B1A2A6F114ED667C0E1604205F2A35C6214F3A0CBC9EDB5048A13F1D41ED96C5CD2D69925F97D6730B5342FC70BCEF968D5A6BD52CEDA08A53F83A9D8AF44DA73918D0AA",
"identity_token": "F795F354C5FEEBF98A35AA27BF4FB65D",
"key_seed": "9E41319F2ABE29937E2BC9AADB59DF37DDD64CC95470A1FA92FCD92284C49C07",
- "nonce": "8D134D36CACDFA48AD2E1624",
+ "nonce": "ADA115EFDE54937EF3BDF555",
"section_mic_hmac_key": "8F01AC5CEA8B33DC8E3A6917CACE845E3113D054A6F190CCA4F8B19B00789CCE",
"section_salt": "71B85F63AC3C61F03F39EF0E169D1D82"
},
@@ -2121,18 +2121,18 @@
"aes_key": "1AEFDBD92CF0BFDD813CED1AD930424B",
"data_elements": [
{
- "contents": "89",
- "de_type": 537
- },
- {
"contents": "B5838F8BFF3E1FF8CBE34A",
"de_type": 148
+ },
+ {
+ "contents": "89",
+ "de_type": 537
}
],
- "encoded_section": "02AD263B2C711BFC36EDFA522E9607CE18DA76E578E9130C9FEE33FEDA62BD179322CF68031C2EF90E092EDB3707CD7F86FA9CD806C56ABC93321E301D4986832966C748",
+ "encoded_section": "02AD263B2C711BFC36EDFA522E9607CE18BEE13C92E5179082F3BAC2568811536922F90745394582DED3FB93C0952C6B7B56E52CA72E441EC96A46AD4C384998E91E9506",
"identity_token": "03785872BE7F3F5A06BA8DE5A547A0B6",
"key_seed": "E3D6F78BC1A45CB75B06E63B81733A1B31DAF8AF1E4790B884599718BB790412",
- "nonce": "67A8B67A65C9C4D46FCCDD11",
+ "nonce": "A41261DB93A26EC153EC9FBA",
"section_mic_hmac_key": "6C89903E5DFF8B5D2CF1B2DE9EDDC78FAAC03B5EBCC77B9CF810A9F29E6AB15A",
"section_salt": "AD263B2C711BFC36EDFA522E9607CE18"
},
@@ -2140,10 +2140,10 @@
"adv_header_byte": "20",
"aes_key": "11FEBEE577A641BE1BD6D7A95DA0DC61",
"data_elements": [],
- "encoded_section": "029C0F1303B8D7E8FB226FC19EB2D987E443667F2221C7DAEF257221200FC24B6210F1D684DDAEE3DFA978FE9A0C807887B4",
+ "encoded_section": "029C0F1303B8D7E8FB226FC19EB2D987E4166C59D6B9088A239793510A33449F8F10E6B3759BEC0D0EAD9618419F758B0C66",
"identity_token": "9BF79D0640C3F033616E88C085BA0E84",
"key_seed": "E71E5C9532BFF3AE23003A89003A6D78BCD3D11705FC47C56D55ACDF0C1E8625",
- "nonce": "727CC259D161A356AEA07884",
+ "nonce": "BC9172A6F2E48F901BF03E5B",
"section_mic_hmac_key": "4D2C247CB47A94E69FB4D4BE132D0F7C3FFBC4756234ED5B1E6D3A8DB4A88F7A",
"section_salt": "9C0F1303B8D7E8FB226FC19EB2D987E4"
},
@@ -2151,10 +2151,10 @@
"adv_header_byte": "20",
"aes_key": "72909F4BB264D9EE250C302AB93F9691",
"data_elements": [],
- "encoded_section": "027BE514C8BD6C7BE897EA35578108DD35C1D8154719977EA32D96CA42E86AE207107F459409E5AAAA74A2DB0542D6062DEA",
+ "encoded_section": "027BE514C8BD6C7BE897EA35578108DD3548E4718E0C9B40EC0FEDCB3E27827CF8100022107CE5BC7F82032BB3A7A24E54E7",
"identity_token": "5846C49FD8AC98C1892ECFA87C40496D",
"key_seed": "8D6D511E1B70E96DCF325405832C77CC3565D1503AA3965894616C1F0C57E113",
- "nonce": "1524A30566D6169A501F43C0",
+ "nonce": "8BE4565F41BBD77EBEFDEF6A",
"section_mic_hmac_key": "C743F000CE8B2ED5C3735E9CCEA37E634A0965E71CB8CE9DA599917FA6337C50",
"section_salt": "7BE514C8BD6C7BE897EA35578108DD35"
},
@@ -2167,22 +2167,22 @@
"de_type": 208
},
{
+ "contents": "9931CB53B91C9EF8F62E358284A44B00D8AC1A95AA73",
+ "de_type": 222
+ },
+ {
"contents": "952B14BF77506C",
"de_type": 549
},
{
"contents": "6E5D3C18ACD4B106A55B4A2637FA57DCD34D",
"de_type": 782
- },
- {
- "contents": "9931CB53B91C9EF8F62E358284A44B00D8AC1A95AA73",
- "de_type": 222
}
],
- "encoded_section": "02D14BDD76436FA2B760E9AF470F4321DDDA37CA2E790A42149020904EB4F7C88A560D3F833B34C52AFE8DF576F04DD03F9E961F946B06501E88B0D7EFCB951EB7D876BE437FE69385B7CF0F331CA1B3A2E3E95279F83E363542E7D5F757AE3091B5A288B485C50EF7BB755A39296357C1864AC797E35505",
+ "encoded_section": "02D14BDD76436FA2B760E9AF470F4321DD92AFE5AAFFCFD0D5869981F0962526D2566222DBD3B6850FAE88F9872621931FCAF25636D12BF380F20D37BC39CF5F50C35D302E539BC03A66C1D817FCE2B6A0B8FF50CC6D5ED1129CE78BF98FD7FC174912C2567E9A51CAAF226C04AC699664F949B495FE3A92",
"identity_token": "36D94D61F32E7B6B24F59C8295F1AF96",
"key_seed": "4BB6613384D29BA3C848795DD8C4A2C43DC9510A8EC03F6043E815AF783B7F38",
- "nonce": "C59C14410DA46F23624443C5",
+ "nonce": "7A5D16DDC8EE0B8B0CE1691E",
"section_mic_hmac_key": "DF3E0EA835945BC60C5F661AFE7A4E33A056508943126AC0D057BB51C453F71A",
"section_salt": "D14BDD76436FA2B760E9AF470F4321DD"
},
@@ -2191,23 +2191,23 @@
"aes_key": "CE880F0EE78F5EEF297475B681DFC310",
"data_elements": [
{
+ "contents": "467C",
+ "de_type": 91
+ },
+ {
"contents": "99F0EA",
"de_type": 289
},
{
"contents": "98F41B89B20F1FE7",
"de_type": 498
- },
- {
- "contents": "467C",
- "de_type": 91
}
],
- "encoded_section": "02F18F6DCDBD4B8E8DE00A1A433A282C7EF840CA1A3788B4E9D8C8A6B9D413047925F0E300086648E857BB9ADFF9F6F6391661A854C2FA5F1AA7CA204D3DD126F6294C1E788D05",
+ "encoded_section": "02F18F6DCDBD4B8E8DE00A1A433A282C7E9655D742657DEB6D36D6691ED83FC383258175C7CCCB4DE0BFABCD65418EA943276BF0033DF3FB727959E9C9F71DC540D0DE0E779DB1",
"identity_token": "04ED862F43B938ABBCF539EBC70A59C7",
"key_seed": "710AE30C71188D8EB7E644A28219FF52966F449DC5B6BF342CC62A387E9FD166",
- "nonce": "742EB407037A9A1001C248D7",
+ "nonce": "C6BF8B9A9388872B4009E0EE",
"section_mic_hmac_key": "49363E312316016C76C23E7AA07FB33C1CE4DBD0DDF9390FCA2601294E8740F0",
"section_salt": "F18F6DCDBD4B8E8DE00A1A433A282C7E"
}
-]
\ No newline at end of file
+]
diff --git a/nearby/presence/np_adv/src/array_vec.rs b/nearby/presence/np_adv/src/array_vec.rs
index 3298ace..2b62b49 100644
--- a/nearby/presence/np_adv/src/array_vec.rs
+++ b/nearby/presence/np_adv/src/array_vec.rs
@@ -138,6 +138,12 @@
}
}
+impl<A, const N: usize> core::ops::IndexMut<usize> for ArrayVecOption<A, N> {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ self.0[index].as_mut().expect("This panics if provided index is out of bounds")
+ }
+}
+
#[cfg(test)]
mod test {
extern crate std;
diff --git a/nearby/presence/np_adv/src/credential/book.rs b/nearby/presence/np_adv/src/credential/book.rs
index ef6b269..7ab3b5f 100644
--- a/nearby/presence/np_adv/src/credential/book.rs
+++ b/nearby/presence/np_adv/src/credential/book.rs
@@ -22,8 +22,7 @@
v1::{
MicExtendedSaltSectionIdentityResolutionMaterial,
MicExtendedSaltSectionVerificationMaterial, MicShortSaltSectionIdentityResolutionMaterial,
- MicShortSaltSectionVerificationMaterial, SignedSectionIdentityResolutionMaterial,
- SignedSectionVerificationMaterial, V1DiscoveryCryptoMaterial, V1,
+ MicShortSaltSectionVerificationMaterial, V1DiscoveryCryptoMaterial, V1,
},
DiscoveryMetadataCryptoMaterial, MatchableCredential, MatchedCredential, ProtocolVersion,
};
@@ -32,6 +31,7 @@
#[cfg(any(feature = "alloc", test))]
extern crate alloc;
+
#[cfg(any(feature = "alloc", test))]
use crate::credential::ReferencedMatchedCredential;
#[cfg(any(feature = "alloc", test))]
@@ -184,7 +184,9 @@
discovery_credential: &V::DiscoveryCredential,
) -> Self::Output;
}
+
pub enum Marker {}
+
impl MappingTrait<V0> for Marker {
type Output = PrecalculatedV0DiscoveryCryptoMaterial;
fn precalculate<C: CryptoProvider>(
@@ -193,6 +195,7 @@
PrecalculatedV0DiscoveryCryptoMaterial::new::<C>(discovery_credential)
}
}
+
impl MappingTrait<V1> for Marker {
type Output = PrecalculatedV1DiscoveryCryptoMaterial;
fn precalculate<C: CryptoProvider>(
@@ -352,8 +355,8 @@
}
}
-impl<'a, V: ProtocolVersion> DiscoveryMetadataCryptoMaterial<V>
- for PossiblyCachedDiscoveryCryptoMaterial<'a, V>
+impl<V: ProtocolVersion> DiscoveryMetadataCryptoMaterial<V>
+ for PossiblyCachedDiscoveryCryptoMaterial<'_, V>
where
precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
{
@@ -365,7 +368,7 @@
}
}
-impl<'a> V0DiscoveryCryptoMaterial for PossiblyCachedDiscoveryCryptoMaterial<'a, V0> {
+impl V0DiscoveryCryptoMaterial for PossiblyCachedDiscoveryCryptoMaterial<'_, V0> {
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
match &self.wrapped {
PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => x.ldt_adv_cipher::<C>(),
@@ -374,20 +377,7 @@
}
}
-impl<'a> V1DiscoveryCryptoMaterial for PossiblyCachedDiscoveryCryptoMaterial<'a, V1> {
- fn signed_identity_resolution_material<C: CryptoProvider>(
- &self,
- ) -> SignedSectionIdentityResolutionMaterial {
- match &self.wrapped {
- PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
- x.signed_identity_resolution_material::<C>()
- }
- PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
- x.signed_identity_resolution_material::<C>()
- }
- }
- }
-
+impl V1DiscoveryCryptoMaterial for PossiblyCachedDiscoveryCryptoMaterial<'_, V1> {
fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
&self,
) -> MicShortSaltSectionIdentityResolutionMaterial {
@@ -414,17 +404,6 @@
}
}
- fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
- match &self.wrapped {
- PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
- x.signed_verification_material::<C>()
- }
- PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
- x.signed_verification_material::<C>()
- }
- }
- }
-
fn mic_short_salt_verification_material<C: CryptoProvider>(
&self,
) -> MicShortSaltSectionVerificationMaterial {
@@ -488,8 +467,7 @@
}
#[cfg(any(feature = "alloc", test))]
-impl<'a, V: ProtocolVersion + 'a, M: MatchedCredential + 'a>
- PrecalculatedOwnedCredentialSource<V, M>
+impl<V: ProtocolVersion, M: MatchedCredential> PrecalculatedOwnedCredentialSource<V, M>
where
precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
{
diff --git a/nearby/presence/np_adv/src/credential/matched.rs b/nearby/presence/np_adv/src/credential/matched.rs
index 90265d4..e559976 100644
--- a/nearby/presence/np_adv/src/credential/matched.rs
+++ b/nearby/presence/np_adv/src/credential/matched.rs
@@ -75,13 +75,13 @@
}
}
-impl<'a, M: MatchedCredential> AsRef<M> for ReferencedMatchedCredential<'a, M> {
+impl<M: MatchedCredential> AsRef<M> for ReferencedMatchedCredential<'_, M> {
fn as_ref(&self) -> &M {
self.wrapped
}
}
-impl<'a, M: MatchedCredential> MatchedCredential for ReferencedMatchedCredential<'a, M> {
+impl<M: MatchedCredential> MatchedCredential for ReferencedMatchedCredential<'_, M> {
type EncryptedMetadata = <M as MatchedCredential>::EncryptedMetadata;
type EncryptedMetadataFetchError = <M as MatchedCredential>::EncryptedMetadataFetchError;
fn fetch_encrypted_metadata(
@@ -252,8 +252,8 @@
contents: T,
}
-impl<'a, M: MatchedCredential + Clone, T: HasIdentityMatch>
- WithMatchedCredential<ReferencedMatchedCredential<'a, M>, T>
+impl<M: MatchedCredential + Clone, T: HasIdentityMatch>
+ WithMatchedCredential<ReferencedMatchedCredential<'_, M>, T>
{
/// Clones the referenced match-data to update this container
/// so that the match-data is owned, rather than borrowed.
diff --git a/nearby/presence/np_adv/src/credential/source.rs b/nearby/presence/np_adv/src/credential/source.rs
index f22ed97..b1ce26a 100644
--- a/nearby/presence/np_adv/src/credential/source.rs
+++ b/nearby/presence/np_adv/src/credential/source.rs
@@ -75,7 +75,7 @@
core::slice::Iter<'a, MatchableCredential<V, M>>,
fn(
&'a MatchableCredential<V, M>,
- ) -> (&'a V::DiscoveryCredential, ReferencedMatchedCredential<M>),
+ ) -> (&'a V::DiscoveryCredential, ReferencedMatchedCredential<'a, M>),
>;
fn iter(&'a self) -> Self::Iterator {
diff --git a/nearby/presence/np_adv/src/credential/tests.rs b/nearby/presence/np_adv/src/credential/tests.rs
index 7e92934..00c99b9 100644
--- a/nearby/presence/np_adv/src/credential/tests.rs
+++ b/nearby/presence/np_adv/src/credential/tests.rs
@@ -30,23 +30,15 @@
};
use crate::extended::{V1IdentityToken, V1_IDENTITY_TOKEN_LEN};
use alloc::vec::Vec;
-use crypto_provider::{ed25519, CryptoProvider};
use crypto_provider_default::CryptoProviderImpl;
-type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
-
fn get_zeroed_v0_discovery_credential() -> V0DiscoveryCredential {
V0DiscoveryCredential::new([0u8; 32], [0u8; 32])
}
fn get_constant_packed_v1_discovery_credential(value: u8) -> V1DiscoveryCredential {
- V1BroadcastCredential::new(
- [value; 32],
- V1IdentityToken::from([value; V1_IDENTITY_TOKEN_LEN]),
- // NOTE: This winds up being unused in these test cases
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- )
- .derive_discovery_credential::<CryptoProviderImpl>()
+ V1BroadcastCredential::new([value; 32], V1IdentityToken::from([value; V1_IDENTITY_TOKEN_LEN]))
+ .derive_discovery_credential::<CryptoProviderImpl>()
}
#[test]
diff --git a/nearby/presence/np_adv/src/credential/v0.rs b/nearby/presence/np_adv/src/credential/v0.rs
index 84ff768..0dbe47b 100644
--- a/nearby/presence/np_adv/src/credential/v0.rs
+++ b/nearby/presence/np_adv/src/credential/v0.rs
@@ -132,25 +132,25 @@
// Implementations for reference types -- we don't provide a blanket impl for references
// due to the potential to conflict with downstream crates' implementations.
-impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a V0DiscoveryCredential {
+impl DiscoveryMetadataCryptoMaterial<V0> for &V0DiscoveryCredential {
fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
(*self).metadata_nonce::<C>()
}
}
-impl<'a> V0DiscoveryCryptoMaterial for &'a V0DiscoveryCredential {
+impl V0DiscoveryCryptoMaterial for &V0DiscoveryCredential {
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
(*self).ldt_adv_cipher::<C>()
}
}
-impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a PrecalculatedV0DiscoveryCryptoMaterial {
+impl DiscoveryMetadataCryptoMaterial<V0> for &PrecalculatedV0DiscoveryCryptoMaterial {
fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
(*self).metadata_nonce::<C>()
}
}
-impl<'a> V0DiscoveryCryptoMaterial for &'a PrecalculatedV0DiscoveryCryptoMaterial {
+impl V0DiscoveryCryptoMaterial for &PrecalculatedV0DiscoveryCryptoMaterial {
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
(*self).ldt_adv_cipher::<C>()
}
diff --git a/nearby/presence/np_adv/src/credential/v1.rs b/nearby/presence/np_adv/src/credential/v1.rs
index b6b9c71..be7ee6b 100644
--- a/nearby/presence/np_adv/src/credential/v1.rs
+++ b/nearby/presence/np_adv/src/credential/v1.rs
@@ -16,7 +16,7 @@
use crate::credential::{protocol_version_seal, DiscoveryMetadataCryptoMaterial, ProtocolVersion};
use crate::extended::V1IdentityToken;
-use crypto_provider::{aead::Aead, aes, ed25519, CryptoProvider};
+use crypto_provider::{aead::Aead, aes, CryptoProvider};
use np_hkdf::{DerivedSectionKeys, NpKeySeedHkdf};
/// Cryptographic information about a particular V1 discovery credential
@@ -31,12 +31,6 @@
/// The MIC-extended-salt variant of the HMAC of the identity token.
pub expected_mic_extended_salt_identity_token_hmac: [u8; 32],
-
- /// The signed-extended-salt variant of the HMAC of the identity token.
- pub expected_signed_extended_salt_identity_token_hmac: [u8; 32],
-
- /// The ed25519 public key used for verification of signed sections.
- pub public_key: ed25519::PublicKey,
}
impl V1DiscoveryCredential {
@@ -45,15 +39,11 @@
key_seed: [u8; 32],
expected_mic_short_salt_identity_token_hmac: [u8; 32],
expected_mic_extended_salt_identity_token_hmac: [u8; 32],
- expected_signed_extended_salt_identity_token_hmac: [u8; 32],
- public_key: ed25519::PublicKey,
) -> Self {
Self {
key_seed,
expected_mic_short_salt_identity_token_hmac,
expected_mic_extended_salt_identity_token_hmac,
- expected_signed_extended_salt_identity_token_hmac,
- public_key,
}
}
@@ -62,12 +52,10 @@
&self,
) -> PrecalculatedV1DiscoveryCryptoMaterial {
PrecalculatedV1DiscoveryCryptoMaterial {
- signed_identity_resolution_material: self.signed_identity_resolution_material::<C>(),
mic_short_salt_identity_resolution_material: self
.mic_short_salt_identity_resolution_material::<C>(),
mic_extended_salt_identity_resolution_material: self
.mic_extended_salt_identity_resolution_material::<C>(),
- signed_verification_material: self.signed_verification_material::<C>(),
mic_short_salt_verification_material: self.mic_short_salt_verification_material::<C>(),
mic_extended_salt_verification_material: self
.mic_extended_salt_verification_material::<C>(),
@@ -83,16 +71,6 @@
}
impl V1DiscoveryCryptoMaterial for V1DiscoveryCredential {
- fn signed_identity_resolution_material<C: CryptoProvider>(
- &self,
- ) -> SignedSectionIdentityResolutionMaterial {
- let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
- SignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_identity_token_hmac(
- &hkdf,
- self.expected_signed_extended_salt_identity_token_hmac,
- )
- }
-
fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
&self,
) -> MicShortSaltSectionIdentityResolutionMaterial {
@@ -113,10 +91,6 @@
)
}
- fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
- SignedSectionVerificationMaterial { public_key: self.public_key.clone() }
- }
-
fn mic_short_salt_verification_material<C: CryptoProvider>(
&self,
) -> MicShortSaltSectionVerificationMaterial {
@@ -180,43 +154,6 @@
}
/// Cryptographic materials necessary for determining whether or not
-/// a given V1 signed advertisement section matches an identity.
-#[derive(Clone)]
-pub struct SignedSectionIdentityResolutionMaterial(SectionIdentityResolutionMaterial);
-
-impl SignedSectionIdentityResolutionMaterial {
- #[cfg(test)]
- pub(crate) fn from_raw(raw: SectionIdentityResolutionMaterial) -> Self {
- Self(raw)
- }
- /// Extracts the underlying section-identity resolution material carried around
- /// within this wrapper for resolution of signed sections.
- pub(crate) fn into_raw_resolution_material(self) -> SectionIdentityResolutionMaterial {
- self.0
- }
- #[cfg(any(test, feature = "devtools"))]
- /// Gets the underlying section-identity resolution material carried around
- /// within this wrapper for resolution of signed sections.
- pub(crate) fn as_raw_resolution_material(&self) -> &SectionIdentityResolutionMaterial {
- &self.0
- }
-
- /// Constructs identity-resolution material for a signed section whose
- /// discovery credential leverages the provided HKDF and has the given
- /// expected metadata-key HMAC.
- pub(crate) fn from_hkdf_and_expected_identity_token_hmac<C: CryptoProvider>(
- hkdf: &np_hkdf::NpKeySeedHkdf<C>,
- expected_identity_token_hmac: [u8; 32],
- ) -> Self {
- Self(SectionIdentityResolutionMaterial {
- aes_key: hkdf.v1_signature_keys().aes_key(),
- identity_token_hmac_key: *hkdf.v1_signature_keys().identity_token_hmac_key().as_bytes(),
- expected_identity_token_hmac,
- })
- }
-}
-
-/// Cryptographic materials necessary for determining whether or not
/// a given V1 MIC extended salt advertisement section matches an identity.
#[derive(Clone)]
pub struct MicExtendedSaltSectionIdentityResolutionMaterial(SectionIdentityResolutionMaterial);
@@ -299,23 +236,6 @@
}
}
-/// Crypto materials for V1 signed sections which are not employed in identity resolution,
-/// but may be necessary to verify a signed section.
-#[derive(Clone)]
-pub struct SignedSectionVerificationMaterial {
- /// The np_ed25519 public key to be
- /// used for signature verification of signed sections.
- pub(crate) public_key: ed25519::PublicKey,
-}
-
-impl SignedSectionVerificationMaterial {
- /// Gets the np_ed25519 public key for the given identity,
- /// used for signature verification of signed sections.
- pub(crate) fn signature_verification_public_key(&self) -> ed25519::PublicKey {
- self.public_key.clone()
- }
-}
-
/// Crypto materials for V1 MIC short salt sections which are not employed in identity resolution,
/// but may be necessary to fully decrypt a MIC short salt section.
#[derive(Clone)]
@@ -361,11 +281,6 @@
/// Cryptographic material for an individual NP credential used to decrypt and verify v1 sections.
pub trait V1DiscoveryCryptoMaterial: DiscoveryMetadataCryptoMaterial<V1> {
- /// Constructs or copies the identity resolution material for signed sections
- fn signed_identity_resolution_material<C: CryptoProvider>(
- &self,
- ) -> SignedSectionIdentityResolutionMaterial;
-
/// Constructs or copies the identity resolution material for MIC short salt sections
fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
&self,
@@ -376,10 +291,6 @@
&self,
) -> MicExtendedSaltSectionIdentityResolutionMaterial;
- /// Constructs or copies non-identity-resolution deserialization material for signed
- /// sections.
- fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial;
-
/// Constructs or copies non-identity-resolution deserialization material for MIC short salt
/// sections.
fn mic_short_salt_verification_material<C: CryptoProvider>(
@@ -396,12 +307,10 @@
/// [`V1DiscoveryCryptoMaterial`] that minimizes CPU time when providing key material at
/// the expense of occupied memory
pub struct PrecalculatedV1DiscoveryCryptoMaterial {
- pub(crate) signed_identity_resolution_material: SignedSectionIdentityResolutionMaterial,
pub(crate) mic_short_salt_identity_resolution_material:
MicShortSaltSectionIdentityResolutionMaterial,
pub(crate) mic_extended_salt_identity_resolution_material:
MicExtendedSaltSectionIdentityResolutionMaterial,
- pub(crate) signed_verification_material: SignedSectionVerificationMaterial,
pub(crate) mic_short_salt_verification_material: MicShortSaltSectionVerificationMaterial,
pub(crate) mic_extended_salt_verification_material: MicExtendedSaltSectionVerificationMaterial,
pub(crate) metadata_nonce: [u8; 12],
@@ -414,12 +323,6 @@
}
impl V1DiscoveryCryptoMaterial for PrecalculatedV1DiscoveryCryptoMaterial {
- fn signed_identity_resolution_material<C: CryptoProvider>(
- &self,
- ) -> SignedSectionIdentityResolutionMaterial {
- self.signed_identity_resolution_material.clone()
- }
-
fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
&self,
) -> MicShortSaltSectionIdentityResolutionMaterial {
@@ -431,9 +334,6 @@
) -> MicExtendedSaltSectionIdentityResolutionMaterial {
self.mic_extended_salt_identity_resolution_material.clone()
}
- fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
- self.signed_verification_material.clone()
- }
fn mic_short_salt_verification_material<C: CryptoProvider>(
&self,
@@ -451,19 +351,13 @@
// Implementations for reference types -- we don't provide a blanket impl for references
// due to the potential to conflict with downstream crates' implementations.
-impl<'a> DiscoveryMetadataCryptoMaterial<V1> for &'a V1DiscoveryCredential {
+impl DiscoveryMetadataCryptoMaterial<V1> for &V1DiscoveryCredential {
fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
(*self).metadata_nonce::<C>()
}
}
-impl<'a> V1DiscoveryCryptoMaterial for &'a V1DiscoveryCredential {
- fn signed_identity_resolution_material<C: CryptoProvider>(
- &self,
- ) -> SignedSectionIdentityResolutionMaterial {
- (*self).signed_identity_resolution_material::<C>()
- }
-
+impl V1DiscoveryCryptoMaterial for &V1DiscoveryCredential {
fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
&self,
) -> MicShortSaltSectionIdentityResolutionMaterial {
@@ -475,9 +369,6 @@
) -> MicExtendedSaltSectionIdentityResolutionMaterial {
(*self).mic_extended_salt_identity_resolution_material::<C>()
}
- fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
- (*self).signed_verification_material::<C>()
- }
fn mic_short_salt_verification_material<C: CryptoProvider>(
&self,
@@ -492,19 +383,13 @@
}
}
-impl<'a> DiscoveryMetadataCryptoMaterial<V1> for &'a PrecalculatedV1DiscoveryCryptoMaterial {
+impl DiscoveryMetadataCryptoMaterial<V1> for &PrecalculatedV1DiscoveryCryptoMaterial {
fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
(*self).metadata_nonce::<C>()
}
}
-impl<'a> V1DiscoveryCryptoMaterial for &'a PrecalculatedV1DiscoveryCryptoMaterial {
- fn signed_identity_resolution_material<C: CryptoProvider>(
- &self,
- ) -> SignedSectionIdentityResolutionMaterial {
- (*self).signed_identity_resolution_material::<C>()
- }
-
+impl V1DiscoveryCryptoMaterial for &PrecalculatedV1DiscoveryCryptoMaterial {
fn mic_short_salt_identity_resolution_material<C: CryptoProvider>(
&self,
) -> MicShortSaltSectionIdentityResolutionMaterial {
@@ -516,9 +401,6 @@
) -> MicExtendedSaltSectionIdentityResolutionMaterial {
(*self).mic_extended_salt_identity_resolution_material::<C>()
}
- fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
- (*self).signed_verification_material::<C>()
- }
fn mic_short_salt_verification_material<C: CryptoProvider>(
&self,
@@ -541,20 +423,13 @@
/// The 16-byte identity-token which identifies the sender.
pub identity_token: V1IdentityToken,
-
- /// The ed25519 private key to be used for signing section contents.
- pub private_key: ed25519::PrivateKey,
}
impl V1BroadcastCredential {
- /// Builds some simple V1 signed broadcast crypto-materials out of
- /// the provided key-seed, metadata-key, and ed25519 private key.
- pub fn new(
- key_seed: [u8; 32],
- identity_token: V1IdentityToken,
- private_key: ed25519::PrivateKey,
- ) -> Self {
- Self { key_seed, identity_token, private_key }
+ /// Builds some simple V1 encrypted broadcast crypto-materials out of
+ /// the provided key-seed and identity token.
+ pub fn new(key_seed: [u8; 32], identity_token: V1IdentityToken) -> Self {
+ Self { key_seed, identity_token }
}
/// Key seed from which other keys are derived.
@@ -581,15 +456,6 @@
hkdf.v1_mic_extended_salt_keys()
.identity_token_hmac_key()
.calculate_hmac::<C>(self.identity_token.as_slice()),
- hkdf.v1_signature_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<C>(self.identity_token.as_slice()),
- self.signing_key().derive_public_key::<C::Ed25519>(),
)
}
-
- /// Key used for signature-protected sections
- pub(crate) fn signing_key(&self) -> ed25519::PrivateKey {
- self.private_key.clone()
- }
}
diff --git a/nearby/presence/np_adv/src/extended/data_elements/actions.rs b/nearby/presence/np_adv/src/extended/data_elements/actions.rs
index 3679555..e2cc134 100644
--- a/nearby/presence/np_adv/src/extended/data_elements/actions.rs
+++ b/nearby/presence/np_adv/src/extended/data_elements/actions.rs
@@ -14,7 +14,6 @@
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
-
use itertools::Itertools;
use nom::error::ErrorKind;
use nom::Err::Error;
@@ -23,19 +22,26 @@
use tinyvec::ArrayVec;
use crate::array_vec::ArrayVecOption;
-use crate::extended::de_type::DeType;
-use crate::extended::deserialize::data_element::DataElement;
-use crate::extended::serialize::{DeHeader, SingleTypeDataElement, WriteDataElement};
+use crate::extended::de_type::{DeType, HasDEType};
+use crate::extended::deserialize::data_element::DeserializedDataElement;
+use crate::extended::salt::{DeSalt, Unsalted};
+use crate::extended::serialize::WriteDataElement;
use crate::extended::MAX_DE_LEN;
#[cfg(test)]
mod tests;
+/// The maximum value of an action ID which may be expressed.
pub const MAX_ACTION_ID_VALUE: u16 = 2047;
+/// The number of bytes required to express a bitmap of every
+/// possible action ID.
+pub const ACTIONS_BITMAP_LEN: usize = 256;
+
+/// The maximum size in bytes of an actions container element (part of an actions DE payload).
pub const MAX_ACTIONS_CONTAINER_LENGTH: usize = 64;
-// The minimum length of a single container is 2 (one header byte + 1 byte payload) so an actions DE
+/// The minimum length of a single container is 2 (one header byte + 1 byte payload) so an actions DE
/// can fit at most 127/2 = 63 total actions containers
pub const MAX_NUM_ACTIONS_CONTAINERS: usize = 63;
@@ -50,8 +56,9 @@
}
}
-// The provided value was not in the range of valid action ids [0, 2047]
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+/// The provided value was not in the range of valid action ids [0, 2047]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, thiserror::Error)]
+#[error("Action ID out of range")]
pub struct ActionIdOutOfRange;
impl TryFrom<u16> for ActionId {
@@ -107,32 +114,22 @@
/// Actions data element parsed from a slice of bytes, this type references the original slice of
/// bytes which was parsed
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct DeserializedActionsDE<'adv> {
containers: ArrayVecOption<DeserializedActionsContainer<'adv>, MAX_NUM_ACTIONS_CONTAINERS>,
}
-impl<'adv> SingleTypeDataElement for DeserializedActionsDE<'adv> {
+impl HasDEType for DeserializedActionsDE<'_> {
const DE_TYPE: DeType = DeType::const_from(0x06);
}
-#[derive(Debug, Eq, PartialEq)]
-pub enum ActionsDeserializationError {
- InvalidTypeCode,
- InvalidContainerLength,
- InvalidContainerType,
- GenericDeserializationError,
-}
-
-/// Convert a deserialized DE into one you can serialize
-impl<'a> TryFrom<&'a DataElement<'a>> for DeserializedActionsDE<'a> {
- type Error = ActionsDeserializationError;
-
- fn try_from(value: &'a DataElement<'a>) -> Result<Self, Self::Error> {
- if value.de_type() != Self::DE_TYPE {
- return Err(ActionsDeserializationError::InvalidTypeCode);
- }
- DeserializedActionsDE::deserialize(value.contents()).map_err(|e| match e {
+impl<'adv> DeserializedDataElement<'adv> for DeserializedActionsDE<'adv> {
+ type DeserializationError = ActionsDeserializationError;
+ fn try_deserialize(
+ _maybe_salt: Option<&DeSalt>,
+ contents: &'adv [u8],
+ ) -> Result<Self, Self::DeserializationError> {
+ DeserializedActionsDE::deserialize(contents).map_err(|e| match e {
Error(e) => match e.code {
ErrorKind::Eof | ErrorKind::Verify => {
ActionsDeserializationError::InvalidContainerLength
@@ -145,9 +142,50 @@
}
}
+/// Errors raised when attempting to parse a V1 Actions
+/// DE from a generic deserialized V1 DE with the right type code.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum ActionsDeserializationError {
+ /// The length of one or more action containers
+ /// was not a valid container length.
+ InvalidContainerLength,
+ /// The type code of one or more action containers
+ /// was not a recognized container type.
+ InvalidContainerType,
+ /// Some other error occurred with deserializing
+ /// the contents of one or more action containers.
+ GenericDeserializationError,
+}
+
impl<'adv> DeserializedActionsDE<'adv> {
+ /// Returns a bitmap whose bit-indices correspond to the absence/presence
+ /// of given action-IDs, where the bytes are in big-endian order and individual
+ /// bits within a byte are assumed to be in little-endian order.
+ ///
+ /// If _any_ deserialized action is outside of the range
+ /// of allowed action-IDs, this method will return `ActionIdOutOfRange`.
+ pub fn action_ids_bitmap(&self) -> Result<[u8; ACTIONS_BITMAP_LEN], ActionIdOutOfRange> {
+ let mut result = [0u8; ACTIONS_BITMAP_LEN];
+ for container in self.containers.iter() {
+ for maybe_id in container.iter_action_ids() {
+ match maybe_id {
+ Ok(action_id) => {
+ let action_id = action_id.as_u16() as usize;
+ let byte_index = action_id / 8;
+ let bit_index = action_id % 8;
+ // Won't panic because of the range restriction on ActionId.
+ result[byte_index] |= 0b0000_0001u8 << bit_index;
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+ }
+ Ok(result)
+ }
/// Returns a collection over of all action ids contained within the data element
- #[cfg(feature = "alloc")]
+ #[cfg(any(test, feature = "alloc"))]
pub fn collect_action_ids(&self) -> Vec<Result<ActionId, ActionIdOutOfRange>> {
let mut result = Vec::new();
self.containers.iter().for_each(|container| {
@@ -156,12 +194,15 @@
result
}
- /// Parses the raw bytes of an Actions DE into an intermediate format, that is the contents
+ /// Parses the raw byte contents of an Actions DE into an intermediate format, that is the contents
/// are separated into containers with their corresponding container type, but no further decoding
/// is done on the contents of the containers, leaving the bytes in their compact encoded format.
- fn deserialize(
+ ///
+ /// The passed byte-slice is assumed to have a valid length for the payload of a V1 DE.
+ pub fn deserialize(
de_contents: &'adv [u8],
- ) -> Result<DeserializedActionsDE, nom::Err<nom::error::Error<&[u8]>>> {
+ ) -> Result<DeserializedActionsDE<'adv>, nom::Err<nom::error::Error<&'adv [u8]>>> {
+ debug_assert!(de_contents.len() <= 127);
combinator::all_consuming(multi::fold_many_m_n(
1,
MAX_NUM_ACTIONS_CONTAINERS,
@@ -190,7 +231,7 @@
}
}
-#[derive(Debug)]
+#[derive(Clone, Debug)]
struct DeserializedActionsContainer<'adv> {
container_type: ContainerType,
offset: u16,
@@ -198,7 +239,7 @@
}
impl<'adv> DeserializedActionsContainer<'adv> {
- fn parse(bytes: &'adv [u8]) -> nom::IResult<&[u8], Self> {
+ fn parse(bytes: &'adv [u8]) -> nom::IResult<&'adv [u8], Self> {
let (input, (container_type, container_encoded_len)) =
combinator::map_opt(nom::number::complete::u8, |b| {
// right shift by 6 to obtain the upper 2 type bits TTLLLLLL
@@ -222,6 +263,14 @@
.map(|(remaining, byte)| (remaining, u16::from(byte) * 8))?
}
};
+ if container_type == ContainerType::DeltaEncodedWithOffset {
+ // Special-case: delta-encoded with offset should always have a first
+ // delta which is 0-7, with other bits reserved and set to 0.
+ let _ =
+ combinator::verify(combinator::peek(nom::number::complete::u8), |delta: &u8| {
+ *delta <= 7u8
+ })(payload)?;
+ }
Ok((input, DeserializedActionsContainer { container_type, offset, payload }))
}
@@ -263,7 +312,7 @@
}
}
-impl<'c> Iterator for ActionsContainerIterator<'c> {
+impl Iterator for ActionsContainerIterator<'_> {
type Item = Result<ActionId, ActionIdOutOfRange>;
fn next(&mut self) -> Option<Self::Item> {
@@ -315,6 +364,7 @@
}
}
}
+
/// Actions data element consists of one or more containers
#[derive(Debug)]
pub struct ActionsDataElement {
@@ -323,6 +373,39 @@
}
impl ActionsDataElement {
+ /// Tries to construct an Actions DE from a bitmap whose bit-indices
+ /// correspond to the absence/presence of given action-IDs, where
+ /// the bytes are in big-endian order and individual bits within
+ /// a byte are assumed to be in little-endian order.
+ ///
+ /// Construction may fail if too many actions are enabled relative
+ /// to what is actually representable in a single Actions DE
+ /// under the delta-encoding scheme.
+ // TODO: provide more flexibility over which encoding scheme is used
+ pub fn try_from_actions_bitmap(
+ bitmap: &[u8; ACTIONS_BITMAP_LEN],
+ ) -> Result<Self, ActionsDataElementError> {
+ let mut actions: ArrayVec<[ActionId; 64]> = ArrayVec::new();
+ for (byte_index, byte) in bitmap.iter().enumerate() {
+ let byte = *byte;
+ if byte > 0 {
+ let byte_offset = (byte_index * 8) as u16;
+ for bit_index in 0..=7u16 {
+ if (byte & (0b0000_0001u8 << bit_index)) > 0 {
+ let action_id = byte_offset + bit_index;
+ let action_id = ActionId::try_from(action_id).expect(
+ "Actions expressed in a fixed-size bitmap should have valid IDs",
+ );
+ if actions.try_push(action_id).is_some() {
+ return Err(ActionsDataElementError::TooManyActions);
+ }
+ }
+ }
+ }
+ }
+ Self::try_from_actions(actions)
+ }
+
/// Creates an Actions DE storing the provided action ids in memory using the default encoding
/// scheme of simple delta encoding. This limits the amount of encodable actions to 64, and the
/// action id range to what can fit in a byte after being delta encoded.
@@ -511,38 +594,30 @@
}
/// Errors that can occur constructing an [ActionsDataElement].
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[allow(unused)]
pub enum ActionsDataElementError {
/// Must specify at least one action id to encode
+ #[error("Empty actions")]
EmptyActions,
/// The provided ActionIds cannot be delta encoded into u8 values
+ #[error("Action ID delta overflow")]
ActionIdDeltaOverflow,
/// The provided range of action ids cannot be encoded into a single container
+ #[error("Action ID out of range")]
ActionIdOutOfRange,
/// Too many actions provided than what can be encoded into a single container
+ #[error("Too many actions")]
TooManyActions,
}
-impl SingleTypeDataElement for ActionsDataElement {
+impl HasDEType for ActionsDataElement {
const DE_TYPE: DeType = DeType::const_from(0x06);
}
impl WriteDataElement for ActionsDataElement {
- fn de_header(&self) -> DeHeader {
- DeHeader::new(
- Self::DE_TYPE,
- // each containers length is the header byte + the length of its encoded contents
- self.containers
- .iter()
- .map(|a| 1 + a.payload.len())
- .sum::<usize>()
- .try_into()
- .expect("An actions DE will always be <= 127, this is enforced upon creation"),
- )
- }
-
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
// write actions container header and bytes for each container in the de
let mut encoded_actions_de_bytes = ArrayVec::<[u8; MAX_DE_LEN]>::new();
self.containers.iter().for_each(|a| {
diff --git a/nearby/presence/np_adv/src/extended/data_elements/actions/tests.rs b/nearby/presence/np_adv/src/extended/data_elements/actions/tests.rs
index 3abdbd1..4f0ef8c 100644
--- a/nearby/presence/np_adv/src/extended/data_elements/actions/tests.rs
+++ b/nearby/presence/np_adv/src/extended/data_elements/actions/tests.rs
@@ -26,21 +26,22 @@
use crate::extended::data_elements::actions::{
ActionsContainer, ActionsDataElement, ActionsDataElementError, ActionsDeserializationError,
BitVectorOffsetContainer, ContainerEncoder, ContainerType, DeltaEncodedContainer,
- DeltaEncodedOffsetContainer, DeserializedActionsDE, MAX_ACTIONS_CONTAINER_LENGTH,
+ DeltaEncodedOffsetContainer, DeserializedActionsDE, ACTIONS_BITMAP_LEN,
+ MAX_ACTIONS_CONTAINER_LENGTH,
};
use crate::extended::data_elements::ActionId;
-use crate::extended::de_type::DeType;
-use crate::extended::deserialize::data_element::DataElement;
+use crate::extended::de_type::HasDEType;
+use crate::extended::deserialize::data_element::DeserializedDataElement;
use crate::extended::deserialize::{Section, V1AdvertisementContents, V1DeserializedSection};
+use crate::extended::salt::Unsalted;
use crate::extended::serialize::{
- AdvBuilder, AdvertisementType, EncodedAdvertisement, SingleTypeDataElement,
- UnencryptedSectionEncoder,
+ AdvBuilder, EncodedAdvertisement, UnencryptedSectionEncoder, WriteDataElement,
};
+use crate::extended::MAX_DE_LEN;
use crate::{deserialization_arena, deserialize_advertisement};
use crypto_provider_default::CryptoProviderImpl;
use nom::error::ErrorKind;
use nom::Err::Error;
-use np_hkdf::v1_salt::DataElementOffset;
use rand::distributions::uniform::SampleRange;
use rand::distributions::Standard;
use rand::prelude::Distribution;
@@ -68,8 +69,8 @@
assert_eq!(1, data_elements.len());
let de = &data_elements[0];
assert_eq!(ActionsDataElement::DE_TYPE, de.de_type());
- let actions_de =
- DeserializedActionsDE::try_from(de).expect("Should succeed since this de is an actions de");
+ let actions_de = DeserializedActionsDE::try_deserialize(None, de.contents())
+ .expect("Should succeed since this de is a serialized actions de");
assert_eq!(
actions_de
.collect_action_ids()
@@ -107,7 +108,7 @@
assert_eq!(1, data_elements.len());
let de = &data_elements[0];
assert_eq!(ActionsDataElement::DE_TYPE, de.de_type());
- let actions_de = DeserializedActionsDE::try_from(de)
+ let actions_de = DeserializedActionsDE::try_deserialize(None, de.contents())
.expect("Should succeed since this de is an actions de");
assert_eq!(actions_de.containers.len(), expected.len());
let mut expected_action_ids =
@@ -145,17 +146,17 @@
assert_eq!(1, data_elements.len());
let de = &data_elements[0];
assert_eq!(ActionsDataElement::DE_TYPE, de.de_type());
- let actions_de =
- DeserializedActionsDE::try_from(de).expect("Should succeed since this de is an actions de");
+ let actions_de = DeserializedActionsDE::try_deserialize(None, de.contents())
+ .expect("Should succeed since this de is an actions de");
let decoded_actions =
actions_de.collect_action_ids().iter().map(|c| c.expect("")).collect::<Vec<ActionId>>();
assert_eq!(actions.as_slice(), decoded_actions.as_slice())
}
fn create_adv_with_de(actions_de: ActionsDataElement) -> EncodedAdvertisement {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
- section_builder.add_de(|_salt| actions_de).unwrap();
+ section_builder.add_de(&actions_de).unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
adv_builder.into_advertisement()
}
@@ -204,7 +205,17 @@
actions
}
-fn gen_delta_actions<const N: usize, R: SampleRange<u16> + Clone>(
+fn gen_delta_encodeable_actions<const N: usize>(rng: &mut StdRng) -> ArrayVec<[ActionId; N]> {
+ gen_delta_actions_with_starting_range::<N, _>(rng, 0..=255)
+}
+
+fn gen_delta_encodeable_with_offset_actions<const N: usize>(
+ rng: &mut StdRng,
+) -> ArrayVec<[ActionId; N]> {
+ gen_delta_actions_with_starting_range::<N, _>(rng, 0..=2047)
+}
+
+fn gen_delta_actions_with_starting_range<const N: usize, R: SampleRange<u16> + Clone>(
rng: &mut StdRng,
gen_range: R,
) -> ArrayVec<[ActionId; N]> {
@@ -226,11 +237,11 @@
let container_type: ContainerType = rng.gen();
match container_type {
ContainerType::DeltaEncoded => {
- let actions = gen_delta_actions(rng, 0..=255);
+ let actions = gen_delta_encodeable_actions(rng);
(DeltaEncodedContainer::try_from_actions(actions).unwrap().into(), actions.to_vec())
}
ContainerType::DeltaEncodedWithOffset => {
- let actions = gen_delta_actions(rng, 0..=2047);
+ let actions = gen_delta_encodeable_with_offset_actions(rng);
(
DeltaEncodedOffsetContainer::try_from_actions(actions).unwrap().into(),
actions.to_vec(),
@@ -313,6 +324,17 @@
}
#[test]
+fn parse_single_container_delta_encoded_with_offset_reserved_first_delta() {
+ // Like the previous test-case, but the first delta has a reserved
+ // bit set to something other than zero.
+ for i in 8u8..=255u8 {
+ let bytes = [0x42, 0x06, i, 0x66];
+ let _ = DeserializedActionsDE::deserialize(&bytes)
+ .expect_err("First deltas over 7 should be invalid.");
+ }
+}
+
+#[test]
fn encode_single_container_delta_encoded_with_offset() {
let actions = array_vec!([ActionId; 63] => ActionId::try_from(50).unwrap(), ActionId::try_from(153).unwrap());
let container = DeltaEncodedOffsetContainer::try_from_actions(actions).expect("Should succeed");
@@ -462,21 +484,10 @@
}
#[test]
-fn try_from_invalid_type_code() {
- let data = [];
- let de = DataElement::new(DataElementOffset::from(0), DeType::const_from(5), &data);
- assert_eq!(
- DeserializedActionsDE::try_from(&de).unwrap_err(),
- ActionsDeserializationError::InvalidTypeCode
- );
-}
-
-#[test]
fn try_from_invalid_container_length_empty_after_subheader() {
let data = [0x40, 0x64];
- let de = DataElement::new(DataElementOffset::from(0), DeType::const_from(6), &data);
assert_eq!(
- DeserializedActionsDE::try_from(&de).unwrap_err(),
+ DeserializedActionsDE::try_deserialize(None, &data).unwrap_err(),
ActionsDeserializationError::InvalidContainerLength
);
}
@@ -484,9 +495,8 @@
#[test]
fn try_from_invalid_container_length_not_enough_data() {
let data = [0x02, 0x64, 0xC8];
- let de = DataElement::new(DataElementOffset::from(0), DeType::const_from(6), &data);
assert_eq!(
- DeserializedActionsDE::try_from(&de).unwrap_err(),
+ DeserializedActionsDE::try_deserialize(None, &data).unwrap_err(),
ActionsDeserializationError::InvalidContainerLength
);
}
@@ -494,9 +504,8 @@
#[test]
fn try_from_invalid_container_type() {
let data = [0xC0, 0x64];
- let de = DataElement::new(DataElementOffset::from(0), DeType::const_from(6), &data);
assert_eq!(
- DeserializedActionsDE::try_from(&de).unwrap_err(),
+ DeserializedActionsDE::try_deserialize(None, &data).unwrap_err(),
ActionsDeserializationError::InvalidContainerType
);
}
@@ -634,3 +643,67 @@
let err = ActionsDataElement::try_from_actions(actions).unwrap_err();
assert_eq!(err, ActionsDataElementError::ActionIdDeltaOverflow);
}
+
+#[test]
+fn actions_bitmap_encodes_single_elements_correctly() {
+ // Check every encodeable single action with the delta encoding.
+ for action_id in 0u8..=255u8 {
+ let mut actions_bitmap = [0u8; 256];
+ // Big-endian by byte, little-endian within each byte.
+ actions_bitmap[(action_id / 8u8) as usize] = 0b0000_0001u8 << (action_id % 8u8);
+ let actions_bitmap = actions_bitmap;
+
+ // Go through serialization -> deserialization
+ let actions_de = ActionsDataElement::try_from_actions_bitmap(&actions_bitmap).unwrap();
+ // Extract the payload of the serialized DE.
+ let mut de_payload = ArrayVec::<[u8; MAX_DE_LEN]>::new();
+ actions_de.write_de_contents(Unsalted, &mut de_payload).unwrap();
+ // Deserialize the DE payload
+ let deserialized_actions = DeserializedActionsDE::deserialize(&de_payload).unwrap();
+
+ // Check that there's only one action, and it's the one we set.
+ let actions = deserialized_actions.collect_action_ids();
+ let action_id = ActionId::try_from(action_id as u16).unwrap();
+ assert_eq!(actions, vec![Ok(action_id)]);
+ }
+}
+
+#[test]
+fn actions_bitmap_too_many_actions() {
+ let actions_bitmap = [255u8; 256];
+ let err = ActionsDataElement::try_from_actions_bitmap(&actions_bitmap).unwrap_err();
+ assert_eq!(err, ActionsDataElementError::TooManyActions);
+}
+
+fn actions_de_to_bitmap(de: ActionsDataElement) -> [u8; ACTIONS_BITMAP_LEN] {
+ // Extract the payload of the serialized DE.
+ let mut de_payload = ArrayVec::<[u8; MAX_DE_LEN]>::new();
+ de.write_de_contents(Unsalted, &mut de_payload).unwrap();
+ // Deserialize the DE payload
+ let deserialized_actions = DeserializedActionsDE::deserialize(&de_payload).unwrap();
+ // Output the action-ID bitmap.
+ deserialized_actions.action_ids_bitmap().unwrap()
+}
+
+#[test]
+fn randomized_bitmaps_roundtrip_tests() {
+ let mut rng = StdRng::from_entropy();
+ for _ in 0..10_000 {
+ // Encode a random set of actions which we know'll fit.
+ let action_ids: ArrayVec<[ActionId; 64]> = gen_delta_encodeable_actions(&mut rng);
+ // Construct a real actions DE from 'em.
+ let actions_de = ActionsDataElement::try_from_actions(action_ids).unwrap();
+ // Turn it into a bitmap (deserializes the DE contents).
+ let actions_bitmap = actions_de_to_bitmap(actions_de);
+
+ // Take the bitmap and turn it back into an actions DE.
+ let reconstructed_actions_de =
+ ActionsDataElement::try_from_actions_bitmap(&actions_bitmap).unwrap();
+
+ // Take the reconstructed actions DE and deserialize it again.
+ let reconstructed_bitmap = actions_de_to_bitmap(reconstructed_actions_de);
+
+ // Verify that the two bitmaps are identical
+ assert_eq!(actions_bitmap, reconstructed_bitmap);
+ }
+}
diff --git a/nearby/presence/np_adv/src/extended/data_elements/mod.rs b/nearby/presence/np_adv/src/extended/data_elements/mod.rs
index a5b7f91..98633a8 100644
--- a/nearby/presence/np_adv/src/extended/data_elements/mod.rs
+++ b/nearby/presence/np_adv/src/extended/data_elements/mod.rs
@@ -19,24 +19,61 @@
use crate::{
extended::{
- de_type::DeType,
- deserialize::data_element::DataElement,
- serialize::{DeHeader, SingleTypeDataElement, WriteDataElement},
+ de_type::{DeType, HasDEType},
+ deserialize::data_element::{
+ define_data_element_deserializer, DataElement, DeserializedDataElement,
+ },
+ salt::{DeSalt, Unsalted},
+ serialize::{ProvidesDEType, WriteDataElement},
MAX_DE_LEN,
},
shared_data::*,
};
use array_view::ArrayView;
+use core::net;
use sink::Sink;
+use strum_macros::FromRepr;
-mod actions;
+/// Define Actions of Presence Spec.
+pub mod actions;
pub use actions::ActionId;
pub use actions::ActionsDataElement;
+pub use actions::ActionsDataElementError;
+pub use actions::ActionsDeserializationError;
pub use actions::DeserializedActionsDE;
#[cfg(test)]
mod tests;
+define_data_element_deserializer! {
+ /// Result of deserializing a V1 data element
+ /// with one of the Google DE type-codes.
+ #[allow(clippy::large_enum_variant)]
+ pub enum DeserializedGoogleDE<'adv> borrows {
+ /// A Google cast ID
+ CastId: CastIdDataElement<'adv>,
+ /// A collection of actions
+ Actions: DeserializedActionsDE<'adv>,
+ /// A media deduplication ID
+ MediaDeduplicationId: MediaDeduplicationIdDataElement<'adv>,
+ } owns {
+ /// A transmission power
+ TxPower: TxPowerDataElement,
+ /// A context sync sequence number
+ ContextSyncSeqNum: ContextSyncSeqNumDataElement,
+ /// A deduplication hint.
+ DedupHint: DeduplicationHintDataElement,
+ /// A device type.
+ DeviceType: DeviceTypeDataElement,
+ /// Connectivity information.
+ ConnectivityInfo: ConnectivityInfoDataElement,
+ /// Device capabilities
+ Capabilities: CapabilitiesDataElement,
+ /// Required capabilities for remote device.
+ Requirements: RequirementsDataElement,
+ }
+}
+
/// A general purpose data element for use cases that don't fit into an existing DE type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GenericDataElement {
@@ -57,32 +94,36 @@
}
/// Errors that can occur constructing a [GenericDataElement]
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum GenericDataElementError {
/// The DE data is too long
+ #[error("DE data is too long")]
DataTooLong,
}
-impl WriteDataElement for GenericDataElement {
- fn de_header(&self) -> DeHeader {
- DeHeader::new(self.de_type, self.data.len().try_into().expect("length <= max DE len"))
+impl ProvidesDEType for GenericDataElement {
+ fn de_type(&self) -> DeType {
+ self.de_type
}
+}
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
+impl WriteDataElement for GenericDataElement {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
sink.try_extend_from_slice(self.data.as_slice())
}
}
/// Convert a deserialized DE into one you can serialize
-impl<'a> From<&'a DataElement<'a>> for GenericDataElement {
- fn from(de: &'a DataElement<'a>) -> Self {
+impl<'adv> From<&'adv DataElement<'adv>> for GenericDataElement {
+ fn from(de: &'adv DataElement<'adv>) -> Self {
Self::try_from(de.de_type(), de.contents())
.expect("Deserialized DE must have a valid length")
}
}
/// Advertising power
-#[derive(Clone)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TxPowerDataElement {
tx_power: TxPower,
}
@@ -93,21 +134,102 @@
}
}
-impl SingleTypeDataElement for TxPowerDataElement {
+impl From<TxPowerDataElement> for TxPower {
+ fn from(de: TxPowerDataElement) -> Self {
+ de.tx_power
+ }
+}
+
+/// Errors raised when attempting to deserialize a Tx power DE.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum TxPowerMalformed {
+ /// Tx power was out of the valid range `[-100, 20]`.
+ OutOfBounds,
+ /// There wasn't exactly one byte in the payload, but instead
+ /// something else.
+ WrongLength,
+}
+
+impl HasDEType for TxPowerDataElement {
const DE_TYPE: DeType = DeType::const_from(0x05);
}
-impl WriteDataElement for TxPowerDataElement {
- fn de_header(&self) -> DeHeader {
- DeHeader::new(Self::DE_TYPE, 1_u8.try_into().expect("1 is a valid length"))
+impl DeserializedDataElement<'_> for TxPowerDataElement {
+ type DeserializationError = TxPowerMalformed;
+ fn try_deserialize(
+ _maybe_salt: Option<&DeSalt>,
+ contents: &[u8],
+ ) -> Result<Self, Self::DeserializationError> {
+ let len = contents.len();
+ if len == 1 {
+ let tx_power =
+ TxPower::try_from(contents[0] as i8).map_err(|_| TxPowerMalformed::OutOfBounds)?;
+ Ok(tx_power.into())
+ } else {
+ Err(TxPowerMalformed::WrongLength)
+ }
}
+}
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
+impl WriteDataElement for TxPowerDataElement {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
sink.try_push(self.tx_power.as_i8() as u8)
}
}
+/// The length in bytes of a [`DeduplicationHintDataElement`].
+pub const DEDUP_HINT_LENGTH: usize = 8;
+
+/// Information to dedupe advertisements from the same device across different mediums.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct DeduplicationHintDataElement {
+ hint_bytes: [u8; DEDUP_HINT_LENGTH],
+}
+impl From<[u8; DEDUP_HINT_LENGTH]> for DeduplicationHintDataElement {
+ fn from(hint_bytes: [u8; DEDUP_HINT_LENGTH]) -> Self {
+ Self { hint_bytes }
+ }
+}
+impl DeduplicationHintDataElement {
+ /// Gets the raw bytes of this dedup hint.
+ pub fn as_bytes(&self) -> [u8; DEDUP_HINT_LENGTH] {
+ self.hint_bytes
+ }
+}
+
+impl HasDEType for DeduplicationHintDataElement {
+ const DE_TYPE: DeType = DeType::const_from(0x12);
+}
+
+/// Errors raised when attempting to deserialize a dedup hint DE
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum DeduplicationHintMalformed {
+ /// The size of the payload in bytes didn't match `DEDUP_HINT_LENGTH`.
+ WrongLength,
+}
+
+impl DeserializedDataElement<'_> for DeduplicationHintDataElement {
+ type DeserializationError = DeduplicationHintMalformed;
+ fn try_deserialize(
+ _maybe_salt: Option<&DeSalt>,
+ contents: &[u8],
+ ) -> Result<Self, Self::DeserializationError> {
+ let hint_bytes: [u8; DEDUP_HINT_LENGTH] =
+ contents.try_into().map_err(|_| DeduplicationHintMalformed::WrongLength)?;
+ Ok(Self { hint_bytes })
+ }
+}
+
+impl WriteDataElement for DeduplicationHintDataElement {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
+ sink.try_extend_from_slice(&self.hint_bytes)
+ }
+}
+
/// Context sync sequence number
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ContextSyncSeqNumDataElement {
num: ContextSyncSeqNum,
}
@@ -118,109 +240,757 @@
}
}
-impl SingleTypeDataElement for ContextSyncSeqNumDataElement {
+impl From<ContextSyncSeqNumDataElement> for ContextSyncSeqNum {
+ fn from(de: ContextSyncSeqNumDataElement) -> Self {
+ de.num
+ }
+}
+
+/// Errors raised when attempting to deserialize a context
+/// sync sequence number DE.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ContextSyncSeqNumMalformed {
+ /// The sequence number was out of the valid range.
+ OutOfBounds,
+ /// There wasn't exactly one byte in the payload, but instead
+ /// something else.
+ WrongLength,
+}
+
+impl HasDEType for ContextSyncSeqNumDataElement {
const DE_TYPE: DeType = DeType::const_from(0x13);
}
+impl DeserializedDataElement<'_> for ContextSyncSeqNumDataElement {
+ type DeserializationError = ContextSyncSeqNumMalformed;
+ fn try_deserialize(
+ _maybe_salt: Option<&DeSalt>,
+ contents: &[u8],
+ ) -> Result<Self, Self::DeserializationError> {
+ let len = contents.len();
+ if len == 1 {
+ let num = ContextSyncSeqNum::try_from(contents[0])
+ .map_err(|_| ContextSyncSeqNumMalformed::OutOfBounds)?;
+ Ok(num.into())
+ } else {
+ Err(ContextSyncSeqNumMalformed::WrongLength)
+ }
+ }
+}
impl WriteDataElement for ContextSyncSeqNumDataElement {
- fn de_header(&self) -> DeHeader {
- DeHeader::new(Self::DE_TYPE, 1_u8.try_into().expect("1 is a valid length"))
- }
-
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
sink.try_push(self.num.as_u8())
}
}
-/// Connectivity info
-pub struct ConnectivityInfoDataElement {
- // TODO len
- info: ArrayView<u8, 24>,
+/// The length of a Google Cast ID in bytes.
+pub const CAST_ID_LENGTH: usize = 32;
+
+/// Google Cast ID
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct CastIdDataElement<'adv> {
+ id: &'adv [u8; CAST_ID_LENGTH],
}
-
-impl ConnectivityInfoDataElement {
- /// Construct connectivity info for bluetooth.
- pub fn bluetooth(svc_id: [u8; 4], mac: [u8; 6]) -> Self {
- let mut array = [0; 24];
- array[0] = 0x1; // bluetooth
- array[1..5].copy_from_slice(&svc_id);
- array[5..11].copy_from_slice(&mac);
-
- Self { info: ArrayView::try_from_array(array, 11).expect("length is fixed") }
+impl<'adv> From<&'adv [u8; CAST_ID_LENGTH]> for CastIdDataElement<'adv> {
+ fn from(id: &'adv [u8; CAST_ID_LENGTH]) -> Self {
+ Self { id }
}
-
- /// Construct connectivity info for mDNS.
- // TODO port type, bssid
- pub fn mdns(ip: [u8; 4], port: u8) -> Self {
- let mut array = [0; 24];
- array[0] = 0x2; // mdns
- array[1..5].copy_from_slice(&ip);
- array[5] = port;
-
- Self { info: ArrayView::try_from_array(array, 6).expect("length is fixed") }
- }
-
- /// Construct connectivity info for WiFi Direct.
- pub fn wifi_direct(ssid: [u8; 10], password: [u8; 10], freq: [u8; 2], port: u8) -> Self {
- let mut array = [0; 24];
- array[0] = 0x3; // wifi direct
- array[1..11].copy_from_slice(&ssid);
- array[11..21].copy_from_slice(&password);
- array[21..23].copy_from_slice(&freq);
- array[23] = port;
-
- Self { info: ArrayView::try_from_array(array, 24).expect("length is fixed") }
+}
+impl CastIdDataElement<'_> {
+ /// Gets the cast ID as raw bytes.
+ pub fn get_id_as_bytes(&self) -> &[u8; CAST_ID_LENGTH] {
+ self.id
}
}
-impl SingleTypeDataElement for ConnectivityInfoDataElement {
+/// Potential errors raised when attempting to deserialize
+/// a Google Cast ID data element.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum CastIdDeserializationError {
+ /// The DE payload was of the wrong length.
+ WrongLength,
+}
+
+impl HasDEType for CastIdDataElement<'_> {
const DE_TYPE: DeType = DeType::const_from(0x11);
}
+impl<'adv> DeserializedDataElement<'adv> for CastIdDataElement<'adv> {
+ type DeserializationError = CastIdDeserializationError;
+ fn try_deserialize(
+ _maybe_salt: Option<&DeSalt>,
+ contents: &'adv [u8],
+ ) -> Result<Self, Self::DeserializationError> {
+ let id = <&[u8; CAST_ID_LENGTH]>::try_from(contents)
+ .map_err(|_| CastIdDeserializationError::WrongLength)?;
+ Ok(Self { id })
+ }
+}
+
+impl WriteDataElement for CastIdDataElement<'_> {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
+ sink.try_extend_from_slice(self.id)
+ }
+}
+
+/// The length of a Media Deduplication ID in bytes.
+pub const MEDIA_DEDUPLICATION_ID_LENGTH: usize = 20;
+
+/// Media Deduplication ID
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct MediaDeduplicationIdDataElement<'adv> {
+ id: &'adv [u8; MEDIA_DEDUPLICATION_ID_LENGTH],
+}
+impl<'adv> From<&'adv [u8; MEDIA_DEDUPLICATION_ID_LENGTH]>
+ for MediaDeduplicationIdDataElement<'adv>
+{
+ fn from(id: &'adv [u8; MEDIA_DEDUPLICATION_ID_LENGTH]) -> Self {
+ Self { id }
+ }
+}
+impl MediaDeduplicationIdDataElement<'_> {
+ /// Gets the media deduplication ID as raw bytes.
+ pub fn get_id_as_bytes(&self) -> &[u8; MEDIA_DEDUPLICATION_ID_LENGTH] {
+ self.id
+ }
+}
+
+/// Potential errors raised when attempting to deserialize
+/// a Media Deduplication ID data element.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum MediaDeduplicationIdDeserializationError {
+ /// The DE payload was of the wrong length.
+ WrongLength,
+}
+
+impl HasDEType for MediaDeduplicationIdDataElement<'_> {
+ const DE_TYPE: DeType = DeType::const_from(0x17);
+}
+
+impl<'adv> DeserializedDataElement<'adv> for MediaDeduplicationIdDataElement<'adv> {
+ type DeserializationError = MediaDeduplicationIdDeserializationError;
+ fn try_deserialize(
+ _maybe_salt: Option<&DeSalt>,
+ contents: &'adv [u8],
+ ) -> Result<Self, Self::DeserializationError> {
+ let id = <&[u8; MEDIA_DEDUPLICATION_ID_LENGTH]>::try_from(contents)
+ .map_err(|_| MediaDeduplicationIdDeserializationError::WrongLength)?;
+ Ok(Self { id })
+ }
+}
+
+impl WriteDataElement for MediaDeduplicationIdDataElement<'_> {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
+ sink.try_extend_from_slice(self.id)
+ }
+}
+
+/// A description of what kind of device is broadcasting.
+#[repr(u8)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, FromRepr)]
+#[non_exhaustive]
+pub enum DeviceType {
+ /// The type of device doing the broadcasting is completely unknown.
+ Unknown = 0,
+ /// The broadcasting device is a mobile phone.
+ Phone = 1,
+ /// The broadcasting device is a tablet.
+ Tablet = 2,
+ /// The broadcasting device is a (non-TV) display.
+ Display = 3,
+ /// The broadcasting device is a (non-CrOS) laptop.
+ Laptop = 4,
+ /// The broadcasting device is a TV.
+ TV = 5,
+ /// The broadcasting device is a watch.
+ Watch = 6,
+ /// The broadcasting device is a Chromebook.
+ ChromeOS = 7,
+ /// The broadcasting device is some kind of foldable.
+ Foldable = 8,
+ /// The broadcasting device is a car.
+ Automotive = 9,
+ /// The broadscasting device is a speaker.
+ Speaker = 10,
+}
+
+impl TryFrom<DeviceTypeDataElement> for DeviceType {
+ type Error = u8;
+ fn try_from(de: DeviceTypeDataElement) -> Result<Self, Self::Error> {
+ Self::from_repr(de.0).ok_or(de.0)
+ }
+}
+
+impl From<DeviceType> for DeviceTypeDataElement {
+ fn from(device_type: DeviceType) -> Self {
+ DeviceTypeDataElement(device_type as u8)
+ }
+}
+
+/// Data element representing a device type, which
+/// may be a recognized [`DeviceType`] or some other
+/// byte value which may not be recognized by this
+/// iteration of the program.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct DeviceTypeDataElement(pub u8);
+
+/// Potential errors raised when attempting to deserialize
+/// a device type data element.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum DeviceTypeDeserializationError {
+ /// The DE payload was of the wrong length.
+ WrongLength,
+}
+impl HasDEType for DeviceTypeDataElement {
+ const DE_TYPE: DeType = DeType::const_from(0x16);
+}
+
+impl<'adv> DeserializedDataElement<'adv> for DeviceTypeDataElement {
+ type DeserializationError = DeviceTypeDeserializationError;
+ fn try_deserialize(
+ _maybe_salt: Option<&DeSalt>,
+ contents: &'adv [u8],
+ ) -> Result<Self, Self::DeserializationError> {
+ let value: [u8; 1] =
+ contents.try_into().map_err(|_| DeviceTypeDeserializationError::WrongLength)?;
+ Ok(Self(value[0]))
+ }
+}
+
+impl WriteDataElement for DeviceTypeDataElement {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
+ sink.try_push(self.0)
+ }
+}
+
+/// Combined connectivity information about a device.
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub struct ConnectivityInfoDataElement {
+ /// BLE connectivity information.
+ pub ble_info: BleConnectivityInfo,
+ /// Wifi-LAN connectivity information.
+ pub wifi_lan_info: WifiLanConnectivityInfo,
+}
+
+impl ConnectivityInfoDataElement {
+ /// Returns true iff this connectivity info data element
+ /// has some medium with defined contents.
+ ///
+ /// This should always be checked prior to serializing
+ /// a connectivity info DE into a section to avoid wasting
+ /// space on empty connectivity info DEs.
+ pub fn is_some(&self) -> bool {
+ self.ble_info.is_some() || self.wifi_lan_info.is_some()
+ }
+}
+
+impl HasDEType for ConnectivityInfoDataElement {
+ const DE_TYPE: DeType = DeType::const_from(0x14);
+}
+
impl WriteDataElement for ConnectivityInfoDataElement {
- fn de_header(&self) -> DeHeader {
- DeHeader::new(
- Self::DE_TYPE,
- self.info.len().try_into().expect("conn info is a valid length"),
- )
- }
-
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
- sink.try_extend_from_slice(self.info.as_slice())
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
+ self.ble_info.serialize_if_populated(sink)?;
+ self.wifi_lan_info.serialize_if_populated(sink)?;
+ Some(())
}
}
-/// Connectivity capabilities
-pub struct ConnectivityCapabilityDataElement {
- // TODO len
- capability: ArrayView<u8, 7>,
+/// Errors which may be raised when attempting to deserialize a [`ConnectivityInfoDataElement`].
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ConnectivityInfoDeserializationError {
+ /// There are no included mediums - why did we even broadcast this?
+ NoComponents,
+ /// The included mediums are not sorted by their medium type.
+ ComponentsOutOfOrder,
+ /// There are duplicate mediums.
+ DuplicateComponents,
+ /// Parsing the component with the given medium type failed.
+ ComponentParseFailure(u8),
}
-impl ConnectivityCapabilityDataElement {
- /// Construct connectivity capabilities for WiFi Direct.
- pub fn wifi_direct(supported_freqs: [u8; 3], connected_freqs: [u8; 3]) -> Self {
- let mut array = [0; 7];
- array[0] = 0x2; // wifi direct
- array[1..4].copy_from_slice(&supported_freqs);
- array[4..7].copy_from_slice(&connected_freqs);
+impl<'adv> DeserializedDataElement<'adv> for ConnectivityInfoDataElement {
+ type DeserializationError = ConnectivityInfoDeserializationError;
+ fn try_deserialize(
+ _maybe_salt: Option<&DeSalt>,
+ mut contents: &'adv [u8],
+ ) -> Result<Self, Self::DeserializationError> {
+ if contents.is_empty() {
+ return Err(ConnectivityInfoDeserializationError::NoComponents);
+ }
+ let mut result = ConnectivityInfoDataElement::default();
+ let mut maybe_previous_encountered_medium_type: Option<u8> = None;
+ while !contents.is_empty() {
+ let medium_type = contents[0];
+ contents = &contents[1..];
- Self { capability: ArrayView::try_from_array(array, 7).expect("length is fixed") }
+ // Check that medium types are strictly increasing.
+ if let Some(previous_encountered_medium_type) = maybe_previous_encountered_medium_type {
+ match medium_type.cmp(&previous_encountered_medium_type) {
+ core::cmp::Ordering::Less => {
+ return Err(ConnectivityInfoDeserializationError::ComponentsOutOfOrder);
+ }
+ core::cmp::Ordering::Equal => {
+ return Err(ConnectivityInfoDeserializationError::DuplicateComponents);
+ }
+ core::cmp::Ordering::Greater => {}
+ }
+ }
+ maybe_previous_encountered_medium_type = Some(medium_type);
+
+ // Parse individual medium types, with parser errors delivered
+ // if the component for any individual medium is empty (it should not have been
+ // serialized in the broadcast section to begin with.)
+ let parse_err =
+ ConnectivityInfoDeserializationError::ComponentParseFailure(medium_type);
+ match medium_type {
+ BleConnectivityInfo::MEDIUM_TYPE => {
+ let (remaining, ble_info) =
+ BleConnectivityInfo::parse_from_payload(contents).map_err(|_| parse_err)?;
+ if !ble_info.is_some() {
+ return Err(parse_err);
+ }
+ result.ble_info = ble_info;
+ contents = remaining;
+ }
+ WifiLanConnectivityInfo::MEDIUM_TYPE => {
+ let (remaining, wifi_lan_info) =
+ WifiLanConnectivityInfo::parse_from_payload(contents)
+ .map_err(|_| parse_err)?;
+ if !wifi_lan_info.is_some() {
+ return Err(parse_err);
+ }
+ result.wifi_lan_info = wifi_lan_info;
+ contents = remaining;
+ }
+ _ => {
+ // Unknown medium type, bail with success, because
+ // we want deserialization to be forwards-compatible.
+ return Ok(result);
+ }
+ }
+ }
+ Ok(result)
}
}
-impl SingleTypeDataElement for ConnectivityCapabilityDataElement {
- const DE_TYPE: DeType = DeType::const_from(0x12);
+/// Common trait for fields of `ConnectivityInfoDataElement`.
+pub(crate) trait ConnectivityInfoComponent {
+ /// The byte-value of the identifying code for the connection medium represented by this component.
+ const MEDIUM_TYPE: u8;
+
+ /// Returns the field bitmask for this component indicating populated fields.
+ fn field_bitmask(&self) -> u8;
+ /// Serializes the contents of this component of `ConnectivityInfoDataElement`
+ /// coming after the medium type identifier and field bitmask into a given `Sink`.
+ fn serialize_field_values_into_payload<S: Sink<u8>>(&self, sink: &mut S) -> Option<()>;
+ /// Parses this component of `ConnectivityInfoDataElement` from a given byte-slice (which
+ /// includes the leading bitmask, but not the preceding medium type).
+ fn parse_from_payload(payload: &[u8]) -> nom::IResult<&[u8], Self>
+ where
+ Self: Sized;
+
+ /// Returns true iff this connectivity info component's contents are non-empty.
+ fn is_some(&self) -> bool {
+ self.field_bitmask() > 0
+ }
+
+ /// Serializes this connectivity info component into the given `Sink`
+ /// if its contents are non-empty.
+ fn serialize_if_populated<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
+ let field_bitmask = self.field_bitmask();
+ if field_bitmask > 0 {
+ sink.try_push(Self::MEDIUM_TYPE)?;
+ sink.try_push(field_bitmask)?;
+ self.serialize_field_values_into_payload(sink)?;
+ }
+ Some(())
+ }
}
-impl WriteDataElement for ConnectivityCapabilityDataElement {
- fn de_header(&self) -> DeHeader {
- DeHeader::new(
- Self::DE_TYPE,
- self.capability.len().try_into().expect("capability is a valid length"),
- )
+/// Maximum length of a gatt service identifier.
+pub const MAX_GATT_SERVICE_IDENTIFIER_LEN: usize = 17;
+
+/// Length of a BLE MAC address in bytes.
+pub const BLE_MAC_ADDRESS_LEN: usize = 6;
+
+/// Length of a Nearby device token.
+pub const DEVICE_TOKEN_LEN: usize = 2;
+
+/// Length of a PSM port identifier.
+pub const PSM_LEN: usize = 2;
+
+/// Bitmask for the BLE MAC address.
+const BLE_MAC_ADDRESS_BITMASK: u8 = 0b10000000;
+
+/// Bitmask for the GATT service identifier.
+const GATT_SERVICE_BITMASK: u8 = 0b01000000;
+
+/// Bitmask for the PSM port.
+const PSM_BITMASK: u8 = 0b00100000;
+
+/// Bitmask for the Nearby device token.
+const DEVICE_TOKEN_BITMASK: u8 = 0b00010000;
+
+/// Disallowed bits for the BLE connectivity info bitmask.
+const BLE_BITMASK_DISALLOWED: u8 =
+ !(BLE_MAC_ADDRESS_BITMASK | GATT_SERVICE_BITMASK | PSM_BITMASK | DEVICE_TOKEN_BITMASK);
+
+/// Information about BLE connectivity to a given device.
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub struct BleConnectivityInfo {
+ /// Bluetooth MAC address
+ pub mac_address: Option<[u8; BLE_MAC_ADDRESS_LEN]>,
+ /// An identifier for the GATT service to connect to.
+ /// This could be a UUID + instance ID (17 bytes),
+ /// an attribute handle + instance ID (3 bytes),
+ /// or just a UUID (16 bytes) or attribute handle (2 bytes).
+ pub gatt_service_identifier: Option<tinyvec::ArrayVec<[u8; MAX_GATT_SERVICE_IDENTIFIER_LEN]>>,
+ /// The PSM (L2CAP) port to connect on.
+ pub psm: Option<[u8; PSM_LEN]>,
+ /// Physical identifier ("device token") for the device.
+ pub device_token: Option<[u8; DEVICE_TOKEN_LEN]>,
+}
+
+/// Modified version of `nom::bytes::complete::take` which returns an owned array
+/// of the exact length which was taken from the input.
+fn parse_fixed_length_byte_array<const N: usize>(input: &[u8]) -> nom::IResult<&[u8], [u8; N]> {
+ nom::combinator::map(nom::bytes::complete::take(N), |slice: &[u8]| {
+ slice.try_into().expect("We took N bytes, but we also didn't?")
+ })(input)
+}
+
+impl ConnectivityInfoComponent for BleConnectivityInfo {
+ const MEDIUM_TYPE: u8 = 0;
+ fn field_bitmask(&self) -> u8 {
+ let mut result: u8 = 0b00000000;
+ if self.mac_address.is_some() {
+ result |= BLE_MAC_ADDRESS_BITMASK;
+ }
+ if self.gatt_service_identifier.is_some() {
+ result |= GATT_SERVICE_BITMASK;
+ }
+ if self.psm.is_some() {
+ result |= PSM_BITMASK;
+ }
+ if self.device_token.is_some() {
+ result |= DEVICE_TOKEN_BITMASK;
+ }
+ result
+ }
+ fn serialize_field_values_into_payload<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
+ if let Some(mac_address) = self.mac_address.as_ref() {
+ sink.try_extend_from_slice(mac_address)?;
+ }
+ if let Some(gatt_service_identifier) = self.gatt_service_identifier.as_ref() {
+ sink.try_push(gatt_service_identifier.len() as u8)?;
+ sink.try_extend_from_slice(gatt_service_identifier.as_slice())?;
+ }
+ if let Some(psm) = self.psm.as_ref() {
+ sink.try_extend_from_slice(psm)?;
+ }
+ if let Some(device_token) = self.device_token.as_ref() {
+ sink.try_extend_from_slice(device_token)?;
+ }
+ Some(())
+ }
+ fn parse_from_payload(payload: &[u8]) -> nom::IResult<&[u8], Self>
+ where
+ Self: Sized,
+ {
+ let (remaining, bitmask) = nom::combinator::verify(nom::number::complete::u8, |bitmask| {
+ (bitmask & BLE_BITMASK_DISALLOWED) == 0u8
+ })(payload)?;
+ let has_mac = (bitmask & BLE_MAC_ADDRESS_BITMASK) > 0;
+ let has_gatt_service_identifier = (bitmask & GATT_SERVICE_BITMASK) > 0;
+ let has_psm = (bitmask & PSM_BITMASK) > 0;
+ let has_device_token = (bitmask & DEVICE_TOKEN_BITMASK) > 0;
+ nom::combinator::map(
+ nom::sequence::pair(
+ nom::sequence::pair(
+ nom::combinator::cond(
+ has_mac,
+ parse_fixed_length_byte_array::<BLE_MAC_ADDRESS_LEN>,
+ ),
+ nom::combinator::cond(
+ has_gatt_service_identifier,
+ nom::combinator::map_opt(
+ nom::multi::length_data(nom::number::complete::u8),
+ |gatt_service_identifier: &[u8]| {
+ let mut result = tinyvec::ArrayVec::new();
+ if gatt_service_identifier.len() > MAX_GATT_SERVICE_IDENTIFIER_LEN {
+ None
+ } else {
+ result.extend_from_slice(gatt_service_identifier);
+ Some(result)
+ }
+ },
+ ),
+ ),
+ ),
+ nom::sequence::pair(
+ nom::combinator::cond(has_psm, parse_fixed_length_byte_array::<PSM_LEN>),
+ nom::combinator::cond(
+ has_device_token,
+ parse_fixed_length_byte_array::<DEVICE_TOKEN_LEN>,
+ ),
+ ),
+ ),
+ |((mac_address, gatt_service_identifier), (psm, device_token))| Self {
+ mac_address,
+ gatt_service_identifier,
+ psm,
+ device_token,
+ },
+ )(remaining)
+ }
+}
+
+/// Length of a wifi BSSID.
+pub const WIFI_BSSID_LEN: usize = 6;
+
+/// Bitmask for the IPV4 of a [`WifiLanConnectivityInfo`].
+const IPV4_BITMASK: u8 = 0b10000000;
+/// Bitmask for the IPV6 of a [`WifiLanConnectivityInfo`].
+const IPV6_BITMASK: u8 = 0b01000000;
+/// Bitmask for the port of a [`WifiLanConnectivityInfo`].
+const PORT_BITMASK: u8 = 0b00100000;
+/// Bitmask for the BSSID of a [`WifiLanConnectivityInfo`].
+const BSSID_BITMASK: u8 = 0b00010000;
+
+/// Disallowed bits for the wifi connectivity info bitmask.
+const WIFI_BITMASK_DISALLOWED: u8 = !(IPV4_BITMASK | IPV6_BITMASK | PORT_BITMASK | BSSID_BITMASK);
+
+/// Information about Wifi/LAN connectivity info to a given device.
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub struct WifiLanConnectivityInfo {
+ /// The IP address of the device to connect to.
+ pub ip: Option<net::IpAddr>,
+ /// The port number of the device to connect to.
+ pub port: Option<u16>,
+ /// The BSSID of the device to connect to.
+ pub bssid: Option<[u8; WIFI_BSSID_LEN]>,
+}
+
+impl ConnectivityInfoComponent for WifiLanConnectivityInfo {
+ const MEDIUM_TYPE: u8 = 1;
+ fn field_bitmask(&self) -> u8 {
+ let mut result = 0b00000000;
+ match self.ip.as_ref() {
+ Some(net::IpAddr::V4(_)) => result |= IPV4_BITMASK,
+ Some(net::IpAddr::V6(_)) => result |= IPV6_BITMASK,
+ None => {}
+ }
+ if self.port.is_some() {
+ result |= PORT_BITMASK
+ }
+ if self.bssid.is_some() {
+ result |= BSSID_BITMASK
+ }
+ result
+ }
+ fn serialize_field_values_into_payload<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
+ if let Some(ip) = self.ip.as_ref() {
+ match ip {
+ net::IpAddr::V4(ip) => {
+ sink.try_extend_from_slice(&ip.octets())?;
+ }
+ net::IpAddr::V6(ip) => {
+ sink.try_extend_from_slice(&ip.octets())?;
+ }
+ }
+ }
+ if let Some(port) = self.port.as_ref() {
+ sink.try_extend_from_slice(&port.to_be_bytes())?;
+ }
+ if let Some(bssid) = self.bssid.as_ref() {
+ sink.try_extend_from_slice(bssid)?;
+ }
+ Some(())
+ }
+ fn parse_from_payload(payload: &[u8]) -> nom::IResult<&[u8], Self>
+ where
+ Self: Sized,
+ {
+ let (after_bitmask, bitmask) =
+ nom::combinator::verify(nom::number::complete::u8, |bitmask| {
+ (bitmask & WIFI_BITMASK_DISALLOWED) == 0u8
+ })(payload)?;
+ let has_ipv4 = (bitmask & IPV4_BITMASK) > 0u8;
+ let has_ipv6 = (bitmask & IPV6_BITMASK) > 0u8;
+ let has_port = (bitmask & PORT_BITMASK) > 0u8;
+ let has_bssid = (bitmask & BSSID_BITMASK) > 0u8;
+ let (after_ip, ip) = match (has_ipv4, has_ipv6) {
+ (false, false) => (after_bitmask, None),
+ (true, false) => nom::combinator::map(parse_fixed_length_byte_array::<4>, |octets| {
+ Some(net::Ipv4Addr::from_bits(u32::from_be_bytes(octets)).into())
+ })(after_bitmask)?,
+ (false, true) => nom::combinator::map(parse_fixed_length_byte_array::<16>, |octets| {
+ Some(net::Ipv6Addr::from_bits(u128::from_be_bytes(octets)).into())
+ })(after_bitmask)?,
+ (true, true) => {
+ return Err(nom::Err::Failure(nom::error::Error::new(
+ after_bitmask,
+ nom::error::ErrorKind::Verify,
+ )));
+ }
+ };
+ nom::combinator::map(
+ nom::sequence::pair(
+ nom::combinator::cond(
+ has_port,
+ nom::number::complete::u16(nom::number::Endianness::Big),
+ ),
+ nom::combinator::cond(has_bssid, parse_fixed_length_byte_array::<WIFI_BSSID_LEN>),
+ ),
+ move |(port, bssid)| Self { ip, port, bssid },
+ )(after_ip)
+ }
+}
+
+/// A characteristic identifying a capability of a device.
+#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
+#[repr(u8)]
+pub enum Capability {
+ /// The device has internet connectivity.
+ #[default]
+ InternetConnectivity = 0,
+ /// The device is a cast receiver.
+ CastReceiver = 1,
+ /// The device is a camera.
+ Camera = 2,
+ /// The device is a speaker.
+ Speaker = 3,
+}
+
+/// Trait to represent the kind of capability bitset.
+/// This is a "marker" trait, it has no methods, but we can implement it for
+/// different types to differentiate them.
+pub trait CapabilityBitsetKind {
+ /// The DE type code of the associated capability bitset DE.
+ const DE_TYPE: DeType;
+}
+
+/// Capabilities DE bitset kind.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Capabilities;
+
+impl CapabilityBitsetKind for Capabilities {
+ const DE_TYPE: DeType = DeType::const_from(0x18);
+}
+
+/// Requirements DE bitset kind.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Requirements;
+
+impl CapabilityBitsetKind for Requirements {
+ const DE_TYPE: DeType = DeType::const_from(0x19);
+}
+
+/// A generic data element for capabilities and requirements.
+/// The T type parameter will be either `Capabilities` or `Requirements`.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct CapabilityBitsetDataElement<T: CapabilityBitsetKind>(u8, core::marker::PhantomData<T>);
+
+/// Data element representing a set of device capabilities.
+pub type CapabilitiesDataElement = CapabilityBitsetDataElement<Capabilities>;
+/// Data element representing a set of required device capabilities for a remote device.
+pub type RequirementsDataElement = CapabilityBitsetDataElement<Requirements>;
+
+impl<T: CapabilityBitsetKind> Default for CapabilityBitsetDataElement<T> {
+ fn default() -> Self {
+ Self(0, core::marker::PhantomData)
+ }
+}
+
+impl<T: CapabilityBitsetKind> CapabilityBitsetDataElement<T> {
+ /// Creates a new empty capability bitset data element.
+ pub fn new() -> Self {
+ Self::default()
}
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
- sink.try_extend_from_slice(self.capability.as_slice())
+ /// Adds a capability to the bitset.
+ pub fn add(&mut self, capability: Capability) {
+ self.0 |= 1 << (capability as u8);
}
+
+ /// Checks if a capability is present in the bitset.
+ pub fn has(&self, capability: Capability) -> bool {
+ (self.0 >> (capability as u8)) & 1 == 1
+ }
+
+ /// Creates a `CapabilityBitSetDataElement<T>` from a list of capabilities.
+ pub fn from_capabilities(capabilities: tinyvec::ArrayVec<[Capability; 4]>) -> Self {
+ let mut caps = Self::new();
+ for &capability in capabilities.iter() {
+ caps.add(capability);
+ }
+ caps
+ }
+}
+
+impl<T: CapabilityBitsetKind> From<CapabilityBitsetDataElement<T>> for u8 {
+ fn from(capabilities: CapabilityBitsetDataElement<T>) -> Self {
+ capabilities.0
+ }
+}
+
+impl<T: CapabilityBitsetKind> TryFrom<u8> for CapabilityBitsetDataElement<T> {
+ type Error = CapabilityBitsetDeserializationError;
+
+ fn try_from(byte: u8) -> Result<Self, Self::Error> {
+ // Check for reserved bits. The 4 high bits are reserved.
+ if byte & 0b1111_0000 != 0 {
+ return Err(CapabilityBitsetDeserializationError::ReservedBitsSet);
+ }
+ Ok(Self(byte, core::marker::PhantomData))
+ }
+}
+
+impl<T: CapabilityBitsetKind> HasDEType for CapabilityBitsetDataElement<T> {
+ const DE_TYPE: DeType = T::DE_TYPE;
+}
+
+impl<T: CapabilityBitsetKind> WriteDataElement for CapabilityBitsetDataElement<T> {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Self::Salt, sink: &mut S) -> Option<()> {
+ sink.try_push(self.0)
+ }
+}
+
+impl<'adv, T: CapabilityBitsetKind + 'adv> DeserializedDataElement<'adv>
+ for CapabilityBitsetDataElement<T>
+{
+ type DeserializationError = CapabilityBitsetDeserializationError;
+
+ fn try_deserialize(
+ _maybe_salt: Option<&DeSalt>,
+ contents: &'adv [u8],
+ ) -> Result<Self, Self::DeserializationError> {
+ if contents.len() != 1 {
+ return Err(CapabilityBitsetDeserializationError::InvalidLength);
+ }
+ // Eagerly check format
+ let byte = contents[0];
+ if byte & 0b1111_0000 != 0 {
+ return Err(CapabilityBitsetDeserializationError::ReservedBitsSet);
+ }
+ Ok(Self(byte, core::marker::PhantomData))
+ }
+}
+
+/// Errors that can occur when deserializing a capability bitset DE.
+#[derive(Debug, PartialEq, Eq, Clone, Copy, thiserror::Error)]
+pub enum CapabilityBitsetDeserializationError {
+ /// The DE payload was not exactly 1 byte.
+ #[error("Invalid length")]
+ InvalidLength,
+ /// Reserved bits in the byte were set.
+ #[error("Reserved bits set")]
+ ReservedBitsSet,
}
diff --git a/nearby/presence/np_adv/src/extended/data_elements/tests.rs b/nearby/presence/np_adv/src/extended/data_elements/tests.rs
index 868a127..d17e92b 100644
--- a/nearby/presence/np_adv/src/extended/data_elements/tests.rs
+++ b/nearby/presence/np_adv/src/extended/data_elements/tests.rs
@@ -16,27 +16,197 @@
extern crate std;
+use alloc::format;
+use alloc::vec::Vec;
+use rand::rngs::StdRng;
+use rand::SeedableRng;
use tinyvec::ArrayVec;
use crypto_provider_default::CryptoProviderImpl;
use crate::extended::data_elements::actions::{ActionId, ActionsDataElement};
+use crate::extended::deserialize::data_element::DataElementDeserializationResult;
+use crate::extended::serialize::UnencryptedSectionEncoder;
use crate::extended::serialize::{section_tests::SectionBuilderExt, AdvBuilder};
-use crate::extended::serialize::{AdvertisementType, UnencryptedSectionEncoder};
use crate::extended::V1_ENCODING_UNENCRYPTED;
use super::*;
#[test]
+fn deserialize_cast_id() {
+ // Successful case
+ let cast_id: [u8; CAST_ID_LENGTH] = [5u8; CAST_ID_LENGTH];
+ let de = DataElement::new(CastIdDataElement::DE_TYPE, &cast_id, None);
+ let de = DeserializedGoogleDE::try_deserialize(de).expect("Should recognize a cast id.");
+ let DeserializedGoogleDE::CastId(de) = de else {
+ panic!("Cast Id data elements don't deserialize to Cast Ids?");
+ };
+ let de = de.expect("A proper-length cast ID should deserialize correctly.");
+ assert_eq!(&cast_id, de.get_id_as_bytes());
+
+ // Failure case
+ let bad_cast_id: [u8; CAST_ID_LENGTH + 1] = [5u8; CAST_ID_LENGTH + 1];
+ let bad_de = DataElement::new(CastIdDataElement::DE_TYPE, &bad_cast_id, None);
+ let bad_de = DeserializedGoogleDE::try_deserialize(bad_de)
+ .expect("Should recognize even a malformatted cast id.");
+ let DeserializedGoogleDE::CastId(bad_de) = bad_de else {
+ panic!("Cast Id data elements don't deserialize to Cast Ids?");
+ };
+ let _ = bad_de.expect_err("Wrong-length cast IDs should yield a deserialization error.");
+}
+
+#[test]
+fn serialize_cast_id() {
+ let cast_id: [u8; CAST_ID_LENGTH] = [5u8; CAST_ID_LENGTH];
+ let de = CastIdDataElement::from(&cast_id);
+ let mut sink: ArrayVec<[u8; CAST_ID_LENGTH]> = ArrayVec::new();
+ de.write_de_contents(Unsalted, &mut sink).expect("Should be able to write a Cast Id.");
+ assert_eq!(sink.as_slice(), &cast_id);
+}
+
+#[test]
+fn deserialize_media_deduplication_id() {
+ // Successful case
+ let media_deduplication_id: [u8; MEDIA_DEDUPLICATION_ID_LENGTH] =
+ [5u8; MEDIA_DEDUPLICATION_ID_LENGTH];
+ let de =
+ DataElement::new(MediaDeduplicationIdDataElement::DE_TYPE, &media_deduplication_id, None);
+ let de = DeserializedGoogleDE::try_deserialize(de)
+ .expect("Should recognize a media deduplication id.");
+ let DeserializedGoogleDE::MediaDeduplicationId(de) = de else {
+ panic!(
+ "Media deduplication Id data elements don't deserialize to Media deduplication Ids?"
+ );
+ };
+ let de = de.expect("A proper-length media deduplication ID should deserialize correctly.");
+ assert_eq!(&media_deduplication_id, de.get_id_as_bytes());
+
+ // Failure case
+ let bad_media_deduplication_id: [u8; MEDIA_DEDUPLICATION_ID_LENGTH + 1] =
+ [5u8; MEDIA_DEDUPLICATION_ID_LENGTH + 1];
+ let bad_de = DataElement::new(
+ MediaDeduplicationIdDataElement::DE_TYPE,
+ &bad_media_deduplication_id,
+ None,
+ );
+ let bad_de = DeserializedGoogleDE::try_deserialize(bad_de)
+ .expect("Should recognize even a malformatted cast id.");
+ let DeserializedGoogleDE::MediaDeduplicationId(bad_de) = bad_de else {
+ panic!(
+ "Media deduplication Id data elements don't deserialize to Media deduplication Ids?"
+ );
+ };
+ assert_eq!(
+ bad_de.expect_err(
+ "Wrong-length media deduplication IDs should yield a deserialization error."
+ ),
+ MediaDeduplicationIdDeserializationError::WrongLength
+ );
+}
+
+#[test]
+fn serialize_media_deduplication_id() {
+ let media_deduplication_id: [u8; MEDIA_DEDUPLICATION_ID_LENGTH] =
+ [5u8; MEDIA_DEDUPLICATION_ID_LENGTH];
+ let de = MediaDeduplicationIdDataElement::from(&media_deduplication_id);
+ let mut sink: ArrayVec<[u8; MEDIA_DEDUPLICATION_ID_LENGTH]> = ArrayVec::new();
+ de.write_de_contents(Unsalted, &mut sink)
+ .expect("Should be able to write a Media deduplication Id.");
+ assert_eq!(sink.as_slice(), &media_deduplication_id);
+}
+
+#[test]
+fn deserialize_dedup_hint() {
+ // Successful case
+ let dedup_hint: [u8; DEDUP_HINT_LENGTH] = [2u8; DEDUP_HINT_LENGTH];
+ let de = DataElement::new(DeduplicationHintDataElement::DE_TYPE, &dedup_hint, None);
+ let de = DeserializedGoogleDE::try_deserialize(de).expect("Should recognize a dedup hint.");
+ let DeserializedGoogleDE::DedupHint(de) = de else {
+ panic!("Dedup Hint data elements don't deserialize to dedup hints?");
+ };
+ let de = de.expect("A proper-length deduplication hint should deserialize correctly.");
+ assert_eq!(dedup_hint, de.as_bytes());
+
+ // Failure case
+ let bad_dedup_hint: [u8; DEDUP_HINT_LENGTH + 2] = [2u8; DEDUP_HINT_LENGTH + 2];
+ let bad_de = DataElement::new(DeduplicationHintDataElement::DE_TYPE, &bad_dedup_hint, None);
+ let bad_de = DeserializedGoogleDE::try_deserialize(bad_de)
+ .expect("Should recognize even a malformatted dedup hint.");
+ let DeserializedGoogleDE::DedupHint(bad_de) = bad_de else {
+ panic!("Dedup Hint data elements don't deserialize to dedup hints?");
+ };
+ let _ = bad_de.expect_err("Wrong-length deduplication hints should fail to deserialize.");
+}
+
+#[test]
+fn serialize_dedup_hint() {
+ let dedup_hint: [u8; DEDUP_HINT_LENGTH] = [3u8; DEDUP_HINT_LENGTH];
+ let de = DeduplicationHintDataElement::from(dedup_hint);
+ let mut sink: ArrayVec<[u8; DEDUP_HINT_LENGTH]> = ArrayVec::new();
+ de.write_de_contents(Unsalted, &mut sink).expect("Should be able to write a Dedup hint.");
+ assert_eq!(sink.as_slice(), &dedup_hint);
+}
+
+#[test]
+fn deserialize_device_type() {
+ // Success case, with a recognized device type.
+ let device_type = DeviceType::TV;
+ let device_type_bytes: [u8; 1] = [device_type as u8];
+ let de = DataElement::new(DeviceTypeDataElement::DE_TYPE, &device_type_bytes, None);
+ let de = DeserializedGoogleDE::try_deserialize(de).expect("Should recognize a device type.");
+ let DeserializedGoogleDE::DeviceType(de) = de else {
+ panic!("Device type data elements don't deserialize to device types?");
+ };
+ let de = de.expect("One byte device types should always deserialize.");
+ let reconstructed_device_type = DeviceType::try_from(de).expect("TV should be a device type.");
+ assert_eq!(reconstructed_device_type, device_type);
+
+ // Success case, with an unrecognized device type.
+ let device_type_bytes: [u8; 1] = [255u8];
+ let de = DataElement::new(DeviceTypeDataElement::DE_TYPE, &device_type_bytes, None);
+ let de = DeserializedGoogleDE::try_deserialize(de)
+ .expect("Should recognize even an out-of-range device type.");
+ let DeserializedGoogleDE::DeviceType(de) = de else {
+ panic!("Device type data elements don't deserialize to device types?");
+ };
+ let de = de.expect("One byte device types should always deserialize.");
+ let device_type_val = DeviceType::try_from(de)
+ .expect_err("I strongly doubt that there are 255 recognizable kinds of devices.");
+ assert_eq!(device_type_val, 255u8);
+
+ // Failure case: More than one byte.
+ let bad_device_type_bytes: [u8; 2] = [1u8, 255u8];
+ let de = DataElement::new(DeviceTypeDataElement::DE_TYPE, &bad_device_type_bytes, None);
+ let de = DeserializedGoogleDE::try_deserialize(de)
+ .expect("Should recognize even a malformatted device type.");
+ let DeserializedGoogleDE::DeviceType(de) = de else {
+ panic!("Device type data elements don't deserialize to device types?");
+ };
+ let err = de.expect_err("Device types should always be expressed in one byte.");
+ assert_eq!(DeviceTypeDeserializationError::WrongLength, err);
+}
+
+#[test]
+fn serialize_device_type() {
+ let device_type = DeviceTypeDataElement::from(DeviceType::Laptop);
+ let mut sink: ArrayVec<[u8; 1]> = ArrayVec::new();
+ device_type
+ .write_de_contents(Unsalted, &mut sink)
+ .expect("Should be able to write a device type.");
+ assert_eq!(sink.as_slice(), &[DeviceType::Laptop as u8]);
+}
+
+#[test]
fn serialize_tx_power_de() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
- section_builder.add_de_res(|_| TxPower::try_from(3_i8).map(TxPowerDataElement::from)).unwrap();
+ let tx_power = TxPower::try_from(3_i8).unwrap();
+ section_builder.add_de(&TxPowerDataElement::from(tx_power)).unwrap();
assert_eq!(
&[
- V1_ENCODING_UNENCRYPTED,
+ V1_ENCODING_UNENCRYPTED.0,
2, // section len
0x15, // len 1 type 0x05
3
@@ -53,21 +223,20 @@
#[test]
fn serialize_actions_de_non_empty() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
- section_builder
- .add_de_res(|_| {
- ActionsDataElement::try_from_actions(actions_ids_from_u16_collection([
- 1, 1, 2, 3, 5, 8, // fibonacci, of course
- ]))
- })
- .unwrap();
+ let actions = ActionsDataElement::try_from_actions(actions_ids_from_u16_collection([
+ 1, 1, 2, 3, 5, 8, // fibonacci, of course
+ ]))
+ .unwrap();
+
+ section_builder.add_de(&actions).unwrap();
#[rustfmt::skip]
assert_eq!(
&[
- V1_ENCODING_UNENCRYPTED,
+ V1_ENCODING_UNENCRYPTED.0,
7, // section len
0x66, // len 6 type 0x06
0b00000100, // container type and len TTLLLLLL
@@ -80,17 +249,17 @@
#[rustfmt::skip]
#[test]
fn serialize_context_sync_seq_num_de() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder =
adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
- section_builder
- .add_de_res(|_| ContextSyncSeqNum::try_from(3).map(ContextSyncSeqNumDataElement::from))
- .unwrap();
+ let de = ContextSyncSeqNum::try_from(3).unwrap();
+ let de = ContextSyncSeqNumDataElement::from(de);
+ section_builder.add_de(&de).unwrap();
assert_eq!(
&[
- V1_ENCODING_UNENCRYPTED,
+ V1_ENCODING_UNENCRYPTED.0,
3, // section len
0x81, 0x13, // len 1 type 0x13
3, // seq num
@@ -99,98 +268,334 @@
);
}
-#[rustfmt::skip]
-#[test]
-fn serialize_connectivity_info_de_bluetooth() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- let mut section_builder =
- adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
-
- section_builder.add_de(|_| ConnectivityInfoDataElement::bluetooth([1; 4], [2; 6])).unwrap();
-
- assert_eq!(
- &[
- V1_ENCODING_UNENCRYPTED,
- 13, // section len
- 0x8B, 0x11, // len 11 type 0x11
- 1, // connectivity type
- 1, 1, 1, 1, // svc id
- 2, 2, 2, 2, 2, 2 // mac
- ],
- section_builder.into_section::<CryptoProviderImpl>().as_slice()
- );
+fn random_ble_connectivity_info<R: rand::Rng>(rng: &mut R) -> BleConnectivityInfo {
+ let mut result = BleConnectivityInfo::default();
+ if rng.gen_bool(0.5) {
+ result.mac_address = Some(rand_ext::random_bytes_rc(rng));
+ }
+ if rng.gen_bool(0.5) {
+ let len = rng.gen_range(0..=MAX_GATT_SERVICE_IDENTIFIER_LEN);
+ let contents = rand_ext::random_bytes_rc(rng);
+ result.gatt_service_identifier = Some(tinyvec::ArrayVec::from_array_len(contents, len));
+ }
+ if rng.gen_bool(0.5) {
+ result.psm = Some(rand_ext::random_bytes_rc(rng));
+ }
+ if rng.gen_bool(0.5) {
+ result.device_token = Some(rand_ext::random_bytes_rc(rng));
+ }
+ result
}
-#[rustfmt::skip]
-#[test]
-fn serialize_connectivity_info_de_mdns() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- let mut section_builder =
- adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
-
- section_builder.add_de(|_| ConnectivityInfoDataElement::mdns([1; 4], 2)).unwrap();
-
- assert_eq!(
- &[
- V1_ENCODING_UNENCRYPTED,
- 8, // section len
- 0x86, 0x11, // len 11 type 0x11
- 2, // connectivity type
- 1, 1, 1, 1, // svc id
- 2 // port
- ],
- section_builder.into_section::<CryptoProviderImpl>().as_slice()
- );
+fn random_wifi_lan_connectivity_info<R: rand::Rng>(rng: &mut R) -> WifiLanConnectivityInfo {
+ let mut result = WifiLanConnectivityInfo::default();
+ match rng.gen_range(0..=2) {
+ 1 => {
+ let addr: u32 = rng.gen();
+ result.ip = Some(core::net::Ipv4Addr::from(addr).into());
+ }
+ 2 => {
+ let addr: u128 = rng.gen();
+ result.ip = Some(core::net::Ipv6Addr::from(addr).into());
+ }
+ _ => {}
+ }
+ if rng.gen_bool(0.5) {
+ result.port = Some(rng.gen());
+ }
+ if rng.gen_bool(0.5) {
+ result.bssid = Some(rand_ext::random_bytes_rc(rng));
+ }
+ result
}
-#[rustfmt::skip]
-#[test]
-fn serialize_connectivity_info_de_wifi_direct() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- let mut section_builder =
- adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
+fn test_connectivity_info_component_roundtrip<
+ T: ConnectivityInfoComponent + Eq + core::fmt::Debug,
+>(
+ input: T,
+) {
+ if input.is_some() {
+ let mut serialized = Vec::new();
+ serialized.push(input.field_bitmask());
+ input
+ .serialize_field_values_into_payload(&mut serialized)
+ .expect("Should be able to serialize nonempty connectivity info components.");
+ let serialized = serialized;
- section_builder
- .add_de(|_| ConnectivityInfoDataElement::wifi_direct([1; 10], [2; 10], [3; 2], 4))
- .unwrap();
-
- assert_eq!(
- &[
- V1_ENCODING_UNENCRYPTED,
- 26, // section len
- 0x98, 0x11, // len 24 type 0x11
- 3, // connectivity type
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // ssid
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // password
- 3, 3, // freq
- 4 // port
- ],
- section_builder.into_section::<CryptoProviderImpl>().as_slice()
- );
+ let (remaining, output) = T::parse_from_payload(&serialized)
+ .expect("Serializable connectivity info components should be deserializable.");
+ let empty_slice: &[u8] = &[];
+ assert_eq!(
+ remaining, empty_slice,
+ "Deserializing serialized connectivity info components should take all bytes."
+ );
+ assert_eq!(input, output, "Serialization->Deserialization round trip for connectivity info components should be the identity.");
+ }
}
-#[rustfmt::skip]
#[test]
-fn serialize_connectivity_capabilities_de_wifi_direct() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- let mut section_builder =
- adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
+fn roundtrip_ble_connectivity_info() {
+ let mut rng = StdRng::from_entropy();
+ for _ in 0..10_000 {
+ let input = random_ble_connectivity_info(&mut rng);
+ test_connectivity_info_component_roundtrip(input);
+ }
+}
- section_builder
- .add_de(|_| ConnectivityCapabilityDataElement::wifi_direct([1; 3], [2; 3]))
- .unwrap();
+#[test]
+fn roundtrip_wifi_lan_connectivity_info() {
+ let mut rng = StdRng::from_entropy();
+ for _ in 0..10_000 {
+ let input = random_wifi_lan_connectivity_info(&mut rng);
+ test_connectivity_info_component_roundtrip(input);
+ }
+}
- assert_eq!(
- &[
- V1_ENCODING_UNENCRYPTED,
- 9, // section len
- 0x87, 0x12, // len 7 type 0x12
- 2, // connectivity type
- 1, 1, 1, // supported
- 2, 2, 2, // connected
- ],
- section_builder.into_section::<CryptoProviderImpl>().as_slice()
+#[test]
+fn roundtrip_connectivity_info() {
+ let mut rng = StdRng::from_entropy();
+ for _ in 0..10_000 {
+ let input = ConnectivityInfoDataElement {
+ ble_info: random_ble_connectivity_info(&mut rng),
+ wifi_lan_info: random_wifi_lan_connectivity_info(&mut rng),
+ };
+
+ if !input.is_some() {
+ continue;
+ }
+
+ let mut serialized = Vec::new();
+ input
+ .write_de_contents(Unsalted, &mut serialized)
+ .expect("Should be able to serialize nonempty connectivity info");
+ let serialized = serialized;
+
+ let output = ConnectivityInfoDataElement::try_deserialize(None, &serialized)
+ .expect("Should be able to successfully deserialize serialized connectivity info");
+ assert_eq!(
+ input, output,
+ "Connectivity info serialization->deserialization round-trip should be the identity."
+ );
+ }
+}
+
+#[test]
+fn connectivity_info_trailing_mediums_ignored() {
+ for extra_medium in 2..=255u8 {
+ let input = ConnectivityInfoDataElement {
+ ble_info: BleConnectivityInfo { psm: Some([0u8, 255u8]), ..Default::default() },
+ wifi_lan_info: WifiLanConnectivityInfo { port: Some(u16::MAX), ..Default::default() },
+ };
+
+ let mut buffer = Vec::new();
+ input.write_de_contents(Unsalted, &mut buffer).unwrap();
+ buffer.push(extra_medium);
+ // Fake medium contents.
+ buffer.extend_from_slice(&[1u8; 10]);
+ let buffer = buffer;
+
+ let output = ConnectivityInfoDataElement::try_deserialize(None, &buffer)
+ .expect("Connectivity info should be forward-extensible in deserialization");
+ assert_eq!(
+ input, output,
+ "Connectivity info forward-extensibility should not affect known components"
+ );
+ }
+}
+
+#[test]
+fn connectivity_info_no_components() {
+ let err = ConnectivityInfoDataElement::try_deserialize(None, &[])
+ .expect_err("Empty connectivity info should not successfully deserialize.");
+ assert_eq!(err, ConnectivityInfoDeserializationError::NoComponents);
+}
+
+#[test]
+fn connectivity_info_duplicate_components() {
+ let ble_info = BleConnectivityInfo { psm: Some([1u8, 2u8]), ..Default::default() };
+
+ let mut buffer = Vec::new();
+ ble_info.serialize_if_populated(&mut buffer).unwrap();
+ ble_info.serialize_if_populated(&mut buffer).unwrap();
+ let buffer = buffer;
+
+ let err = ConnectivityInfoDataElement::try_deserialize(None, &buffer).expect_err(
+ "Duplicate connectivity info components should yield an error on deserialization",
);
+ assert_eq!(err, ConnectivityInfoDeserializationError::DuplicateComponents);
+}
+
+#[test]
+fn connectivity_info_components_out_of_order() {
+ let ble_info = BleConnectivityInfo { psm: Some([1u8, 2u8]), ..Default::default() };
+ let wifi_lan_info = WifiLanConnectivityInfo { port: Some(16u16), ..Default::default() };
+
+ let mut buffer = Vec::new();
+ wifi_lan_info.serialize_if_populated(&mut buffer).unwrap();
+ ble_info.serialize_if_populated(&mut buffer).unwrap();
+ let buffer = buffer;
+
+ let err = ConnectivityInfoDataElement::try_deserialize(None, &buffer).expect_err(
+ "Out-of-order connectivity info components should yield an error on deserialization",
+ );
+ assert_eq!(err, ConnectivityInfoDeserializationError::ComponentsOutOfOrder);
+}
+
+#[test]
+fn connectivity_info_deserialize_empty_component() {
+ // Wifi-LAN with empty bitmask.
+ let buffer = [1u8, 0u8];
+
+ let err = ConnectivityInfoDataElement::try_deserialize(None, &buffer).expect_err(
+ "Connectivity info with an empty component should yield a deserialization error.",
+ );
+ assert_eq!(err, ConnectivityInfoDeserializationError::ComponentParseFailure(1));
+}
+
+#[test]
+fn wifi_connectivity_info_no_ipv4_and_ipv6() {
+ let contents = [
+ 0b11000000, // Bitmask selecting ipv4 and ipv6.
+ 192u8, 168u8, 1u8, 1u8, // Test Ipv4 address
+ 1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8, 9u8, 10u8, 11u8, 12u8, 13u8, 14u8, 15u8,
+ 16u8, // Test Ipv6 address
+ ];
+ let _ = WifiLanConnectivityInfo::parse_from_payload(&contents)
+ .expect_err("Should not be able to specify both IPv4 and Ipv6 in WifiLanConnectivityInfo");
+}
+
+#[test]
+fn ble_connectivity_info_no_disallowed_bitmasks() {
+ // The top four bits of the BLE mask are taken, and the bottom
+ // four bits are explicitly disallowed.
+ for disallowed_bitmask in 1u8..=15u8 {
+ let _ = BleConnectivityInfo::parse_from_payload(&[disallowed_bitmask])
+ .expect_err(&format!("Bitmask {} should be disallowed", disallowed_bitmask));
+ }
+}
+
+#[test]
+fn wifi_lan_connectivity_info_no_disallowed_bitmasks() {
+ // The top four bits of the Wifi-lan mask are taken, and the bottom
+ // four bits are explicitly disallowed.
+ for disallowed_bitmask in 1u8..=15u8 {
+ let _ = WifiLanConnectivityInfo::parse_from_payload(&[disallowed_bitmask])
+ .expect_err(&format!("Bitmask {} should be disallowed", disallowed_bitmask));
+ }
+}
+
+#[test]
+fn capabilities_bitmask() {
+ let mut caps = CapabilitiesDataElement::new();
+ assert_eq!(caps.0, 0b0000_0000);
+
+ caps.add(Capability::InternetConnectivity);
+ assert_eq!(caps.0, 0b0000_0001);
+ assert!(caps.has(Capability::InternetConnectivity));
+ assert!(!caps.has(Capability::Speaker));
+
+ caps.add(Capability::Speaker);
+ assert_eq!(caps.0, 0b0000_1001);
+ assert!(caps.has(Capability::InternetConnectivity));
+ assert!(caps.has(Capability::Speaker));
+
+ let byte: u8 = caps.into();
+ assert_eq!(byte, 0b0000_1001);
+}
+
+#[test]
+fn capabilities_from_byte() {
+ let caps = CapabilitiesDataElement::try_from(0b0000_1101).unwrap();
+ assert!(caps.has(Capability::InternetConnectivity));
+ assert!(!caps.has(Capability::CastReceiver));
+ assert!(caps.has(Capability::Camera));
+ assert!(caps.has(Capability::Speaker));
+}
+
+#[test]
+fn capabilities_from_byte_reserved_bits_set() {
+ let err = CapabilitiesDataElement::try_from(0b0001_0001).unwrap_err();
+ assert_eq!(err, CapabilityBitsetDeserializationError::ReservedBitsSet);
+}
+
+#[test]
+fn deserialize_capabilities_de() {
+ let caps = CapabilitiesDataElement::try_deserialize(None, &[0b0000_0101]).unwrap();
+ assert!(caps.has(Capability::InternetConnectivity));
+ assert!(!caps.has(Capability::CastReceiver));
+ assert!(caps.has(Capability::Camera));
+ assert!(!caps.has(Capability::Speaker));
+}
+
+#[test]
+fn deserialize_capabilities_de_invalid_length() {
+ let err = CapabilitiesDataElement::try_deserialize(None, &[0b0, 0b1]).unwrap_err();
+ assert_eq!(err, CapabilityBitsetDeserializationError::InvalidLength);
+ let err = CapabilitiesDataElement::try_deserialize(None, &[]).unwrap_err();
+ assert_eq!(err, CapabilityBitsetDeserializationError::InvalidLength);
+}
+
+#[test]
+fn deserialize_capabilities_de_reserved_bits() {
+ let err = CapabilitiesDataElement::try_deserialize(None, &[0b1000_0001]).unwrap_err();
+ assert_eq!(err, CapabilityBitsetDeserializationError::ReservedBitsSet);
+}
+
+#[test]
+fn requirements_bitmask() {
+ let mut reqs = RequirementsDataElement::new();
+ assert_eq!(reqs.0, 0b0000_0000);
+
+ reqs.add(Capability::InternetConnectivity);
+ assert_eq!(reqs.0, 0b0000_0001);
+ assert!(reqs.has(Capability::InternetConnectivity));
+ assert!(!reqs.has(Capability::Speaker));
+
+ reqs.add(Capability::Speaker);
+ assert_eq!(reqs.0, 0b0000_1001);
+ assert!(reqs.has(Capability::InternetConnectivity));
+ assert!(reqs.has(Capability::Speaker));
+
+ let byte: u8 = reqs.into();
+ assert_eq!(byte, 0b0000_1001);
+}
+
+#[test]
+fn requirements_from_byte() {
+ let reqs = RequirementsDataElement::try_from(0b0000_1101).unwrap();
+ assert!(reqs.has(Capability::InternetConnectivity));
+ assert!(!reqs.has(Capability::CastReceiver));
+ assert!(reqs.has(Capability::Camera));
+ assert!(reqs.has(Capability::Speaker));
+}
+
+#[test]
+fn requirements_from_byte_reserved_bits_set() {
+ let err = RequirementsDataElement::try_from(0b0001_0001).unwrap_err();
+ assert_eq!(err, CapabilityBitsetDeserializationError::ReservedBitsSet);
+}
+
+#[test]
+fn deserialize_requirements_de() {
+ let reqs = RequirementsDataElement::try_deserialize(None, &[0b0000_0101]).unwrap();
+ assert!(reqs.has(Capability::InternetConnectivity));
+ assert!(!reqs.has(Capability::CastReceiver));
+ assert!(reqs.has(Capability::Camera));
+ assert!(!reqs.has(Capability::Speaker));
+}
+
+#[test]
+fn deserialize_requirements_de_invalid_length() {
+ let err = RequirementsDataElement::try_deserialize(None, &[0b0, 0b1]).unwrap_err();
+ assert_eq!(err, CapabilityBitsetDeserializationError::InvalidLength);
+ let err = RequirementsDataElement::try_deserialize(None, &[]).unwrap_err();
+ assert_eq!(err, CapabilityBitsetDeserializationError::InvalidLength);
+}
+
+#[test]
+fn deserialize_requirements_de_reserved_bits() {
+ let err = RequirementsDataElement::try_deserialize(None, &[0b1000_0001]).unwrap_err();
+ assert_eq!(err, CapabilityBitsetDeserializationError::ReservedBitsSet);
}
mod coverage_gaming {
@@ -216,7 +621,7 @@
#[test]
fn generic_data_element_debug() {
let generic =
- GenericDataElement::try_from(DeType::from(1000_u32), &[10, 11, 12, 13]).unwrap();
+ GenericDataElement::try_from(DeType::from(1000_u16), &[10, 11, 12, 13]).unwrap();
let _ = format!("{:?}", generic);
}
}
diff --git a/nearby/presence/np_adv/src/extended/de_type.rs b/nearby/presence/np_adv/src/extended/de_type.rs
index be26555..674c3ee 100644
--- a/nearby/presence/np_adv/src/extended/de_type.rs
+++ b/nearby/presence/np_adv/src/extended/de_type.rs
@@ -14,41 +14,17 @@
//! V1 DE type types
-/// Data element types for extended advertisements
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub struct DeType {
- // 4 billion type codes should be enough for anybody
- code: u32,
+pub use np_hkdf::v1_salt::{DeType, InvalidDeType, OptionDeType};
+
+/// Common base trait for things which have an
+/// associated [`DeType`] value.
+pub trait HasDEType {
+ /// The DE type for this structure.
+ const DE_TYPE: DeType;
}
-impl DeType {
- /// A `const` equivalent to `From<u32>` since trait methods can't yet be const.
- pub const fn const_from(value: u32) -> Self {
- Self { code: value }
- }
-
- /// Returns the type as a u32
- pub fn as_u32(&self) -> u32 {
- self.code
- }
-}
-
-impl From<u8> for DeType {
- fn from(value: u8) -> Self {
- DeType { code: value.into() }
- }
-}
-
-impl From<u32> for DeType {
- fn from(value: u32) -> Self {
- DeType { code: value }
- }
-}
-
-impl From<DeType> for u32 {
- fn from(value: DeType) -> Self {
- value.code
- }
+impl<H: HasDEType> HasDEType for &H {
+ const DE_TYPE: DeType = <H as HasDEType>::DE_TYPE;
}
#[cfg(test)]
@@ -57,7 +33,7 @@
#[test]
fn u32_from_de_type() {
- let de = DeType::from(8u32);
+ let de = DeType::from(8u8);
let _val: u32 = de.into();
}
}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/data_element/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/data_element/mod.rs
index b90de8a..0dd3b00 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/data_element/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/data_element/mod.rs
@@ -14,87 +14,509 @@
//! Parsing logic for V1 data elements, header + contents
-use crate::extended::{de_requires_extended_bit, de_type::DeType, deserialize, DeLength};
-use array_view::ArrayView;
-use core::fmt;
+use crate::extended::salt::DeSalt;
+use crate::extended::{
+ de_requires_extended_bit,
+ de_type::{DeType, HasDEType, OptionDeType},
+ deserialize, DeLength,
+};
+use core::{fmt, marker::PhantomData};
use nom::{branch, bytes, combinator, error, number, sequence};
-use np_hkdf::v1_salt;
+use np_hkdf::v1_salt::ExtendedV1Salt;
#[cfg(test)]
mod tests;
+/// A representation of a V1 data element with a particular
+/// type-code, together with a mechanism to convert to
+/// the alternative representation. The `Self` type is taken
+/// to be this alternative representation.
+pub trait DeserializedDataElement<'a>: HasDEType
+where
+ Self: Sized + 'a,
+{
+ /// The type of errors rasied by `Self::try_deserialize`.
+ type DeserializationError: 'a;
+
+ /// Attempts to convert a DE with the `Self::DE_TYPE` type-code,
+ /// the given offset within the section, and the given
+ /// contents into an instance of this alternative representation.
+ ///
+ /// This method may possibly fail with an error.
+ ///
+ /// The returned representation may borrow from the raw slice contents
+ /// of the DE payload for situations where "zero-copy" deserialization
+ /// makes sense.
+ fn try_deserialize(
+ maybe_salt: Option<&DeSalt>,
+ contents: &'a [u8],
+ ) -> Result<Self, Self::DeserializationError>;
+}
+
+/// Type alias for the result type returned by
+/// [`DeserializedDataElement<'static>#try_deserialize`].
+/// While the `'static` lifetime will almost-never appear as the actual lifetime
+/// in a call to `try_deserialize`, this is useful for referring to the result
+/// type when the method's return type is in fact invariant with respect
+/// to the lifetime, as is the case for owned (lifetime-parameter-free)
+/// [`DeserializedDataElement`]-implementing structures via parametricity.
+pub type OwnedDEDeserializationResult<D> = DEDeserializationResult<'static, D>;
+
+/// Type alias for the result type returned by [`DeserializedDataElement<'a>#try_deserialize`]
+pub type DEDeserializationResult<'a, D> =
+ Result<D, <D as DeserializedDataElement<'a>>::DeserializationError>;
+
+/// Common trait for the results obtained via attempting to deserialize
+/// a [`DataElement`] into one or more DE-type-specific form(s).
+/// May be generated from individual [`DeserializedDataElement`]s
+/// via the [`define_data_element_deserializer`] macro.
+pub trait DataElementDeserializationResult<'adv>
+where
+ Self: Sized + 'adv,
+{
+ /// Attempts to deserialize a data element to this deserialized representation,
+ /// passing the DE along if the DE type code or other data means that it may
+ /// not be deserialized to this representation.
+ fn try_deserialize(data_element: DataElement<'adv>) -> Result<Self, DataElement<'adv>>;
+}
+
+/// An error raised when attempting to parse and deserialize a data
+/// element to a given type implementing [`DataElementDeserializationResult`].
+pub enum DataElementDeserializationError<'adv> {
+ /// The error occurred when attempting to parse the data element
+ /// from the containing section.
+ ParseError(DataElementParseError),
+ /// The deserializer gave up on trying to parse the data element,
+ /// likely because it doesn't have a recognized DE type.
+ Unrecognized(DataElement<'adv>),
+}
+
+/// Iterator which wraps a [`DataElementParsingIterator`] to perform additional
+/// per-DE deserialization steps beyond just interpreting generic [`DataElement`]s.
+pub struct DataElementDeserializingIterator<'adv, F: DataElementDeserializationResult<'adv>> {
+ iter: DataElementParsingIterator<'adv>,
+ _phantom: PhantomData<fn() -> F>,
+}
+
+impl<'adv, F: DataElementDeserializationResult<'adv>> From<DataElementParsingIterator<'adv>>
+ for DataElementDeserializingIterator<'adv, F>
+{
+ fn from(iter: DataElementParsingIterator<'adv>) -> Self {
+ Self { iter, _phantom: Default::default() }
+ }
+}
+
+impl<'adv, F: DataElementDeserializationResult<'adv>> Iterator
+ for DataElementDeserializingIterator<'adv, F>
+{
+ type Item = Result<F, DataElementDeserializationError<'adv>>;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next().map(|maybe_data_element| match maybe_data_element {
+ Ok(data_element) => {
+ <F as DataElementDeserializationResult<'adv>>::try_deserialize(data_element)
+ .map_err(DataElementDeserializationError::Unrecognized)
+ }
+ Err(parse_err) => Err(DataElementDeserializationError::ParseError(parse_err)),
+ })
+ }
+}
+
+#[macro_export]
+/// Macro to define [`DataElementDeserializationResult`]s by "gluing"
+/// together the associated result types of one or more [`DeserializedDataElement`]s
+/// which may or may not have data-to-be-borrowed from advertisement contents.
+/// The generated `enum` has a variant for each distinct DE type specified as part of the
+/// macro, with payloads set as the appropriate choice of either
+/// the associated [`DEDeserializationResult`] or [`OwnedDEDeserializationResult`] for
+/// the type, and the semantics of the `try_deserialize` method are specified to
+/// match against the incoming DE type and forward deserialization to the
+/// `try_deserialize` method on the associated deserialized DE type.
+///
+/// Caveats about the current form of the macro:
+///
+/// - The wrapped enum declaration may have a lifetime if the data is borrowed,
+/// but if so, this lifetime currently _must_ be named `'adv` (after the
+/// fact that any borrowed contents ultimately are of the same lifetime
+/// as a particular deserialized advertisement.)
+///
+/// - Any variants which borrow data from a deserialized advertisement
+/// must also be listed before variants which own data.
+///
+/// - Enumeration discriminants cannot be manually specified. (However,
+/// there is a generated `de_type` method which allows obtaining
+/// the data element type code of a deserialized DE).
+///
+/// # Examples
+/// ```
+/// #[macro_use] extern crate np_adv;
+/// use np_adv::extended::data_elements::*;
+/// define_data_element_deserializer! {
+/// /// Sample deserialization result type which
+/// /// owns all its data, and includes result types
+/// /// for deserializing Tx Power and context-sync
+/// /// sequence number data elements.
+/// #[derive(Debug, Clone)]
+/// pub enum OwnedDEDeserializationResult owns {
+/// /// A transmission power
+/// TxPower: TxPowerDataElement,
+/// /// A context sync sequence number
+/// ContextSyncSeqNum: ContextSyncSeqNumDataElement,
+/// }
+/// }
+///
+/// // Generated result enum:
+/// // /// Sample deserialization result type which
+/// // /// owns all its data, and includes result types
+/// // /// for deserializing Tx Power and context-sync
+/// // /// sequence number data elements.
+/// // #[derive(Debug, Clone)]
+/// // pub enum OwnedDeserializationResult {
+/// // TxPower(Result<TxPowerDataElement, TxPowerMalformed>),
+/// // ContextSyncSeqNum(Result<ContextSyncSeqNumDataElement, ContextSyncSeqNumMalformed>),
+/// // }
+/// // impl<'adv> DataElementDeserializationResult<'adv> for OwnedDeserializationResult {
+/// // ...
+/// // }
+///
+/// define_data_element_deserializer! {
+/// /// Sample deserialization result type which
+/// /// borrows all its data, and includes result types
+/// /// for deserializing Actions and a Cast ID.
+/// ///
+/// /// Note how we can specify arbitrary attributes (including derives)
+/// /// on the generated enum and arbitrary visibility specifiers.
+/// #[derive(Debug, Clone)]
+/// pub(crate) enum BorrowedDEDeserializationResult<'adv> borrows {
+/// /// A collection of actions.
+/// Actions: DeserializedActionsDE<'adv>,
+/// /// A Google Cast ID.
+/// CastId: CastIdDataElement<'adv>,
+/// }
+/// }
+///
+/// define_data_element_deserializer! {
+/// /// Sample deserialization result type which
+/// /// borrows some data for an Actions DE target, but
+/// /// owns other data for a Tx Power DE target.
+/// #[repr(u8)]
+/// enum CombinedDEDeserializationResult<'adv> borrows {
+/// /// A collection of actions.
+/// Actions: DeserializedActionsDE<'adv>,
+/// } owns {
+/// /// A transmission power
+/// TxPower: TxPowerDataElement,
+/// }
+/// }
+/// ```
+macro_rules! define_data_element_deserializer {
+ // The combined type is owned.
+ (
+ $(#[$combined_meta:meta])*
+ $visibility:vis enum $combined_type_name:ident owns {
+ $(
+ $(#[$owned_constituent_meta:meta])*
+ $owned_constituent_name:ident : $owned_constituent_type:ident
+ ),+ $(,)?
+ }
+ )
+ => {
+ $(#[$combined_meta])*
+ $visibility enum $combined_type_name {
+ $(
+ $(#[$owned_constituent_meta])*
+ $owned_constituent_name($crate::extended::deserialize::data_element::OwnedDEDeserializationResult<
+ $owned_constituent_type
+ >
+ )),+
+ }
+ impl $combined_type_name {
+ /// Gets the data-element type of the deserialized DE.
+ $visibility fn de_type(&self) -> $crate::extended::de_type::DeType {
+ match &self {
+ $(Self::$owned_constituent_name(_) =>
+ <$owned_constituent_type as $crate::extended::de_type::HasDEType>::DE_TYPE),+
+ }
+ }
+ }
+ impl<'adv> $crate::extended::deserialize::data_element::DataElementDeserializationResult<'adv> for $combined_type_name {
+ fn try_deserialize(data_element: $crate::extended::deserialize::data_element::DataElement<'adv>)
+ -> Result<Self, $crate::extended::deserialize::data_element::DataElement<'adv>> {
+ match data_element.de_type() {
+ $(
+ <$owned_constituent_type as $crate::extended::de_type::HasDEType>::DE_TYPE => {
+ let result = <$owned_constituent_type as $crate::extended::deserialize::data_element::DeserializedDataElement<'adv>>::
+ try_deserialize(data_element.salt().as_ref(), data_element.contents());
+ Ok(
+ $combined_type_name::$owned_constituent_name(result)
+ )
+ }
+ ),+ ,
+ _ => {
+ Err(data_element)
+ },
+ }
+ }
+ }
+ };
+
+ // The combined type is (at least partially) borrowed.
+ (
+ $(#[$combined_meta:meta])*
+ $visibility:vis enum $combined_type_name:ident <'adv> borrows {
+ $(
+ $(#[$borrowed_constituent_meta:meta])*
+ $borrowed_constituent_name:ident : $borrowed_constituent_type:ident <'adv>
+ ),+ $(,)?
+ } $( owns {
+ $(
+ $(#[$owned_constituent_meta:meta])*
+ $owned_constituent_name:ident : $owned_constituent_type:ident
+ ),* $(,)?
+ })?
+ )
+
+ => {
+ $(#[$combined_meta])*
+ $visibility enum $combined_type_name<'adv> {
+ $(
+ $(#[$borrowed_constituent_meta])*
+ $borrowed_constituent_name($crate::extended::deserialize::data_element::DEDeserializationResult<
+ 'adv,
+ $borrowed_constituent_type<'adv>
+ >
+ )),+ ,
+ $($(
+ $(#[$owned_constituent_meta])*
+ $owned_constituent_name($crate::extended::deserialize::data_element::OwnedDEDeserializationResult<
+ $owned_constituent_type
+ >
+ )),*)?
+ }
+ impl<'adv> $combined_type_name<'adv> {
+ /// Gets the data-element type of the deserialized DE.
+ $visibility fn de_type(&self) -> $crate::extended::de_type::DeType {
+ match &self {
+ $(Self::$borrowed_constituent_name(_) =>
+ <$borrowed_constituent_type<'adv> as $crate::extended::de_type::HasDEType>::DE_TYPE),+ ,
+ $($(Self::$owned_constituent_name(_) =>
+ <$owned_constituent_type as $crate::extended::de_type::HasDEType>::DE_TYPE),*)?
+ }
+ }
+ }
+
+ impl<'adv> $crate::extended::deserialize::data_element::DataElementDeserializationResult<'adv> for $combined_type_name<'adv> {
+ fn try_deserialize(data_element: $crate::extended::deserialize::data_element::DataElement<'adv>)
+ -> Result<Self, $crate::extended::deserialize::data_element::DataElement<'adv>> {
+ match data_element.de_type() {
+ $(
+ <$borrowed_constituent_type<'adv> as $crate::extended::de_type::HasDEType>::DE_TYPE => {
+ let result = <$borrowed_constituent_type as $crate::extended::deserialize::data_element::DeserializedDataElement<'adv>>::
+ try_deserialize(data_element.salt().as_ref(), data_element.contents());
+ Ok(
+ $combined_type_name::$borrowed_constituent_name(result)
+ )
+ }
+ ),+ ,
+ $($(
+ <$owned_constituent_type as $crate::extended::de_type::HasDEType>::DE_TYPE => {
+ let result = <$owned_constituent_type as $crate::extended::deserialize::data_element::DeserializedDataElement<'adv>>::
+ try_deserialize(data_element.salt().as_ref(), data_element.contents());
+ Ok(
+ $combined_type_name::$owned_constituent_name(result)
+ )
+ }
+ ),* ,)?
+ _ => {
+ Err(data_element)
+ },
+ }
+ }
+ }
+ };
+}
+
+pub use define_data_element_deserializer;
+
/// A deserialized data element in a section.
///
-/// The DE has been processed to the point of exposing a DE type and its contents as a `&[u8]`, but
-/// no DE-type-specific processing has been performed.
+/// The DE has been processed to the point of exposing a DE type, a way to get derived salts,
+/// and its contents as a `&[u8]`, but no DE-type-specific processing has been performed.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DataElement<'adv> {
- offset: v1_salt::DataElementOffset,
- de_type: DeType,
- contents: &'adv [u8],
+ proto_de: ProtoDataElement<'adv>,
+ /// Copy of the containing section's extended salt, if any.
+ salt: Option<ExtendedV1Salt>,
}
impl<'adv> DataElement<'adv> {
- /// The offset of the DE in its containing Section.
- ///
- /// Used with the section salt to derive per-DE salt.
- pub fn offset(&self) -> v1_salt::DataElementOffset {
- self.offset
+ /// Gets derived salts for this data element, if this is possible.
+ /// (Note: In the wild, only decrypted sections with extended salts
+ /// will have this populated.)
+ pub fn salt(&self) -> Option<DeSalt> {
+ self.salt.as_ref().map(|salt| DeSalt { de_type: self.de_type(), salt: *salt })
}
-
/// The type of the DE
pub fn de_type(&self) -> DeType {
- self.de_type
+ self.proto_de.de_type
}
- /// The contents of the DE
+ /// The raw bytes of this DE's payload
pub fn contents(&self) -> &'adv [u8] {
- self.contents
+ self.proto_de.contents
+ }
+ /// Constructs a data element by optionally augmenting the given [`ProtoDataElement`]
+ /// with an [`ExtendedV1Salt`] if the containing section makes it possible to
+ /// derive salts for the DE.
+ pub fn from_proto_de(proto_de: ProtoDataElement<'adv>, salt: Option<ExtendedV1Salt>) -> Self {
+ Self { proto_de, salt }
+ }
+ /// Constructs a data element from the DE header, the contents of the DE,
+ /// and an optional [`DeSalt`] for computing derived salts.
+ pub fn new(de_type: DeType, contents: &'adv [u8], salt: Option<ExtendedV1Salt>) -> Self {
+ let proto_de = ProtoDataElement { de_type, contents };
+ Self { proto_de, salt }
}
- pub(crate) fn new(
- offset: v1_salt::DataElementOffset,
- de_type: DeType,
- contents: &'adv [u8],
- ) -> Self {
- Self { offset, de_type, contents }
+ /// Constructs a data element without the ability to derive DE salts
+ /// from the DE type and its contents.
+ pub fn unsalted(de_type: DeType, contents: &'adv [u8]) -> Self {
+ Self::new(de_type, contents, None)
}
+}
- /// Exposes the ability to create a DE for testing purposes, real clients should only obtain
- /// one by deserializing an advertisement
- #[cfg(feature = "testing")]
- pub fn new_for_testing(
- offset: v1_salt::DataElementOffset,
- de_type: DeType,
- contents: &'adv [u8],
- ) -> Self {
- Self { offset, de_type, contents }
+/// An iterator that parses the given [`DataElement`]s iteratively. In environments where memory is
+/// not severely constrained, it is usually safer to collect this into `Result<Vec<OffsetDataElement>>`
+/// so the validity of the whole advertisement can be checked before proceeding with further
+/// processing.
+pub struct DataElementParsingIterator<'adv> {
+ /// A reference to the slice of section contents that we're parsing.
+ input: &'adv [u8],
+ /// The data element type of the most-recently-parsed DE (if any).
+ last_encountered_de_type: OptionDeType,
+ /// A copy of the enclosing section's extended salt (if any).
+ maybe_extended_salt: Option<ExtendedV1Salt>,
+}
+
+impl<'adv> DataElementParsingIterator<'adv> {
+ pub(crate) fn new(input: &'adv [u8], maybe_extended_salt: Option<ExtendedV1Salt>) -> Self {
+ Self { input, last_encountered_de_type: OptionDeType::NONE, maybe_extended_salt }
}
}
+/// Each [`Self::Item`] is a parsing result of a Data Element. The result includes a parsed
+/// Data Element for success, or [`DataElementParseError`] for failure.
+/// Suggest to throw away all the Data Elements from the iterator if one Data Element is corrupted.
+/// The caller can collect the Result to the top level as [`Result<Vec<_>, _>`] before iterating
+/// the Data Elements.
+impl<'adv> Iterator for DataElementParsingIterator<'adv> {
+ type Item = Result<DataElement<'adv>, DataElementParseError>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match ProtoDataElement::parse(self.input) {
+ Ok((rem, proto_de)) => {
+ // Parsed the DE successfully, verify that the type-code is
+ // strictly increasing.
+ let current_de_type: OptionDeType = proto_de.de_type.into();
+ if current_de_type > self.last_encountered_de_type {
+ self.input = rem;
+ self.last_encountered_de_type = current_de_type;
+ Some(Ok(DataElement { proto_de, salt: self.maybe_extended_salt }))
+ } else {
+ Some(Err(DataElementParseError::TypeCodesOutOfOrder))
+ }
+ }
+ Err(nom::Err::Failure(e)) => Some(Err(DataElementParseError::NomError(e.code))),
+ Err(nom::Err::Incomplete(_)) => {
+ panic!("Should always complete since we are parsing using the `nom::complete` APIs")
+ }
+ Err(nom::Err::Error(_)) => {
+ // nom `Error` is recoverable, it usually means we should move on the parsing the
+ // next section. There is nothing after data elements within a section, so we just
+ // check that there is no remaining data.
+ if !self.input.is_empty() {
+ return Some(Err(DataElementParseError::UnexpectedDataAfterEnd));
+ }
+ None
+ }
+ }
+ }
+}
+
+/// The error that may arise while parsing data elements.
+#[derive(Debug, PartialEq, Eq)]
+pub enum DataElementParseError {
+ /// Unexpected data found after the end of the data elements portion. This means either the
+ /// parser was fed with additional data (it should only be given the bytes within a section,
+ /// not the whole advertisement), or the length field in the header of the data element is
+ /// malformed.
+ UnexpectedDataAfterEnd,
+ /// The data elements were not in strictly increasing order by type code.
+ TypeCodesOutOfOrder,
+ /// A parse error is returned during nom.
+ NomError(error::ErrorKind),
+}
+
+impl fmt::Display for DataElementParseError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ DataElementParseError::UnexpectedDataAfterEnd => write!(f, "Unexpected data after end"),
+ DataElementParseError::TypeCodesOutOfOrder => {
+ write!(f, "DE type codes are out of order")
+ }
+ DataElementParseError::NomError(_) => write!(f, "Nom error"),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for DataElementParseError {}
+
+/// A reduced-information variant of a [`DataElement`] that completely
+/// lacks the ability to derive DE salts due to not tracking its offset
+/// within a section (in any form).
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct ProtoDataElement<'d> {
+ /// The data element's type code.
+ pub de_type: DeType,
+ /// The raw bytes of this data element's payload.
+ pub contents: &'d [u8],
+}
+
+impl ProtoDataElement<'_> {
+ pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], ProtoDataElement> {
+ let (remaining, header) = DeHeader::parse(input)?;
+ let len = header.contents_len;
+ let de_type = header.de_type;
+ combinator::map(bytes::complete::take(len.as_u8()), move |contents| ProtoDataElement {
+ de_type,
+ contents,
+ })(remaining)
+ }
+}
+
+/// Deserialize-specific version of a DE header that incorporates the
+/// length of the subsequent DE contents (unlike the representation
+/// in serialization, where the length is implicitly given as part
+/// of the data-to-be-serialized).
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub(crate) struct DeHeader {
+ pub(crate) de_type: DeType,
+ pub(crate) contents_len: DeLength,
+}
+
impl DeHeader {
pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], DeHeader> {
// 1-byte header: 0b0LLLTTTT
- let parse_single_byte_de_header =
- combinator::map_opt::<&[u8], _, DeHeader, error::Error<&[u8]>, _, _>(
- combinator::consumed(combinator::map_res(
- combinator::verify(number::complete::u8, |&b| !deserialize::hi_bit_set(b)),
- |b| {
- // L bits
- let len = (b >> 4) & 0x07;
- // T bits
- let de_type = ((b & 0x0F) as u32).into();
+ let parse_single_byte_de_header = combinator::map_res(
+ combinator::verify(number::complete::u8, |&b| !deserialize::hi_bit_set(b)),
+ |b| {
+ // L bits
+ let len = (b >> 4) & 0x07;
+ // T bits
+ let de_type = (b & 0x0F).into();
- len.try_into().map(|l| (l, de_type))
- },
- )),
- |(header_bytes, (len, de_type))| {
- ArrayView::try_from_slice(header_bytes).map(|header_bytes| DeHeader {
- header_bytes,
- contents_len: len,
- de_type,
- })
- },
- );
+ len.try_into().map(|contents_len| DeHeader { contents_len, de_type })
+ },
+ );
// multi-byte headers: 0b1LLLLLLL (0b1TTTTTTT)* 0b0TTTTTTT
// leading 1 in first byte = multibyte format
@@ -111,7 +533,7 @@
let parse_ext_de_header = combinator::verify(
combinator::map_opt(
- combinator::consumed(sequence::pair(
+ sequence::pair(
// length byte w/ leading 1
combinator::map_res(
combinator::verify(number::complete::u8::<&[u8], _>, |&b| {
@@ -144,8 +566,8 @@
}),
))),
)),
- )),
- |(header_bytes, (len, type_bytes))| {
+ ),
+ |(contents_len, type_bytes)| {
// snag the low 7 bits of each type byte and accumulate
type_bytes
.iter()
@@ -153,13 +575,8 @@
accum.checked_shl(7).map(|n| n + ((b & 0x7F) as u64))
})
.and_then(|type_code| u32::try_from(type_code).ok())
- .and_then(|type_code| {
- ArrayView::try_from_slice(header_bytes).map(|header_bytes| DeHeader {
- header_bytes,
- contents_len: len,
- de_type: type_code.into(),
- })
- })
+ .and_then(|type_code| DeType::try_from(type_code).ok())
+ .map(|de_type| DeHeader { contents_len, de_type })
},
),
|header| {
@@ -171,114 +588,3 @@
branch::alt((parse_single_byte_de_header, parse_ext_de_header))(input)
}
}
-
-/// An iterator that parses the given data elements iteratively. In environments where memory is
-/// not severely constrained, it is usually safer to collect this into `Result<Vec<DataElement>>`
-/// so the validity of the whole advertisement can be checked before proceeding with further
-/// processing.
-#[derive(Debug)]
-pub struct DataElementParsingIterator<'adv> {
- input: &'adv [u8],
- // The index of the data element this is currently at
- offset: u8,
-}
-
-impl<'adv> DataElementParsingIterator<'adv> {
- pub(crate) fn new(input: &'adv [u8]) -> Self {
- Self { input, offset: 0 }
- }
-}
-
-impl<'adv> Iterator for DataElementParsingIterator<'adv> {
- type Item = Result<DataElement<'adv>, DataElementParseError>;
-
- fn next(&mut self) -> Option<Self::Item> {
- match ProtoDataElement::parse(self.input) {
- Ok((rem, pde)) => {
- self.input = rem;
- let current_offset = self.offset;
- self.offset = if let Some(offset) = self.offset.checked_add(1) {
- offset
- } else {
- return Some(Err(DataElementParseError::TooManyDataElements));
- };
- Some(Ok(pde.into_data_element(v1_salt::DataElementOffset::from(current_offset))))
- }
- Err(nom::Err::Failure(e)) => Some(Err(DataElementParseError::NomError(e.code))),
- Err(nom::Err::Incomplete(_)) => {
- panic!("Should always complete since we are parsing using the `nom::complete` APIs")
- }
- Err(nom::Err::Error(_)) => {
- // nom `Error` is recoverable, it usually means we should move on the parsing the
- // next section. There is nothing after data elements within a section, so we just
- // check that there is no remaining data.
- if !self.input.is_empty() {
- return Some(Err(DataElementParseError::UnexpectedDataAfterEnd));
- }
- None
- }
- }
- }
-}
-
-/// The error that may arise while parsing data elements.
-#[derive(Debug, PartialEq, Eq)]
-pub enum DataElementParseError {
- /// Unexpected data found after the end of the data elements portion. This means either the
- /// parser was fed with additional data (it should only be given the bytes within a section,
- /// not the whole advertisement), or the length field in the header of the data element is
- /// malformed.
- UnexpectedDataAfterEnd,
- /// There are too many data elements in the advertisement. The maximum number supported by the
- /// current parsing logic is 255.
- TooManyDataElements,
- /// A parse error is returned during nom.
- NomError(error::ErrorKind),
-}
-
-impl fmt::Display for DataElementParseError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- DataElementParseError::UnexpectedDataAfterEnd => write!(f, "Unexpected data after end"),
- DataElementParseError::TooManyDataElements => write!(f, "Too many data elements"),
- DataElementParseError::NomError(_) => write!(f, "Nom error"),
- }
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for DataElementParseError {}
-
-/// Deserialize-specific version of a DE header that incorporates the header length.
-/// This is needed for encrypted identities that need to construct a slice of everything in the
-/// section following the identity DE header.
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub(crate) struct DeHeader {
- /// The original bytes of the header, at most 6 bytes long (1 byte len, 5 bytes type)
- pub(crate) header_bytes: ArrayView<u8, 6>,
- pub(crate) de_type: DeType,
- pub(crate) contents_len: DeLength,
-}
-
-/// An intermediate stage in parsing a [DataElement] that lacks `offset`.
-#[derive(Debug, PartialEq, Eq)]
-pub struct ProtoDataElement<'d> {
- header: DeHeader,
- /// `len()` must equal `header.contents_len`
- contents: &'d [u8],
-}
-
-impl<'d> ProtoDataElement<'d> {
- pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], ProtoDataElement> {
- let (remaining, header) = DeHeader::parse(input)?;
- let len = header.contents_len;
- combinator::map(bytes::complete::take(len.as_u8()), move |slice| {
- let header_clone = header.clone();
- ProtoDataElement { header: header_clone, contents: slice }
- })(remaining)
- }
-
- fn into_data_element(self, offset: v1_salt::DataElementOffset) -> DataElement<'d> {
- DataElement::new(offset, self.header.de_type, self.contents)
- }
-}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/data_element/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/data_element/tests.rs
index 473cf37..6187199 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/data_element/tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/data_element/tests.rs
@@ -48,17 +48,7 @@
fn parse_de_with_1_byte_header() {
let data = [0x51, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0xFF];
assert_eq!(
- Ok((
- &data[6..],
- ProtoDataElement {
- header: DeHeader {
- de_type: 1_u8.into(),
- header_bytes: ArrayView::try_from_slice(&[0x51]).unwrap(),
- contents_len: 5_u8.try_into().unwrap(),
- },
- contents: &data[1..6],
- }
- )),
+ Ok((&data[6..], ProtoDataElement { de_type: 1_u8.into(), contents: &data[1..6] })),
ProtoDataElement::parse(&data)
);
}
@@ -67,17 +57,7 @@
fn parse_de_with_2_byte_header() {
let data = [0x85, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0xFF];
assert_eq!(
- Ok((
- &data[7..],
- ProtoDataElement {
- header: DeHeader {
- de_type: 16_u8.into(),
- header_bytes: ArrayView::try_from_slice(&[0x85, 0x10]).unwrap(),
- contents_len: 5_u8.try_into().unwrap(),
- },
- contents: &data[2..7],
- }
- )),
+ Ok((&data[7..], ProtoDataElement { de_type: 16_u8.into(), contents: &data[2..7] })),
ProtoDataElement::parse(&data)
);
}
@@ -88,14 +68,7 @@
assert_eq!(
Ok((
&data[8..],
- ProtoDataElement {
- header: DeHeader {
- header_bytes: ArrayView::try_from_slice(&[0x85, 0xC1, 0x41]).unwrap(),
- contents_len: 5_u8.try_into().unwrap(),
- de_type: 0b0000_0000_0000_0000_0010_0000_1100_0001_u32.into(),
- },
- contents: &data[3..8],
- }
+ ProtoDataElement { de_type: 0b0010_0000_1100_0001_u16.into(), contents: &data[3..8] }
)),
ProtoDataElement::parse(&data)
);
@@ -105,14 +78,7 @@
fn parse_de_header_1_byte() {
let data = [0x51, 0xFF, 0xFF];
assert_eq!(
- Ok((
- &data[1..],
- DeHeader {
- de_type: 1_u8.into(),
- contents_len: 5_u8.try_into().unwrap(),
- header_bytes: ArrayView::try_from_slice(&[0x51]).unwrap(),
- }
- )),
+ Ok((&data[1..], DeHeader { de_type: 1_u8.into(), contents_len: 5_u8.try_into().unwrap() })),
DeHeader::parse(&data)
);
}
@@ -121,14 +87,7 @@
fn parse_de_header_2_bytes() {
let data = [0x88, 0x01];
assert_eq!(
- Ok((
- &data[2..],
- DeHeader {
- de_type: 1_u8.into(),
- contents_len: 8_u8.try_into().unwrap(),
- header_bytes: ArrayView::try_from_slice(&[0x88, 0x01]).unwrap(),
- }
- )),
+ Ok((&data[2..], DeHeader { de_type: 1_u8.into(), contents_len: 8_u8.try_into().unwrap() })),
DeHeader::parse(&data)
);
}
@@ -140,9 +99,8 @@
Ok((
&data[3..],
DeHeader {
- de_type: 0b0000_0000_0000_0000_0010_0000_1100_0001_u32.into(),
+ de_type: 0b0010_0000_1100_0001_u16.into(),
contents_len: 3_u8.try_into().unwrap(),
- header_bytes: ArrayView::try_from_slice(&[0x83, 0xC1, 0x41]).unwrap(),
}
)),
DeHeader::parse(&data)
@@ -156,9 +114,8 @@
Ok((
&data[4..],
DeHeader {
- de_type: 0b0000_0000_0001_0000_0110_0000_1100_0001_u32.into(),
+ de_type: 0b0000_0000_0001_0000_0110_0000_1100_0001_u32.try_into().unwrap(),
contents_len: 3_u8.try_into().unwrap(),
- header_bytes: ArrayView::try_from_slice(&[0x83, 0xC1, 0xC1, 0x41]).unwrap(),
}
)),
DeHeader::parse(&data)
@@ -172,15 +129,13 @@
// does make it to 5 bytes, we will only read the least significant 32 bits out of a possible 35.
// The contents of the most significant 3 bits of type code must be 0's, otherwise the adv will
// be discarded
- let data = [0x80, 0x8F, 0xFF, 0xFF, 0xFF, 0x7F];
+ let data = [0x80, 0x8F, 0xFF, 0xFF, 0xFF, 0x7E];
assert_eq!(
Ok((
&data[6..],
DeHeader {
- de_type: u32::MAX.into(),
- contents_len: 0_u8.try_into().unwrap(),
- header_bytes: ArrayView::try_from_slice(&[0x80, 0x8F, 0xFF, 0xFF, 0xFF, 0x7F])
- .unwrap(),
+ de_type: 0xFFFFFFFEu32.try_into().unwrap(),
+ contents_len: 0_u8.try_into().unwrap()
}
)),
DeHeader::parse(&data)
@@ -240,20 +195,41 @@
}
#[test]
-fn de_iteration_exposes_correct_data() {
+fn unsalted_de_iteration_exposes_correct_data() {
let mut de_data = vec![];
// de 1 byte header, type 5, len 5
de_data.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
// de 2 byte header, type 16, len 1
de_data.extend_from_slice(&[0x81, 0x10, 0x01]);
- let iterator = DataElementParsingIterator::new(&de_data);
+ let iterator = DataElementParsingIterator::new(&de_data, None);
let des = iterator.collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(
vec![
- DataElement::new(0.into(), 5_u32.into(), &[0x01, 0x02, 0x03, 0x04, 0x05]),
- DataElement::new(1.into(), 16_u32.into(), &[0x01]),
+ DataElement::new(5_u8.into(), &[0x01, 0x02, 0x03, 0x04, 0x05], None),
+ DataElement::new(16_u8.into(), &[0x01], None),
+ ],
+ des
+ );
+}
+
+#[test]
+fn salted_de_iteration_exposes_correct_data() {
+ let salt = ExtendedV1Salt::from([3u8; 16]);
+ let mut de_data = vec![];
+ // de 1 byte header, type 5, len 5
+ de_data.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
+ // de 2 byte header, type 16, len 1
+ de_data.extend_from_slice(&[0x81, 0x10, 0x01]);
+
+ let iterator = DataElementParsingIterator::new(&de_data, Some(salt));
+ let des = iterator.collect::<Result<Vec<_>, _>>().unwrap();
+
+ assert_eq!(
+ vec![
+ DataElement::new(5_u8.into(), &[0x01, 0x02, 0x03, 0x04, 0x05], Some(salt),),
+ DataElement::new(16_u8.into(), &[0x01], Some(salt),),
],
des
);
@@ -265,10 +241,10 @@
// de 2 byte header, type 16, len 1
de_data.extend_from_slice(&[0x81, 0x10, 0x01]);
- let iterator = DataElementParsingIterator::new(&de_data);
+ let iterator = DataElementParsingIterator::new(&de_data, None);
let des = iterator.collect::<Result<Vec<_>, _>>().unwrap();
- assert_eq!(vec![DataElement::new(0.into(), 16_u32.into(), &[0x01]),], des);
+ assert_eq!(vec![DataElement::new(16_u8.into(), &[0x01], None),], des);
}
#[test]
@@ -277,40 +253,24 @@
// de 1 byte header, type 1, len 0
de_data.extend_from_slice(&[0x01]);
- let iterator = DataElementParsingIterator::new(&de_data);
+ let iterator = DataElementParsingIterator::new(&de_data, None);
let des = iterator.collect::<Result<Vec<_>, _>>().unwrap();
- assert_eq!(vec![DataElement::new(0.into(), 1_u32.into(), &[])], des);
+ assert_eq!(vec![DataElement::new(1_u8.into(), &[], None)], des);
}
#[test]
-fn de_iteration_max_number_des() {
+fn de_iteration_type_codes_out_of_order() {
let mut de_data = vec![];
- // de 1 byte header, type 1, len 0
- // add this 255 times which is the max amount of
- // supported DEs in a single section
- for _ in 0..255 {
- de_data.extend_from_slice(&[0x01]);
- }
+ // de 1 byte header, type 1, len 0.
+ de_data.extend_from_slice(&[0x01]);
+ // de 1 byte header, type 0, len 0.
+ de_data.extend_from_slice(&[0x00]);
- let iterator = DataElementParsingIterator::new(&de_data);
- assert!(iterator.collect::<Result<Vec<_>, _>>().is_ok());
-}
+ let iterator = DataElementParsingIterator::new(&de_data, None);
+ let des = iterator.collect::<Result<Vec<_>, _>>().unwrap_err();
-#[test]
-fn de_iteration_over_max_number_des() {
- let mut de_data = vec![];
- // de 1 byte header, type 1, len 0
- // add this 256 times to exceed max number of des in a section
- for _ in 0..256 {
- de_data.extend_from_slice(&[0x01]);
- }
-
- let iterator = DataElementParsingIterator::new(&de_data);
- assert_eq!(
- iterator.collect::<Result<Vec<_>, _>>(),
- Err(DataElementParseError::TooManyDataElements)
- );
+ assert_eq!(DataElementParseError::TypeCodesOutOfOrder, des)
}
#[test]
@@ -319,7 +279,7 @@
// de 1 byte header, type 1, len 2, but only one byte left to process
de_data.extend_from_slice(&[0x21, 0x00]);
- let iterator = DataElementParsingIterator::new(&de_data);
+ let iterator = DataElementParsingIterator::new(&de_data, None);
assert_eq!(
iterator.collect::<Result<Vec<_>, _>>(),
Err(DataElementParseError::UnexpectedDataAfterEnd)
@@ -333,12 +293,9 @@
#[allow(clippy::clone_on_copy)]
#[test]
fn data_element_debug_and_clone() {
- let de = DataElement::new(0.into(), 0_u32.into(), &[]);
+ let de = DataElement::new(0_u8.into(), &[], None);
let _ = format!("{:?}", de);
let _ = de.clone();
- let iterator = DataElementParsingIterator::new(&[]);
- let _ = format!("{:?}", iterator);
- let _ = format!("{:?}", DataElementParseError::TooManyDataElements);
let (_, header) = DeHeader::parse(&[0x11, 0xFF]).unwrap();
let _ = format!("{:?}", header);
let _ = header.clone();
diff --git a/nearby/presence/np_adv/src/extended/deserialize/dev_tools.rs b/nearby/presence/np_adv/src/extended/deserialize/dev_tools.rs
index e77e083..90663e6 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/dev_tools.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/dev_tools.rs
@@ -40,8 +40,6 @@
pub enum V1EncryptionScheme {
/// Indicates MIC-based encryption and verification.
Mic,
- /// Indicates signature-based encryption and verification.
- Signature,
}
/// Decrypt, but do not further deserialize the v1 bytes, intended for developer tooling uses only.
@@ -77,7 +75,6 @@
));
let encryption_scheme = match cipher_section {
- CiphertextSection::SignatureEncrypted(_) => V1EncryptionScheme::Signature,
CiphertextSection::MicEncrypted(_) => V1EncryptionScheme::Mic,
};
return Ok((pt, encryption_scheme));
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs
index fc38b1c..1f6b53d 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs
@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#[cfg(feature = "devtools")]
+use crate::extended::NP_ADV_MAX_SECTION_LEN;
use crate::{
credential::v1::*,
extended::{
deserialize::{DecryptedSection, SectionMic, VerificationMode},
- section_signature_payload::*,
- V1IdentityToken, NP_ADV_MAX_SECTION_LEN, V1_IDENTITY_TOKEN_LEN,
+ V1IdentityToken, NP_ADV_MAX_SECTION_CONTENTS_LEN,
},
- NP_SVC_UUID,
};
use crate::deserialization_arena::DeserializationArenaAllocator;
@@ -44,7 +44,6 @@
hmac::Hmac,
CryptoProvider,
};
-use np_hkdf::v1_salt::ExtendedV1Salt;
#[cfg(test)]
use crate::extended::deserialize::encrypted_section::tests::IdentityResolutionOrDeserializationError;
@@ -75,7 +74,7 @@
/// stored in some [`SectionIdentityResolutionMaterial`].
///
/// This method does not decrypt an entire section's ciphertext aside from the metadata key,
- /// and so verification (MIC or signature) needs to be done elsewhere.
+ /// and so verification needs to be done elsewhere.
///
/// Returns `Some` if decrypting the metadata-key ciphertext produces plaintext whose HMAC
/// matches the expected MAC. Otherwise, returns `None`.
@@ -119,10 +118,6 @@
cipher: C::AesCtr128,
}
-/// Maximum length of a section's contents, after the metadata-key.
-#[allow(unused)]
-const MAX_SECTION_CONTENTS_LEN: usize = NP_ADV_MAX_SECTION_LEN - V1_IDENTITY_TOKEN_LEN;
-
/// Bare, decrypted contents from an encrypted section,
/// including the decrypted metadata key and the decrypted section ciphertext.
/// At this point, verification of the plaintext contents has not yet been performed.
@@ -130,12 +125,13 @@
// Only used with feature = "devtools"
#[allow(unused)]
pub(crate) identity_token: V1IdentityToken,
+ #[allow(unused)]
pub(crate) nonce: AesCtrNonce,
pub(crate) plaintext_contents: &'a [u8],
}
#[cfg(feature = "devtools")]
-impl<'a> RawDecryptedSection<'a> {
+impl RawDecryptedSection<'_> {
pub(crate) fn to_raw_bytes(&self) -> ArrayView<u8, NP_ADV_MAX_SECTION_LEN> {
let mut result = Vec::new();
result.extend_from_slice(self.identity_token.as_slice());
@@ -149,15 +145,15 @@
#[derive(PartialEq, Eq, Debug)]
pub(crate) struct EncryptedSectionContents<'adv, S> {
adv_header: V1AdvHeader,
- format_bytes: &'adv [u8],
+ format_byte: u8,
pub(crate) salt: S,
/// Ciphertext of identity token (part of section header)
identity_token: CiphertextExtendedIdentityToken,
- /// The portion of the ciphertext that has been encrypted.
- /// Length must be in `[0, NP_ADV_MAX_SECTION_LEN]`.
+ /// The contents of the section after the length byte.
+ /// Length must be in `[0, NP_ADV_MAX_SECTION_CONTENTS_LEN]`.
section_contents: &'adv [u8],
- // The length byte exactly as it appears in the adv. This is the length of the encrypted
- // contents plus any additional bytes of suffix
+ /// The length byte exactly as it appears in the adv. This is the length of the encrypted
+ /// contents plus any additional bytes of suffix
total_section_contents_len: u8,
}
@@ -168,20 +164,20 @@
/// and the entire section contents as an undecrypted ciphertext.
///
/// # Panics
- /// If `all_ciphertext` is greater than `NP_ADV_MAX_SECTION_LEN` bytes,
+ /// If `all_ciphertext` is greater than `NP_ADV_MAX_SECTION_CONTENTS_LEN` bytes,
/// or less than `IDENTITY_TOKEN_LEN` bytes.
pub(crate) fn new(
adv_header: V1AdvHeader,
- format_bytes: &'adv [u8],
+ format_byte: u8,
salt: S,
identity_token: CiphertextExtendedIdentityToken,
section_contents_len: u8,
section_contents: &'adv [u8],
) -> Self {
- assert!(section_contents.len() <= NP_ADV_MAX_SECTION_LEN - V1_IDENTITY_TOKEN_LEN);
+ assert!(section_contents.len() <= NP_ADV_MAX_SECTION_CONTENTS_LEN);
Self {
adv_header,
- format_bytes,
+ format_byte,
salt,
identity_token,
total_section_contents_len: section_contents_len,
@@ -243,106 +239,6 @@
}
}
-/// An encrypted section which is verified using a ed25519 signature
-#[derive(PartialEq, Eq, Debug)]
-pub(crate) struct SignatureEncryptedSection<'a> {
- pub(crate) contents: EncryptedSectionContents<'a, ExtendedV1Salt>,
-}
-
-impl<'a> SignatureEncryptedSection<'a> {
- /// Try deserializing into a [`DecryptedSection`] given an identity-match
- /// with some paired verification material for the matched identity.
- pub(crate) fn try_deserialize<P>(
- &self,
- arena: &mut DeserializationArenaAllocator<'a>,
- identity_match: IdentityMatch<P>,
- verification_material: &SignedSectionVerificationMaterial,
- ) -> Result<DecryptedSection<'a>, DeserializationError<SignatureVerificationError>>
- where
- P: CryptoProvider,
- {
- let identity_token = identity_match.identity_token;
- let raw_decrypted = self.contents.decrypt_ciphertext(arena, identity_match)?;
- let nonce = raw_decrypted.nonce;
- let remaining = raw_decrypted.plaintext_contents;
-
- let (plaintext_des, sig) = remaining
- .split_last_chunk::<{ crypto_provider::ed25519::SIGNATURE_LENGTH }>()
- .ok_or(SignatureVerificationError::SignatureMissing)?;
-
- let expected_signature = crypto_provider::ed25519::Signature::from(*sig);
-
- let section_signature_payload = SectionSignaturePayload::new(
- self.contents.format_bytes,
- self.contents.salt.bytes(),
- &nonce,
- identity_token.as_slice(),
- self.contents.total_section_contents_len,
- plaintext_des,
- );
-
- let public_key = verification_material.signature_verification_public_key();
-
- section_signature_payload.verify::<P::Ed25519>(expected_signature, &public_key).map_err(
- |e| {
- // Length of the payload should fit in the signature verification buffer.
- debug_assert!(e != np_ed25519::SignatureVerificationError::PayloadTooBig);
- SignatureVerificationError::SignatureMismatch
- },
- )?;
-
- Ok(DecryptedSection::new(
- VerificationMode::Signature,
- self.contents.salt(),
- identity_token,
- plaintext_des,
- ))
- }
-
- /// Try decrypting into some raw bytes given some raw signed crypto-material.
- #[cfg(feature = "devtools")]
- pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>(
- &self,
- allocator: &mut DeserializationArenaAllocator<'a>,
- identity_resolution_material: &SignedSectionIdentityResolutionMaterial,
- ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
- self.contents.try_resolve_identity_and_decrypt::<P>(
- allocator,
- identity_resolution_material.as_raw_resolution_material(),
- )
- }
-
- /// Try deserializing into a [Section] given some raw signed crypto-material.
- ///
- /// A less-efficient, one-shot way of getting
- /// [EncryptedSectionContents::compute_identity_resolution_contents] and then attempting
- /// deserialization.
- ///
- /// Normally, id resolution contents would be calculated for a bunch of sections, and then have
- /// many identities tried on them. This just works for one identity.
- #[cfg(test)]
- pub(crate) fn try_resolve_identity_and_deserialize<P: CryptoProvider>(
- &self,
- allocator: &mut DeserializationArenaAllocator<'a>,
- identity_resolution_material: &SignedSectionIdentityResolutionMaterial,
- verification_material: &SignedSectionVerificationMaterial,
- ) -> Result<
- DecryptedSection,
- IdentityResolutionOrDeserializationError<SignatureVerificationError>,
- > {
- match self
- .contents
- .compute_identity_resolution_contents::<P>()
- .try_match::<P>(identity_resolution_material.as_raw_resolution_material())
- {
- Some(identity_match) => self
- .try_deserialize(allocator, identity_match, verification_material)
- .map_err(|e| e.into()),
- None => Err(IdentityResolutionOrDeserializationError::IdentityMatchingError),
- }
- }
-}
-
/// An error when attempting to deserialize an encrypted advertisement,
/// assuming that we already have an identity-match.
///
@@ -375,17 +271,6 @@
/// detailed.
pub(crate) trait VerificationError: Debug + PartialEq + Eq {}
-/// An error when attempting to verify a signature
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) enum SignatureVerificationError {
- /// The provided signature did not match the calculated signature
- SignatureMismatch,
- /// The provided signature is missing
- SignatureMissing,
-}
-
-impl VerificationError for SignatureVerificationError {}
-
/// An encrypted section whose contents are verified to match a message integrity code (MIC)
#[derive(PartialEq, Eq, Debug)]
pub(crate) struct MicEncryptedSection<'a> {
@@ -417,10 +302,9 @@
let mut mic_hmac = hmac_key.build_hmac::<P>();
// if mic is ok, the section was generated by someone holding at least the shared credential
- mic_hmac.update(&NP_SVC_UUID);
mic_hmac.update(&[self.contents.adv_header.contents()]);
// section format
- mic_hmac.update(self.contents.format_bytes);
+ mic_hmac.update(&[self.contents.format_byte]);
// salt bytes
mic_hmac.update(self.contents.salt.as_slice());
// nonce
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests.rs
index 2afa41c..ad21a4e 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests.rs
@@ -16,15 +16,11 @@
use super::*;
use crate::extended::V1_IDENTITY_TOKEN_LEN;
-use np_hkdf::v1_salt::{ExtendedV1Salt, EXTENDED_SALT_LEN};
#[cfg(test)]
mod mic_decrypt_tests;
#[cfg(test)]
-mod signature_decrypt_tests;
-
-#[cfg(test)]
mod coverage_gaming;
/// An error when attempting to resolve an identity and then
@@ -54,10 +50,6 @@
}
}
-pub(crate) fn first_section_contents(after_version_header: &[u8]) -> &[u8] {
- &after_version_header[1 + EXTENDED_SALT_LEN + V1_IDENTITY_TOKEN_LEN + 1..]
-}
-
pub(crate) fn first_section_identity_token(
salt: MultiSalt,
after_version_header: &[u8],
@@ -68,21 +60,3 @@
.map(|arr: [u8; V1_IDENTITY_TOKEN_LEN]| arr.into())
.unwrap()
}
-
-pub(crate) fn first_section_format(after_version_header: &[u8]) -> &[u8] {
- // 1 byte of format comes at the beginning
- &after_version_header[..1]
-}
-
-pub(crate) fn first_section_salt(after_version_header: &[u8]) -> ExtendedV1Salt {
- // Next 16 bytes after 1 byte format
- after_version_header[1..][..EXTENDED_SALT_LEN]
- .try_into()
- .map(|arr: [u8; EXTENDED_SALT_LEN]| arr.into())
- .unwrap()
-}
-
-pub(crate) fn first_section_contents_len(after_version_header: &[u8]) -> u8 {
- // section len is the first byte after format + salt + identity token
- after_version_header[1 + EXTENDED_SALT_LEN + V1_IDENTITY_TOKEN_LEN..][0]
-}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/coverage_gaming.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/coverage_gaming.rs
index 0d8ba8f..4d3c79f 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/coverage_gaming.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/coverage_gaming.rs
@@ -15,6 +15,7 @@
#![allow(clippy::unwrap_used)]
use super::super::*;
+use crate::extended::salt::ExtendedV1Salt;
use alloc::format;
use crypto_provider_default::CryptoProviderImpl;
@@ -29,28 +30,10 @@
}
#[test]
-fn sig_encrypted_section_debug() {
- let ss = SignatureEncryptedSection {
- contents: EncryptedSectionContents::new(
- V1AdvHeader::new(0),
- &[0],
- [0x00; 16].into(),
- [0x00; V1_IDENTITY_TOKEN_LEN].into(),
- 1,
- &[0x00; 1],
- ),
- };
- let _ = format!("{:?}", ss);
-}
-
-#[test]
fn error_enum_debug_derives() {
let mic_err = MicVerificationError::MicMismatch;
let _ = format!("{:?}", mic_err);
- let sig_err = SignatureVerificationError::SignatureMissing;
- let _ = format!("{:?}", sig_err);
-
let deser_err = DeserializationError::ArenaOutOfSpace::<MicVerificationError>;
let _ = format!("{:?}", deser_err);
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/mic_decrypt_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/mic_decrypt_tests.rs
index 5d2f759..7439286 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/mic_decrypt_tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/mic_decrypt_tests.rs
@@ -21,6 +21,7 @@
deserialization_arena,
extended::{
data_elements::TxPowerDataElement,
+ de_type::{DeType, HasDEType},
deserialize::{
encrypted_section::tests::first_section_identity_token,
section::intermediate::{
@@ -28,23 +29,16 @@
},
DataElement, DataElementParseError, Section,
},
- salt::{ShortV1Salt, SHORT_SALT_LEN},
- serialize::{
- AdvBuilder, AdvertisementType, CapacityLimitedVec, MicEncryptedSectionEncoder,
- WriteDataElement,
- },
+ salt::{ExtendedV1Salt, MultiSalt, ShortV1Salt, Unsalted, SHORT_SALT_LEN},
+ serialize::{AdvBuilder, MicEncryptedSectionEncoder, WriteDataElement},
V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
- V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN,
+ V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN, V1_IDENTITY_TOKEN_LEN,
},
shared_data::TxPower,
NpVersionHeader,
};
-use crypto_provider::ed25519;
use crypto_provider_default::CryptoProviderImpl;
use np_hkdf::{v1_salt::EXTENDED_SALT_LEN, DerivedSectionKeys};
-use sink::Sink;
-
-type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
#[test]
fn deserialize_mic_encrypted_correct_keys_extended_salt() {
@@ -59,13 +53,9 @@
fn deserialize_mic_encrypted_correct_keys(salt: MultiSalt) {
let identity_token = V1IdentityToken::from([1; 16]);
let key_seed = [2; 32];
- let broadcast_cm = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cm = V1BroadcastCredential::new(key_seed, identity_token);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder
.section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
salt,
@@ -74,7 +64,7 @@
.unwrap();
let txpower_de = TxPowerDataElement::from(TxPower::try_from(5).unwrap());
- section_builder.add_de(|_| txpower_de.clone()).unwrap();
+ section_builder.add_de(&txpower_de).unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
let adv = adv_builder.into_advertisement();
@@ -92,15 +82,10 @@
let section = sections.into_iter().next().unwrap();
let enc_section = section.as_ciphertext().unwrap();
- let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
- contents
- } else {
- panic!("incorrect flavor");
- };
+ let CiphertextSection::MicEncrypted(contents) = &enc_section;
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
// deserializing to Section works
- let discovery_credential = V1BroadcastCredential::new(key_seed, identity_token, private_key)
+ let discovery_credential = V1BroadcastCredential::new(key_seed, identity_token)
.derive_discovery_credential::<CryptoProviderImpl>();
let arena = deserialization_arena!();
@@ -117,12 +102,17 @@
VerificationMode::Mic,
salt,
identity_token,
- &[txpower_de.de_header().serialize().as_slice(), &[5],].concat(),
+ // Length one, unextended De header.
+ &[0b00010000u8 + (TxPowerDataElement::DE_TYPE.as_u32() as u8), 5u8],
),
section
);
let data_elements = section.collect_data_elements().unwrap();
- assert_eq!(data_elements, &[DataElement::new(0.into(), 0x05_u8.into(), &[5])]);
+ let maybe_extended_salt = match salt {
+ MultiSalt::Short(_) => None,
+ MultiSalt::Extended(salt) => Some(salt),
+ };
+ assert_eq!(data_elements, &[DataElement::new(0x05_u8.into(), &[5], maybe_extended_salt)]);
let (_header, contents_bytes) =
remaining.split_at(1 + 1 + salt.as_slice().len() + V1_IDENTITY_TOKEN_LEN);
@@ -130,14 +120,14 @@
&MicEncryptedSection {
contents: EncryptedSectionContents {
adv_header,
- format_bytes: &[match salt {
+ format_byte: match salt {
MultiSalt::Short(_) => {
- V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN
+ V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN.0
}
MultiSalt::Extended(_) => {
- V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN
+ V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0
}
- }],
+ },
salt,
identity_token: first_section_identity_token(salt, remaining),
section_contents: &contents_bytes
@@ -176,8 +166,7 @@
let mut expected = Vec::new();
// battery de
- expected.extend_from_slice(txpower_de.clone().de_header().serialize().as_slice());
- let _ = txpower_de.write_de_contents(&mut expected);
+ let _ = txpower_de.write_de(Unsalted, &mut expected);
assert_eq!(identity_token, decrypted.identity_token);
assert_eq!(&expected, decrypted.plaintext_contents);
@@ -300,7 +289,7 @@
DeserializeError::IdentityResolutionOrDeserializationError(
IdentityResolutionOrDeserializationError::IdentityMatchingError,
),
- |_| {},
+ None,
// replace the extended salt bytes
|adv| adv[2..18].fill(0xFF),
);
@@ -312,7 +301,7 @@
do_bad_deserialize_tampered(
ExtendedV1Salt::from([3; EXTENDED_SALT_LEN]).into(),
DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
- |sec| sec.try_push(0xFF).unwrap(),
+ Some(0xFF),
|_| {},
);
}
@@ -325,7 +314,7 @@
DeserializeError::IdentityResolutionOrDeserializationError(
MicVerificationError::MicMismatch.into(),
),
- |_| {},
+ None,
|adv| {
let mic_start = adv.len() - 16;
adv[mic_start] ^= 0x01
@@ -341,7 +330,7 @@
DeserializeError::IdentityResolutionOrDeserializationError(
MicVerificationError::MicMismatch.into(),
),
- |_| {},
+ None,
|adv| {
let before_mic = adv.len() - 17;
adv[before_mic] ^= 0x01
@@ -356,7 +345,7 @@
DeserializeError::IdentityResolutionOrDeserializationError(
IdentityResolutionOrDeserializationError::IdentityMatchingError,
),
- |_| {},
+ None,
// replace the extended salt bytes
|adv| adv[2..4].fill(0xFF),
);
@@ -368,7 +357,7 @@
do_bad_deserialize_tampered(
ShortV1Salt::from([3; SHORT_SALT_LEN]).into(),
DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
- |sec| sec.try_push(0xFF).unwrap(),
+ Some(0xFF),
|_| {},
);
}
@@ -381,7 +370,7 @@
DeserializeError::IdentityResolutionOrDeserializationError(
MicVerificationError::MicMismatch.into(),
),
- |_| {},
+ None,
|adv| {
let mic_start = adv.len() - 16;
adv[mic_start] ^= 0x01
@@ -397,7 +386,7 @@
DeserializeError::IdentityResolutionOrDeserializationError(
MicVerificationError::MicMismatch.into(),
),
- |_| {},
+ None,
|adv| {
let before_mic = adv.len() - 17;
adv[before_mic] ^= 0x01
@@ -410,13 +399,9 @@
let identity_token = V1IdentityToken::from([1; 16]);
let key_seed = [2; 32];
let section_salt = ExtendedV1Salt::from([3; 16]);
- let broadcast_cm = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cm = V1BroadcastCredential::new(key_seed, identity_token);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder
.section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
section_salt,
@@ -425,7 +410,7 @@
.unwrap();
let txpower_de = TxPowerDataElement::from(TxPower::try_from(5).unwrap());
- section_builder.add_de(|_| txpower_de.clone()).unwrap();
+ section_builder.add_de(&txpower_de).unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
let adv = adv_builder.into_advertisement();
@@ -443,15 +428,10 @@
let section = sections.into_iter().next().unwrap();
let enc_section = section.as_ciphertext().unwrap();
- let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
- contents
- } else {
- panic!("incorrect flavor");
- };
+ let CiphertextSection::MicEncrypted(contents) = &enc_section;
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
// deserializing to Section works
- let discovery_credential = V1BroadcastCredential::new(key_seed, identity_token, private_key)
+ let discovery_credential = V1BroadcastCredential::new(key_seed, identity_token)
.derive_discovery_credential::<CryptoProviderImpl>();
let arena = deserialization_arena!();
@@ -480,13 +460,9 @@
let key_seed = [2; 32];
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
- let broadcast_cm = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cm = V1BroadcastCredential::new(key_seed, identity_token);
let mut section_builder = adv_builder
.section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
@@ -495,7 +471,7 @@
))
.unwrap();
- section_builder.add_de(|_| TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
+ section_builder.add_de(&TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
@@ -514,11 +490,7 @@
let section = sections.into_iter().next().unwrap();
let enc_section = section.as_ciphertext().unwrap();
- let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
- contents
- } else {
- panic!("incorrect flavor");
- };
+ let CiphertextSection::MicEncrypted(contents) = &enc_section;
// start with correct crypto material
let mut crypto_material = V1DiscoveryCredential::new(
@@ -531,12 +503,6 @@
.v1_mic_extended_salt_keys()
.identity_token_hmac_key()
.calculate_hmac::<C>(&identity_token.0),
- key_seed_hkdf
- .v1_signature_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<C>(&identity_token.0),
- crypto_provider::ed25519::PrivateKey::generate::<C::Ed25519>()
- .derive_public_key::<C::Ed25519>(),
)
.to_precalculated::<C>();
@@ -565,19 +531,15 @@
fn do_bad_deserialize_tampered(
salt: MultiSalt,
expected_error: DeserializeError,
- mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
+ mangle_section_extra_byte: Option<u8>,
mangle_adv: impl Fn(&mut Vec<u8>),
) {
let metadata_key = V1IdentityToken([1; 16]);
let key_seed = [2; 32];
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
- let broadcast_cm = V1BroadcastCredential::new(
- key_seed,
- metadata_key,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cm = V1BroadcastCredential::new(key_seed, metadata_key);
let mut section_builder = adv_builder
.section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
@@ -586,9 +548,17 @@
))
.unwrap();
- section_builder.add_de(|_| TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
+ section_builder.add_de(&TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
- mangle_section(&mut section_builder.section);
+ if let Some(mangle_section_extra_byte) = mangle_section_extra_byte {
+ section_builder
+ .section
+ .push_slice(
+ DeType::try_from(0xFFFFFFFEu32).unwrap().into(),
+ &[mangle_section_extra_byte],
+ )
+ .unwrap();
+ }
section_builder.add_to_advertisement::<CryptoProviderImpl>();
@@ -609,17 +579,9 @@
let section = sections.into_iter().next().unwrap();
let enc_section = section.as_ciphertext().unwrap();
- let contents = if let CiphertextSection::MicEncrypted(contents) = &enc_section {
- contents
- } else {
- panic!("incorrect flavor");
- };
+ let CiphertextSection::MicEncrypted(contents) = &enc_section;
- // generate a random key pair since we need _some_ public key in our discovery
- // credential, even if it winds up going unused
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
-
- let discovery_credential = V1BroadcastCredential::new(key_seed, metadata_key, private_key)
+ let discovery_credential = V1BroadcastCredential::new(key_seed, metadata_key)
.derive_discovery_credential::<CryptoProviderImpl>();
match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/signature_decrypt_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/signature_decrypt_tests.rs
deleted file mode 100644
index d843cb0..0000000
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/tests/signature_decrypt_tests.rs
+++ /dev/null
@@ -1,589 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// Licensed under the Apache License, Version 2.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.
-
-#![allow(clippy::unwrap_used)]
-
-extern crate std;
-
-use super::*;
-use crate::{
- deserialization_arena,
- extended::{
- data_elements::TxPowerDataElement,
- deserialize::{
- encrypted_section,
- section::intermediate::{
- parse_sections, tests::IntermediateSectionExt, CiphertextSection,
- },
- DataElement, DataElementParseError, Section,
- },
- serialize::{
- AdvBuilder, AdvertisementType, CapacityLimitedVec, SignedEncryptedSectionEncoder,
- SingleTypeDataElement, WriteDataElement,
- },
- V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN,
- },
- shared_data::TxPower,
- NpVersionHeader,
-};
-use crypto_provider::ed25519;
-use crypto_provider_default::CryptoProviderImpl;
-use np_hkdf::{v1_salt, DerivedSectionKeys};
-use sink::Sink;
-use std::{prelude::rust_2021::*, vec};
-
-type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
-
-#[test]
-fn deserialize_signature_encrypted_correct_keys_extended_salt() {
- let identity_token = V1IdentityToken::from([1; 16]);
- let key_seed = [2; 32];
- let section_salt = ExtendedV1Salt::from([3; 16]);
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
-
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
- let broadcast_cm = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
-
- let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new::<CryptoProviderImpl>(
- section_salt,
- &broadcast_cm,
- ))
- .unwrap();
-
- let txpower_de = TxPowerDataElement::from(TxPower::try_from(7).unwrap());
- section_builder.add_de(|_| txpower_de.clone()).unwrap();
-
- section_builder.add_to_advertisement::<CryptoProviderImpl>();
-
- let adv = adv_builder.into_advertisement();
-
- let (after_version_header, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
-
- let adv_header = if let NpVersionHeader::V1(h) = header {
- h
- } else {
- panic!("incorrect header");
- };
-
- let sections = parse_sections(adv_header, after_version_header).unwrap();
- assert_eq!(1, sections.len());
-
- let section = sections.into_iter().next().unwrap();
- let enc_section = section.as_ciphertext().unwrap();
- let contents = if let CiphertextSection::SignatureEncrypted(contents) = &enc_section {
- contents
- } else {
- panic!("incorrect flavor");
- };
-
- assert_eq!(
- &SignatureEncryptedSection {
- contents: EncryptedSectionContents {
- adv_header,
- format_bytes: &[V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN],
- salt: section_salt,
- identity_token: first_section_identity_token(
- section_salt.into(),
- after_version_header
- ),
- // adv header + section len + format + salt + identity token
- section_contents: first_section_contents(after_version_header),
- total_section_contents_len: contents.contents.total_section_contents_len
- },
- },
- contents
- );
-
- let mut de_bytes = Vec::new();
- de_bytes.extend_from_slice(txpower_de.de_header().serialize().as_slice());
- let _ = txpower_de.write_de_contents(&mut de_bytes);
- let de_bytes = de_bytes;
-
- // plaintext is correct
- {
- let credential = V1DiscoveryCredential::new(
- key_seed,
- key_seed_hkdf
- .v1_mic_short_salt_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
- key_seed_hkdf
- .v1_mic_extended_salt_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
- key_seed_hkdf
- .v1_signature_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
- private_key.derive_public_key::<Ed25519ProviderImpl>(),
- );
- let signed_identity_resolution_material =
- credential.signed_identity_resolution_material::<CryptoProviderImpl>();
- let identity_resolution_contents =
- contents.contents.compute_identity_resolution_contents::<CryptoProviderImpl>();
- let identity_match = identity_resolution_contents
- .try_match::<CryptoProviderImpl>(
- &signed_identity_resolution_material.into_raw_resolution_material(),
- )
- .unwrap();
-
- let arena = deserialization_arena!();
- let mut allocator = arena.into_allocator();
- let decrypted =
- contents.contents.decrypt_ciphertext(&mut allocator, identity_match).unwrap();
-
- let nonce: AesCtrNonce = section_salt.compute_nonce::<CryptoProviderImpl>();
-
- let sig_payload = SectionSignaturePayload::new(
- &[V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN],
- section_salt.as_slice(),
- &nonce,
- identity_token.as_slice(),
- (de_bytes.len() + crypto_provider::ed25519::SIGNATURE_LENGTH).try_into().unwrap(),
- &de_bytes,
- );
-
- let mut de_and_sig = de_bytes.clone();
- de_and_sig
- .extend_from_slice(&sig_payload.sign::<Ed25519ProviderImpl>(&private_key).to_bytes());
- assert_eq!(identity_token, decrypted.identity_token);
- assert_eq!(nonce, decrypted.nonce);
- assert_eq!(&de_and_sig, decrypted.plaintext_contents);
- }
-
- // deserialization to Section works
- {
- let credential = V1DiscoveryCredential::new(
- key_seed,
- key_seed_hkdf
- .v1_mic_short_salt_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
- key_seed_hkdf
- .v1_mic_extended_salt_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
- key_seed_hkdf
- .v1_signature_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
- private_key.derive_public_key::<Ed25519ProviderImpl>(),
- );
- let signed_identity_resolution_material =
- credential.signed_identity_resolution_material::<CryptoProviderImpl>();
- let signed_verification_material =
- credential.signed_verification_material::<CryptoProviderImpl>();
-
- let arena = deserialization_arena!();
- let mut allocator = arena.into_allocator();
- let section = contents
- .try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
- &mut allocator,
- &signed_identity_resolution_material,
- &signed_verification_material,
- )
- .unwrap();
-
- assert_eq!(
- DecryptedSection::new(
- VerificationMode::Signature,
- section_salt.into(),
- identity_token,
- &de_bytes,
- ),
- section
- );
- let data_elements = section.collect_data_elements().unwrap();
- assert_eq!(data_elements, &[DataElement::new(0.into(), 0x05_u8.into(), &[7])]);
-
- assert_eq!(
- vec![(v1_salt::DataElementOffset::from(0_u8), TxPowerDataElement::DE_TYPE, vec![7u8])],
- data_elements
- .into_iter()
- .map(|de| (de.offset(), de.de_type(), de.contents().to_vec()))
- .collect::<Vec<_>>()
- );
- }
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_aes_key_error() {
- // bad aes key -> bad metadata key plaintext
- do_bad_deserialize_params::<CryptoProviderImpl>(
- IdentityResolutionOrDeserializationError::IdentityMatchingError,
- Some([0xFF; 16].into()),
- None,
- None,
- None,
- );
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_metadata_key_hmac_key_error() {
- // bad metadata key hmac key -> bad calculated metadata key mac
- do_bad_deserialize_params::<CryptoProviderImpl>(
- IdentityResolutionOrDeserializationError::IdentityMatchingError,
- None,
- Some([0xFF; 32].into()),
- None,
- None,
- );
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_expected_metadata_key_hmac_error() {
- // bad expected metadata key mac
- do_bad_deserialize_params::<CryptoProviderImpl>(
- IdentityResolutionOrDeserializationError::IdentityMatchingError,
- None,
- None,
- Some([0xFF; 32]),
- None,
- );
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_pub_key_error() {
- // a random pub key will lead to signature mismatch
- do_bad_deserialize_params::<CryptoProviderImpl>(
- SignatureVerificationError::SignatureMismatch.into(),
- None,
- None,
- None,
- Some(
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>()
- .derive_public_key::<Ed25519ProviderImpl>(),
- ),
- );
-}
-
-#[test]
-fn deserialize_signature_encrypted_incorrect_salt_error() {
- // bad salt -> bad iv -> bad metadata key plaintext
- do_bad_deserialize_tampered(
- DeserializeError::IdentityResolutionOrDeserializationError(
- IdentityResolutionOrDeserializationError::IdentityMatchingError,
- ),
- None,
- |_| {},
- |adv_mut| {
- adv_mut[1 + 1 + 1..][..EXTENDED_SALT_LEN].copy_from_slice(&[0xFF; EXTENDED_SALT_LEN])
- },
- )
-}
-
-#[test]
-fn deserialize_signature_encrypted_tampered_signature_error() {
- do_bad_deserialize_tampered(
- DeserializeError::IdentityResolutionOrDeserializationError(
- SignatureVerificationError::SignatureMismatch.into(),
- ),
- None,
- |_| {},
- // flip a bit in the middle of the signature
- |adv_mut| {
- let len = adv_mut.len();
- adv_mut[len - 30] ^= 0x1
- },
- )
-}
-
-#[test]
-fn deserialize_signature_encrypted_tampered_ciphertext_error() {
- do_bad_deserialize_tampered(
- DeserializeError::IdentityResolutionOrDeserializationError(
- SignatureVerificationError::SignatureMismatch.into(),
- ),
- None,
- |_| {},
- // flip a bit outside of the signature
- |adv_mut| {
- let len = adv_mut.len();
- adv_mut[len - 1 - 64] ^= 0x1
- },
- )
-}
-
-#[test]
-fn deserialize_signature_encrypted_missing_signature_error() {
- let de_len = 2;
- do_bad_deserialize_tampered(
- DeserializeError::IdentityResolutionOrDeserializationError(
- SignatureVerificationError::SignatureMissing.into(),
- ),
- Some(de_len as u8),
- |_| {},
- |adv_mut| {
- // chop off signature
- adv_mut.truncate(adv_mut.len() - 64);
- // fix section length
- adv_mut[1 + 1 + EXTENDED_SALT_LEN + V1_IDENTITY_TOKEN_LEN..][0] = de_len as u8;
- },
- )
-}
-
-#[test]
-fn deserialize_signature_encrypted_des_wont_parse() {
- do_bad_deserialize_tampered(
- DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
- Some(2 + 1 + crypto_provider::ed25519::SIGNATURE_LENGTH as u8),
- // add an impossible DE
- |section| {
- section.try_push(0xFF).unwrap();
- },
- |_| {},
- )
-}
-
-#[test]
-fn arena_out_of_space_on_signature_verify() {
- let salt: ExtendedV1Salt = [0; 16].into();
- let nonce = salt.compute_nonce::<CryptoProviderImpl>();
- let sig_enc_section = SignatureEncryptedSection {
- contents: EncryptedSectionContents::new(
- V1AdvHeader::new(0),
- &[0],
- [0; 16].into(),
- [0x00; V1_IDENTITY_TOKEN_LEN].into(),
- 1,
- &[0x00; 1],
- ),
- };
-
- let arena = deserialization_arena!();
- let mut allocator = arena.into_allocator();
- let _ = allocator.allocate(250).unwrap();
-
- let cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
- &[0u8; 16].into(),
- NonceAndCounter::from_nonce(nonce),
- );
- let im: encrypted_section::IdentityMatch<CryptoProviderImpl> =
- IdentityMatch { cipher, identity_token: V1IdentityToken([0; 16]), nonce };
-
- let signed_verification_material = SignedSectionVerificationMaterial {
- public_key: ed25519::PublicKey::from_bytes::<Ed25519ProviderImpl>([0u8; 32])
- .expect("public key should be valid bytes"),
- };
-
- let _ = sig_enc_section.try_deserialize(&mut allocator, im, &signed_verification_material);
-}
-
-/// Attempt a deserialization that will fail when using the provided parameters for decryption only.
-/// `None` means use the correct value for that parameter.
-fn do_bad_deserialize_params<C: CryptoProvider>(
- error: IdentityResolutionOrDeserializationError<SignatureVerificationError>,
- aes_key: Option<crypto_provider::aes::Aes128Key>,
- metadata_key_hmac_key: Option<np_hkdf::NpHmacSha256Key>,
- expected_metadata_key_hmac: Option<[u8; 32]>,
- pub_key: Option<ed25519::PublicKey>,
-) {
- let metadata_key = V1IdentityToken([1; 16]);
- let key_seed = [2; 32];
- let section_salt: v1_salt::ExtendedV1Salt = [3; 16].into();
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
- let private_key = ed25519::PrivateKey::generate::<C::Ed25519>();
-
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
- let broadcast_cm = V1BroadcastCredential::new(key_seed, metadata_key, private_key.clone());
-
- let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new::<C>(section_salt, &broadcast_cm))
- .unwrap();
-
- section_builder.add_de_res(|_| TxPower::try_from(2).map(TxPowerDataElement::from)).unwrap();
-
- section_builder.add_to_advertisement::<CryptoProviderImpl>();
-
- let adv = adv_builder.into_advertisement();
-
- let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
-
- let v1_header = if let NpVersionHeader::V1(h) = header {
- h
- } else {
- panic!("incorrect header");
- };
-
- let sections = parse_sections(v1_header, remaining).unwrap();
- assert_eq!(1, sections.len());
-
- let section = sections.into_iter().next().unwrap();
- let enc_section = section.as_ciphertext().unwrap();
- let contents = if let CiphertextSection::SignatureEncrypted(contents) = &enc_section {
- contents
- } else {
- panic!("incorrect flavor");
- };
-
- let signed_identity_resolution_material =
- SignedSectionIdentityResolutionMaterial::from_raw(SectionIdentityResolutionMaterial {
- aes_key: aes_key.unwrap_or_else(|| key_seed_hkdf.v1_signature_keys().aes_key()),
-
- identity_token_hmac_key: *metadata_key_hmac_key
- .unwrap_or_else(|| key_seed_hkdf.v1_signature_keys().identity_token_hmac_key())
- .as_bytes(),
- expected_identity_token_hmac: expected_metadata_key_hmac.unwrap_or_else(|| {
- key_seed_hkdf
- .v1_signature_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&metadata_key.0)
- }),
- });
-
- let signed_verification_material = SignedSectionVerificationMaterial {
- public_key: pub_key
- .unwrap_or_else(|| private_key.derive_public_key::<Ed25519ProviderImpl>()),
- };
-
- assert_eq!(
- error,
- contents
- .try_resolve_identity_and_deserialize::<C>(
- &mut deserialization_arena!().into_allocator(),
- &signed_identity_resolution_material,
- &signed_verification_material,
- )
- .unwrap_err()
- );
-}
-
-#[derive(Debug, PartialEq)]
-enum DeserializeError {
- IdentityResolutionOrDeserializationError(
- IdentityResolutionOrDeserializationError<SignatureVerificationError>,
- ),
- DataElementParseError(DataElementParseError),
-}
-
-/// Run a test that mangles the advertisement contents before attempting to deserialize.
-///
-/// The section will have a TxPower DE added.
-fn do_bad_deserialize_tampered(
- expected_error: DeserializeError,
- expected_section_de_len: Option<u8>,
- mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
- mangle_adv_contents: impl Fn(&mut Vec<u8>),
-) {
- let identity_token = V1IdentityToken::from([1; V1_IDENTITY_TOKEN_LEN]);
- let key_seed = [2; 32];
- let section_salt: v1_salt::ExtendedV1Salt = [3; EXTENDED_SALT_LEN].into();
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
-
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
- let broadcast_cm = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
-
- let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new::<CryptoProviderImpl>(
- section_salt,
- &broadcast_cm,
- ))
- .unwrap();
-
- section_builder.add_de_res(|_| TxPower::try_from(2).map(TxPowerDataElement::from)).unwrap();
-
- mangle_section(&mut section_builder.section);
-
- section_builder.add_to_advertisement::<CryptoProviderImpl>();
-
- let adv = adv_builder.into_advertisement();
- let mut adv_mut = adv.as_slice().to_vec();
- mangle_adv_contents(&mut adv_mut);
-
- let (after_version_header, header) = NpVersionHeader::parse(&adv_mut).unwrap();
-
- let adv_header = if let NpVersionHeader::V1(h) = header {
- h
- } else {
- panic!("incorrect header");
- };
-
- let sections = parse_sections(adv_header, after_version_header).unwrap();
- assert_eq!(1, sections.len());
-
- let section = sections.into_iter().next().unwrap();
- let enc_section = section.as_ciphertext().unwrap();
- let contents = if let CiphertextSection::SignatureEncrypted(contents) = &enc_section {
- contents
- } else {
- panic!("incorrect flavor");
- };
- if let Some(len) = expected_section_de_len {
- assert_eq!(usize::from(len), contents.contents.section_contents.len());
- }
-
- let extracted_salt = first_section_salt(after_version_header);
- assert_eq!(
- &SignatureEncryptedSection {
- contents: EncryptedSectionContents {
- adv_header,
- // extract data from adv buffer in case the caller tampered with things
- salt: extracted_salt,
- identity_token: first_section_identity_token(
- extracted_salt.into(),
- after_version_header
- ),
- section_contents: first_section_contents(after_version_header),
- format_bytes: first_section_format(after_version_header),
- total_section_contents_len: first_section_contents_len(after_version_header),
- },
- },
- contents
- );
-
- let credential = V1DiscoveryCredential::new(
- key_seed,
- key_seed_hkdf
- .v1_mic_short_salt_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
- key_seed_hkdf
- .v1_mic_extended_salt_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
- key_seed_hkdf
- .v1_signature_keys()
- .identity_token_hmac_key()
- .calculate_hmac::<CryptoProviderImpl>(&identity_token.0),
- private_key.derive_public_key::<Ed25519ProviderImpl>(),
- );
- let identity_resolution_material =
- credential.signed_identity_resolution_material::<CryptoProviderImpl>();
- let verification_material = credential.signed_verification_material::<CryptoProviderImpl>();
-
- match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
- &mut deserialization_arena!().into_allocator(),
- &identity_resolution_material,
- &verification_material,
- ) {
- Ok(section) => {
- assert_eq!(
- expected_error,
- DeserializeError::DataElementParseError(
- section.collect_data_elements().unwrap_err()
- ),
- );
- }
- Err(e) => assert_eq!(
- expected_error,
- DeserializeError::IdentityResolutionOrDeserializationError(e),
- ),
- };
-}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
index 123cee6..ca6ba72 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
@@ -28,10 +28,12 @@
deserialization_arena::{ArenaOutOfSpace, DeserializationArena, DeserializationArenaAllocator},
extended::{
deserialize::{
- data_element::{DataElement, DataElementParseError, DataElementParsingIterator},
+ data_element::{
+ DataElementDeserializationResult, DataElementDeserializingIterator,
+ DataElementParsingIterator,
+ },
encrypted_section::{
DeserializationError, MicVerificationError, SectionIdentityResolutionContents,
- SignatureVerificationError,
},
section::intermediate::{
parse_sections, CiphertextSection, IntermediateSection, PlaintextSection,
@@ -43,6 +45,10 @@
header::V1AdvHeader,
AdvDeserializationError, AdvDeserializationErrorDetailsHazmat,
};
+
+#[cfg(any(test, feature = "alloc"))]
+use crate::extended::deserialize::data_element::{DataElement, DataElementParseError};
+
use crypto_provider::CryptoProvider;
#[cfg(test)]
@@ -50,7 +56,8 @@
pub mod data_element;
pub(crate) mod encrypted_section;
-pub(crate) mod section;
+/// Define Section of Presence V1 Spec.
+pub mod section;
/// Provides deserialization APIs which expose more of the internals, suitable for use only in
/// dev tools.
@@ -69,9 +76,7 @@
P: CryptoProvider,
{
let mut sections_in_processing =
- SectionsInProcessing::<'_, B::Matched>::from_advertisement_contents::<P>(
- header, remaining,
- )?;
+ SectionsInProcessing::<'_, B::Matched>::from_advertisement_contents(header, remaining)?;
let mut allocator = arena.into_allocator();
@@ -95,13 +100,16 @@
/// A section deserialized from a V1 advertisement.
pub trait Section<'adv> {
- /// The iterator type used to iterate over data elements
- type Iterator: Iterator<Item = Result<DataElement<'adv>, DataElementParseError>>;
+ /// Iterator over the data elements in a section.
+ fn iter_data_elements(&self) -> DataElementParsingIterator<'adv>;
- /// Iterator over the data elements in a section, except for any DEs related to resolving the
- /// identity or otherwise validating the payload (e.g. MIC, Signature, any identity DEs like
- /// Private Identity).
- fn iter_data_elements(&self) -> Self::Iterator;
+ /// An iterator which attempts to deserialize the data elements
+ /// in a section to some target type implementing [`DataElementDeserializationResult`].
+ fn iter_deserialize_data_elements<F: DataElementDeserializationResult<'adv>>(
+ &self,
+ ) -> DataElementDeserializingIterator<'adv, F> {
+ DataElementDeserializingIterator::<'adv, F>::from(self.iter_data_elements())
+ }
/// Collects the data elements into a vector, eagerly catching and resolving any errors during
/// parsing.
@@ -155,7 +163,7 @@
}
}
-impl<'adv> HasIdentityMatch for DecryptedSection<'adv> {
+impl HasIdentityMatch for DecryptedSection<'_> {
type Version = V1;
fn identity_token(&self) -> V1IdentityToken {
self.identity_token
@@ -163,10 +171,12 @@
}
impl<'adv> Section<'adv> for DecryptedSection<'adv> {
- type Iterator = DataElementParsingIterator<'adv>;
-
- fn iter_data_elements(&self) -> Self::Iterator {
- DataElementParsingIterator::new(self.plaintext)
+ fn iter_data_elements(&self) -> DataElementParsingIterator<'adv> {
+ let maybe_extended_salt = match self.salt() {
+ MultiSalt::Extended(x) => Some(x),
+ _ => None,
+ };
+ DataElementParsingIterator::new(self.plaintext, maybe_extended_salt.copied())
}
}
@@ -193,29 +203,26 @@
}
}
-impl From<DeserializationError<SignatureVerificationError>> for SectionDeserializeError {
- fn from(
- signature_deserialization_error: DeserializationError<SignatureVerificationError>,
- ) -> Self {
- match signature_deserialization_error {
- DeserializationError::VerificationError(
- SignatureVerificationError::SignatureMissing,
- ) => Self::ParseError,
- DeserializationError::VerificationError(
- SignatureVerificationError::SignatureMismatch,
- ) => Self::IncorrectCredential,
- DeserializationError::ArenaOutOfSpace => Self::ArenaOutOfSpace,
- }
- }
+/// A ciphertext section which has not yet been resolved to an identity. This will cache the
+/// calculation of `SectionIdentityResolutionContents` on first use for speedy identity-resolution.
+struct ResolvableCiphertextSection<'a> {
+ identity_resolution_contents: Option<SectionIdentityResolutionContents>,
+ ciphertext_section: CiphertextSection<'a>,
}
-/// A ciphertext section which has not yet been
-/// resolved to an identity, but for which some
-/// `SectionIdentityResolutionContents` have been
-/// pre-computed for speedy identity-resolution.
-struct ResolvableCiphertextSection<'a> {
- identity_resolution_contents: SectionIdentityResolutionContents,
- ciphertext_section: CiphertextSection<'a>,
+impl<'a> ResolvableCiphertextSection<'a> {
+ fn from_section(ciphertext_section: CiphertextSection<'a>) -> Self {
+ Self { identity_resolution_contents: None, ciphertext_section }
+ }
+
+ /// Get the identity resolution contents. They will be calculated on the first call to this
+ /// method and cached for further calls.
+ fn identity_resolution_contents<C: CryptoProvider>(
+ &mut self,
+ ) -> &SectionIdentityResolutionContents {
+ self.identity_resolution_contents
+ .get_or_insert_with(|| self.ciphertext_section.identity_resolution_contents::<C>())
+ }
}
/// A collection of possibly-deserialized sections which are separated according
@@ -236,7 +243,7 @@
/// Attempts to parse a V1 advertisement's contents after the version header
/// into a collection of not-yet-fully-deserialized sections which may
/// require credentials to be decrypted.
- fn from_advertisement_contents<C: CryptoProvider>(
+ fn from_advertisement_contents(
header: V1AdvHeader,
remaining: &'adv [u8],
) -> Result<Self, AdvDeserializationError> {
@@ -253,13 +260,8 @@
deserialized_sections.push((idx, V1DeserializedSection::Plaintext(p)))
}
IntermediateSection::Ciphertext(ciphertext_section) => {
- let identity_resolution_contents =
- ciphertext_section.identity_resolution_contents::<C>();
- let resolvable_ciphertext_section = ResolvableCiphertextSection {
- identity_resolution_contents,
- ciphertext_section,
- };
- encrypted_sections.push((idx, resolvable_ciphertext_section));
+ encrypted_sections
+ .push((idx, ResolvableCiphertextSection::from_section(ciphertext_section)));
}
}
}
@@ -283,10 +285,8 @@
) -> Result<(), ArenaOutOfSpace> {
let mut i = 0;
while i < self.encrypted_sections.len() {
- let (section_idx, section): &(usize, ResolvableCiphertextSection) =
- &self.encrypted_sections[i];
+ let (section_idx, ref mut section) = self.encrypted_sections[i];
// Fast-path: Check for an identity match, ignore if there's no identity match.
- let identity_resolution_contents = §ion.identity_resolution_contents;
let identity_resolution_material = match §ion.ciphertext_section {
CiphertextSection::MicEncrypted(m) => match m.contents.salt {
MultiSalt::Short(_) => crypto_material
@@ -296,12 +296,11 @@
.mic_extended_salt_identity_resolution_material::<P>()
.into_raw_resolution_material(),
},
-
- CiphertextSection::SignatureEncrypted(_) => crypto_material
- .signed_identity_resolution_material::<P>()
- .into_raw_resolution_material(),
};
- match identity_resolution_contents.try_match::<P>(&identity_resolution_material) {
+ match section
+ .identity_resolution_contents::<P>()
+ .try_match::<P>(&identity_resolution_material)
+ {
None => {
// Try again with another section
i += 1;
@@ -311,13 +310,6 @@
// The identity matched, so now we need to more closely scrutinize
// the provided ciphertext. Try to decrypt and parse the section.
let deserialization_result = match §ion.ciphertext_section {
- CiphertextSection::SignatureEncrypted(c) => c
- .try_deserialize(
- arena,
- identity_match,
- &crypto_material.signed_verification_material::<P>(),
- )
- .map_err(SectionDeserializeError::from),
CiphertextSection::MicEncrypted(c) => c
.try_deserialize(arena, identity_match, &crypto_material)
.map_err(SectionDeserializeError::from),
@@ -325,7 +317,7 @@
match deserialization_result {
Ok(s) => {
self.deserialized_sections.push((
- *section_idx,
+ section_idx,
V1DeserializedSection::Decrypted(WithMatchedCredential::new(
match_data.clone(),
crypto_material.metadata_nonce::<P>(),
@@ -426,9 +418,7 @@
// Allow easy DE iteration if the user doesn't care which kind of section it is
impl<'adv, M: MatchedCredential> Section<'adv> for V1DeserializedSection<'adv, M> {
- type Iterator = DataElementParsingIterator<'adv>;
-
- fn iter_data_elements(&self) -> Self::Iterator {
+ fn iter_data_elements(&self) -> DataElementParsingIterator<'adv> {
match self {
V1DeserializedSection::Plaintext(p) => p.iter_data_elements(),
V1DeserializedSection::Decrypted(d) => d.contents().iter_data_elements(),
@@ -444,11 +434,6 @@
/// Since this is a symmetric operation, holders of the key material needed to verify a MIC
/// can also forge MICs.
Mic,
- /// An asymmetric signature was verified.
- ///
- /// Since this is an asymmetric operation, only the holder of the private key can generate
- /// signatures, so it offers a stronger level of authenticity protection than [Self::Mic].
- Signature,
}
#[derive(PartialEq, Eq, Debug)]
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section/header/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/section/header/mod.rs
index cb6a955..1f04968 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/section/header/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/header/mod.rs
@@ -15,18 +15,19 @@
//! High-level, early-stage parsed structures for a section: the header, then everything else.
use crate::extended::{
- deserialize, salt::ShortV1Salt, V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
- V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN,
- V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
+ salt::ShortV1Salt, EncodingType, V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
+ V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
V1_IDENTITY_TOKEN_LEN,
};
use crate::helpers::parse_byte_array;
-use nom::{combinator, error, number, sequence};
+use nom::{combinator, number, sequence};
use np_hkdf::v1_salt::{ExtendedV1Salt, EXTENDED_SALT_LEN};
#[cfg(test)]
mod tests;
+/// A successfully-parsed (pre-length) section header
+/// for a known encoding type.
#[derive(PartialEq, Eq, Debug)]
pub(crate) enum SectionHeader {
Unencrypted,
@@ -34,79 +35,55 @@
}
impl SectionHeader {
- /// Returns the parsed header and the remaining length of the section contents
+ /// Gets the 4-bit encoding ID of the encoding to be employed
+ /// according to the section header.
+ pub(crate) fn encoding_type(&self) -> EncodingType {
+ match self {
+ Self::Unencrypted => V1_ENCODING_UNENCRYPTED,
+ Self::Encrypted(x) => x.encoding_type(),
+ }
+ }
+ /// Attempts to parse a section header from the given bytes.
///
- /// This structure makes it easy to get a slice of the entire header with
- /// [combinator::consumed] for later inclusion in signatures etc, but also
- /// to [nom::bytes::complete::take] the rest of the section with a minimum of
- /// error-prone length calculations.
- pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], (&[u8], Self, u8)> {
- // Consume first 1-2 bytes of format
- // 0bERRRSSSS first header byte
- let (input, (format_bytes, (first_header_byte, maybe_second_byte))) =
- combinator::consumed(nom::branch::alt((
- // Extended bit not set, verify all reserved bits are 0
- combinator::map(
- combinator::verify(number::complete::u8, |b| {
- !deserialize::hi_bit_set(*b) && (*b & 0x70) == 0
- }),
- |b| (b, None),
- ),
- // Extended bit is set, take another byte and verify all reserved bits are 0
- combinator::map(
- nom::sequence::pair(
- combinator::verify(number::complete::u8, |b| {
- deserialize::hi_bit_set(*b) && (*b & 0x70) == 0
- }),
- combinator::verify(number::complete::u8, |second_byte| *second_byte == 0u8),
- ),
- |(b1, b2)| (b1, Some(b2)),
- ),
- )))(input)?;
-
- let encoding = first_header_byte & 0x0F;
-
- let (input, section_header) = match (encoding, maybe_second_byte) {
- (V1_ENCODING_UNENCRYPTED, None) => Ok((input, (SectionHeader::Unencrypted))),
- (V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN, None) => combinator::map(
- sequence::tuple((ShortV1Salt::parse, CiphertextExtendedIdentityToken::parse)),
- |(salt, token)| {
- SectionHeader::Encrypted(EncryptedSectionHeader::MicShortSalt { salt, token })
- },
- )(input),
- (V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN, None) => combinator::map(
- sequence::tuple((parse_v1_extended_salt, CiphertextExtendedIdentityToken::parse)),
- |(salt, token)| {
- SectionHeader::Encrypted(EncryptedSectionHeader::MicExtendedSalt {
- salt,
- token,
- })
- },
- )(input),
- (V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, None) => {
- combinator::map(
- sequence::tuple((
- parse_v1_extended_salt,
- CiphertextExtendedIdentityToken::parse,
- )),
- |(salt, token)| {
- SectionHeader::Encrypted(EncryptedSectionHeader::SigExtendedSalt {
- salt,
- token,
- })
- },
- )(input)
- }
- _ => Err(nom::Err::Error(error::Error::new(input, error::ErrorKind::Alt))),
- }?;
-
- //finally parse the section payload length, this is the same regardless of encoding scheme
- let (input, section_len) = combinator::verify(number::complete::u8, |b| {
- // length cannot be 0 for an unencrypted section as this is meaningless
- !(encoding == V1_ENCODING_UNENCRYPTED && *b == 0u8)
+ /// If the encoding type of the section header is recognized,
+ /// this method will return:
+ /// - The entire parsed section header if the formatting was valid.
+ /// - A nom parser error if the formatting was invalid.
+ ///
+ /// If the encoding type is unrecognized, this parser will
+ /// succeed, but it will only consume a single byte and
+ /// the result will be an `Err(EncodingType)` containing
+ /// the unrecognized encoding type.
+ pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], Result<Self, EncodingType>> {
+ // 0bRRRRSSSS first header byte expressing the section encoding.
+ let (input, encoding) = combinator::verify(number::complete::u8, |b| {
+ // Verify all reserved bits are zero.
+ (*b & 0xF0) == 0
})(input)?;
- Ok((input, (format_bytes, section_header, section_len)))
+ let encoding = EncodingType(encoding);
+ match encoding {
+ V1_ENCODING_UNENCRYPTED => Ok((input, Ok(SectionHeader::Unencrypted))),
+ V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN => combinator::map(
+ sequence::tuple((ShortV1Salt::parse, CiphertextExtendedIdentityToken::parse)),
+ |(salt, token)| {
+ Ok(SectionHeader::Encrypted(EncryptedSectionHeader::MicShortSalt {
+ salt,
+ token,
+ }))
+ },
+ )(input),
+ V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN => combinator::map(
+ sequence::tuple((parse_v1_extended_salt, CiphertextExtendedIdentityToken::parse)),
+ |(salt, token)| {
+ Ok(SectionHeader::Encrypted(EncryptedSectionHeader::MicExtendedSalt {
+ salt,
+ token,
+ }))
+ },
+ )(input),
+ _ => Ok((input, Err(encoding))),
+ }
}
}
@@ -119,7 +96,20 @@
pub(crate) enum EncryptedSectionHeader {
MicShortSalt { salt: ShortV1Salt, token: CiphertextExtendedIdentityToken },
MicExtendedSalt { salt: ExtendedV1Salt, token: CiphertextExtendedIdentityToken },
- SigExtendedSalt { salt: ExtendedV1Salt, token: CiphertextExtendedIdentityToken },
+}
+impl EncryptedSectionHeader {
+ /// Gets the 4-bit encoding ID of the encoding to be employed
+ /// according to the section header.
+ pub(crate) fn encoding_type(&self) -> EncodingType {
+ match self {
+ Self::MicShortSalt { salt: _, token: _ } => {
+ V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN
+ }
+ Self::MicExtendedSalt { salt: _, token: _ } => {
+ V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN
+ }
+ }
+ }
}
/// 16-byte identity token, straight out of the section.
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section/header/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/section/header/tests.rs
index 92cd5da..0db8a4f 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/section/header/tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/header/tests.rs
@@ -17,98 +17,47 @@
use alloc::vec;
#[test]
- fn parse_extended_bit_not_set() {
+ fn parse_unencrypted() {
assert_eq!(
- SectionHeader::parse(&[0b0000_0000, 3]).expect("Parsing should succeed"),
- ([].as_slice(), (&[0u8][..], SectionHeader::Unencrypted, 3u8))
+ SectionHeader::parse(&[0b0000_0000]).expect("Parsing should succeed"),
+ ([].as_slice(), Ok(SectionHeader::Unencrypted))
);
}
#[test]
- fn parse_with_extended_bit_set() {
- let _ = SectionHeader::parse(&[0b1000_0000, 0b0000_0000, 4])
- .expect_err("Extended bits should not be parsed at this time");
- }
-
- #[test]
fn parse_section_header_mic_with_short_salt() {
let mut section_header = vec![];
section_header.push(0b0000_0001u8); //format
section_header.extend_from_slice(&[0xFF; 2]); // short salt
section_header.extend_from_slice(&[0xCC; 16]); // identity token
- section_header.push(100); // payload length
assert_eq!(
SectionHeader::parse(section_header.as_slice()).expect("Parsing should succeed"),
(
[].as_slice(),
- (
- &[0b0000_0001u8][..],
- SectionHeader::Encrypted(EncryptedSectionHeader::MicShortSalt {
- salt: [0xFF; 2].into(),
- token: [0xCC; 16].into(),
- }),
- 100
- )
+ Ok(SectionHeader::Encrypted(EncryptedSectionHeader::MicShortSalt {
+ salt: [0xFF; 2].into(),
+ token: [0xCC; 16].into(),
+ }))
)
);
}
#[test]
- fn parse_section_header_multi_byte_mic_with_short_salt() {
- let mut section_header = vec![];
- section_header.extend_from_slice(&[0b1000_0001u8, 0b0000_0000]); //format
- section_header.extend_from_slice(&[0xFF; 2]); // short salt
- section_header.extend_from_slice(&[0xCC; 16]); // identity token
- section_header.push(100); // payload length
- let _ = SectionHeader::parse(section_header.as_slice())
- .expect_err("Extended bit parsing should fail at this time");
- }
-
- #[test]
fn parse_section_header_mic_with_extended_salt() {
let mut section_header = vec![];
section_header.push(0b0000_0010u8); //format
section_header.extend_from_slice(&[0xFF; 16]); // short salt
section_header.extend_from_slice(&[0xCC; 16]); // identity token
- section_header.push(100); // payload length
assert_eq!(
SectionHeader::parse(section_header.as_slice()).expect("Parsing should succeed"),
(
[].as_slice(),
- (
- &[0b0000_0010u8][..],
- SectionHeader::Encrypted(EncryptedSectionHeader::MicExtendedSalt {
- salt: [0xFF; 16].into(),
- token: [0xCC; 16].into(),
- }),
- 100
- )
- )
- );
- }
-
- #[test]
- fn parse_section_header_sig_with_extended_salt() {
- let mut section_header = vec![];
- section_header.push(0b0000_0011u8); //format
- section_header.extend_from_slice(&[0xFF; 16]); // short salt
- section_header.extend_from_slice(&[0xCC; 16]); // identity token
- section_header.push(100); // payload length
-
- assert_eq!(
- SectionHeader::parse(section_header.as_slice()).expect("Parsing should succeed"),
- (
- [].as_slice(),
- (
- &[0b0000_0011u8][..],
- SectionHeader::Encrypted(EncryptedSectionHeader::SigExtendedSalt {
- salt: [0xFF; 16].into(),
- token: [0xCC; 16].into(),
- }),
- 100
- )
+ Ok(SectionHeader::Encrypted(EncryptedSectionHeader::MicExtendedSalt {
+ salt: [0xFF; 16].into(),
+ token: [0xCC; 16].into(),
+ }),)
)
);
}
@@ -116,40 +65,17 @@
mod error_condition {
use super::super::*;
- use alloc::vec;
#[test]
fn parse_single_byte_invalid_reserve() {
- let _ = SectionHeader::parse(&[0b0001_0000, 3])
+ let _ = SectionHeader::parse(&[0b0001_0000])
.expect_err("Invalid reserve bits should fail to parse");
- let _ = SectionHeader::parse(&[0b0011_0000, 3])
+ let _ = SectionHeader::parse(&[0b0011_0000])
.expect_err("Invalid reserve bits should fail to parse");
- let _ = SectionHeader::parse(&[0b0111_0000, 3])
+ let _ = SectionHeader::parse(&[0b0111_0000])
.expect_err("Invalid reserve bits should fail to parse");
- }
-
- #[test]
- fn parse_multi_byte_invalid_reserve_bits() {
- let _ = SectionHeader::parse(&[0b1000_0000, 0b1000_0000, 4])
+ let _ = SectionHeader::parse(&[0b1111_0000])
.expect_err("Invalid reserve bits should fail to parse");
- let _ = SectionHeader::parse(&[0b1000_0000, 0b1111_1111, 4])
- .expect_err("Invalid reserve bits should fail to parse");
- let _ = SectionHeader::parse(&[0b1000_0000, 0b0000_0001, 4])
- .expect_err("Invalid reserve bits should fail to parse");
- let _ = SectionHeader::parse(&[0b1000_0000, 0b0001_0000, 4])
- .expect_err("Invalid reserve bits should fail to parse");
- let _ = SectionHeader::parse(&[0b1001_0000, 0b0000_0000, 4])
- .expect_err("Invalid reserve bits should fail to parse");
- let _ = SectionHeader::parse(&[0b1100_0000, 0b0000_0000, 4])
- .expect_err("Invalid reserve bits should fail to parse");
- let _ = SectionHeader::parse(&[0b1100_0000, 0b0000_0001, 4])
- .expect_err("Invalid reserve bits should fail to parse");
- }
-
- #[test]
- fn parse_section_header_unencrypted_encoding_zero_length_payload() {
- let _ = SectionHeader::parse(&[0b0000_0000, 0])
- .expect_err("0 is an invalid section length for unencrypted sections");
}
#[test]
@@ -157,17 +83,6 @@
let _ = SectionHeader::parse(&[0b0000_0001])
.expect_err("Not enough bytes present to parse a mic with short salt");
}
-
- #[test]
- fn parse_section_header_mic_with_short_salt_missing_length() {
- let mut section_header = vec![];
- section_header.push(0b0000_0001u8); //format
- section_header.extend_from_slice(&[0xFF; 2]); // short salt
- section_header.extend_from_slice(&[0xCC; 16]); // identity token
-
- let _ = SectionHeader::parse(section_header.as_slice())
- .expect_err("Section payload length byte is missing");
- }
}
mod coverage_gaming {
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/mod.rs
index 707b593..1f9be8b 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/mod.rs
@@ -21,7 +21,6 @@
deserialize::{
encrypted_section::{
EncryptedSectionContents, MicEncryptedSection, SectionIdentityResolutionContents,
- SignatureEncryptedSection,
},
section::header::{
CiphertextExtendedIdentityToken, EncryptedSectionHeader, SectionHeader,
@@ -29,12 +28,13 @@
DataElementParsingIterator, Section, SectionMic,
},
salt::MultiSalt,
- NP_V1_ADV_MAX_SECTION_COUNT,
+ EncodingType, NP_V1_ADV_MAX_SECTION_COUNT,
},
header::V1AdvHeader,
};
+
use crypto_provider::CryptoProvider;
-use nom::{branch, bytes, combinator, error, multi};
+use nom::{combinator, error};
#[cfg(feature = "devtools")]
use crate::{
@@ -57,31 +57,66 @@
ArrayVecOption<IntermediateSection, NP_V1_ADV_MAX_SECTION_COUNT>,
nom::Err<error::Error<&[u8]>>,
> {
- combinator::all_consuming(branch::alt((
- // Public advertisement
- multi::fold_many_m_n(
- 1,
- NP_V1_ADV_MAX_SECTION_COUNT,
- IntermediateSection::parser_unencrypted_section,
- ArrayVecOption::default,
- |mut acc, item| {
- acc.push(item);
- acc
- },
- ),
- // Encrypted advertisement
- multi::fold_many_m_n(
- 1,
- NP_V1_ADV_MAX_SECTION_COUNT,
- IntermediateSection::parser_encrypted_with_header(adv_header),
- ArrayVecOption::default,
- |mut acc, item| {
- acc.push(item);
- acc
- },
- ),
- )))(adv_body)
- .map(|(_rem, sections)| sections)
+ let mut result = ArrayVecOption::default();
+
+ // Parse through at most NP_V1_ADV_MAX_SECTION_COUNT
+ // sections, bailing with errors if any section with a
+ // known encoding type fails to parse, or if the encoding
+ // types are not sorted.
+ //
+ // Not using nom parser combinators here due to the relatively complex
+ // logic around deciding whether/not to continue parsing,
+ // which is not entirely based on the number of sections
+ // nor on the content of the sections alone.
+ let mut remaining_contents = adv_body;
+ let mut largest_encountered_encoding_type: u8 = 0;
+ while !remaining_contents.is_empty() && result.len() < NP_V1_ADV_MAX_SECTION_COUNT {
+ // Attempt to grab a new section.
+ let (updated_remaining_contents, maybe_section_contents) =
+ SectionContents::parse(remaining_contents)?;
+ remaining_contents = updated_remaining_contents;
+
+ let Ok(section_contents) = maybe_section_contents else {
+ // We hit an encoding type which we do not recognize. Truncate the
+ // advertisement to only what we've parsed and bail from the loop.
+ remaining_contents = &[];
+ break;
+ };
+ // Ensure that the encoding type is nondecreasing through the adverisement sections.
+ let current_encoding_type = section_contents.header.encoding_type().0;
+ if current_encoding_type < largest_encountered_encoding_type {
+ return Err(nom::Err::Failure(nom::error::Error::new(
+ remaining_contents,
+ nom::error::ErrorKind::Verify,
+ )));
+ }
+ largest_encountered_encoding_type = current_encoding_type;
+
+ // Attempt to lift the section contents to an [`IntermediateSection`].
+ let intermediate_section = IntermediateSection::try_parse(adv_header, section_contents)
+ .ok_or(nom::Err::Failure(nom::error::Error::new(
+ remaining_contents,
+ nom::error::ErrorKind::NonEmpty,
+ )))?;
+ result.push(intermediate_section);
+ }
+ // We've parsed as many sections as we can, ensure there's at least one section
+ // and check whether/not there are trailing bytes to determine overall success/failure.
+ if !result.is_empty() {
+ if remaining_contents.is_empty() {
+ Ok(result)
+ } else {
+ Err(nom::Err::Failure(nom::error::Error::new(
+ remaining_contents,
+ nom::error::ErrorKind::Complete,
+ )))
+ }
+ } else {
+ Err(nom::Err::Failure(nom::error::Error::new(
+ remaining_contents,
+ nom::error::ErrorKind::Many1,
+ )))
+ }
}
/// A partially processed section that hasn't been decrypted (if applicable) yet.
@@ -94,66 +129,59 @@
}
impl<'a> IntermediateSection<'a> {
- fn parser_unencrypted_section(
- np_adv_body: &'a [u8],
- ) -> nom::IResult<&'a [u8], IntermediateSection<'a>> {
- combinator::map_opt(SectionContents::parse, |sc| match sc.header {
- SectionHeader::Unencrypted => {
- Some(IntermediateSection::Plaintext(PlaintextSection::new(sc.contents)))
- }
- SectionHeader::Encrypted(_) => None,
- })(np_adv_body)
- }
-
- pub(crate) fn parser_encrypted_with_header(
+ /// Given some section contents, attempts to construct
+ /// an [`IntermediateSection`] out of them. May return
+ /// `None` if the section is malformed according to
+ /// the rules of its encoding type.
+ pub(crate) fn try_parse(
adv_header: V1AdvHeader,
- ) -> impl Fn(&'a [u8]) -> nom::IResult<&[u8], IntermediateSection> {
- move |adv_body| {
- fn split_at_mic(contents: &[u8]) -> Option<(&[u8], SectionMic)> {
- contents.len().checked_sub(SectionMic::CONTENTS_LEN).map(|len_before_mic| {
- let (before_mic, mic) = contents.split_at(len_before_mic);
- let mic = SectionMic::try_from(mic).expect("MIC length checked above");
+ section_contents: SectionContents<'a>,
+ ) -> Option<IntermediateSection<'a>> {
+ fn split_at_mic(contents: &[u8]) -> Option<(&[u8], SectionMic)> {
+ contents.len().checked_sub(SectionMic::CONTENTS_LEN).map(|len_before_mic| {
+ let (before_mic, mic) = contents.split_at(len_before_mic);
+ let mic = SectionMic::try_from(mic).expect("MIC length checked above");
- (before_mic, mic)
- })
- }
+ (before_mic, mic)
+ })
+ }
- fn build_mic_section<'a>(
- adv_header: V1AdvHeader,
- format_bytes: &'a [u8],
- salt: MultiSalt,
- token: CiphertextExtendedIdentityToken,
- contents_len: u8,
- contents: &'a [u8],
- ) -> Option<IntermediateSection<'a>> {
- split_at_mic(contents).map(|(before_mic, mic)| {
- IntermediateSection::Ciphertext(CiphertextSection::MicEncrypted(
- MicEncryptedSection {
- contents: EncryptedSectionContents::new(
- adv_header,
- format_bytes,
- salt,
- token,
- contents_len,
- before_mic,
- ),
- mic,
- },
- ))
- })
- }
-
- combinator::map_opt(
- combinator::map_opt(SectionContents::parse, |sc| match sc.header {
- SectionHeader::Unencrypted => None,
- SectionHeader::Encrypted(e) => {
- Some((sc.format_bytes, sc.contents, sc.contents_len, e))
- }
- }),
- move |(format_bytes, contents, contents_len, header)| match header {
+ fn build_mic_section(
+ adv_header: V1AdvHeader,
+ format_byte: u8,
+ salt: MultiSalt,
+ token: CiphertextExtendedIdentityToken,
+ contents_len: u8,
+ contents: &[u8],
+ ) -> Option<IntermediateSection<'_>> {
+ split_at_mic(contents).map(|(before_mic, mic)| {
+ IntermediateSection::Ciphertext(CiphertextSection::MicEncrypted(
+ MicEncryptedSection {
+ contents: EncryptedSectionContents::new(
+ adv_header,
+ format_byte,
+ salt,
+ token,
+ contents_len,
+ before_mic,
+ ),
+ mic,
+ },
+ ))
+ })
+ }
+ match section_contents.header {
+ SectionHeader::Unencrypted => Some(IntermediateSection::Plaintext(
+ PlaintextSection::new(section_contents.contents),
+ )),
+ SectionHeader::Encrypted(e) => {
+ let format_byte = section_contents.format_byte;
+ let contents = section_contents.contents;
+ let contents_len = section_contents.contents_len;
+ match e {
EncryptedSectionHeader::MicShortSalt { salt, token } => build_mic_section(
adv_header,
- format_bytes,
+ format_byte,
salt.into(),
token,
contents_len,
@@ -161,28 +189,14 @@
),
EncryptedSectionHeader::MicExtendedSalt { salt, token } => build_mic_section(
adv_header,
- format_bytes,
+ format_byte,
salt.into(),
token,
contents_len,
contents,
),
- EncryptedSectionHeader::SigExtendedSalt { salt, token } => {
- Some(IntermediateSection::Ciphertext(
- CiphertextSection::SignatureEncrypted(SignatureEncryptedSection {
- contents: EncryptedSectionContents::new(
- adv_header,
- format_bytes,
- salt,
- token,
- contents_len,
- contents,
- ),
- }),
- ))
- }
- },
- )(adv_body)
+ }
+ }
}
}
}
@@ -192,8 +206,8 @@
/// This is just the first stage of parsing sections, followed by [IntermediateSection].
#[derive(PartialEq, Eq, Debug)]
pub(crate) struct SectionContents<'adv> {
- /// 1-2 bytes of the format saved for later use in verification
- pub(crate) format_bytes: &'adv [u8],
+ /// The section header format byte saved for later use in verification
+ pub(crate) format_byte: u8,
/// Section header contents which includes salt + identity token
pub(crate) header: SectionHeader,
/// Contents of the section after the header.
@@ -204,11 +218,32 @@
}
impl<'adv> SectionContents<'adv> {
- pub(crate) fn parse(input: &'adv [u8]) -> nom::IResult<&'adv [u8], Self> {
- let (input, (format_bytes, header, contents_len)) = SectionHeader::parse(input)?;
- let (input, contents) = bytes::complete::take(contents_len)(input)?;
+ /// Attempts to parse the contents of a section (including the header)
+ /// from the given bytes.
+ ///
+ /// If the encoding type of the section header was unrecognized,
+ /// this method will bail on attempting to parse the entire section
+ /// contents and will instead consume only the first encoding-type byte,
+ /// but the parser WILL succeed as a nom parser.
+ pub(crate) fn parse(input: &'adv [u8]) -> nom::IResult<&'adv [u8], Result<Self, EncodingType>> {
+ match SectionHeader::parse(input)? {
+ (input, Ok(header)) => {
+ // Header parsed successfully, with a known encoding type.
+ let format_byte = header.encoding_type().0;
+ let (input, contents_len) = combinator::verify(nom::number::complete::u8, |b| {
+ // length must be non-zero.
+ *b != 0
+ })(input)?;
- Ok((input, Self { format_bytes, header, contents, contents_len }))
+ let (input, contents) = nom::bytes::complete::take(contents_len)(input)?;
+
+ Ok((input, Ok(Self { format_byte, header, contents, contents_len })))
+ }
+ (input, Err(encoding_type)) => {
+ // Unknown encoding type.
+ Ok((input, Err(encoding_type)))
+ }
+ }
}
}
@@ -225,22 +260,19 @@
}
impl<'adv> Section<'adv> for PlaintextSection<'adv> {
- type Iterator = DataElementParsingIterator<'adv>;
-
- fn iter_data_elements(&self) -> Self::Iterator {
- DataElementParsingIterator::new(self.contents)
+ fn iter_data_elements(&self) -> DataElementParsingIterator<'adv> {
+ DataElementParsingIterator::new(self.contents, None)
}
}
#[derive(PartialEq, Eq, Debug)]
pub(crate) enum CiphertextSection<'a> {
- SignatureEncrypted(SignatureEncryptedSection<'a>),
MicEncrypted(MicEncryptedSection<'a>),
}
+#[cfg(feature = "devtools")]
impl<'a> CiphertextSection<'a> {
/// Try decrypting into some raw bytes given some raw unsigned crypto-material.
- #[cfg(feature = "devtools")]
pub(crate) fn try_resolve_identity_and_decrypt<
C: V1DiscoveryCryptoMaterial,
P: CryptoProvider,
@@ -250,11 +282,6 @@
crypto_material: &C,
) -> Option<Result<ArrayView<u8, { NP_ADV_MAX_SECTION_LEN }>, ArenaOutOfSpace>> {
match self {
- CiphertextSection::SignatureEncrypted(x) => {
- let identity_resolution_material =
- crypto_material.signed_identity_resolution_material::<P>();
- x.try_resolve_identity_and_decrypt::<P>(allocator, &identity_resolution_material)
- }
CiphertextSection::MicEncrypted(x) => match x.contents.salt {
MultiSalt::Short(_) => x.try_resolve_short_salt_identity_and_decrypt::<P>(
allocator,
@@ -267,7 +294,9 @@
},
}
}
+}
+impl CiphertextSection<'_> {
/// Return the data needed to resolve identities.
///
/// In the typical case of trying many identities across a few sections,
@@ -277,9 +306,6 @@
&self,
) -> SectionIdentityResolutionContents {
match self {
- CiphertextSection::SignatureEncrypted(x) => {
- x.contents.compute_identity_resolution_contents::<C>()
- }
CiphertextSection::MicEncrypted(x) => {
x.contents.compute_identity_resolution_contents::<C>()
}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/tests.rs
index 313a906..276536f 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/tests.rs
@@ -25,16 +25,13 @@
extended::{
deserialize::{
data_element::DataElement,
- encrypted_section::{
- EncryptedSectionContents, MicEncryptedSection, SignatureEncryptedSection,
- },
+ encrypted_section::{EncryptedSectionContents, MicEncryptedSection},
SectionMic,
},
salt::{ShortV1Salt, SHORT_SALT_LEN},
- serialize::{AdvBuilder, AdvertisementType},
+ serialize::AdvBuilder,
NP_V1_ADV_MAX_SECTION_COUNT, V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
- V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN,
- V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
+ V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
V1_IDENTITY_TOKEN_LEN,
},
header::V1AdvHeader,
@@ -42,7 +39,7 @@
};
use crypto_provider_default::CryptoProviderImpl;
use nom::error;
-use np_hkdf::v1_salt::EXTENDED_SALT_LEN;
+use np_hkdf::v1_salt::{ExtendedV1Salt, EXTENDED_SALT_LEN};
use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng as _};
/// Section header length after the length byte for ext salt + token
@@ -57,13 +54,12 @@
extended::{data_elements::TxPowerDataElement, serialize::UnencryptedSectionEncoder},
shared_data::TxPower,
};
- use np_hkdf::v1_salt::ExtendedV1Salt;
#[test]
fn parse_adv_ext_public_identity() {
let mut adv_body = vec![];
// public identity
- adv_body.push(V1_ENCODING_UNENCRYPTED);
+ adv_body.push(V1_ENCODING_UNENCRYPTED.0);
// section len
adv_body.push(6 + 3);
// de 1 byte header, type 5, len 5
@@ -78,9 +74,9 @@
);
let expected_des = [
// 1 byte header, len 5
- DataElement::new(0_u8.into(), 5_u8.into(), &[0x01, 0x02, 0x03, 0x04, 0x05]),
+ DataElement::unsalted(5_u8.into(), &[0x01, 0x02, 0x03, 0x04, 0x05]),
// 2 byte header, len 1
- DataElement::new(1_u8.into(), 22_u8.into(), &[0x01]),
+ DataElement::unsalted(22_u8.into(), &[0x01]),
];
assert_eq!(
@@ -91,12 +87,12 @@
#[test]
fn do_deserialize_max_number_of_public_sections() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT {
let mut section_builder =
adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
section_builder
- .add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(7).unwrap()))
+ .add_de(&TxPowerDataElement::from(TxPower::try_from(7).unwrap()))
.unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
}
@@ -124,110 +120,11 @@
}
#[test]
- fn max_number_encrypted_sections_sig() {
- let mut adv_body = vec![];
- for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT {
- let _ = add_sig_encrpyted_section(&mut adv_body, 5, &[0x55; EXTENDED_SALT_LEN]);
- }
- let adv_header = V1AdvHeader::new(0x20);
- assert!(parse_sections(adv_header, &adv_body).is_ok());
- }
-
- #[test]
- fn both_mic_and_sig_sections() {
- let mut adv_body = vec![];
- let short_salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
- let token_bytes = [0x55; V1_IDENTITY_TOKEN_LEN];
- let extended_salt = ExtendedV1Salt::from([0x55; EXTENDED_SALT_LEN]);
- let total_section_1_len = add_mic_short_salt_section_to_adv(&mut adv_body);
- let _ = add_sig_encrpyted_section(&mut adv_body, 5, extended_salt.bytes());
-
- let adv_header = V1AdvHeader::new(0x20);
- let section1 = &adv_body[0..total_section_1_len];
- let section2 = &adv_body[total_section_1_len..];
- let expected_sections = [
- IntermediateSection::from(MicEncryptedSection {
- contents: EncryptedSectionContents::new(
- adv_header,
- §ion1[..1],
- short_salt.into(),
- token_bytes.into(),
- (10 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
- &[0xFF; 10],
- ),
- mic: SectionMic::from([0x33; SectionMic::CONTENTS_LEN]),
- }),
- IntermediateSection::from(SignatureEncryptedSection {
- contents: EncryptedSectionContents::new(
- adv_header,
- §ion2[..1],
- extended_salt,
- [0x33; V1_IDENTITY_TOKEN_LEN].into(),
- 5,
- &[0xFF; 5],
- ),
- }),
- ];
- let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
- assert_eq!(parsed_sections.into_vec(), expected_sections);
- }
-
- #[test]
- fn parse_adv_sig_encrypted_sections() {
- // 3 sections
- let mut adv_body = vec![];
- let salt_bytes = [0x11; EXTENDED_SALT_LEN];
-
- let section_1_len = add_sig_encrpyted_section(&mut adv_body, 10, &salt_bytes);
- let section_2_len = add_sig_encrpyted_section(&mut adv_body, 11, &salt_bytes);
- let _ = add_sig_encrpyted_section(&mut adv_body, 12, &salt_bytes);
-
- let adv_header = V1AdvHeader::new(0x20);
- let section1 = &adv_body[..section_1_len];
- let section2 = &adv_body[section_1_len..][..section_2_len];
- let section3 = &adv_body[(section_1_len + section_2_len)..];
- let expected_sections = [
- SignatureEncryptedSection {
- contents: EncryptedSectionContents::new(
- adv_header,
- §ion1[..1],
- salt_bytes.into(),
- [0x33; V1_IDENTITY_TOKEN_LEN].into(),
- 10,
- &[0xFF; 10],
- ),
- },
- SignatureEncryptedSection {
- contents: EncryptedSectionContents::new(
- adv_header,
- §ion2[..1],
- salt_bytes.into(),
- [0x33; V1_IDENTITY_TOKEN_LEN].into(),
- 11,
- &[0xFF; 11],
- ),
- },
- SignatureEncryptedSection {
- contents: EncryptedSectionContents::new(
- adv_header,
- §ion3[..1],
- salt_bytes.into(),
- [0x33; V1_IDENTITY_TOKEN_LEN].into(),
- 12,
- &[0xFF; 12],
- ),
- },
- ];
- let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
- assert_eq!(parsed_sections.into_vec(), expected_sections.map(IntermediateSection::from));
- }
-
- #[test]
fn parse_adv_ext_salt_mic_sections() {
// 3 sections
let mut adv_body = vec![];
// section
- adv_body.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+ adv_body.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
let salt = ExtendedV1Salt::from([0x11; EXTENDED_SALT_LEN]);
adv_body.extend_from_slice(salt.bytes().as_slice());
let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
@@ -240,7 +137,7 @@
adv_body.extend_from_slice(&[0x33; SectionMic::CONTENTS_LEN]);
// section
- adv_body.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+ adv_body.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
adv_body.extend_from_slice(salt.bytes().as_slice());
let token_bytes_2 = [0x77; V1_IDENTITY_TOKEN_LEN];
adv_body.extend_from_slice(&token_bytes_2);
@@ -252,7 +149,7 @@
adv_body.extend_from_slice(&[0x66; SectionMic::CONTENTS_LEN]);
// section
- adv_body.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+ adv_body.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
adv_body.extend_from_slice(salt.bytes().as_slice());
let token_bytes_3 = [0xAA; V1_IDENTITY_TOKEN_LEN];
adv_body.extend_from_slice(&token_bytes_3);
@@ -271,7 +168,7 @@
MicEncryptedSection {
contents: EncryptedSectionContents::new(
adv_header,
- §ion1[..1],
+ section1[0],
salt.into(),
token_bytes_1.into(),
(10 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
@@ -282,7 +179,7 @@
MicEncryptedSection {
contents: EncryptedSectionContents::new(
adv_header,
- §ion2[..1],
+ section2[0],
salt.into(),
token_bytes_2.into(),
(11 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
@@ -293,7 +190,7 @@
MicEncryptedSection {
contents: EncryptedSectionContents::new(
adv_header,
- §ion3[..1],
+ section3[0],
salt.into(),
token_bytes_3.into(),
(12 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
@@ -317,13 +214,13 @@
let _ = add_mic_short_salt_section_to_adv(&mut adv_body);
let adv_header = V1AdvHeader::new(0x20);
- let section1 = &adv_body[..(1 + section_1_len)];
+ let section1 = &adv_body[..section_1_len];
let mut expected_sections = [None, None, None];
for section in &mut expected_sections {
*section = Some(MicEncryptedSection {
contents: EncryptedSectionContents::new(
adv_header,
- §ion1[..1],
+ section1[0],
short_salt.into(),
token_bytes.into(),
(10 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
@@ -337,6 +234,52 @@
let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
assert_eq!(parsed_sections.into_vec(), expected);
}
+
+ #[test]
+ fn parse_adv_mixed_salt_mic() {
+ let short_salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
+ let ext_salt = ExtendedV1Salt::from([0x11; EXTENDED_SALT_LEN]);
+ let token_bytes = [0x55; V1_IDENTITY_TOKEN_LEN];
+
+ let mut adv_body = vec![];
+ let section_1_len = add_mic_short_salt_section_to_adv(&mut adv_body);
+ let section_2_len = add_mic_ext_salt_section_to_adv(&mut adv_body);
+
+ let adv_header = V1AdvHeader::new(0x20);
+
+ let (section1, rest) = adv_body.split_at(section_1_len);
+ let (section2, rest) = rest.split_at(section_2_len);
+ assert!(rest.is_empty());
+
+ let expected_sections = [
+ MicEncryptedSection {
+ contents: EncryptedSectionContents::new(
+ adv_header,
+ section1[0],
+ short_salt.into(),
+ token_bytes.into(),
+ (10 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
+ &[0xFF; 10],
+ ),
+ mic: SectionMic::from([0x33; SectionMic::CONTENTS_LEN]),
+ },
+ MicEncryptedSection {
+ contents: EncryptedSectionContents::new(
+ adv_header,
+ section2[0],
+ ext_salt.into(),
+ token_bytes.into(),
+ (10 + SectionMic::CONTENTS_LEN).try_into().unwrap(),
+ &[0xFF; 10],
+ ),
+ mic: SectionMic::from([0x33; SectionMic::CONTENTS_LEN]),
+ },
+ ];
+ let expected: Vec<IntermediateSection> =
+ expected_sections.into_iter().map(IntermediateSection::from).collect();
+ let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
+ assert_eq!(parsed_sections.into_vec(), expected);
+ }
}
mod error_condition {
@@ -348,7 +291,7 @@
for _ in 0..=NP_V1_ADV_MAX_SECTION_COUNT {
// section 1..=MAX+1 - plaintext - 11 bytes
- adv_body.push(V1_ENCODING_UNENCRYPTED);
+ adv_body.push(V1_ENCODING_UNENCRYPTED.0);
adv_body.push(6 + 3); // section len
// de 1 byte header, type 5, len 5
adv_body.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
@@ -358,13 +301,7 @@
let adv_header = V1AdvHeader::new(0x20);
- assert_eq!(
- nom::Err::Error(error::Error {
- input: &adv_body[(NP_V1_ADV_MAX_SECTION_COUNT * 11)..],
- code: error::ErrorKind::Eof
- }),
- parse_sections(adv_header, &adv_body).unwrap_err()
- );
+ let _ = parse_sections(adv_header, &adv_body).unwrap_err();
}
#[test]
@@ -438,46 +375,9 @@
}
#[test]
- fn parse_empty_section_as_encrypted() {
- // empty section - should return an EOF error
- let input = [];
- assert_eq!(
- nom::Err::Error(error::Error {
- // attempted to read section contents
- input: input.as_slice(),
- code: error::ErrorKind::Eof,
- }),
- IntermediateSection::parser_encrypted_with_header(V1AdvHeader::new(0x20))(&input)
- .unwrap_err()
- );
- }
-
- #[test]
- fn parse_unencrypted_as_encrypted() {
- let mut input = vec![];
- // public identity
- input.push(V1_ENCODING_UNENCRYPTED);
- // section len
- input.push(6 + 3);
- // de 1 byte header, type 5, len 5
- input.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
- // de 2 byte header, type 22, len 1
- input.extend_from_slice(&[0x81, 0x16, 0x01]);
- assert_eq!(
- nom::Err::Error(error::Error {
- // attempted to read section contents
- input: input.as_slice(),
- code: error::ErrorKind::MapOpt,
- }),
- IntermediateSection::parser_encrypted_with_header(V1AdvHeader::new(0x20))(&input)
- .unwrap_err()
- );
- }
-
- #[test]
fn parse_section_length_overrun() {
// section of length 0xF0 - legal but way longer than 2
- let input = [V1_ENCODING_UNENCRYPTED, 0xF0, 0x10, 0x11];
+ let input = [V1_ENCODING_UNENCRYPTED.0, 0xF0, 0x10, 0x11];
assert_eq!(
nom::Err::Error(error::Error {
// attempted to read section contents after parsing header
@@ -490,7 +390,7 @@
#[test]
fn do_deserialize_empty_section() {
- let adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let adv_builder = AdvBuilder::new();
let adv = adv_builder.into_advertisement();
let (remaining, header) = NpVersionHeader::parse(adv.as_slice()).unwrap();
let v1_header = if let NpVersionHeader::V1(h) = header {
@@ -501,22 +401,6 @@
let _ = parse_sections(v1_header, remaining).expect_err("Expected an error");
}
- #[test]
- fn parse_adv_sig_encrypted_section_with_short_salt() {
- // 3 sections
- let mut adv_body = vec![];
- let salt_bytes = [0x11; SHORT_SALT_LEN];
- let section_len = EXT_SALT_SECTION_HEADER_LEN as u8 + 10;
- adv_body.push(section_len);
- adv_body.push(V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN);
- adv_body.extend_from_slice(&salt_bytes);
- adv_body.extend_from_slice(&[0x33; V1_IDENTITY_TOKEN_LEN]);
- adv_body.extend_from_slice(&[0x22; 10]);
- let adv_header = V1AdvHeader::new(0x20);
- let parsed_sections = parse_sections(adv_header, &adv_body);
- assert!(parsed_sections.is_err());
- }
-
// specify extended salt in the header but adv actual contains short salt
#[test]
fn parse_adv_mic_encrypted_wrong_salt_for_extended_header() {
@@ -524,7 +408,7 @@
let mut adv = vec![];
let section_len = SHORT_SALT_SECTION_HEADER_LEN as u8 + 10 + SectionMic::CONTENTS_LEN as u8;
adv.push(section_len);
- adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+ adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
let salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
adv.extend_from_slice(salt.bytes().as_slice());
let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
@@ -546,7 +430,7 @@
let mut adv = vec![];
let section_len = SHORT_SALT_SECTION_HEADER_LEN as u8 + 10 + SectionMic::CONTENTS_LEN as u8;
adv.push(section_len);
- adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN);
+ adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN.0);
let salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
adv.extend_from_slice(salt.bytes().as_slice());
let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
@@ -560,7 +444,7 @@
// section len
adv.push(1 + 6 + 3);
// public identity
- adv.push(V1_ENCODING_UNENCRYPTED);
+ adv.push(V1_ENCODING_UNENCRYPTED.0);
// de 1 byte header, type 5, len 5
adv.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
// de 2 byte header, type 6, len 1
@@ -580,7 +464,7 @@
// section len
adv.push(1 + 6 + 3);
// public identity
- adv.push(V1_ENCODING_UNENCRYPTED);
+ adv.push(V1_ENCODING_UNENCRYPTED.0);
// de 1 byte header, type 5, len 5
adv.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
// de 2 byte header, type 6, len 1
@@ -589,7 +473,7 @@
// mic encrypted section
let section_len = SHORT_SALT_SECTION_HEADER_LEN as u8 + 10 + SectionMic::CONTENTS_LEN as u8;
adv.push(section_len);
- adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN);
+ adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN.0);
let salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
adv.extend_from_slice(salt.bytes().as_slice());
let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
@@ -612,7 +496,7 @@
#[test]
fn section_contents() {
let sc = SectionContents {
- format_bytes: &[],
+ format_byte: 0,
header: SectionHeader::Unencrypted,
contents: &[],
contents_len: 0,
@@ -631,7 +515,7 @@
let ms = MicEncryptedSection {
contents: EncryptedSectionContents::new(
V1AdvHeader::new(1),
- &[],
+ 1u8,
ShortV1Salt::from([0x00; 2]).into(),
CiphertextExtendedIdentityToken([0x00; 16]),
0,
@@ -670,7 +554,7 @@
// returns the number of bytes appended to adv
fn add_mic_short_salt_section_to_adv(adv: &mut Vec<u8>) -> usize {
// section
- adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN);
+ adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN.0);
let salt = ShortV1Salt::from([0x11; SHORT_SALT_LEN]);
adv.extend_from_slice(salt.bytes().as_slice());
let token_bytes_1 = [0x55; V1_IDENTITY_TOKEN_LEN];
@@ -684,31 +568,22 @@
1 + SHORT_SALT_LEN + V1_IDENTITY_TOKEN_LEN + 1 + 10 + SectionMic::CONTENTS_LEN
}
-// returns the total number of bytes appended to adv
-fn add_sig_encrpyted_section(
- adv_body: &mut Vec<u8>,
- len: u8,
- salt_bytes: &[u8; EXTENDED_SALT_LEN],
-) -> usize {
- adv_body.push(V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN);
- adv_body.extend_from_slice(salt_bytes);
- adv_body.extend_from_slice(&[0x33; V1_IDENTITY_TOKEN_LEN]);
- adv_body.push(len);
- // len bytes of 0xFF ciphertext -- in a real adv this would include the
- // signature, but for the purposes of this parser, it's all just ciphertext
- for _ in 0..len {
- adv_body.push(0xFF);
- }
- 1 + EXTENDED_SALT_LEN + V1_IDENTITY_TOKEN_LEN + 1 + len as usize
+fn add_mic_ext_salt_section_to_adv(adv: &mut Vec<u8>) -> usize {
+ adv.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
+ let salt = ExtendedV1Salt::from([0x11; EXTENDED_SALT_LEN]);
+ adv.extend_from_slice(salt.bytes().as_slice());
+ let token_bytes = [0x55; V1_IDENTITY_TOKEN_LEN];
+ adv.extend_from_slice(&token_bytes);
+ adv.push(10 + SectionMic::CONTENTS_LEN as u8);
+ // 10 bytes of 0xFF ciphertext
+ adv.extend_from_slice(&[0xFF; 10]);
+ // mic - 16x 0x33
+ adv.extend_from_slice(&[0x33; SectionMic::CONTENTS_LEN]);
+
+ EXT_SALT_SECTION_HEADER_LEN + 10 + SectionMic::CONTENTS_LEN
}
// for convenient .into() in expected test data
-impl<'a> From<SignatureEncryptedSection<'a>> for IntermediateSection<'a> {
- fn from(s: SignatureEncryptedSection<'a>) -> Self {
- IntermediateSection::Ciphertext(CiphertextSection::SignatureEncrypted(s))
- }
-}
-
impl<'a> From<MicEncryptedSection<'a>> for IntermediateSection<'a> {
fn from(s: MicEncryptedSection<'a>) -> Self {
IntermediateSection::Ciphertext(CiphertextSection::MicEncrypted(s))
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/section/mod.rs
index 1020e8d..43e5be9 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/section/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/mod.rs
@@ -13,4 +13,4 @@
// limitations under the License.
pub(crate) mod header;
-pub(crate) mod intermediate;
+pub mod intermediate;
diff --git a/nearby/presence/np_adv/src/extended/deserialize/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/tests.rs
index 3164c2b..478fd78 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/tests.rs
@@ -20,28 +20,17 @@
credential::book::CredentialBook,
deserialization_arena,
deserialization_arena::ArenaOutOfSpace,
- extended::serialize::{AddSectionError, AdvBuilder, AdvertisementType},
+ extended::serialize::{AddSectionError, AdvBuilder},
header::NpVersionHeader,
- tests::deser_v1_tests::{
- add_mic_rand_salt_to_adv, add_sig_rand_salt_to_adv, SectionConfig, TestIdentity,
- },
+ tests::deser_v1_tests::{add_mic_rand_length_salt_to_adv, SectionConfig, TestIdentity},
};
use crypto_provider_default::CryptoProviderImpl;
use rand::{prelude::StdRng, seq::IteratorRandom, SeedableRng};
#[test]
-fn v1_arena_out_of_space_error_sig() {
- v1_arena_out_of_space_error_encrypted_adv(
- // Need to use many DE's to be sure we go over the arena limit
- add_sig_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 5>,
- )
-}
-
-#[test]
fn v1_arena_out_of_space_error_mic() {
v1_arena_out_of_space_error_encrypted_adv(
- // Need to use many DE's to be sure we go over the arena limit
- add_mic_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 5>,
+ add_mic_rand_length_salt_to_adv::<StdRng, CryptoProviderImpl, 5>,
)
}
@@ -53,11 +42,8 @@
) -> Result<SectionConfig<'a>, AddSectionError>,
) {
let mut rng = StdRng::from_entropy();
- let mut builder = AdvBuilder::new(AdvertisementType::Encrypted);
- let identities =
- crate::tests::deser_v1_tests::TestIdentities::generate::<1, _, CryptoProviderImpl>(
- &mut rng,
- );
+ let mut builder = AdvBuilder::new();
+ let identities = crate::tests::deser_v1_tests::TestIdentities::generate::<1, _>(&mut rng);
let _ = add_to_adv(&mut rng, &identities.0[0], &mut builder);
let adv = builder.into_advertisement().as_slice().to_vec();
let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
@@ -68,9 +54,9 @@
if let NpVersionHeader::V1(v1_header) = header { Some(v1_header) } else { None }.unwrap();
let mut sections_in_processing =
- crate::extended::deserialize::SectionsInProcessing::<'_, _>::from_advertisement_contents::<
- CryptoProviderImpl,
- >(h, remaining)
+ crate::extended::deserialize::SectionsInProcessing::<'_, _>::from_advertisement_contents(
+ h, remaining,
+ )
.unwrap();
// fill up allocator so we will run out of space
@@ -138,7 +124,7 @@
#[test]
fn verification_mode_derives() {
- let m = VerificationMode::Signature;
+ let m = VerificationMode::Mic;
#[allow(clippy::clone_on_copy)]
let _ = format!("{:?}", m.clone());
}
diff --git a/nearby/presence/np_adv/src/extended/mod.rs b/nearby/presence/np_adv/src/extended/mod.rs
index 128fbf1..945ed7f 100644
--- a/nearby/presence/np_adv/src/extended/mod.rs
+++ b/nearby/presence/np_adv/src/extended/mod.rs
@@ -21,9 +21,10 @@
pub mod de_type;
pub mod deserialize;
pub mod salt;
-pub mod section_signature_payload;
pub mod serialize;
+use serialize::section::header::SECTION_HEADER_MAX_LEN;
+
// TODO make this easy to use w/ configurable arena size
/// Maximum size of an NP advertisement, including the adv header
pub const BLE_5_ADV_SVC_MAX_CONTENT_LEN: usize = 254
@@ -35,12 +36,12 @@
/// Maximum number of sections in an advertisement
pub const NP_V1_ADV_MAX_SECTION_COUNT: usize = 8;
-/// Maximum size of a NP section, including its length header byte
-pub const NP_ADV_MAX_SECTION_LEN: usize = NP_ADV_MAX_SECTION_CONTENTS_LEN + 1;
+/// Maximum size of a NP section, including its header
+pub const NP_ADV_MAX_SECTION_LEN: usize =
+ SECTION_HEADER_MAX_LEN + 1 + NP_ADV_MAX_SECTION_CONTENTS_LEN;
-// TODO should this be 255 (or 256, if we +1 the length)?
-/// Maximum hypothetical size of a NP section's contents, excluding its header
-/// byte. This is longer than can fit in a BLE 5 extended adv, but other media
+/// Maximum hypothetical size of a NP section's contents, excluding its header.
+/// This is longer than can fit in a BLE 5 extended adv, but other media
/// could fit it, like mDNS.
const NP_ADV_MAX_SECTION_CONTENTS_LEN: usize = 255;
@@ -49,19 +50,30 @@
// 4-bit encoding ids
/// Encoding ID for unencrypted sections with no salt
-pub const V1_ENCODING_UNENCRYPTED: u8 = 0x00;
+pub const V1_ENCODING_UNENCRYPTED: EncodingType = EncodingType(0x00);
/// Encoding ID for encrypted sections with a MIC and a short salt
-pub const V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN: u8 = 0x01;
+pub const V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN: EncodingType = EncodingType(0x01);
/// Encoding ID for encrypted sections with a MIC and an extended salt
-pub const V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN: u8 = 0x02;
-/// Encoding ID for encrypted sections with a signature and an extended salt
-pub const V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN: u8 = 0x03;
+pub const V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN: EncodingType = EncodingType(0x02);
// The maximum de length that fits into a non-extended de header
const MAX_NON_EXTENDED_LEN: u8 = 7;
// The maximum type code that fits into a non-extended de header
const MAX_NON_EXTENDED_TYPE_CODE: u32 = 15;
+/// The type of encoding employed for a section, which
+/// may be any 4-bit unsigned integer.
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+pub struct EncodingType(pub(crate) u8);
+
+impl EncodingType {
+ /// Gets the single-byte representation of this encoding-type,
+ /// with all four high bits set to zero (reserved).
+ pub fn byte_value(&self) -> u8 {
+ self.0
+ }
+}
+
fn de_requires_extended_bit(type_code: u32, de_len: u8) -> bool {
de_len > MAX_NON_EXTENDED_LEN || type_code > MAX_NON_EXTENDED_TYPE_CODE
}
diff --git a/nearby/presence/np_adv/src/extended/salt.rs b/nearby/presence/np_adv/src/extended/salt.rs
index 2799173..5d539e9 100644
--- a/nearby/presence/np_adv/src/extended/salt.rs
+++ b/nearby/presence/np_adv/src/extended/salt.rs
@@ -20,7 +20,25 @@
use crate::helpers::parse_byte_array;
-pub use np_hkdf::v1_salt::ExtendedV1Salt;
+pub use np_hkdf::v1_salt::{DeType, ExtendedV1Salt, OptionDeType};
+
+/// Derived salt for an individual V1 data element.
+#[derive(Clone, Copy, PartialEq, Debug, Eq)]
+pub struct DeSalt {
+ /// The containing section's extended V1 salt.
+ pub(crate) salt: ExtendedV1Salt,
+ /// The DE type of the data element.
+ pub(crate) de_type: DeType,
+}
+
+impl DeSalt {
+ /// Derive salt of the requested length.
+ ///
+ /// The length must be a valid HKDF-SHA256 length.
+ pub fn derive<const N: usize, C: CryptoProvider>(&self) -> Option<[u8; N]> {
+ self.salt.derive::<N, C>(OptionDeType::from(self.de_type))
+ }
+}
/// Common behavior for V1 section salts.
pub trait V1Salt: Copy + Into<MultiSalt> {
@@ -33,7 +51,7 @@
impl V1Salt for ExtendedV1Salt {
fn compute_nonce<C: CryptoProvider>(&self) -> AesCtrNonce {
- self.derive::<12, C>(None).expect("AES-CTR nonce is a valid HKDF size")
+ self.derive::<12, C>(OptionDeType::NONE).expect("AES-CTR nonce is a valid HKDF size")
}
}
@@ -112,3 +130,46 @@
}
}
}
+
+/// Type-level predicate which indicates that
+/// a derived salt type can be used in call
+/// sites which expect this derived salt type.
+///
+/// Specifically, if `S: SaltConvertible<T>`,
+/// then a salt of type `T` suffices in
+/// all call sites which expect a salt
+/// of type `S` (possibly by omitting
+/// information, if `S` is `Unsalted`).
+pub trait SaltConvertible<T: Into<Option<DeSalt>>>: Into<Option<DeSalt>> {
+ /// Converts a salt of one type into a salt
+ /// usable in place of this salt type in method parameters.
+ fn convert(salt: T) -> Self;
+}
+
+/// Type-level token indicating that a section encoding
+/// scheme does not employ any sort of salt.
+pub struct Unsalted;
+
+impl From<Unsalted> for Option<DeSalt> {
+ fn from(_: Unsalted) -> Self {
+ None
+ }
+}
+
+impl SaltConvertible<DeSalt> for DeSalt {
+ fn convert(salt: DeSalt) -> Self {
+ salt
+ }
+}
+
+impl<T: Into<Option<DeSalt>>> SaltConvertible<T> for Option<DeSalt> {
+ fn convert(salt: T) -> Self {
+ salt.into()
+ }
+}
+
+impl<T: Into<Option<DeSalt>>> SaltConvertible<T> for Unsalted {
+ fn convert(_salt: T) -> Unsalted {
+ Unsalted
+ }
+}
diff --git a/nearby/presence/np_adv/src/extended/section_signature_payload.rs b/nearby/presence/np_adv/src/extended/section_signature_payload.rs
deleted file mode 100644
index 3cdaead..0000000
--- a/nearby/presence/np_adv/src/extended/section_signature_payload.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.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.
-
-//! V1 advertisement section np_ed25519 signature payload
-//! after the included context bytes, and utilities for
-//! performing signatures and signature verification.
-
-use crate::header::VERSION_HEADER_V1;
-use crypto_provider::{
- aes::ctr::AesCtrNonce,
- ed25519::{Ed25519Provider, PrivateKey, PublicKey, Signature},
-};
-use sink::{Sink, SinkWriter};
-
-use crate::NP_SVC_UUID;
-
-/// A struct representing the necessary contents
-/// of an v1 advertisement section np_ed25519 signature payload which
-/// come after the context prefix (shared among all advs).
-pub(crate) struct SectionSignaturePayload<'a> {
- /// first 1-2 bytes of format
- format_bytes: &'a [u8],
- /// Salt bytes
- salt_bytes: &'a [u8],
- /// Nonce for en/decrypting the section
- nonce: &'a AesCtrNonce,
- /// plaintext identity token
- plaintext_identity_token: &'a [u8],
- /// the len of the rest of the section contents stored as an u8
- section_payload_len: u8,
- /// plaintext identity token
- plaintext_data_elements: &'a [u8],
-}
-
-const ADV_SIGNATURE_CONTEXT: np_ed25519::SignatureContext = {
- match np_ed25519::SignatureContext::from_string_bytes("Advertisement Signed Section") {
- Ok(x) => x,
- Err(_) => panic!(),
- }
-};
-
-impl<'a> SectionSignaturePayload<'a> {
- /// Construct a section signature payload with separate section len and
- /// remaining contents of the section header.
- ///
- /// The section header should be in its on-the-wire form.
- pub(crate) fn new(
- format_bytes: &'a [u8],
- salt_bytes: &'a [u8],
- nonce: &'a AesCtrNonce,
- plaintext_identity_token: &'a [u8],
- section_payload_len: u8,
- plaintext_data_elements: &'a [u8],
- ) -> Self {
- Self {
- format_bytes,
- salt_bytes,
- nonce,
- plaintext_identity_token,
- section_payload_len,
- plaintext_data_elements,
- }
- }
-
- /// Generates a signature for this section signing payload using
- /// the given Ed25519 key-pair.
- pub(crate) fn sign<E: Ed25519Provider>(self, private_key: &PrivateKey) -> Signature {
- np_ed25519::sign_with_context::<E, _>(private_key, &ADV_SIGNATURE_CONTEXT, self)
- .expect("section signature payloads should fit in signature buffer")
- }
-
- /// Verifies a signature for this section signing payload using
- /// the given Ed25519 public key.
- pub(crate) fn verify<E: Ed25519Provider>(
- self,
- signature: Signature,
- public_key: &PublicKey,
- ) -> Result<(), np_ed25519::SignatureVerificationError> {
- np_ed25519::verify_signature_with_context::<E, _>(
- public_key,
- &ADV_SIGNATURE_CONTEXT,
- self,
- signature,
- )
- }
-}
-
-impl<'a> SinkWriter for SectionSignaturePayload<'a> {
- type DataType = u8;
-
- fn write_payload<S: Sink<u8> + ?Sized>(self, sink: &mut S) -> Option<()> {
- sink.try_extend_from_slice(&NP_SVC_UUID)?;
- sink.try_push(VERSION_HEADER_V1)?;
- sink.try_extend_from_slice(self.format_bytes)?;
- sink.try_extend_from_slice(self.salt_bytes)?;
- sink.try_extend_from_slice(self.nonce)?;
- sink.try_extend_from_slice(self.plaintext_identity_token)?;
- sink.try_push(self.section_payload_len)?;
- sink.try_extend_from_slice(self.plaintext_data_elements)
- }
-}
diff --git a/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs b/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs
index 2d3827e..df17d5b 100644
--- a/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs
@@ -23,12 +23,12 @@
#[test]
fn adv_encode_unencrypted() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut public_identity_section_builder =
adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
public_identity_section_builder
- .add_de(|_| DummyDataElement { de_type: 30_u32.into(), data: vec![] })
+ .add_de(&DummyDataElement { de_type: 30_u8.into(), data: vec![] })
.unwrap();
public_identity_section_builder.add_to_advertisement::<CryptoProviderImpl>();
@@ -36,7 +36,7 @@
assert_eq!(
&[
0x20, // NP version header
- V1_ENCODING_UNENCRYPTED,
+ V1_ENCODING_UNENCRYPTED.0,
0x02, // section len
0x80, // de header, 0 length
30,
@@ -50,7 +50,7 @@
// up to section len - 2 to leave room for NP version header, section length,
// and header
for section_contents_len in 0..=BLE_5_ADV_SVC_MAX_CONTENT_LEN - 3 {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
fill_section_builder(section_contents_len, &mut section_builder).unwrap();
@@ -67,27 +67,23 @@
}
// one longer won't fit, though
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
assert_eq!(
- AddDataElementError::InsufficientSectionSpace,
+ AddDataElementError::InsufficientSpace,
fill_section_builder(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 2, &mut section_builder).unwrap_err()
);
}
#[test]
fn building_capacity_0_ble5_section_works() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
// leave room for NP version header, section length and header
fill_section_builder(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 3, &mut section_builder).unwrap();
- // this section can fill everything except the NP version header
- assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 1, section_builder.section.capacity);
- assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 1, section_builder.section.len());
-
section_builder.add_to_advertisement::<CryptoProviderImpl>();
assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN, adv_builder.into_advertisement().as_slice().len());
diff --git a/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs b/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs
index 515ae97..ef8f5dc 100644
--- a/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs
@@ -21,46 +21,47 @@
#[test]
fn de_header_1_byte_0s() {
- let hdr = DeHeader { de_type: 0_u32.into(), len: 0_u8.try_into().unwrap() };
+ let hdr = DeHeader { de_type: 0_u8.into(), len: 0_u8.try_into().unwrap() };
assert_eq!(&[0x00], hdr.serialize().as_slice());
}
#[test]
fn de_header_1_byte_max() {
- let hdr = DeHeader { de_type: 15_u32.into(), len: 7_u8.try_into().unwrap() };
+ let hdr = DeHeader { de_type: 15_u8.into(), len: 7_u8.try_into().unwrap() };
assert_eq!(&[0x7F], hdr.serialize().as_slice());
}
#[test]
fn de_header_2_byte_len_too_big() {
- let hdr = DeHeader { de_type: 15_u32.into(), len: 8_u8.try_into().unwrap() };
+ let hdr = DeHeader { de_type: 15_u8.into(), len: 8_u8.try_into().unwrap() };
assert_eq!(&[0x88, 0x0F], hdr.serialize().as_slice());
}
#[test]
fn de_header_2_byte_type_too_big() {
- let hdr = DeHeader { de_type: 16_u32.into(), len: 7_u8.try_into().unwrap() };
+ let hdr = DeHeader { de_type: 16_u8.into(), len: 7_u8.try_into().unwrap() };
assert_eq!(&[0x87, 0x10], hdr.serialize().as_slice());
}
#[test]
fn de_header_max() {
- let hdr = DeHeader { de_type: u32::MAX.into(), len: 127_u8.try_into().unwrap() };
+ let hdr =
+ DeHeader { de_type: 0xFFFFFFFEu32.try_into().unwrap(), len: 127_u8.try_into().unwrap() };
assert_eq!(
// first type byte has 3x 0 bits because there are 35 total bits in 5 chunks of 7 bits, but
// only 32 bits to start with
- &[0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0x7F],
+ &[0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0x7E],
hdr.serialize().as_slice()
);
}
#[test]
fn de_header_special_values() {
- for de_type in [0_u32, 1, 15, 16, u32::MAX - 1, u32::MAX].iter().map(|t| DeType::from(*t)) {
+ for de_type in [0_u32, 1, 15, 16, 0xFFFFFFFEu32].iter().map(|t| DeType::try_from(*t).unwrap()) {
for len in [0_u8, 1, 7, 8, 126, 127].iter().map(|l| DeLength::try_from(*l).unwrap()) {
let hdr = DeHeader { de_type, len };
let buf = hdr.serialize();
@@ -70,14 +71,7 @@
let (_, deser) = deserialize::data_element::DeHeader::parse(buf.as_slice()).unwrap();
- assert_eq!(
- deserialize::data_element::DeHeader {
- de_type,
- contents_len: len,
- header_bytes: buf
- },
- deser
- )
+ assert_eq!(deserialize::data_element::DeHeader { de_type, contents_len: len }, deser)
}
}
}
@@ -87,7 +81,10 @@
let mut rng = rand_ext::seeded_rng();
for _ in 0..100_000 {
- let hdr = DeHeader { de_type: rng.gen(), len: rng.gen() };
+ let hdr = DeHeader {
+ de_type: rng.gen_range(0..=0xFFFFFFFEu32).try_into().unwrap(),
+ len: rng.gen(),
+ };
let buf = hdr.serialize();
let header_len = expected_header_len(hdr);
@@ -95,11 +92,7 @@
let (_, deser) = deserialize::data_element::DeHeader::parse(buf.as_slice()).unwrap();
assert_eq!(
- deserialize::data_element::DeHeader {
- de_type: hdr.de_type,
- contents_len: hdr.len,
- header_bytes: buf
- },
+ deserialize::data_element::DeHeader { de_type: hdr.de_type, contents_len: hdr.len },
deser
)
}
@@ -119,9 +112,3 @@
DeLength::try_from(rng.gen_range(0_u8..128)).unwrap()
}
}
-
-impl distributions::Distribution<DeType> for distributions::Standard {
- fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> DeType {
- rng.gen::<u32>().into()
- }
-}
diff --git a/nearby/presence/np_adv/src/extended/serialize/mod.rs b/nearby/presence/np_adv/src/extended/serialize/mod.rs
index ad9acab..5e1af0b 100644
--- a/nearby/presence/np_adv/src/extended/serialize/mod.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/mod.rs
@@ -26,14 +26,14 @@
//! };
//!
//! // no section identities or DEs need salt in this example
-//! let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+//! let mut adv_builder = AdvBuilder::new();
//! let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
//!
-//! section_builder.add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
+//! section_builder.add_de(&TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
//!
//! // add some other DE with type = 1000
-//! section_builder.add_de_res(|_salt|
-//! GenericDataElement::try_from( DeType::from(1000_u32), &[10, 11, 12, 13])
+//! section_builder.add_de(
+//! &GenericDataElement::try_from( DeType::from(1000_u16), &[10, 11, 12, 13]).unwrap()
//! ).unwrap();
//!
//! section_builder.add_to_advertisement::<CryptoProviderImpl>();
@@ -41,7 +41,7 @@
//! assert_eq!(
//! &[
//! 0x20, // version header
-//! V1_ENCODING_UNENCRYPTED, //section format
+//! V1_ENCODING_UNENCRYPTED.byte_value(), //section format
//! 0x09, // section length
//! 0x15, 3, // tx power
//! 0x84, 0x87, 0x68, 10, 11, 12, 13, // other DE
@@ -55,14 +55,16 @@
//! ```
//! use np_adv::{
//! credential::{ v1::{V1, V1BroadcastCredential}},
-//! extended::{data_elements::*, serialize::*, de_type::DeType, V1IdentityToken },
+//! extended::{data_elements::*, serialize::*, de_type::{DeType, HasDEType}, V1IdentityToken },
+//! extended::salt::DeSalt,
//! };
//! use rand::{Rng as _, SeedableRng as _};
//! use crypto_provider::{CryptoProvider, CryptoRng, ed25519};
//! use crypto_provider_default::CryptoProviderImpl;
//! use np_adv::shared_data::TxPower;
+//! use sink::Sink;
//!
-//! let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+//! let mut adv_builder = AdvBuilder::new();
//!
//! // these would come from the credential
//!
@@ -75,7 +77,6 @@
//! let broadcast_cm = V1BroadcastCredential::new(
//! key_seed,
//! identity_token,
-//! ed25519::PrivateKey::generate::<<CryptoProviderImpl as CryptoProvider>::Ed25519>(),
//! );
//!
//! let mut section_builder = adv_builder.section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
@@ -83,14 +84,28 @@
//! &broadcast_cm,
//! )).unwrap();
//!
-//! section_builder.add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
+//! section_builder.add_de(&TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
//!
-//! // add some other DE with type = 1000
-//! section_builder.add_de_res(|salt|
-//! GenericDataElement::try_from(
-//! DeType::from(1000_u32),
-//! &do_fancy_crypto(salt.derive::<16, CryptoProviderImpl>().expect("16 is a valid HKDF length")))
-//! ).unwrap();
+//! // Sample `WriteDataElement` implementor which
+//! // leverages a provided salt to write a payload.
+//! struct FancyCryptoDataElement;
+//!
+//! impl HasDEType for FancyCryptoDataElement {
+//! const DE_TYPE: DeType = DeType::const_from(1000_u32);
+//! }
+//!
+//! impl WriteDataElement for FancyCryptoDataElement {
+//! // Mark that we actually want derived DE salts.
+//! type Salt = DeSalt;
+//! fn write_de_contents<S: Sink<u8>>(&self, salt: Self::Salt, sink: &mut S) -> Option<()> {
+//! let derived_salt = salt.derive::<16, CryptoProviderImpl>().expect("16 is a valid HDKF length");
+//! let contents = do_fancy_crypto(derived_salt);
+//! sink.try_extend_from_slice(&contents)
+//! }
+//! }
+//!
+//! // add our fancy-crypto DE with type = 1000
+//! section_builder.add_de(&FancyCryptoDataElement).unwrap();
//!
//! section_builder.add_to_advertisement::<CryptoProviderImpl>();
//!
@@ -114,32 +129,29 @@
extern crate std;
use core::fmt::{self, Display};
+use core::marker::PhantomData;
use array_view::ArrayView;
-use crypto_provider::CryptoProvider;
-use np_hkdf::v1_salt::{DataElementOffset, ExtendedV1Salt};
-use sink::Sink;
+use sink::{Sink, SinkWriter};
use crate::extended::{
- de_requires_extended_bit, de_type::DeType, serialize::section::EncodedSection, to_array_view,
- DeLength, BLE_5_ADV_SVC_MAX_CONTENT_LEN, NP_ADV_MAX_SECTION_LEN, NP_V1_ADV_MAX_SECTION_COUNT,
+ de_requires_extended_bit,
+ de_type::{DeType, HasDEType},
+ salt::DeSalt,
+ serialize::section::EncodedSection,
+ DeLength, EncodingType, BLE_5_ADV_SVC_MAX_CONTENT_LEN, MAX_DE_LEN, NP_ADV_MAX_SECTION_LEN,
+ NP_V1_ADV_MAX_SECTION_COUNT,
};
-mod section;
+pub(crate) mod section;
use crate::header::VERSION_HEADER_V1;
pub use section::{
- encoder::{
- MicEncryptedSectionEncoder, SectionEncoder, SignedEncryptedSectionEncoder,
- UnencryptedSectionEncoder,
- },
+ encoder::{MicEncryptedSectionEncoder, SectionEncoder, UnencryptedSectionEncoder},
AddDataElementError, SectionBuilder,
};
#[cfg(test)]
-use crate::header::V1AdvHeader;
-
-#[cfg(test)]
pub(crate) mod adv_tests;
#[cfg(test)]
mod de_header_tests;
@@ -152,12 +164,20 @@
#[derive(Debug)]
pub struct AdvBuilder {
// TODO make this configurable, and test making sections whose length is not restricted by BLE limitations
- /// Contains the adv header byte
- adv: tinyvec::ArrayVec<[u8; BLE_5_ADV_SVC_MAX_CONTENT_LEN]>,
- /// To track the number of sections that are in the advertisement
- section_count: usize,
- /// Advertisement type: Public or Encrypted
- advertisement_type: AdvertisementType,
+ /// Contains the adv header byte, and keeps chunks-of-serialized-section
+ /// bytes thereafter sorted by their [`EncodingType`].
+ adv: SortedChunkSlice<
+ EncodingType,
+ u8,
+ MutableSliceInArray<u8, BLE_5_ADV_SVC_MAX_CONTENT_LEN>,
+ NP_V1_ADV_MAX_SECTION_COUNT,
+ >,
+}
+
+impl Default for AdvBuilder {
+ fn default() -> Self {
+ Self::new()
+ }
}
impl AsMut<AdvBuilder> for AdvBuilder {
@@ -168,10 +188,12 @@
impl AdvBuilder {
/// Build an [AdvBuilder].
- pub fn new(advertisement_type: AdvertisementType) -> Self {
- let mut adv = tinyvec::ArrayVec::new();
- adv.push(VERSION_HEADER_V1);
- Self { adv, section_count: 0, advertisement_type }
+ pub fn new() -> Self {
+ let mut adv = [0u8; BLE_5_ADV_SVC_MAX_CONTENT_LEN];
+ adv[0] = VERSION_HEADER_V1;
+ let adv = MutableSliceInArray::new_with_immutable_prefix(adv, 1..);
+ let adv = SortedChunkSlice::new(adv);
+ Self { adv }
}
/// Create a section builder whose contents may be added to this advertisement.
@@ -185,8 +207,8 @@
&mut self,
section_encoder: SE,
) -> Result<SectionBuilder<&mut AdvBuilder, SE>, AddSectionError> {
- let (header_len, contents) = self.prepare_section_builder_buffer(§ion_encoder)?;
- Ok(SectionBuilder::new(header_len, contents, section_encoder, self))
+ let section_buffer = self.prepare_section_builder_buffer(§ion_encoder)?;
+ Ok(SectionBuilder::new(section_buffer, section_encoder, self))
}
/// Create a section builder which actually takes ownership of this advertisement builder.
@@ -201,67 +223,82 @@
section_encoder: SE,
) -> Result<SectionBuilder<AdvBuilder, SE>, (AdvBuilder, AddSectionError)> {
match self.prepare_section_builder_buffer::<SE>(§ion_encoder) {
- Ok((header_len, section)) => {
- Ok(SectionBuilder::new(header_len, section, section_encoder, self))
- }
+ Ok(section_buffer) => Ok(SectionBuilder::new(section_buffer, section_encoder, self)),
Err(err) => Err((self, err)),
}
}
/// Convert the builder into an encoded advertisement.
pub fn into_advertisement(self) -> EncodedAdvertisement {
- EncodedAdvertisement { adv: to_array_view(self.adv) }
+ // Determine how many bytes we've written, and destructure
+ // our wrappers around the underlying output array.
+ let num_post_header_bytes = self.adv.len();
+ let raw_bytes = self.adv.finish().into_data();
+ let total_len = num_post_header_bytes + 1;
+ let adv = ArrayView::try_from_array(raw_bytes, total_len)
+ .expect("Advertisement length calculation should never exceed max length");
+ EncodedAdvertisement { adv }
}
/// Gets the current number of sections added to this advertisement
/// builder, not counting any outstanding SectionBuilders.
pub fn section_count(&self) -> usize {
- self.section_count
+ self.adv.num_chunks()
}
- /// Returns the length of the header (excluding the leading length byte),
- /// and a buffer already populated with a placeholder section length byte and the rest
- /// of the header.
+ /// Returns a restricted-mutability view of a section's byte-buffer which is already
+ /// populated with header information in an immutable prefix, also with an immutable
+ /// reserved suffix of the length of the section encoder's suffix length.
+ /// The mutable region of the returned byte-buffer will correspond only
+ /// to the writeable region for data elements.
fn prepare_section_builder_buffer<SE: SectionEncoder>(
&self,
section_encoder: &SE,
- ) -> Result<(usize, CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>), AddSectionError> {
- if self.section_count >= NP_V1_ADV_MAX_SECTION_COUNT {
+ ) -> Result<MutableSliceInArray<u8, NP_ADV_MAX_SECTION_LEN>, AddSectionError> {
+ if self.section_count() >= NP_V1_ADV_MAX_SECTION_COUNT {
return Err(AddSectionError::MaxSectionCountExceeded);
}
- if self.advertisement_type != SE::ADVERTISEMENT_TYPE {
- return Err(AddSectionError::IncompatibleSectionType);
- }
// The header contains all the header bytes except for the final length byte.
let header = section_encoder.header();
let header_slice = header.as_slice();
- // the max overall len available to the section
- let available_len = self.adv.capacity() - self.adv.len();
+ // The max overall len available to the section
+ // Calculated by subtracting the adv header byte from
+ // the max advertisement content length, and then accounting
+ // for any sections that we've written previously.
+ let available_len = BLE_5_ADV_SVC_MAX_CONTENT_LEN - 1 - self.adv.len();
- let mut prefix = available_len
+ let suffix_start_index = available_len
.checked_sub(SE::SUFFIX_LEN)
- .and_then(CapacityLimitedVec::new)
.ok_or(AddSectionError::InsufficientAdvSpace)?;
- prefix.try_extend_from_slice(header_slice).ok_or(AddSectionError::InsufficientAdvSpace)?;
- // Placeholder for section length, which we do not know yet
- prefix.try_push(0).ok_or(AddSectionError::InsufficientAdvSpace)?;
- Ok((header_slice.len(), prefix))
+
+ // Ensure that header + length byte will not overlap where the start
+ // of the suffix would need to be [or else we're out of space.]
+ if header_slice.len() >= suffix_start_index {
+ return Err(AddSectionError::InsufficientAdvSpace);
+ }
+
+ // Allocate a buffer for the section
+ // and insert the header and a placeholder for the section length.
+ let mut buffer = [0u8; NP_ADV_MAX_SECTION_LEN];
+ buffer[..header_slice.len()].copy_from_slice(header_slice);
+ buffer[header_slice.len()] = 0u8;
+ Ok(MutableSliceInArray::new_with_mutable_bounds(
+ buffer,
+ (header_slice.len() + 1)..suffix_start_index,
+ ))
}
/// Add the section, which must have come from a SectionBuilder generated from this, into this
/// advertisement.
fn add_section(&mut self, section: EncodedSection) {
+ // Peel off the encoding type (first byte) from the encoded section,
+ // since we'll be using that for section sorting.
+ let encoding_type = EncodingType(section.as_slice()[0]);
self.adv
- .try_extend_from_slice(section.as_slice())
- .expect("section capacity enforced in the section builder");
- self.section_count += 1;
- }
-
- #[cfg(test)]
- fn adv_header(&self) -> V1AdvHeader {
- V1AdvHeader::new(self.adv[0])
+ .push_slice(encoding_type, section.as_slice())
+ .expect("Section capacity enforced in the section builder");
}
}
@@ -272,8 +309,6 @@
InsufficientAdvSpace,
/// The advertisement can only hold a maximum of NP_V1_ADV_MAX_SECTION_COUNT number of sections
MaxSectionCountExceeded,
- /// An incompatible section trying to be added
- IncompatibleSectionType,
}
impl Display for AddSectionError {
@@ -285,9 +320,6 @@
AddSectionError::MaxSectionCountExceeded => {
write!(f, "The advertisement can only hold a maximum of {NP_V1_ADV_MAX_SECTION_COUNT} number of sections")
}
- AddSectionError::IncompatibleSectionType => {
- write!(f, "Public and Encrypted sections cannot be mixed in the same advertisement")
- }
}
}
}
@@ -313,56 +345,91 @@
}
}
-/// The advertisement type, which dictates what sections can exist
-#[derive(Debug, PartialEq, Eq)]
-pub enum AdvertisementType {
- /// Plaintext advertisement with only plaintext sections
- Plaintext,
- /// Encrypted advertisement with only encrypted sections
- Encrypted,
+/// Trait for things which may provide a
+/// [`DeType`] for a given instance. This is a slight
+/// relaxation of [`HasDEType`] to data-types whose
+/// associated DE type may be determined at run-time.
+pub trait ProvidesDEType {
+ /// Returns the DE type of this instance.
+ fn de_type(&self) -> DeType;
}
-/// Derived salt for an individual data element.
-pub struct DeSalt {
- salt: ExtendedV1Salt,
- de_offset: DataElementOffset,
-}
-
-impl DeSalt {
- /// Derive salt of the requested length.
- ///
- /// The length must be a valid HKDF-SHA256 length.
- pub fn derive<const N: usize, C: CryptoProvider>(&self) -> Option<[u8; N]> {
- self.salt.derive::<N, C>(Some(self.de_offset))
+impl<H: HasDEType> ProvidesDEType for H {
+ fn de_type(&self) -> DeType {
+ Self::DE_TYPE
}
}
-/// For DE structs that only implement one DE type, rather than multi-type impls.
-pub trait SingleTypeDataElement {
- /// The DE type for the DE.
- const DE_TYPE: DeType;
-}
-
/// Writes data for a V1 DE into a provided buffer.
///
-/// V1 data elements can be hundreds of bytes, so we ideally wouldn't even stack allocate a buffer
+/// V1 data elements can be hundreds of bytes, so we ideally wouldn't maintain a buffer
/// big enough for that, hence an abstraction that writes into an existing buffer.
-pub trait WriteDataElement {
- /// Returns the DE header that will be serialized into the section.
- fn de_header(&self) -> DeHeader;
+///
+/// Implementors should ensure that the length of the contents to write are checked
+/// to ensure that they fit within a DE before they are added to a section to ensure
+/// that errors are surfaced early and accurately in client applications.
+pub trait WriteDataElement: ProvidesDEType {
+ /// The type of derived salts required to write this data element.
+ /// Most likely to be just `Unsalted`, with occasional uses for `DeSalt`
+ /// if the data element's payload requires additional cryptography.
+ type Salt: Into<Option<DeSalt>>;
+
/// Write just the contents of the DE, returning `Some` if all contents could be written and
/// `None` otherwise.
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()>;
+ ///
+ /// This method is allowed to leave the passed [`Sink`] in a dirty state
+ /// for incomplete (failed) writes. The caller of this method must appropriately
+ /// deal with such states.
+ fn write_de_contents<S: Sink<u8>>(&self, salt: Self::Salt, sink: &mut S) -> Option<()>;
+
+ /// Write the entire DE, including the header, returning `Some` if all contents could be
+ /// written and `None` otherwise.
+ ///
+ /// This method is allowed to leave the passed [`Sink`] in a dirty state
+ /// for incomplete (failed) writes. The caller of this method must appropriately
+ /// deal with such states.
+ fn write_de<S: Sink<u8>>(&self, salt: Self::Salt, sink: &mut S) -> Option<()> {
+ // TODO: In some glorious future, we could develop an abstraction layer
+ // where the underlying advertisement content array is actually slightly larger
+ // than the requested size, so that we could write a fake max-length multi-byte
+ // DE header first, then the contents of a DE. Then, after we do so, we could
+ // write the contents of the DE and then "fix up" the header to possibly
+ // use fewer bytes, and finally shift over the DE contents as needed.
+ // For now, we use an auxiliary buffer instead of this more complex process,
+ // which unfortunately means we use more memory, but we keep the interface
+ // of this method using a `Sink<u8>` to allow for this improvement in the future.
+
+ // First, write the DE contents into an internal buffer
+ let mut de_contents: tinyvec::ArrayVec<[u8; MAX_DE_LEN]> = tinyvec::ArrayVec::new();
+ self.write_de_contents(salt, &mut de_contents)?;
+ let de_contents = de_contents;
+
+ // Construct the DE header from what was written.
+ let de_len = DeLength::try_from(de_contents.len())
+ .expect("We made a buffer of the max DE contents len, so all contained lengths should be valid.");
+
+ let de_header = DeHeader::new(self.de_type(), de_len);
+
+ // Try to write the header followed by a copy of the DE contents.
+ sink.try_extend_from_slice(de_header.serialize().as_slice())?;
+ sink.try_extend_from_slice(de_contents.as_slice())
+ }
}
-// convenience impl for &W
-impl<W: WriteDataElement> WriteDataElement for &W {
- fn de_header(&self) -> DeHeader {
- (*self).de_header()
- }
+/// Structure implementing [`WriteDataElement`]
+/// for references to [`WriteDataElement`] instances.
+pub struct RefWriteDataElement<'a, W>(&'a W);
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
- (*self).write_de_contents(sink)
+impl<W: ProvidesDEType> ProvidesDEType for RefWriteDataElement<'_, W> {
+ fn de_type(&self) -> DeType {
+ self.0.de_type()
+ }
+}
+
+impl<W: WriteDataElement> WriteDataElement for RefWriteDataElement<'_, W> {
+ type Salt = <W as WriteDataElement>::Salt;
+ fn write_de_contents<S: Sink<u8>>(&self, salt: Self::Salt, sink: &mut S) -> Option<()> {
+ self.0.write_de_contents(salt, sink)
}
}
@@ -380,6 +447,11 @@
DeHeader { de_type, len }
}
+ /// Gets the DeType of this header.
+ pub fn de_type(&self) -> DeType {
+ self.de_type
+ }
+
/// Serialize the DE header as per the V1 DE header format:
/// - 1 byte form for length <= 3 bits, type <= 4 bits: `0LLLTTTT`
/// - multi byte form: `0b1LLLLLLL [0b1TTTTTTT ...] 0b0TTTTTTT`
@@ -436,6 +508,355 @@
}
}
+/// Wrapper around a fixed-size array which provides an `AsMut<[T]>` implementation
+/// which gives out a fixed-position slice which excludes some fixed-length
+/// prefix of the array and some fixed-length suffix. Useful to provide
+/// a restricted, owned [`AsMut`] view into a slice of an array for which
+/// elements outside the slice are treated as immutable.
+///
+/// Zero-length slices (as determined by the passed index range) are valid,
+/// and can be interpreted as the underlying array being completely immutable
+/// (`AsMut` will hand out an empty slice).
+///
+/// Indexing operations will panic if the range provided upon construction
+/// does not correspond to a valid range of the wrapped data array.
+#[derive(Debug)]
+pub(crate) struct MutableSliceInArray<T, const N: usize> {
+ mutable_range: core::ops::Range<usize>,
+ data: [T; N],
+}
+
+impl<T, const N: usize> MutableSliceInArray<T, N> {
+ /// Constructs a new wrapper around the given
+ /// array for which only the given index range
+ /// is exposed as mutable as part of the [`AsMut`] implementation.
+ pub(crate) fn new_with_mutable_bounds(
+ data: [T; N],
+ mutable_range: core::ops::Range<usize>,
+ ) -> Self {
+ Self { data, mutable_range }
+ }
+ /// Constructs a new wrapper around the given
+ /// array for which only the given index range
+ /// is exposed as mutable as part of the [`AsMut`] implementation.
+ pub(crate) fn new_with_immutable_prefix(
+ data: [T; N],
+ mutable_range: core::ops::RangeFrom<usize>,
+ ) -> Self {
+ Self { data, mutable_range: core::ops::Range { start: mutable_range.start, end: N } }
+ }
+
+ /// Gets the range of mutable indices.
+ pub(crate) fn mutable_range(&self) -> core::ops::Range<usize> {
+ self.mutable_range.clone()
+ }
+
+ /// De-structures this restricted-mutability array wrapper back into
+ /// a regular array, dropping any restrictions on mutability.
+ pub(crate) fn into_data(self) -> [T; N] {
+ self.data
+ }
+}
+
+impl<T, const N: usize> AsMut<[T]> for MutableSliceInArray<T, N> {
+ fn as_mut(&mut self) -> &mut [T] {
+ &mut self.data[self.mutable_range.clone()]
+ }
+}
+
+/// Error raised when a chunk has an improper size for an operation
+/// of [`SortedChunkSlice`].
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub(crate) enum ImproperChunkSizeError {
+ /// The chunk is zero-length
+ ZeroLength,
+ /// The chunk is too long
+ TooLong,
+}
+
+#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
+/// (N - 1) representation of the size of a chunk in a [`SortedChunkSlice`].
+struct ChunkSize(u8);
+
+impl TryFrom<usize> for ChunkSize {
+ type Error = ImproperChunkSizeError;
+ fn try_from(size: usize) -> Result<Self, Self::Error> {
+ if size > 256 {
+ Err(ImproperChunkSizeError::TooLong)
+ } else if size == 0 {
+ Err(ImproperChunkSizeError::ZeroLength)
+ } else {
+ Ok(Self((size - 1) as u8))
+ }
+ }
+}
+
+impl ChunkSize {
+ fn size(&self) -> usize {
+ (self.0 as usize) + 1
+ }
+}
+
+/// Errors which may be raised from [`SortedChunkSlice#push`].
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub(crate) enum ChunkPushError {
+ /// The slice to copy a chunk from was of an improper size.
+ ImproperChunkSizeError(ImproperChunkSizeError),
+ /// The capacity of the sorted array of chunks would be
+ /// exceeded by attempting to push the given slice.
+ CapacityExhaustedError,
+ /// We cannot push the new chunk, because we have hit
+ /// the limit on the allowable number of chunks.
+ TooManyChunks,
+}
+
+impl From<ImproperChunkSizeError> for ChunkPushError {
+ fn from(err: ImproperChunkSizeError) -> Self {
+ Self::ImproperChunkSizeError(err)
+ }
+}
+
+/// A container (+ metadata) of a mutable buffer managing contiguous "chunks"
+/// of items within that buffer [possibly of differing lengths] with associated keys
+/// which are mean to be kept ordered by key as new elements ("chunks") are added.
+///
+/// This structure is add-only, and only ever overwrites as much data in the wrapped
+/// mutable slice as is consumed by the contiguous chunks managed by the structure.
+///
+/// The sizes of individual chunks are restricted to be between 1 and 256, inclusive.
+///
+/// To avoid logic errors, the buffer leveraged by this function should
+/// NEVER have its associated `AsMut` implementation modify the size or extent
+/// of the returned slice. Doing so may result in unexpected results and/or panics.
+///
+/// The type parameter `N` denotes the maximum number of chunks which may be tracked
+/// by this structure.
+#[derive(Debug)]
+pub(crate) struct SortedChunkSlice<K: Ord + Default, T: Clone, S: AsMut<[T]>, const N: usize> {
+ /// The actual contents of the underlying buffer of chunks.
+ buffer: S,
+ /// How many elements (not chunks) are currently being managed by this data
+ /// structure. Equal to the sum of the chunk sizes in `chunk_metadata`, but
+ /// useful for quickly finding the end of the managed region.
+ consumed_buffer_len: usize,
+ /// Metadata about each chunk (key, chunk size)
+ chunk_metadata: tinyvec::ArrayVec<[(K, ChunkSize); N]>,
+ /// Indicator to keep the `AsMut<[T]>` implementation constant, because
+ /// we don't want that or the array type changing, otherwise the chunk sizes may
+ /// no longer hold due to the returned slices being different.
+ _marker: PhantomData<fn(&mut S) -> &mut [T]>,
+}
+
+impl<K: Ord + Default, T: Clone, S: AsMut<[T]>, const N: usize> SortedChunkSlice<K, T, S, N> {
+ /// Constructs a new [`SortedChunkSlice`] from the given mutable buffer,
+ /// with contents initially empty. Nothing is changed about the original buffer.
+ pub(crate) fn new(buffer: S) -> Self {
+ Self {
+ buffer,
+ consumed_buffer_len: 0,
+ chunk_metadata: tinyvec::ArrayVec::new(),
+ _marker: PhantomData,
+ }
+ }
+ /// Returns the current number of chunks maintained by this structure.
+ pub(crate) fn num_chunks(&self) -> usize {
+ self.chunk_metadata.len()
+ }
+
+ /// Returns `true` if this structure has a chunk with the given key.
+ pub(crate) fn contains_key(&self, query_key: &K) -> bool {
+ // A linear scan is likely more efficient than binary search
+ // for the (small) data sizes involved.
+ for (key, _) in self.chunk_metadata.iter() {
+ match key.cmp(query_key) {
+ core::cmp::Ordering::Greater => {
+ return false;
+ }
+ core::cmp::Ordering::Equal => {
+ return true;
+ }
+ core::cmp::Ordering::Less => {
+ continue;
+ }
+ }
+ }
+ false
+ }
+
+ /// Helper which corrects a partially-updated version of this structure
+ /// where all chunks are in order by key except for possibly the last one,
+ /// but the structure is otherwise entirely valid.
+ ///
+ /// Will panic if the structure contains no chunks.
+ fn fixup_last_chunk(&mut self) {
+ // NOTE: This is likely not the most time-efficient way of doing this,
+ // but that's not much of a problem for the intended applications,
+ // since serialization is non-performance-critical. It could be
+ // possible that there are better ways to lay out the data structure
+ // while retaining similar space efficiency, but faster overall.
+
+ // Extract the mutable slice reference to the underlying buffer,
+ // we'll only ever deal with this (no std::mem::replace shenanigans.)
+ let buffer = self.buffer.as_mut();
+
+ // Pop the metadata of the last chunk so we can determine
+ // where it goes. Note that the underlying buffer and the
+ // tracked length have not changed!
+ let (key, chunk_size) =
+ self.chunk_metadata.pop().expect("No last chunk to fixup, why is this being called?");
+
+ // Take the key and determine the destination "metadata index" (0-based)
+ // of the chunk we're adding among all chunks in the buffer,
+ // along with the index into the underlying buffer.
+ // It's okay for this to be O(n), since the rest of this method
+ // is anyway in the case where all elements need to move.
+ let mut dest_metadata_index = 0;
+ let mut dest_buffer_index = 0;
+ for (other_key, other_chunk_size) in &self.chunk_metadata {
+ // Every time we see that our key is bigger, move the tentative
+ // destination down one chunk. Stop when this no longer holds.
+ if &key > other_key {
+ dest_metadata_index += 1;
+ dest_buffer_index += other_chunk_size.size();
+ } else {
+ break;
+ }
+ }
+ let dest_metadata_index = dest_metadata_index;
+ let dest_buffer_index = dest_buffer_index;
+
+ // Information-gathering complete, time to do the big rotate.
+ // We leverage the "triple reversal rotation" mentioned
+ // here: https://github.com/scandum/rotate.
+ // We could do much better using the other techniques there,
+ // but ideally we'd have a crate dependency which implements them.
+
+ let last_chunk_begin_index = self.consumed_buffer_len - chunk_size.size();
+ // Reverse the most-recently added chunk.
+ buffer[last_chunk_begin_index..self.consumed_buffer_len].reverse();
+ // Reverse everything before the most-recently-added
+ // chunk which is after the destination index for the chunk.
+ buffer[dest_buffer_index..last_chunk_begin_index].reverse();
+ // Reverse the concatenation.
+ buffer[dest_buffer_index..self.consumed_buffer_len].reverse();
+
+ // Buffer elements are now in place, all that's left
+ // is to correct the position of the metadata.
+ //
+ // Will not panic because we previously removed this element from
+ // the metadata array at a different position.
+ self.chunk_metadata.insert(dest_metadata_index, (key, chunk_size));
+ }
+
+ /// Attempts to write a new chunk into the buffer with the supplied
+ /// writer. May fail with if the writer exhausts all available space, it
+ /// writes zero or >256 chunk elements, or if writing the chunk would
+ /// result in exceeding the maximum number of chunks storable in this structure.
+ ///
+ /// In case of failure, the contents of the underlying buffer up to the
+ /// reported length (prior to this operation) of this object will
+ /// remain unchanged, but the writer may overwrite bytes in the buffer
+ /// beyond the end of this structure's stated length.
+ pub(crate) fn write_chunk<W: SinkWriter<DataType = T>>(
+ &mut self,
+ key: K,
+ writer: W,
+ ) -> Result<(), ChunkPushError> {
+ let previously_consumed_buffer_len = self.consumed_buffer_len;
+ let buffer = self.buffer.as_mut();
+
+ // Before we do anything wild, make sure that we could actually push
+ // a new chunk to begin with.
+ if self.chunk_metadata.len() == N {
+ return Err(ChunkPushError::TooManyChunks);
+ }
+
+ // Write into our buffer.
+ let mut sink = tinyvec::SliceVec::from_slice_len(buffer, self.consumed_buffer_len);
+ match writer.write_payload(&mut sink) {
+ Some(()) => {
+ // Successful write, determine how much we wrote.
+ let written_chunk_len = sink.len() - previously_consumed_buffer_len;
+
+ // Make sure that the size of what we've written can be expressed for a chunk.
+ // No big deal if this bails, since we haven't updated
+ // any of our state variables other than adding extra
+ // garbage bytes beyond the current cursor.
+ let chunk_size = ChunkSize::try_from(written_chunk_len)?;
+
+ // All looks good, update state variables.
+ self.consumed_buffer_len = sink.len();
+ self.chunk_metadata.push((key, chunk_size));
+
+ // Fix ordering and return.
+ self.fixup_last_chunk();
+ Ok(())
+ }
+ None => {
+ // Unsuccessful write. Thankfully, we haven't updated any
+ // of our state other than adding extra garbage bytes beyond
+ // the current cursor, and so we can just return.
+ Err(ChunkPushError::CapacityExhaustedError)
+ }
+ }
+ }
+
+ /// Attempts to insert a new chunk into the buffer, reordering
+ /// existing chunks as needed to ensure that chunks remain sorted
+ /// by key. May fail due to the underlying buffer capacity being
+ /// exhausted or due to the source slice being of an improper size
+ /// (zero or >256 elements) or due to reaching the maximum number
+ /// of chunks storable in this structure.
+ ///
+ /// In case of failure, the contents of the underlying buffer will
+ /// remain unchanged.
+ pub(crate) fn push_slice(&mut self, key: K, chunk: &[T]) -> Result<(), ChunkPushError> {
+ // Extract the mutable slice reference to the underlying buffer,
+ // we'll only ever deal with this (no std::mem::replace shenanigans.)
+ let buffer = self.buffer.as_mut();
+
+ // Before doing anything, make sure the chunk is the right
+ // size and that we won't run out of space by pushing it.
+ if self.chunk_metadata.len() == N {
+ return Err(ChunkPushError::TooManyChunks);
+ }
+
+ let chunk_size = ChunkSize::try_from(chunk.len())?;
+
+ if self.consumed_buffer_len + chunk.len() > buffer.len() {
+ return Err(ChunkPushError::CapacityExhaustedError);
+ }
+
+ // Append the chunk (ordering is fixed later).
+ self.chunk_metadata.push((key, chunk_size));
+ buffer[self.consumed_buffer_len..(self.consumed_buffer_len + chunk.len())]
+ .clone_from_slice(chunk);
+ self.consumed_buffer_len += chunk.len();
+
+ // Fix ordering and return.
+ self.fixup_last_chunk();
+ Ok(())
+ }
+
+ /// Gets the total length of all chunks consumed via this structure
+ /// in the underlying buffer.
+ pub(crate) fn len(&self) -> usize {
+ self.consumed_buffer_len
+ }
+ /// Deconstructs this sorted slice containing chunks into just the raw
+ /// buffer contents, including any sorted chunks that we've built,
+ /// but discarding any keys or other metadata.
+ ///
+ /// Note that this will yield the _entire_ buffer, not the portion
+ /// of the buffer which was populated using this data-structure.
+ ///
+ /// To truncate appropriately [if necessary], leverage the `len()`
+ /// method on this structure.
+ pub(crate) fn finish(self) -> S {
+ self.buffer
+ }
+}
+
/// A wrapper around a fixed-size tinyvec that can have its capacity further constrained to handle
/// dynamic size limits.
#[derive(Debug)]
@@ -474,10 +895,6 @@
self.capacity
}
- fn truncate(&mut self, len: usize) {
- self.vec.truncate(len);
- }
-
pub(crate) fn into_inner(self) -> tinyvec::ArrayVec<[T; N]> {
self.vec
}
@@ -508,3 +925,183 @@
}
}
}
+
+#[cfg(test)]
+#[allow(clippy::unwrap_used)]
+mod tests {
+ use super::*;
+ use alloc::vec::Vec;
+ use rand::prelude::SliceRandom;
+ use rand::rngs::StdRng;
+ use rand::{Rng, SeedableRng};
+
+ extern crate std;
+
+ #[test]
+ fn backwards_insert_sorted_chunks_array() {
+ let mut result: SortedChunkSlice<usize, _, _, 5> = SortedChunkSlice::new([0u8; 50]);
+ result.push_slice(5, &[5, 4, 3, 2, 1]).unwrap();
+ result.push_slice(4, &[7, 6]).unwrap();
+ result.push_slice(3, &[10, 9, 8]).unwrap();
+ result.push_slice(2, &[15, 14, 13, 12, 11]).unwrap();
+ result.push_slice(1, &[16]).unwrap();
+ let len = result.len();
+ let result = result.finish();
+ assert_eq!(&result[..len], &[16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
+ }
+ #[test]
+ fn insert_empty_slice_sorted_chunks_array() {
+ let mut result: SortedChunkSlice<u8, _, _, 1> = SortedChunkSlice::new([0u8; 10]);
+ let empty_slice: &[u8] = &[];
+ let err = result.push_slice(1, empty_slice).expect_err("Empty slices should be disallowed");
+ assert_eq!(err, ChunkPushError::ImproperChunkSizeError(ImproperChunkSizeError::ZeroLength));
+ let len = result.len();
+ let result = result.finish();
+ assert_eq!(&result[..len], empty_slice);
+ }
+ #[test]
+ fn capacity_exhausted_cancels_write_sorted_chunks_array() {
+ let mut result: SortedChunkSlice<u8, _, _, 2> = SortedChunkSlice::new([0u8; 3]);
+ result.push_slice(2, &[0xFF, 0xEE]).unwrap();
+ let err = result
+ .push_slice(1, &[0xDD, 0xCC])
+ .expect_err("Attempting to push too many elements should fail.");
+ assert_eq!(err, ChunkPushError::CapacityExhaustedError);
+ let len = result.len();
+ let result = result.finish();
+ assert_eq!(&result[..len], &[0xFF, 0xEE]);
+ }
+ #[test]
+ fn too_many_chunks_cancels_write() {
+ let mut result: SortedChunkSlice<u8, _, _, 1> = SortedChunkSlice::new([0u8; 3]);
+ result.push_slice(2, &[0xFF, 0xEE]).unwrap();
+ let err = result
+ .push_slice(1, &[0xDD, 0xCC])
+ .expect_err("Attempting to push too many chunks should fail.");
+ assert_eq!(err, ChunkPushError::TooManyChunks);
+ let len = result.len();
+ let result = result.finish();
+ assert_eq!(&result[..len], &[0xFF, 0xEE]);
+ }
+ #[test]
+ fn chunk_size_out_of_range_sorted_chunks_array() {
+ let mut result: SortedChunkSlice<u8, _, _, 2> = SortedChunkSlice::new([0u8; 600]);
+ result.push_slice(2, &[0xFF; 256]).expect("256 is an okay chunk length.");
+ let err =
+ result.push_slice(1, &[0xEE; 257]).expect_err("len 257 is too large for a chunk.");
+ assert_eq!(err, ChunkPushError::ImproperChunkSizeError(ImproperChunkSizeError::TooLong));
+ let len = result.len();
+ let result = result.finish();
+ assert_eq!(&result[..len], &[0xFF; 256]);
+ }
+
+ /// Simple [`SinkWriter`] implementation
+ /// which pushes bytes from the given slice
+ /// one at a time.
+ struct ByteSliceSinkWriter<'a> {
+ slice: &'a [u8],
+ }
+
+ impl SinkWriter for ByteSliceSinkWriter<'_> {
+ type DataType = u8;
+ fn write_payload<S: Sink<u8>>(self, sink: &mut S) -> Option<()> {
+ for byte in self.slice {
+ sink.try_push(*byte)?;
+ }
+ Some(())
+ }
+ }
+ #[test]
+ fn slice_pushing_matches_writer_pushing() {
+ let mut rng = StdRng::from_entropy();
+ for _ in 0..10_000 {
+ let num_chunks: usize = rng.gen_range(1..=16);
+ let chunk_max_len: usize = rng.gen_range(1..=16);
+
+ // Deliberately picking 15 max chunks to check
+ // the "full" edge-case, and deliberately
+ // picking a 128-byte capacity so that it will
+ // be exceeded in some test-cases.
+ let mut slices_pushed: SortedChunkSlice<u16, _, _, 15> =
+ SortedChunkSlice::new([0u8; 128]);
+ let mut writers_pushed: SortedChunkSlice<u16, _, _, 15> =
+ SortedChunkSlice::new([0u8; 128]);
+
+ for _ in 0..num_chunks {
+ let key: u16 = rng.gen();
+ let chunk_len: usize = rng.gen_range(1..=chunk_max_len);
+ let mut value = Vec::new();
+ for _ in 0..chunk_len {
+ value.push(rng.gen());
+ }
+
+ let slice_push_result = slices_pushed.push_slice(key, &value);
+ let writer = ByteSliceSinkWriter { slice: &value };
+ let writer_push_result = writers_pushed.write_chunk(key, writer);
+ assert_eq!(slice_push_result, writer_push_result);
+ }
+ // Check that the contents of the two SortedChunkSlices
+ // are the same up to their stated written length.
+ let slices_len = slices_pushed.len();
+ let writers_len = writers_pushed.len();
+ let slices_pushed = slices_pushed.finish();
+ let writers_pushed = writers_pushed.finish();
+ assert_eq!(&slices_pushed[..slices_len], &writers_pushed[..writers_len]);
+ }
+ }
+
+ // TODO: Add test that the writer variant is equivalent to
+ // the slice variant for adding chunks.
+
+ #[test]
+ // The ranged loop at the end _is_ necessary, since it doesn't
+ // have the same semantics w.r.t. panics as what the iterator solution
+ // would accomplish.
+ #[allow(clippy::needless_range_loop)]
+ fn randomized_chunk_sorting() {
+ let mut rng = StdRng::from_entropy();
+ for _ in 0..10_000 {
+ let mut runs: Vec<Vec<usize>> = Vec::new();
+ let mut counter: usize = 0;
+ // Create up to 2-16 randomly-split runs of 1-16 elements
+ // each, where the underlying counter always increases.
+ let num_runs = rng.gen_range(2..=16);
+ for _ in 0..num_runs {
+ let run_len = rng.gen_range(1..=16);
+ let mut run = Vec::new();
+ for _ in 0..run_len {
+ run.push(counter);
+ counter += 1;
+ }
+ runs.push(run);
+ }
+ // Gather how many elements were inserted, since
+ // we'll be using this to ensure that the vector
+ // gets sorted in an increasing order.
+ let total_num_elements = counter;
+
+ // Randomize the insertion order.
+ runs.shuffle(&mut rng);
+
+ let mut result: SortedChunkSlice<usize, _, _, 16> =
+ SortedChunkSlice::new([0usize; 256]);
+
+ // Insert the runs into the sorted chunks array, with the key
+ // set as the first element.
+ for run in runs.drain(..) {
+ let key = run[0];
+ result
+ .push_slice(key, run.as_slice())
+ .expect("We should be able to push this many slices of this size.");
+ }
+
+ let result = result.finish();
+
+ // Verify that we have a contiguous vector
+ // counting up to `total_num_elements`.
+ for i in 0..total_num_elements {
+ assert_eq!(result[i], i);
+ }
+ }
+ }
+}
diff --git a/nearby/presence/np_adv/src/extended/serialize/section/encoder.rs b/nearby/presence/np_adv/src/extended/serialize/section/encoder.rs
index 85f55bc..1dcde2e 100644
--- a/nearby/presence/np_adv/src/extended/serialize/section/encoder.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/section/encoder.rs
@@ -13,27 +13,20 @@
// limitations under the License.
use crate::credential::v1::V1BroadcastCredential;
-use crate::extended::salt::{MultiSalt, ShortV1Salt, V1Salt};
-use crate::header::VERSION_HEADER_V1;
-use crate::{
- extended::{
- deserialize::SectionMic,
- section_signature_payload::SectionSignaturePayload,
- serialize::{section::header::SectionHeader, AdvertisementType, DeSalt},
- V1IdentityToken, V1_IDENTITY_TOKEN_LEN,
- },
- NP_SVC_UUID,
+use crate::extended::salt::{DeSalt, MultiSalt, ShortV1Salt, V1Salt};
+use crate::extended::{
+ deserialize::SectionMic, salt::Unsalted, serialize::section::header::SectionHeader,
+ V1IdentityToken, V1_IDENTITY_TOKEN_LEN,
};
+use crate::header::VERSION_HEADER_V1;
use crypto_provider::{
aes,
aes::ctr::{AesCtr as _, AesCtrNonce, NonceAndCounter},
- ed25519,
hmac::Hmac,
CryptoProvider, CryptoRng as _,
};
-use np_hkdf::v1_salt::EXTENDED_SALT_LEN;
use np_hkdf::{
- v1_salt::{DataElementOffset, ExtendedV1Salt},
+ v1_salt::{DeType, ExtendedV1Salt},
DerivedSectionKeys,
};
@@ -42,11 +35,8 @@
/// How much space needs to be reserved after the DEs
const SUFFIX_LEN: usize;
- /// The advertisement type that can support this section
- const ADVERTISEMENT_TYPE: AdvertisementType;
-
/// The type of derived salt produced for a DE sharing a section with this identity.
- type DerivedSalt;
+ type DerivedSalt: Into<Option<DeSalt>>;
/// Header to write at the start of the section contents
fn header(&self) -> SectionHeader;
@@ -68,7 +58,7 @@
);
/// Produce a `Self::Output` salt for a DE.
- fn de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt;
+ fn de_salt(&self, de_type: DeType) -> Self::DerivedSalt;
}
/// Encoder for plaintext data elements
@@ -79,9 +69,8 @@
impl SectionEncoder for UnencryptedSectionEncoder {
const SUFFIX_LEN: usize = 0;
- const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Plaintext;
- type DerivedSalt = ();
+ type DerivedSalt = Unsalted;
fn header(&self) -> SectionHeader {
SectionHeader::unencrypted()
@@ -95,98 +84,8 @@
// no op
}
- fn de_salt(&self, _de_offset: DataElementOffset) -> Self::DerivedSalt {}
-}
-
-/// Encrypts the data elements and protects integrity with an np_ed25519 signature
-/// using key material derived from an NP identity.
-pub struct SignedEncryptedSectionEncoder {
- pub(crate) salt: ExtendedV1Salt,
- identity_token: V1IdentityToken,
- private_key: ed25519::PrivateKey,
- aes_key: aes::Aes128Key,
-}
-
-impl SignedEncryptedSectionEncoder {
- /// Build a [SignedEncryptedSectionEncoder] from an identity type,
- /// some broadcast crypto-material, and with a random salt.
- pub fn new_random_salt<C: CryptoProvider>(
- rng: &mut C::CryptoRng,
- crypto_material: &V1BroadcastCredential,
- ) -> Self {
- let salt: ExtendedV1Salt = rng.gen::<[u8; 16]>().into();
- Self::new::<C>(salt, crypto_material)
- }
-
- /// Build a [SignedEncryptedSectionEncoder] from an identity type,
- /// a provided salt, and some broadcast crypto-material.
- pub(crate) fn new<C: CryptoProvider>(
- salt: ExtendedV1Salt,
- crypto_material: &V1BroadcastCredential,
- ) -> Self {
- let identity_token = crypto_material.identity_token();
- let key_seed = crypto_material.key_seed();
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
- let private_key = crypto_material.signing_key();
- let aes_key = key_seed_hkdf.v1_signature_keys().aes_key();
- Self { salt, identity_token, private_key, aes_key }
- }
-}
-
-impl SectionEncoder for SignedEncryptedSectionEncoder {
- /// Ed25519 signature
- const SUFFIX_LEN: usize = crypto_provider::ed25519::SIGNATURE_LENGTH;
- const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Encrypted;
-
- type DerivedSalt = DeSalt;
-
- fn header(&self) -> SectionHeader {
- SectionHeader::encrypted_signature_extended_salt(&self.salt, &self.identity_token)
- }
-
- fn postprocess<C: CryptoProvider>(
- &mut self,
- section_header_without_length: &mut [u8],
- section_len: u8,
- remaining_contents: &mut [u8],
- ) {
- let nonce: AesCtrNonce = self.salt.compute_nonce::<C>();
- let mut cipher = C::AesCtr128::new(&self.aes_key, NonceAndCounter::from_nonce(nonce));
-
- let start_of_identity_token = section_header_without_length.len() - V1_IDENTITY_TOKEN_LEN;
- let (format_and_salt_bytes, identity_token) =
- section_header_without_length.split_at_mut(start_of_identity_token);
- debug_assert!(identity_token.len() == V1_IDENTITY_TOKEN_LEN);
-
- let start_of_salt = format_and_salt_bytes.len() - EXTENDED_SALT_LEN;
- let (format_bytes, salt_bytes) = format_and_salt_bytes.split_at_mut(start_of_salt);
- debug_assert!(salt_bytes.len() == EXTENDED_SALT_LEN);
- debug_assert!((1..=2).contains(&format_bytes.len()));
-
- let start_of_signature = remaining_contents.len() - Self::SUFFIX_LEN;
- let (plaintext_data_elements, sig) = remaining_contents.split_at_mut(start_of_signature);
- debug_assert!(sig.len() == Self::SUFFIX_LEN);
-
- let section_signature_payload = SectionSignaturePayload::new(
- format_bytes,
- salt_bytes,
- &nonce,
- identity_token,
- section_len,
- plaintext_data_elements,
- );
-
- sig.copy_from_slice(
- §ion_signature_payload.sign::<C::Ed25519>(&self.private_key).to_bytes(),
- );
-
- cipher.apply_keystream(identity_token);
- cipher.apply_keystream(plaintext_data_elements);
- cipher.apply_keystream(sig);
- }
-
- fn de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
- DeSalt { salt: self.salt, de_offset }
+ fn de_salt(&self, _de_offset: DeType) -> Self::DerivedSalt {
+ Unsalted
}
}
@@ -255,8 +154,6 @@
/// Length of mic
const SUFFIX_LEN: usize = SectionMic::CONTENTS_LEN;
- const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Encrypted;
-
type DerivedSalt = S::DerivedSalt;
fn header(&self) -> SectionHeader {
@@ -293,8 +190,6 @@
// calculate MAC per the spec
let mut section_hmac = self.mic_hmac_key.build_hmac::<C>();
- // svc uuid
- section_hmac.update(NP_SVC_UUID.as_slice());
// adv header
section_hmac.update(&[VERSION_HEADER_V1]);
// section format and salt
@@ -312,21 +207,21 @@
mic.copy_from_slice(§ion_hmac.finalize()[..SectionMic::CONTENTS_LEN]);
}
- fn de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
- self.salt.derive_de_salt(de_offset)
+ fn de_salt(&self, de_type: DeType) -> Self::DerivedSalt {
+ self.salt.derive_de_salt(de_type)
}
}
/// Behavior for salts used with MIC sections.
pub trait MicSectionEncoderSalt: V1Salt {
- /// The type of derived data produced, or `()` if no data can be derived.
- type DerivedSalt;
+ /// The type of derived data produced, or `Unsalted` if no data can be derived.
+ type DerivedSalt: Into<Option<DeSalt>>;
/// Build the appropriate header for the type of salt used
fn header(&self, identity_token: &V1IdentityToken) -> SectionHeader;
- /// Derive a DE salt at the specified offset.
- fn derive_de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt;
+ /// Derive a DE salt for the specified DE type.
+ fn derive_de_salt(&self, de_type: DeType) -> Self::DerivedSalt;
/// Derive the AES key suitable for this salt type
fn derive_aes_key<C: CryptoProvider>(&self, hkdf: &np_hkdf::NpKeySeedHkdf<C>)
@@ -346,8 +241,8 @@
SectionHeader::encrypted_mic_extended_salt(self, identity_token)
}
- fn derive_de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
- DeSalt { salt: *self, de_offset }
+ fn derive_de_salt(&self, de_type: DeType) -> Self::DerivedSalt {
+ DeSalt { salt: *self, de_type }
}
fn derive_aes_key<C: CryptoProvider>(
@@ -367,13 +262,15 @@
// TODO is this impl used?
impl MicSectionEncoderSalt for ShortV1Salt {
- type DerivedSalt = ();
+ type DerivedSalt = Unsalted;
fn header(&self, identity_token: &V1IdentityToken) -> SectionHeader {
SectionHeader::encrypted_mic_short_salt(*self, identity_token)
}
- fn derive_de_salt(&self, _de_offset: DataElementOffset) -> Self::DerivedSalt {}
+ fn derive_de_salt(&self, _de_type: DeType) -> Self::DerivedSalt {
+ Unsalted
+ }
fn derive_aes_key<C: CryptoProvider>(
&self,
@@ -400,10 +297,10 @@
}
}
- fn derive_de_salt(&self, de_offset: DataElementOffset) -> Self::DerivedSalt {
+ fn derive_de_salt(&self, de_type: DeType) -> Self::DerivedSalt {
match self {
MultiSalt::Short(_) => None,
- MultiSalt::Extended(s) => Some(DeSalt { salt: *s, de_offset }),
+ MultiSalt::Extended(s) => Some(DeSalt { salt: *s, de_type }),
}
}
diff --git a/nearby/presence/np_adv/src/extended/serialize/section/header.rs b/nearby/presence/np_adv/src/extended/serialize/section/header.rs
index 5e8b2e4..9b97b47 100644
--- a/nearby/presence/np_adv/src/extended/serialize/section/header.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/section/header.rs
@@ -15,15 +15,14 @@
use crate::extended::salt::ShortV1Salt;
use crate::extended::{
V1IdentityToken, V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
- V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN,
- V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
+ V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
};
use np_hkdf::v1_salt::ExtendedV1Salt;
-/// Format || salt || token
-pub(crate) const SECTION_HEADER_MAX_LEN: usize = 2 + 16 + 16;
+/// Length of a section header without its trailing length byte: `format || salt || token`
+pub(crate) const SECTION_HEADER_MAX_LEN: usize = 1 + 16 + 16;
-/// Assembles the header bytes for a section after the section length, before postprocessing by
+/// Assembles the header bytes for a section before the section length, before postprocessing by
/// a [SectionEncoder](crate::extended::serialize::section::SectionEncoder).
///
/// Does not include the overall NP header byte that defines the adv version.
@@ -36,13 +35,13 @@
impl SectionHeader {
pub(crate) fn unencrypted() -> Self {
let mut header_bytes = tinyvec::ArrayVec::new();
- header_bytes.push(V1_ENCODING_UNENCRYPTED);
+ header_bytes.push(V1_ENCODING_UNENCRYPTED.0);
Self { header_bytes }
}
pub(crate) fn encrypted_mic_short_salt(salt: ShortV1Salt, token: &V1IdentityToken) -> Self {
let mut header_bytes = tinyvec::ArrayVec::new();
- header_bytes.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN);
+ header_bytes.push(V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN.0);
header_bytes.extend_from_slice(salt.bytes().as_slice());
header_bytes.extend_from_slice(token.bytes().as_slice());
Self { header_bytes }
@@ -53,18 +52,7 @@
token: &V1IdentityToken,
) -> Self {
let mut header_bytes = tinyvec::ArrayVec::new();
- header_bytes.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
- header_bytes.extend_from_slice(salt.bytes().as_slice());
- header_bytes.extend_from_slice(token.bytes().as_slice());
- Self { header_bytes }
- }
-
- pub(crate) fn encrypted_signature_extended_salt(
- salt: &ExtendedV1Salt,
- token: &V1IdentityToken,
- ) -> Self {
- let mut header_bytes = tinyvec::ArrayVec::new();
- header_bytes.push(V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN);
+ header_bytes.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
header_bytes.extend_from_slice(salt.bytes().as_slice());
header_bytes.extend_from_slice(token.bytes().as_slice());
Self { header_bytes }
@@ -94,7 +82,7 @@
#[test]
fn unencrypted_slice() {
- assert_eq!(&[V1_ENCODING_UNENCRYPTED], SectionHeader::unencrypted().as_slice());
+ assert_eq!(&[V1_ENCODING_UNENCRYPTED.0], SectionHeader::unencrypted().as_slice());
}
#[rustfmt::skip]
@@ -139,26 +127,4 @@
.as_slice()
);
}
-
- #[rustfmt::skip]
- #[test]
- fn encrypted_sig_extended_salt_slice() {
- assert_eq!(
- &[
- // format
- 0x03,
- // salt
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
- 0x1C, 0x1D, 0x1E, 0x1F,
- // token
- 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
- 0x2C, 0x2D, 0x2E, 0x2F,
- ],
- SectionHeader::encrypted_signature_extended_salt(
- &EXTENDED_SALT_BYTES.into(),
- &TOKEN_BYTES.into()
- )
- .as_slice()
- );
- }
}
diff --git a/nearby/presence/np_adv/src/extended/serialize/section/mod.rs b/nearby/presence/np_adv/src/extended/serialize/section/mod.rs
index 1b8a331..d163ecf 100644
--- a/nearby/presence/np_adv/src/extended/serialize/section/mod.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/section/mod.rs
@@ -16,35 +16,43 @@
extern crate std;
use array_view::ArrayView;
-use core::{convert, fmt};
+use core::fmt;
use crypto_provider::CryptoProvider;
-use np_hkdf::v1_salt::DataElementOffset;
-use sink::Sink as _;
+use sink::{Sink, SinkWriter};
use crate::extended::{
+ de_type::{DeType, OptionDeType},
+ salt::SaltConvertible,
serialize::{
- section::encoder::SectionEncoder, AdvBuilder, CapacityLimitedVec, WriteDataElement,
+ section::encoder::SectionEncoder, AdvBuilder, MutableSliceInArray, SortedChunkSlice,
+ WriteDataElement,
},
- to_array_view, NP_ADV_MAX_SECTION_LEN,
+ NP_ADV_MAX_SECTION_LEN,
};
pub(crate) mod encoder;
pub(crate) mod header;
+/// Type of `SectionBuilder.section`, used for keeping track
+/// of the added section header and any DEs.
+pub(crate) type SectionBuilderInternals = SortedChunkSlice<
+ OptionDeType, // These will never be `OptionDeType::NONE`, we just need `Default`.
+ u8,
+ MutableSliceInArray<u8, NP_ADV_MAX_SECTION_LEN>,
+ NP_ADV_MAX_SECTION_LEN, // At most as many DEs as there are bytes.
+>;
+
/// Accumulates data elements and encodes them into a section.
#[derive(Debug)]
pub struct SectionBuilder<R: AsMut<AdvBuilder>, SE: SectionEncoder> {
- /// The length of the header produced by `section_encoder`
- pub(crate) header_len: usize,
- /// Contains the section header, the identity-specified overhead, and any DEs added
- pub(crate) section: CapacityLimitedVec<u8, { NP_ADV_MAX_SECTION_LEN }>,
+ /// Contains the section header + identity-specified overhead, and keeps
+ /// chunks of serialized DE bytes thereafter sorted by their [`DeType`].
+ pub(crate) section: SectionBuilderInternals,
pub(crate) section_encoder: SE,
/// mut ref-able to enforce only one active section builder at a time
pub(crate) adv_builder: R,
- next_de_offset: DataElementOffset,
}
-
-impl<'a, SE: SectionEncoder> SectionBuilder<&'a mut AdvBuilder, SE> {
+impl<SE: SectionEncoder> SectionBuilder<&mut AdvBuilder, SE> {
/// Add this builder to the advertisement that created it.
pub fn add_to_advertisement<C: CryptoProvider>(self) {
let _ = self.add_to_advertisement_internal::<C>();
@@ -52,9 +60,9 @@
}
impl<SE: SectionEncoder> SectionBuilder<AdvBuilder, SE> {
- /// Gets the 0-based index of the section currently under construction
- /// in the context of the containing advertisement.
- pub fn section_index(&self) -> usize {
+ /// Gets the count of the sections which were added to the advertisement
+ /// prior to the creation of this section builder.
+ pub fn previously_added_section_count(&self) -> usize {
self.adv_builder.section_count()
}
/// Add this builder to the advertisement that created it,
@@ -65,158 +73,187 @@
}
impl<R: AsMut<AdvBuilder>, SE: SectionEncoder> SectionBuilder<R, SE> {
+ /// Constructs a new section builder from the given buffer
+ /// already populated with padding for an encoding-specific header
+ /// and whose mutable portion is just the writable space for data elements,
+ /// the section encoder implementation, and the containing advertisement builder.
pub(crate) fn new(
- header_len: usize,
- section: CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>,
+ section: MutableSliceInArray<u8, NP_ADV_MAX_SECTION_LEN>,
section_encoder: SE,
adv_builder: R,
) -> Self {
- Self {
- header_len,
- section,
- section_encoder,
- adv_builder,
- next_de_offset: DataElementOffset::ZERO,
- }
+ let section = SortedChunkSlice::new(section);
+ Self { section, section_encoder, adv_builder }
+ }
+
+ /// Helper function which is used to convert from a completed `SectionBuilder.section`
+ /// into a `MutableSliceInArray` of the format expected by `SectionBuilder#build()`
+ /// (mutable region bounded just around the data elements).
+ pub(crate) fn finished_adding_des(
+ section: SectionBuilderInternals,
+ ) -> MutableSliceInArray<u8, NP_ADV_MAX_SECTION_LEN> {
+ let des_written_len = section.len();
+ let untruncated = section.finish();
+ let mut data_range = untruncated.mutable_range();
+ data_range.end = data_range.start + des_written_len;
+ let data_range = data_range;
+ let underlying = untruncated.into_data();
+ MutableSliceInArray::new_with_mutable_bounds(underlying, data_range)
}
/// Add this builder to the advertisement that created it.
/// Returns the mut-refable to the advertisement builder
/// which the contents of this section builder were added to.
- //TODO: make this fallible, if the section being added is invalid, right now it is possible to
- // create invalid adv's that don't parse
fn add_to_advertisement_internal<C: CryptoProvider>(mut self) -> R {
let adv_builder = self.adv_builder.as_mut();
adv_builder.add_section(Self::build_section::<C>(
- self.header_len,
- self.section.into_inner(),
+ Self::finished_adding_des(self.section),
self.section_encoder,
));
self.adv_builder
}
- /// Gets the derived salt which will be employed for the next DE offset.
+ /// Gets the derived salt which would be employed for the given DE type.
///
/// Suitable for scenarios (like FFI) where a closure would be inappropriate
/// for DE construction, and interaction with the client is preferred.
- pub fn next_de_salt(&self) -> SE::DerivedSalt {
- self.section_encoder.de_salt(self.next_de_offset)
+ pub fn de_salt(&self, de_type: DeType) -> SE::DerivedSalt {
+ self.section_encoder.de_salt(de_type)
}
- /// Add a data element to the section with a closure that returns a `Result`.
+ /// Attempt to add a data element to the section.
///
- /// The provided `build_de` closure will be invoked with the derived salt for this DE.
- pub fn add_de_res<W: WriteDataElement, E, F: FnOnce(SE::DerivedSalt) -> Result<W, E>>(
- &mut self,
- build_de: F,
- ) -> Result<(), AddDataElementError<E>> {
- let writer = build_de(self.next_de_salt()).map_err(AddDataElementError::BuildDeError)?;
+ /// May fail if the contents of the DE exceed the available
+ /// space left in the section. If this happens, the contents
+ /// of the section will not change.
+ pub fn add_de<W: WriteDataElement>(&mut self, de: &W) -> Result<(), AddDataElementError>
+ where
+ <W as WriteDataElement>::Salt: SaltConvertible<SE::DerivedSalt>,
+ {
+ let de_type = de.de_type();
- let orig_len = self.section.len();
- // since we own the writer, and it's immutable, no race risk writing header w/ len then
- // the contents as long as it's not simply an incorrect impl
- let de_header = writer.de_header();
- let content_len = self
- .section
- .try_extend_from_slice(de_header.serialize().as_slice())
- .ok_or(AddDataElementError::InsufficientSectionSpace)
- .and_then(|_| {
- let after_header_len = self.section.len();
- writer
- .write_de_contents(&mut self.section)
- .ok_or(AddDataElementError::InsufficientSectionSpace)
- .map(|_| self.section.len() - after_header_len)
- })
- .map_err(|e| {
- // if anything went wrong, truncate any partial writes (e.g. just the header)
- self.section.truncate(orig_len);
- e
- })?;
-
- if content_len != usize::from(de_header.len.as_u8()) {
- // TODO eliminate this possibility by keeping a 127-byte buffer
- // to write DEs into, then calculating the written length, so the
- // DE impl doesn't have to do it
- panic!(
- "Buggy WriteDataElement impl: header len {}, actual written len {}",
- de_header.len.as_u8(),
- content_len
- );
+ // Before we do anything, make sure that we're not trying to add a DE
+ // with a duplicate type-code.
+ //
+ // NOTE: We could make this slightly more efficient in the non-error case by fusing
+ // the write operation with the "duplicate key" check in the `SortedChunkSlice`
+ // interface, but doing so naively comes at the cost of eroding the elegance
+ // of the exposed interface, and the potential gain is only a [small] constant
+ // factor improvement.
+ if self.section.contains_key(&de_type.into()) {
+ return Err(AddDataElementError::DuplicateDataElementTypeCode);
}
- self.next_de_offset = self.next_de_offset.incremented();
+ let salt = <<W as WriteDataElement>::Salt as SaltConvertible<
+ <SE as SectionEncoder>::DerivedSalt,
+ >>::convert(self.section_encoder.de_salt(de_type));
- Ok(())
- }
+ /// `SinkWriter` implementation for writing the DE with
+ /// the provided salt.
+ struct DataElementWriter<'a, W: WriteDataElement> {
+ de: &'a W,
+ salt: <W as WriteDataElement>::Salt,
+ }
- /// Add a data element to the section with a closure that returns the data element directly.
- ///
- /// The provided `build_de` closure will be invoked with the derived salt for this DE.
- pub fn add_de<W: WriteDataElement, F: FnOnce(SE::DerivedSalt) -> W>(
- &mut self,
- build_de: F,
- ) -> Result<(), AddDataElementError<convert::Infallible>> {
- self.add_de_res(|derived_salt| Ok::<_, convert::Infallible>(build_de(derived_salt)))
+ impl<W: WriteDataElement> SinkWriter for DataElementWriter<'_, W> {
+ type DataType = u8;
+ fn write_payload<S: Sink<u8>>(self, sink: &mut S) -> Option<()> {
+ self.de.write_de(self.salt, sink)
+ }
+ }
+
+ let writer = DataElementWriter { de, salt };
+
+ // Since `write_de` is a derived method which writes the de header,
+ // we will never have a length-zero (invalid) chunk, and so all
+ // reachable failure modes are different expressions of running out of space.
+ self.section
+ .write_chunk(de_type.into(), writer)
+ .map_err(|_| AddDataElementError::InsufficientSpace)
}
/// Convert a section builder's contents into an encoded section.
///
- /// `section_contents` must have size > 0.
- ///
- /// `header_len` is the length of the prefix of `section_contents` that has been populated
- /// with the data returned from [SectionEncoder::header()] which does NOT include the length byte.
+ /// The passed section contents must have both the section header
+ /// and any subsequent data elements, the mutable region
+ /// should be bounded around only the data elements,
+ /// and there should be at least `SE::SUFFIX_LEN` bytes
+ /// available after the end of the mutable region to
+ /// store the section encoder's suffix (if any).
///
/// Implemented without self to avoid partial-move issues.
pub(crate) fn build_section<C: CryptoProvider>(
- header_len: usize,
- mut section_contents: tinyvec::ArrayVec<[u8; NP_ADV_MAX_SECTION_LEN]>,
+ section_contents: MutableSliceInArray<u8, NP_ADV_MAX_SECTION_LEN>,
mut section_encoder: SE,
) -> EncodedSection {
- // there is space because the capacity for DEs was restricted to allow it
- section_contents.resize(section_contents.len() + SE::SUFFIX_LEN, 0);
+ // Extract the bounds around the DEs on the section buffer.
+ // The lower bound is equal to the length of the section header,
+ // and the upper bound is equal to the length of all currently-written
+ // section contents.
+ let core::ops::Range { start: header_len, end: section_written_len } =
+ section_contents.mutable_range();
+ let de_contents_len = section_written_len - header_len;
- let (format_and_salt_and_identity_token, rest_of_contents) =
- section_contents.split_at_mut(header_len);
+ let mut section_contents = section_contents.into_data();
- let (section_length_byte, rest_of_contents) = rest_of_contents.split_at_mut(1);
+ let (section_header, rest_of_contents) = section_contents.split_at_mut(header_len);
+ let (section_length_byte, encoding_type_and_encoding_specific_header) =
+ section_header.split_last_mut().expect("Section header should be at least one byte");
- let section_len = rest_of_contents.len().try_into().expect(
+ let des_and_section_suffix_len = de_contents_len + SE::SUFFIX_LEN;
+ let des_and_section_suffix = &mut rest_of_contents[..des_and_section_suffix_len];
+
+ let section_len = des_and_section_suffix_len.try_into().expect(
"section length will always fit into a u8 and has been validated by the section builder",
);
// set the section length byte
- section_length_byte[0] = section_len;
+ *section_length_byte = section_len;
+
section_encoder.postprocess::<C>(
- format_and_salt_and_identity_token,
+ encoding_type_and_encoding_specific_header,
section_len,
- rest_of_contents,
+ des_and_section_suffix,
);
- to_array_view(section_contents)
+ let total_len = header_len + des_and_section_suffix_len;
+
+ ArrayView::try_from_array(section_contents, total_len)
+ .expect("Section buffer should have enough capacity for the suffix.")
}
}
/// Errors for adding a DE to a section
#[derive(Debug, PartialEq, Eq)]
-pub enum AddDataElementError<E> {
- /// An error occurred when invoking the DE builder closure.
- BuildDeError(E),
- /// Too much data to fit into the section
- InsufficientSectionSpace,
+pub enum AddDataElementError {
+ /// Too much data to fit into the section and/or too much
+ /// data to fit within the limits of a single DE.
+ ///
+ /// [`WriteDataElement`] implementors should ensure (upon
+ /// construction, or during mutating updates) that their
+ /// header and contents fit within 127 bytes to avoid tripping this
+ /// error upon attempting to add the DE, which may be too late,
+ /// depending on the desired client behavior.
+ InsufficientSpace,
+ /// The attempt to add the data element failed because a data
+ /// element with the same type-code already was added to the section.
+ DuplicateDataElementTypeCode,
}
-impl<E: fmt::Display> fmt::Display for AddDataElementError<E> {
+impl fmt::Display for AddDataElementError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- AddDataElementError::BuildDeError(e) => write!(f, "Build DE error: {}", e),
- AddDataElementError::InsufficientSectionSpace => {
- write!(f, "Insufficient section space")
+ AddDataElementError::InsufficientSpace => {
+ write!(f, "Insufficient space to write DE")
+ }
+ AddDataElementError::DuplicateDataElementTypeCode => {
+ write!(f, "A DE with the same type-code was already added")
}
}
}
}
#[cfg(feature = "std")]
-impl<E: fmt::Debug + fmt::Display> std::error::Error for AddDataElementError<E> {}
+impl std::error::Error for AddDataElementError {}
/// The encoded form of an advertisement section
pub(crate) type EncodedSection = ArrayView<u8, NP_ADV_MAX_SECTION_LEN>;
diff --git a/nearby/presence/np_adv/src/extended/serialize/section_tests.rs b/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
index 9fbd039..5346e79 100644
--- a/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
@@ -18,43 +18,36 @@
use super::*;
use crate::extended::{
- V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
- V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
+ V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
};
use crate::{
credential::v1::V1BroadcastCredential,
extended::{
- data_elements::{GenericDataElement, GenericDataElementError},
+ data_elements::GenericDataElement,
deserialize::SectionMic,
- salt::V1Salt,
- section_signature_payload::SectionSignaturePayload,
+ salt::{ExtendedV1Salt, SaltConvertible, Unsalted, V1Salt},
serialize::AddSectionError::MaxSectionCountExceeded,
V1IdentityToken, V1_IDENTITY_TOKEN_LEN,
},
- NP_SVC_UUID,
};
-use crypto_provider::ed25519;
-use crypto_provider::ed25519::SIGNATURE_LENGTH;
use crypto_provider::{
aes::ctr::{AesCtr, NonceAndCounter},
hmac::Hmac,
- CryptoRng,
+ CryptoProvider, CryptoRng,
};
use crypto_provider_default::CryptoProviderImpl;
-use np_hkdf::v1_salt::EXTENDED_SALT_LEN;
+use np_hkdf::v1_salt::{OptionDeType, EXTENDED_SALT_LEN};
use np_hkdf::DerivedSectionKeys;
use rand::{rngs::StdRng, Rng as _, SeedableRng as _};
use std::{prelude::rust_2021::*, vec};
-type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
-
#[test]
fn unencrypted_section_empty() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
assert_eq!(
- &[V1_ENCODING_UNENCRYPTED, 0_u8],
+ &[V1_ENCODING_UNENCRYPTED.0, 0_u8],
section_builder.into_section::<CryptoProviderImpl>().as_slice()
);
}
@@ -72,27 +65,27 @@
for _ in 0..1_000 {
let num_des = rng.gen_range(1..=5);
- let extra_des = (0..num_des)
+ let mut extra_des = (0..num_des)
.map(|_| {
let de_len = rng.gen_range(0..=30);
DummyDataElement {
- de_type: rng.gen_range(0_u32..=u32::MAX).into(),
+ de_type: rng.gen_range(0_u32..0xFFFFFFFF).try_into().unwrap(),
data: rand_ext::random_vec_rc(&mut rng, de_len),
}
})
.collect::<Vec<_>>();
+ extra_des.sort_by_key(|de| de.de_type);
+ extra_des.dedup_by_key(|de| de.de_type);
+ let extra_des = extra_des;
+
let identity_token: V1IdentityToken = crypto_rng.gen();
let key_seed = rng.gen();
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
- let broadcast_cred = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token);
let mut section_builder =
adv_builder
@@ -103,10 +96,10 @@
let section_salt = section_builder.section_encoder.salt;
for de in extra_des.iter() {
- section_builder.add_de(|_| de).unwrap();
+ section_builder.add_de(de).unwrap();
}
- let section_length = mic_section_len(&extra_des);
+ let section_length = mic_section_len(section_salt, &extra_des);
let mut hmac = key_seed_hkdf
.v1_mic_extended_salt_keys()
@@ -126,16 +119,14 @@
let mut de_contents = Vec::new();
for de in extra_des {
- de_contents.extend_from_slice(de.de_header().serialize().as_slice());
- let _ = de.write_de_contents(&mut de_contents);
+ let _ = de.write_de(Unsalted, &mut de_contents);
}
cipher.apply_keystream(&mut de_contents);
// just to be sure, we'll construct our test hmac all in one update() call
let mut hmac_input = vec![];
- hmac_input.extend_from_slice(NP_SVC_UUID.as_slice());
hmac_input.push(VERSION_HEADER_V1);
- hmac_input.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+ hmac_input.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
hmac_input.extend_from_slice(section_salt.as_slice());
hmac_input.extend_from_slice(nonce.as_slice());
hmac_input.extend_from_slice(&ct_identity_token);
@@ -145,7 +136,7 @@
let mic = hmac.finalize();
let mut expected = vec![];
- expected.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+ expected.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
expected.extend_from_slice(section_salt.as_slice());
expected.extend_from_slice(&ct_identity_token);
expected.push(section_length);
@@ -157,105 +148,16 @@
}
#[test]
-fn signature_encrypted_identity_section_random_des() {
- let mut rng = StdRng::from_entropy();
- let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
-
- for _ in 0..1_000 {
- let num_des = rng.gen_range(1..=5);
-
- let identity_token = V1IdentityToken(rng.gen());
- let key_seed = rng.gen();
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
-
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
-
- let broadcast_cred =
- V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
-
- let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
- &mut crypto_rng,
- &broadcast_cred,
- ))
- .unwrap();
- let section_salt = section_builder.section_encoder.salt;
-
- let extra_des = (0..num_des)
- .map(|_| {
- let de_len = rng.gen_range(0..=30);
- DummyDataElement {
- de_type: rng.gen_range(0_u32..=u32::MAX).into(),
- data: rand_ext::random_vec_rc(&mut rng, de_len),
- }
- })
- // the DEs might not all fit; keep those that do
- .filter_map(|de| section_builder.add_de(|_| de.clone()).ok().map(|_| de))
- .collect::<Vec<_>>();
-
- let section_length = sig_section_len(&extra_des);
- let nonce = section_salt.compute_nonce::<CryptoProviderImpl>();
-
- let mut section_body = Vec::new();
- for de in extra_des {
- section_body.extend_from_slice(de.de_header().serialize().as_slice());
- let _ = de.write_de_contents(&mut section_body);
- }
-
- let mut section_header = vec![];
- section_header
- .extend_from_slice(&[V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN]);
- section_header.extend_from_slice(section_salt.as_slice());
- section_header.extend_from_slice(identity_token.as_slice());
-
- let sig_payload = SectionSignaturePayload::new(
- &[V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN],
- section_salt.as_slice(),
- &nonce,
- identity_token.as_slice(),
- section_length,
- §ion_body,
- );
-
- let mut plaintext = section_body.as_slice().to_vec();
- plaintext
- .extend_from_slice(&sig_payload.sign::<Ed25519ProviderImpl>(&private_key).to_bytes());
-
- let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
- &key_seed_hkdf.v1_signature_keys().aes_key(),
- NonceAndCounter::from_nonce(nonce),
- );
- let start_of_identity_token = section_header.len() - V1_IDENTITY_TOKEN_LEN;
- cipher.apply_keystream(&mut section_header[start_of_identity_token..]);
-
- cipher.apply_keystream(&mut plaintext);
- let ciphertext = plaintext;
-
- let mut expected = vec![];
- expected.extend_from_slice(§ion_header);
- expected.push(section_length);
- expected.extend_from_slice(&ciphertext);
-
- assert_eq!(&expected, section_builder.into_section::<CryptoProviderImpl>().as_slice());
- }
-}
-
-#[test]
fn section_builder_too_full_doesnt_advance_de_index() {
let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
let key_seed = [22; 32];
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
let identity_token = V1IdentityToken([33; 16]);
- let broadcast_cred = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token);
let mut section_builder = adv_builder
.section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
@@ -266,27 +168,36 @@
let salt = section_builder.section_encoder.salt;
section_builder
- .add_de(|de_salt| DummyDataElement {
- de_type: 100_u32.into(),
- data: de_salt.derive::<100, CryptoProviderImpl>().unwrap().to_vec(),
+ .add_de(&DummyDataElement {
+ de_type: 100_u8.into(),
+ data: salt
+ .derive::<100, CryptoProviderImpl>(DeType::from(100_u8).into())
+ .unwrap()
+ .to_vec(),
})
.unwrap();
- // this write won't advance the de offset or the internal section buffer length
+ // this write won't advance the internal section buffer length
assert_eq!(
- AddDataElementError::InsufficientSectionSpace,
+ AddDataElementError::InsufficientSpace,
section_builder
- .add_de(|de_salt| DummyDataElement {
- de_type: 101_u32.into(),
- data: de_salt.derive::<100, CryptoProviderImpl>().unwrap().to_vec(),
+ .add_de(&DummyDataElement {
+ de_type: 101_u8.into(),
+ data: salt
+ .derive::<100, CryptoProviderImpl>(DeType::from(101_u8).into())
+ .unwrap()
+ .to_vec(),
})
.unwrap_err()
);
section_builder
- .add_de(|de_salt| DummyDataElement {
- de_type: 102_u32.into(),
- data: de_salt.derive::<10, CryptoProviderImpl>().unwrap().to_vec(),
+ .add_de(&DummyDataElement {
+ de_type: 102_u8.into(),
+ data: salt
+ .derive::<10, CryptoProviderImpl>(DeType::from(102_u8).into())
+ .unwrap()
+ .to_vec(),
})
.unwrap();
@@ -300,11 +211,15 @@
// de header
expected.extend_from_slice(&[0x80 + 100, 100]);
// section 0 de 0
- expected.extend_from_slice(&salt.derive::<100, CryptoProviderImpl>(Some(0.into())).unwrap());
+ expected.extend_from_slice(
+ &salt.derive::<100, CryptoProviderImpl>(DeType::from(100_u8).into()).unwrap(),
+ );
// de header
expected.extend_from_slice(&[0x80 + 10, 102]);
// section 0 de 1
- expected.extend_from_slice(&salt.derive::<10, CryptoProviderImpl>(Some(1.into())).unwrap());
+ expected.extend_from_slice(
+ &salt.derive::<10, CryptoProviderImpl>(DeType::from(102_u8).into()).unwrap(),
+ );
let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
&key_seed_hkdf.v1_mic_extended_salt_keys().aes_key(),
@@ -324,41 +239,38 @@
fn section_de_fits_exactly() {
// leave room for initial filler section's header and the identities
// for section_contents_capacity in 1..NP_ADV_MAX_SECTION_LEN - 3 {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
// fill up space to produce desired capacity
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
// leave space for adv header, 1 section len, 1 section header and 1 extra byte
fill_section_builder(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 1 - 1 - 1 - 1, &mut section_builder)
.unwrap();
- assert_eq!(1, section_builder.section.capacity() - section_builder.section.len(), "capacity: ");
// can't add a 2 byte DE
+ let two_byte_de = GenericDataElement::try_from(1_u8.into(), &[0xFF]).unwrap();
assert_eq!(
- Err(AddDataElementError::InsufficientSectionSpace),
- section_builder.add_de_res(|_| GenericDataElement::try_from(1_u32.into(), &[0xFF])),
+ Err(AddDataElementError::InsufficientSpace),
+ section_builder.add_de(&two_byte_de),
"capacity: "
);
// can add a 1 byte DE
- section_builder.add_de_res(|_| GenericDataElement::try_from(1_u32.into(), &[])).unwrap();
+ let one_byte_de = GenericDataElement::try_from(1_u8.into(), &[]).unwrap();
+ section_builder.add_de(&one_byte_de).unwrap();
}
#[test]
fn section_builder_build_de_error_doesnt_advance_de_index() {
let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
let key_seed = [22; 32];
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
let identity_token = V1IdentityToken([33; 16]);
- let broadcast_cred = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token);
let mut section_builder = adv_builder
.section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
@@ -369,21 +281,22 @@
let salt = section_builder.section_encoder.salt;
section_builder
- .add_de(|de_salt| DummyDataElement {
- de_type: 100_u32.into(),
- data: de_salt.derive::<100, CryptoProviderImpl>().unwrap().to_vec(),
+ .add_de(&DummyDataElement {
+ de_type: 100_u8.into(),
+ data: salt
+ .derive::<100, CryptoProviderImpl>(DeType::from(100_u8).into())
+ .unwrap()
+ .to_vec(),
})
.unwrap();
- assert_eq!(
- AddDataElementError::BuildDeError(()),
- section_builder.add_de_res(|_| Err::<DummyDataElement, _>(())).unwrap_err()
- );
-
section_builder
- .add_de(|de_salt| DummyDataElement {
- de_type: 103_u32.into(),
- data: de_salt.derive::<10, CryptoProviderImpl>().unwrap().to_vec(),
+ .add_de(&DummyDataElement {
+ de_type: 103_u8.into(),
+ data: salt
+ .derive::<10, CryptoProviderImpl>(DeType::from(103_u8).into())
+ .unwrap()
+ .to_vec(),
})
.unwrap();
@@ -397,11 +310,15 @@
// de header
expected.extend_from_slice(&[0x80 + 100, 100]);
// section 0 de 0
- expected.extend_from_slice(&salt.derive::<100, CryptoProviderImpl>(Some(0.into())).unwrap());
+ expected.extend_from_slice(
+ &salt.derive::<100, CryptoProviderImpl>(DeType::from(100u8).into()).unwrap(),
+ );
// de header
expected.extend_from_slice(&[0x80 + 10, 103]);
// section 0 de 1
- expected.extend_from_slice(&salt.derive::<10, CryptoProviderImpl>(Some(1.into())).unwrap());
+ expected.extend_from_slice(
+ &salt.derive::<10, CryptoProviderImpl>(DeType::from(103u8).into()).unwrap(),
+ );
let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
&key_seed_hkdf.v1_mic_extended_salt_keys().aes_key(),
@@ -421,17 +338,13 @@
fn add_multiple_de_correct_de_offsets_mic_encrypted_identity() {
let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
let key_seed = [22; 32];
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
let identity_token = V1IdentityToken([33; 16]);
- let broadcast_cred = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token);
let mut section_builder = adv_builder
.section_builder(MicEncryptedSectionEncoder::<_>::new_random_salt::<CryptoProviderImpl>(
@@ -442,15 +355,21 @@
let salt = section_builder.section_encoder.salt;
section_builder
- .add_de(|de_salt| DummyDataElement {
- de_type: 64_u32.into(),
- data: de_salt.derive::<16, CryptoProviderImpl>().unwrap().to_vec(),
+ .add_de(&DummyDataElement {
+ de_type: 64_u8.into(),
+ data: salt
+ .derive::<16, CryptoProviderImpl>(DeType::from(64_u8).into())
+ .unwrap()
+ .to_vec(),
})
.unwrap();
section_builder
- .add_de(|de_salt| DummyDataElement {
- de_type: 65_u32.into(),
- data: de_salt.derive::<16, CryptoProviderImpl>().unwrap().to_vec(),
+ .add_de(&DummyDataElement {
+ de_type: 65_u8.into(),
+ data: salt
+ .derive::<16, CryptoProviderImpl>(DeType::from(65_u8).into())
+ .unwrap()
+ .to_vec(),
})
.unwrap();
@@ -464,15 +383,21 @@
// de header
expected.extend_from_slice(&[0x90, 0x40]);
// section 0 de 0
- expected.extend_from_slice(&salt.derive::<16, CryptoProviderImpl>(Some(0.into())).unwrap());
+ expected.extend_from_slice(
+ &salt.derive::<16, CryptoProviderImpl>(DeType::from(64u8).into()).unwrap(),
+ );
// de header
expected.extend_from_slice(&[0x90, 0x41]);
// section 0 de 1
- expected.extend_from_slice(&salt.derive::<16, CryptoProviderImpl>(Some(1.into())).unwrap());
+ expected.extend_from_slice(
+ &salt.derive::<16, CryptoProviderImpl>(DeType::from(65u8).into()).unwrap(),
+ );
let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
&key_seed_hkdf.v1_mic_extended_salt_keys().aes_key(),
- NonceAndCounter::from_nonce(salt.derive::<12, CryptoProviderImpl>(None).unwrap()),
+ NonceAndCounter::from_nonce(
+ salt.derive::<12, CryptoProviderImpl>(OptionDeType::NONE).unwrap(),
+ ),
);
cipher.apply_keystream(&mut expected[..V1_IDENTITY_TOKEN_LEN]);
@@ -485,129 +410,12 @@
}
#[test]
-fn add_multiple_de_correct_de_offsets_signature_encrypted_identity() {
- let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
-
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
- let key_seed = [22; 32];
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let identity_token = V1IdentityToken([33; 16]);
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
-
- let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
-
- let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
- &mut crypto_rng,
- &broadcast_cred,
- ))
- .unwrap();
- let salt = section_builder.section_encoder.salt;
-
- section_builder
- .add_de(|de_salt| DummyDataElement {
- de_type: 64_u32.into(),
- data: de_salt.derive::<16, CryptoProviderImpl>().unwrap().to_vec(),
- })
- .unwrap();
- section_builder
- .add_de(|de_salt| DummyDataElement {
- de_type: 65_u32.into(),
- data: de_salt.derive::<16, CryptoProviderImpl>().unwrap().to_vec(),
- })
- .unwrap();
-
- section_builder.add_to_advertisement::<CryptoProviderImpl>();
-
- let mut expected = vec![];
- // identity token
- expected.extend_from_slice(&identity_token.0);
- //len
- expected.push((18 * 2) + u8::try_from(SIGNATURE_LENGTH).unwrap());
- // de header
- expected.extend_from_slice(&[0x90, 0x40]);
- // section 0 de 0
- expected.extend_from_slice(&salt.derive::<16, CryptoProviderImpl>(Some(0.into())).unwrap());
- // de header
- expected.extend_from_slice(&[0x90, 0x41]);
- // section 0 de 1
- expected.extend_from_slice(&salt.derive::<16, CryptoProviderImpl>(Some(1.into())).unwrap());
-
- let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
- &key_seed_hkdf.v1_signature_keys().aes_key(),
- NonceAndCounter::from_nonce(salt.derive::<12, CryptoProviderImpl>(None).unwrap()),
- );
-
- cipher.apply_keystream(&mut expected[..V1_IDENTITY_TOKEN_LEN]);
- cipher.apply_keystream(&mut expected[V1_IDENTITY_TOKEN_LEN + 1..]);
-
- let adv_bytes = adv_builder.into_advertisement();
- // ignoring the signature since that's tested elsewhere
- assert_eq!(
- &expected,
- // skip adv header + section header + salt
- &adv_bytes.as_slice()[1 + 1 + 16..adv_bytes.as_slice().len() - 64]
- )
-}
-
-#[test]
-fn signature_encrypted_section_de_lengths_allow_room_for_suffix() {
- let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
-
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
-
- let key_seed = [22; 32];
- let metadata_key = V1IdentityToken([33; 16]);
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
-
- let broadcast_cred = V1BroadcastCredential::new(key_seed, metadata_key, private_key.clone());
-
- let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
- &mut crypto_rng,
- &broadcast_cred,
- ))
- .unwrap();
-
- // section header + identity + signature
- let max_total_de_len = NP_ADV_MAX_SECTION_LEN - 1 - 2 - 16 - 2 - 64;
-
- // take up 100 bytes to put us within 1 DE of the limit
- section_builder
- .add_de(|_| DummyDataElement { de_type: 100_u32.into(), data: vec![0; 98] })
- .unwrap();
-
- // one byte too many won't fit
- assert_eq!(
- AddDataElementError::InsufficientSectionSpace,
- section_builder
- .add_de(|_| DummyDataElement {
- de_type: 100_u32.into(),
- data: vec![0; max_total_de_len - 100 - 1],
- })
- .unwrap_err()
- );
-
- // but this will, as it allows 2 bytes for this DE's header
- assert_eq!(
- AddDataElementError::InsufficientSectionSpace,
- section_builder
- .add_de(|_| DummyDataElement {
- de_type: 100_u32.into(),
- data: vec![0; max_total_de_len - 100 - 2],
- })
- .unwrap_err()
- );
-}
-
-#[test]
fn serialize_max_number_of_public_sections() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT {
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
section_builder
- .add_de(|_| DummyDataElement { de_type: 100_u32.into(), data: vec![0; 27] })
+ .add_de(&DummyDataElement { de_type: 100_u8.into(), data: vec![0; 27] })
.unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
}
@@ -617,20 +425,19 @@
);
}
-fn do_mic_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_des: &[W]) {
+fn do_mic_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_des: &[W])
+where
+ <W as WriteDataElement>::Salt: SaltConvertible<DeSalt>,
+{
let identity_token = V1IdentityToken([1; 16]);
let key_seed = [2; 32];
let adv_header_byte = 0b00100000;
let section_salt: ExtendedV1Salt = [3; 16].into();
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let broadcast_cred = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder
.section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
section_salt,
@@ -638,12 +445,12 @@
))
.unwrap();
for de in extra_des {
- section_builder.add_de(|_| de).unwrap();
+ section_builder.add_de(&RefWriteDataElement(de)).unwrap();
}
// now construct expected bytes
// mic length + length of des
- let section_length = mic_section_len(extra_des);
+ let section_length = mic_section_len(section_salt, extra_des);
let mut hmac =
key_seed_hkdf.v1_mic_extended_salt_keys().mic_hmac_key().build_hmac::<CryptoProviderImpl>();
@@ -661,16 +468,17 @@
let mut de_contents = Vec::new();
for de in extra_des {
- de_contents.extend_from_slice(de.de_header().serialize().as_slice());
- let _ = de.write_de_contents(&mut de_contents);
+ let de_type = de.de_type();
+ let de_salt = DeSalt { salt: section_salt, de_type };
+ let salt = <<W as WriteDataElement>::Salt as SaltConvertible<DeSalt>>::convert(de_salt);
+ let _ = de.write_de(salt, &mut de_contents);
}
cipher.apply_keystream(&mut de_contents);
// just to be sure, we'll construct our test hmac all in one update() call
let mut hmac_input = vec![];
- hmac_input.extend_from_slice(NP_SVC_UUID.as_slice());
hmac_input.push(adv_header_byte);
- hmac_input.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+ hmac_input.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
hmac_input.extend_from_slice(section_salt.as_slice());
hmac_input.extend_from_slice(nonce.as_slice());
hmac_input.extend_from_slice(&ct_identity_token);
@@ -680,7 +488,7 @@
let mic = hmac.finalize();
let mut expected = vec![];
- expected.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN);
+ expected.push(V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.0);
expected.extend_from_slice(section_salt.as_slice());
expected.extend_from_slice(&ct_identity_token);
expected.push(section_length);
@@ -691,112 +499,37 @@
}
/// Returns the length of a mic section containing `extra_des`
-fn mic_section_len<W: WriteDataElement>(extra_des: &[W]) -> u8 {
+fn mic_section_len<W: WriteDataElement>(section_salt: ExtendedV1Salt, extra_des: &[W]) -> u8
+where
+ <W as WriteDataElement>::Salt: SaltConvertible<DeSalt>,
+{
u8::try_from(SectionMic::CONTENTS_LEN).unwrap()
+ extra_des
.iter()
- .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
+ .map(|de| {
+ let de_type = de.de_type();
+ let de_salt = DeSalt { salt: section_salt, de_type };
+ let salt =
+ <<W as WriteDataElement>::Salt as SaltConvertible<DeSalt>>::convert(de_salt);
+ let mut de_bytes = Vec::new();
+ let _ = de.write_de(salt, &mut de_bytes);
+ de_bytes.len() as u8
+ })
.sum::<u8>()
}
-/// Returns the length of a signed section containing `extra_des`
-fn sig_section_len<W: WriteDataElement>(extra_des: &[W]) -> u8 {
- u8::try_from(crypto_provider::ed25519::SIGNATURE_LENGTH).unwrap()
- + extra_des
- .iter()
- .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
- .sum::<u8>()
-}
-
-#[test]
-fn signature_encrypted_identity_section_empty() {
- do_signature_encrypted_identity_fixed_key_material_test::<DummyDataElement>(&[]);
-}
-
-fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_des: &[W]) {
- // input fixed credential data used to encode the adv
- let identity_token = V1IdentityToken::from([1; 16]);
- let key_seed = [2; 32];
- let section_salt: ExtendedV1Salt = [3; 16].into();
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
- let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
-
- // Build an adv given the provided DEs in extra_des
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
- let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new::<CryptoProviderImpl>(
- section_salt,
- &broadcast_cred,
- ))
- .unwrap();
- for de in extra_des {
- section_builder.add_de(|_| de).unwrap();
- }
- let section = section_builder.into_section::<CryptoProviderImpl>();
-
- // now manually construct the expected output
- let format = [V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN];
- let salt_bytes = section_salt.as_slice();
- let identity_token_bytes = identity_token.as_slice();
- // len of de contents + signature
- let section_length = sig_section_len(extra_des);
-
- let mut section_body = Vec::new();
- for de in extra_des {
- section_body.extend_from_slice(de.de_header().serialize().as_slice());
- let _ = de.write_de_contents(&mut section_body);
- }
-
- let nonce = section_salt.compute_nonce::<CryptoProviderImpl>();
-
- let sig_payload = SectionSignaturePayload::new(
- format.as_slice(),
- salt_bytes,
- &nonce,
- identity_token.as_slice(),
- section_length,
- §ion_body,
- );
- let sig = sig_payload.sign::<Ed25519ProviderImpl>(&private_key).to_bytes();
-
- let mut cipher = <CryptoProviderImpl as CryptoProvider>::AesCtr128::new(
- &key_seed_hkdf.v1_signature_keys().aes_key(),
- NonceAndCounter::from_nonce(nonce),
- );
-
- let mut expected = vec![];
- expected.extend_from_slice(&format);
- expected.extend_from_slice(salt_bytes);
-
- let mut ct_identity_token = Vec::new();
- ct_identity_token.extend_from_slice(identity_token_bytes);
- cipher.apply_keystream(&mut ct_identity_token);
-
- expected.extend_from_slice(&ct_identity_token);
- expected.push(section_length);
-
- let mut remaining_ct = Vec::new();
- remaining_ct.extend_from_slice(§ion_body);
- remaining_ct.extend_from_slice(&sig);
- cipher.apply_keystream(&mut remaining_ct);
-
- expected.extend_from_slice(&remaining_ct);
-
- assert_eq!(&expected, section.as_slice());
-}
-
/// Write `section_contents_len` bytes of DE into `section_builder`
pub(crate) fn fill_section_builder<I: SectionEncoder>(
section_contents_len: usize,
section_builder: &mut SectionBuilder<&mut AdvBuilder, I>,
-) -> Result<(), AddDataElementError<GenericDataElementError>> {
+) -> Result<(), AddDataElementError> {
let original_len = section_builder.section.len();
// DEs can only go up to 127, so we'll need multiple for long sections
- for _ in 0..(section_contents_len / 100) {
+ for i in 0..(section_contents_len / 100) {
let de_contents = vec![0x33; 98];
- section_builder
- .add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &de_contents))?;
+ let generic_de = GenericDataElement::try_from((100_u8 + (i as u8)).into(), &de_contents)
+ .expect("98 is a valid DE contents length");
+ section_builder.add_de(&generic_de)?;
}
let remainder_len = section_contents_len % 100;
@@ -806,18 +539,23 @@
}
1 => {
// 1 byte header
- section_builder.add_de_res(|_| GenericDataElement::try_from(3_u32.into(), &[]))?;
+ let generic_de = GenericDataElement::try_from(3_u8.into(), &[])
+ .expect("0 is a valid DE contents length");
+ section_builder.add_de(&generic_de)?;
}
2 => {
// 2 byte header
- section_builder.add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &[]))?;
+ let generic_de = GenericDataElement::try_from(98_u8.into(), &[])
+ .expect("0 is a valid DE contents length");
+ section_builder.add_de(&generic_de)?;
}
_ => {
// 2 byte header + contents as needed
// leave room for section length, section header, and DE headers
let de_contents = vec![0x44; remainder_len - 2];
- section_builder
- .add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &de_contents))?;
+ let generic_de = GenericDataElement::try_from(99_u8.into(), &de_contents)
+ .expect("DEs with content length less than 100 should be valid DEs");
+ section_builder.add_de(&generic_de)?;
}
}
@@ -832,16 +570,32 @@
pub(crate) data: Vec<u8>,
}
-impl WriteDataElement for DummyDataElement {
- fn de_header(&self) -> DeHeader {
- DeHeader { de_type: self.de_type, len: self.data.len().try_into().unwrap() }
+impl ProvidesDEType for DummyDataElement {
+ fn de_type(&self) -> DeType {
+ self.de_type
}
+}
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
+impl ProvidesDEType for &DummyDataElement {
+ fn de_type(&self) -> DeType {
+ self.de_type
+ }
+}
+
+impl WriteDataElement for DummyDataElement {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, _salt: Unsalted, sink: &mut S) -> Option<()> {
sink.try_extend_from_slice(&self.data)
}
}
+impl WriteDataElement for &DummyDataElement {
+ type Salt = Unsalted;
+ fn write_de_contents<S: Sink<u8>>(&self, salt: Unsalted, sink: &mut S) -> Option<()> {
+ (*self).write_de_contents(salt, sink)
+ }
+}
+
pub(crate) trait SectionBuilderExt {
fn into_section<C: CryptoProvider>(self) -> EncodedSection;
}
@@ -849,6 +603,6 @@
impl<R: AsMut<AdvBuilder>, I: SectionEncoder> SectionBuilderExt for SectionBuilder<R, I> {
/// Convenience method for tests
fn into_section<C: CryptoProvider>(self) -> EncodedSection {
- Self::build_section::<C>(self.header_len, self.section.into_inner(), self.section_encoder)
+ Self::build_section::<C>(Self::finished_adding_des(self.section), self.section_encoder)
}
}
diff --git a/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs b/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs
index 829123c..7538d47 100644
--- a/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs
@@ -19,7 +19,6 @@
use crate::credential::v1::V1BroadcastCredential;
use crate::extended::de_type::DeType;
use crate::extended::salt::V1Salt;
-use crate::extended::serialize::AdvertisementType;
use crate::extended::serialize::{
section_tests::{DummyDataElement, SectionBuilderExt},
AdvBuilder, MicEncryptedSectionEncoder,
@@ -27,7 +26,7 @@
use crate::extended::V1IdentityToken;
use alloc::format;
use anyhow::anyhow;
-use crypto_provider::{aes::ctr::AES_CTR_NONCE_LEN, aes::AesKey, ed25519, CryptoProvider};
+use crypto_provider::{aes::ctr::AES_CTR_NONCE_LEN, aes::AesKey};
use crypto_provider_default::CryptoProviderImpl;
use np_hkdf::{v1_salt, DerivedSectionKeys};
use serde_json::json;
@@ -35,8 +34,6 @@
use test_helper::extract_key_array;
use test_vector_hkdf::TestVectorHkdf;
-type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
-
#[test]
fn mic_encrypted_test_vectors() -> Result<(), anyhow::Error> {
let full_path = test_helper::get_data_file(
@@ -56,7 +53,7 @@
let key_seed = extract_key_array::<32>(&tc, "key_seed");
let identity_token =
V1IdentityToken::from(extract_key_array::<16>(&tc, "identity_token"));
- let adv_header_byte = extract_key_array::<1>(&tc, "adv_header_byte")[0];
+ let _adv_header_byte = extract_key_array::<1>(&tc, "adv_header_byte")[0];
let section_salt =
v1_salt::ExtendedV1Salt::from(extract_key_array::<16>(&tc, "section_salt"));
let data_elements = tc["data_elements"]
@@ -65,7 +62,9 @@
.iter()
.map(|json_de| DummyDataElement {
data: hex::decode(json_de["contents"].as_str().unwrap()).unwrap(),
- de_type: (json_de["de_type"].as_u64().unwrap() as u32).into(),
+ de_type: (json_de["de_type"].as_u64().unwrap() as u32)
+ .try_into()
+ .expect("Test vectors should not include forbidden DE types."),
})
.collect::<Vec<_>>();
@@ -84,15 +83,11 @@
section_salt.compute_nonce::<CryptoProviderImpl>()
);
- let broadcast_cred = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token);
- // make an adv builder in the configuration we need
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
- assert_eq!(adv_header_byte, adv_builder.adv_header().contents());
+ // Make an adv builder and populate it with a MIC section with
+ // DEs as they were previously specified.
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder
.section_builder(MicEncryptedSectionEncoder::<_>::new::<CryptoProviderImpl>(
@@ -101,8 +96,8 @@
))
.unwrap();
- for de in data_elements {
- section_builder.add_de(|_| de).unwrap();
+ for de in data_elements.iter() {
+ section_builder.add_de(de).unwrap();
}
assert_eq!(
@@ -128,7 +123,7 @@
let num_des = test_vector_seed_hkdf.derive_range_element("num des", 0_u64..=5);
- let extra_des =
+ let mut extra_des =
(0..num_des)
.map(|de_index| {
let de_len = test_vector_seed_hkdf
@@ -136,17 +131,21 @@
let data = test_vector_seed_hkdf
.derive_vec(&format!("de data {}", de_index), de_len.try_into().unwrap());
DummyDataElement {
- de_type: DeType::from(
+ de_type: DeType::try_from(
u32::try_from(test_vector_seed_hkdf.derive_range_element(
&format!("de type {}", de_index),
0_u64..=1_000,
))
.unwrap(),
- ),
+ )
+ .unwrap(),
data,
}
})
.collect::<Vec<_>>();
+ extra_des.sort_by_key(|de| de.de_type);
+ extra_des.dedup_by_key(|de| de.de_type);
+ let extra_des = extra_des;
let identity_token =
V1IdentityToken::from(test_vector_seed_hkdf.derive_array("identity token"));
@@ -155,13 +154,9 @@
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let broadcast_cred = V1BroadcastCredential::new(
- key_seed,
- identity_token,
- ed25519::PrivateKey::generate::<Ed25519ProviderImpl>(),
- );
+ let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
let section_salt =
v1_salt::ExtendedV1Salt::from(test_vector_seed_hkdf.derive_array::<16>("salt"));
@@ -173,7 +168,7 @@
.unwrap();
for de in extra_des.iter() {
- section_builder.add_de(|_| de).unwrap();
+ section_builder.add_de(de).unwrap();
}
let nonce = section_salt.compute_nonce::<CryptoProviderImpl>();
diff --git a/nearby/presence/np_adv/src/filter/mod.rs b/nearby/presence/np_adv/src/filter/mod.rs
index e2b37e7..b5a32f6 100644
--- a/nearby/presence/np_adv/src/filter/mod.rs
+++ b/nearby/presence/np_adv/src/filter/mod.rs
@@ -151,7 +151,7 @@
P: CryptoProvider,
{
let contents =
- IntermediateAdvContents::deserialize::<P>(encoding, remaining).map_err(|_| NoMatch)?;
+ IntermediateAdvContents::deserialize(encoding, remaining).map_err(|_| NoMatch)?;
match contents {
IntermediateAdvContents::Unencrypted(p) => match self.identity {
IdentityFilterType::Public | IdentityFilterType::Any => self
@@ -160,9 +160,10 @@
.map(|()| FilterResult::Public),
_ => Err(NoMatch),
},
- IntermediateAdvContents::Ldt(c) => match self.identity {
+ IntermediateAdvContents::Ldt(mut c) => match self.identity {
IdentityFilterType::Private | IdentityFilterType::Any => {
- let (legible_adv, m) = try_decrypt_and_match::<B, P>(cred_book.v0_iter(), &c)?;
+ let (legible_adv, m) =
+ try_decrypt_and_match::<B, P>(cred_book.v0_iter(), &mut c)?;
self.data_elements
.match_v0_legible_adv(|| legible_adv.data_elements())
.map(|()| FilterResult::Private(m))
@@ -175,7 +176,7 @@
fn try_decrypt_and_match<'cred, B, P>(
v0_creds: B::V0Iterator,
- adv: &LdtAdvContents,
+ adv: &mut LdtAdvContents,
) -> Result<(DecryptedAdvContents, B::Matched), NoMatch>
where
B: CredentialBook<'cred>,
diff --git a/nearby/presence/np_adv/src/filter/tests/mod.rs b/nearby/presence/np_adv/src/filter/tests/mod.rs
index 42ab177..35c92cd 100644
--- a/nearby/presence/np_adv/src/filter/tests/mod.rs
+++ b/nearby/presence/np_adv/src/filter/tests/mod.rs
@@ -100,7 +100,7 @@
&[
VERSION_HEADER_V1,
0x03, // Section Header
- V1_ENCODING_UNENCRYPTED,
+ V1_ENCODING_UNENCRYPTED.byte_value(),
0x15,
0x03, // Length 1 Tx Power DE with value 3
],
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/mod.rs b/nearby/presence/np_adv/src/legacy/data_elements/mod.rs
index 762d752..c952d08 100644
--- a/nearby/presence/np_adv/src/legacy/data_elements/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/data_elements/mod.rs
@@ -191,9 +191,9 @@
}
}
-impl<'a, F: PacketFlavor> Sealed for DynamicSerializeDataElement<'a, F> {}
+impl<F: PacketFlavor> Sealed for DynamicSerializeDataElement<'_, F> {}
-impl<'a, F: PacketFlavor> SerializeDataElement<F> for DynamicSerializeDataElement<'a, F> {
+impl<F: PacketFlavor> SerializeDataElement<F> for DynamicSerializeDataElement<'_, F> {
fn de_type_code(&self) -> DeTypeCode {
self.wrapped.de_type_code()
}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/intermediate/mod.rs b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/mod.rs
index 13ede05..f5daa05 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/intermediate/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/mod.rs
@@ -39,6 +39,7 @@
Ldt(LdtAdvContents<'d>),
}
+#[cfg(test)]
impl<'d> IntermediateAdvContents<'d> {
#[cfg(test)]
pub(crate) fn as_unencrypted(&self) -> Option<&UnencryptedAdvContents<'d>> {
@@ -49,16 +50,18 @@
}
#[cfg(test)]
- pub(crate) fn as_ldt(&self) -> Option<&LdtAdvContents<'d>> {
+ pub(crate) fn as_ldt(&mut self) -> Option<&mut LdtAdvContents<'d>> {
match self {
IntermediateAdvContents::Unencrypted(_) => None,
IntermediateAdvContents::Ldt(c) => Some(c),
}
}
+}
+impl IntermediateAdvContents<'_> {
/// Performs basic structural checks on header and content, but doesn't deserialize DEs or
/// decrypt.
- pub(crate) fn deserialize<C: CryptoProvider>(
+ pub(crate) fn deserialize(
encoding: V0Encoding,
input: &[u8],
) -> Result<IntermediateAdvContents<'_>, AdvDeserializeError> {
@@ -72,7 +75,7 @@
V0Encoding::Ldt => {
let (ciphertext, salt) =
parse_v0_salt(input).map_err(|_| AdvDeserializeError::InvalidStructure)?;
- LdtAdvContents::new::<C>(salt, ciphertext)
+ LdtAdvContents::new(salt, ciphertext)
.ok_or(AdvDeserializeError::InvalidStructure)
.map(IntermediateAdvContents::Ldt)
}
@@ -106,9 +109,9 @@
/// Contents of an encrypted advertisement before decryption.
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct LdtAdvContents<'d> {
- /// Salt from the advertisement, converted into a padder.
- /// Pre-calculated so it's only derived once across multiple decrypt attempts.
- salt_padder: ldt::XorPadder<{ crypto_provider::aes::BLOCK_SIZE }>,
+ /// Salt from the advertisement, converted into a padder. This will be calculated on first use
+ /// and cached for later uses.
+ salt_padder: Option<ldt::XorPadder<{ crypto_provider::aes::BLOCK_SIZE }>>,
/// The salt instance used for encryption of this advertisement.
salt: V0Salt,
/// Ciphertext containing the identity token and any data elements.
@@ -118,11 +121,19 @@
impl<'d> LdtAdvContents<'d> {
/// Returns `None` if `ciphertext` is not a valid LDT-XTS-AES ciphertext length.
- pub(crate) fn new<C: CryptoProvider>(salt: V0Salt, ciphertext: &'d [u8]) -> Option<Self> {
+ pub(crate) fn new(salt: V0Salt, ciphertext: &'d [u8]) -> Option<Self> {
if !ldt_np_adv::VALID_INPUT_LEN.contains(&ciphertext.len()) {
return None;
}
- Some(Self { salt_padder: ldt_np_adv::salt_padder::<C>(salt), salt, ciphertext })
+ Some(Self { salt_padder: None, salt, ciphertext })
+ }
+
+ /// Get the salt padder. It will be calculated on the first call to this method and cached for
+ /// further calls.
+ fn salt_padder<C: CryptoProvider>(
+ &mut self,
+ ) -> &ldt::XorPadder<{ crypto_provider::aes::BLOCK_SIZE }> {
+ self.salt_padder.get_or_insert_with(|| ldt_np_adv::salt_padder::<C>(self.salt))
}
/// Try decrypting with an identity's LDT cipher and deserializing the resulting data elements.
@@ -130,11 +141,11 @@
/// Returns the decrypted data if decryption and verification succeeded and the resulting DEs could be parsed
/// successfully, otherwise `Err`.
pub(crate) fn try_decrypt<C: CryptoProvider>(
- &self,
+ &mut self,
cipher: &ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>,
) -> Result<DecryptedAdvContents, DecryptError> {
let (identity_token, plaintext) = cipher
- .decrypt_and_verify(self.ciphertext, &self.salt_padder)
+ .decrypt_and_verify(self.ciphertext, self.salt_padder::<C>())
.map_err(|_e| DecryptError::DecryptOrVerifyError)?;
Ok(DecryptedAdvContents::new(identity_token, self.salt, plaintext))
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/error_conditions.rs b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/error_conditions.rs
index 5cbd784..e8220f4 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/error_conditions.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/error_conditions.rs
@@ -18,17 +18,12 @@
use crate::header::V0Encoding;
use crate::legacy::deserialize::intermediate::IntermediateAdvContents;
use crate::legacy::deserialize::AdvDeserializeError;
- use crypto_provider_default::CryptoProviderImpl;
#[test]
fn parse_zero_len_error() {
assert_eq!(
AdvDeserializeError::NoDataElements,
- IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- &[],
- )
- .unwrap_err()
+ IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, &[],).unwrap_err()
);
}
}
@@ -38,7 +33,7 @@
use crate::legacy::deserialize::intermediate::IntermediateAdvContents;
use crate::legacy::deserialize::AdvDeserializeError;
use alloc::vec;
- use crypto_provider_default::CryptoProviderImpl;
+
use ldt_np_adv::{V0_IDENTITY_TOKEN_LEN, V0_SALT_LEN};
#[test]
@@ -46,18 +41,13 @@
for len in 0..(V0_SALT_LEN + ldt_np_adv::VALID_INPUT_LEN.start) {
assert_eq!(
AdvDeserializeError::InvalidStructure,
- IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Ldt,
- &vec![0; len],
- )
- .unwrap_err()
+ IntermediateAdvContents::deserialize(V0Encoding::Ldt, &vec![0; len],).unwrap_err()
);
}
// 1 more byte is enough
let data = &[0; ldt_np_adv::VALID_INPUT_LEN.start + V0_SALT_LEN];
- assert!(IntermediateAdvContents::deserialize::<CryptoProviderImpl>(V0Encoding::Ldt, data)
- .is_ok())
+ assert!(IntermediateAdvContents::deserialize(V0Encoding::Ldt, data).is_ok())
}
#[test]
@@ -72,14 +62,12 @@
assert_eq!(
AdvDeserializeError::InvalidStructure,
- IntermediateAdvContents::deserialize::<CryptoProviderImpl>(V0Encoding::Ldt, data)
- .unwrap_err()
+ IntermediateAdvContents::deserialize(V0Encoding::Ldt, data).unwrap_err()
);
// 1 fewer byte is enough
let data = &[&salt, [0x11; V0_IDENTITY_TOKEN_LEN].as_slice(), &vec![0xCC; payload_len - 1]]
.concat();
- assert!(IntermediateAdvContents::deserialize::<CryptoProviderImpl>(V0Encoding::Ldt, data)
- .is_ok())
+ assert!(IntermediateAdvContents::deserialize(V0Encoding::Ldt, data).is_ok())
}
}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/happy_path.rs b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/happy_path.rs
index f74c1a9..bdb11e7 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/happy_path.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/intermediate/tests/happy_path.rs
@@ -20,21 +20,17 @@
use crate::legacy::deserialize::intermediate::IntermediateAdvContents;
use crate::legacy::serialize::encode_de_header;
use crate::legacy::NP_MAX_DE_CONTENT_LEN;
- use crypto_provider_default::CryptoProviderImpl;
#[test]
fn parse_min_len() {
let header = encode_de_header(DeTypeCode::try_from(14).unwrap(), DeEncodedLength::from(0));
assert_eq!(
&[header],
- IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- &[header],
- )
- .unwrap()
- .as_unencrypted()
- .unwrap()
- .data
+ IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, &[header],)
+ .unwrap()
+ .as_unencrypted()
+ .unwrap()
+ .data
)
}
@@ -47,14 +43,11 @@
let data = &[&[header], [0x22; NP_MAX_DE_CONTENT_LEN].as_slice()].concat();
assert_eq!(
data,
- IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- data,
- )
- .unwrap()
- .as_unencrypted()
- .unwrap()
- .data
+ IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, data,)
+ .unwrap()
+ .as_unencrypted()
+ .unwrap()
+ .data
)
}
}
@@ -91,16 +84,20 @@
}
fn assert_ldt_contents(salt: [u8; V0_SALT_LEN], data: &[u8]) {
+ let mut intermediate_adv =
+ IntermediateAdvContents::deserialize(V0Encoding::Ldt, data).unwrap();
+ let ldt_contents = intermediate_adv.as_ldt().unwrap();
+
+ // Make sure the salt_padder has been created
+ let _ = ldt_contents.salt_padder::<CryptoProviderImpl>();
+
assert_eq!(
&LdtAdvContents {
- salt_padder: salt_padder::<CryptoProviderImpl>(salt.into()),
+ salt_padder: Some(salt_padder::<CryptoProviderImpl>(salt.into())),
salt: salt.into(),
ciphertext: &data[V0_SALT_LEN..],
},
- IntermediateAdvContents::deserialize::<CryptoProviderImpl>(V0Encoding::Ldt, data)
- .unwrap()
- .as_ldt()
- .unwrap()
+ ldt_contents
);
}
}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
index d2ceab6..075c0c9 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
@@ -69,7 +69,7 @@
impl<'d, D: DataElementDeserializer> RawDataElement<'d, D> {
/// Parse an individual DE into its header and contents.
- fn parse(input: &'d [u8]) -> nom::IResult<&[u8], Self, DataElementDeserializeError> {
+ fn parse(input: &'d [u8]) -> nom::IResult<&'d [u8], Self, DataElementDeserializeError> {
let (input, (de_type, actual_len)) = combinator::map_res(
combinator::map_opt(number::complete::u8, |de_header| {
// header: LLLLTTTT
@@ -114,7 +114,7 @@
}
}
-impl<'d, F: PacketFlavor> Iterator for DeIterator<'d, F> {
+impl<F: PacketFlavor> Iterator for DeIterator<'_, F> {
type Item = Result<DeserializedDataElement<F>, DataElementDeserializeError>;
fn next(&mut self) -> Option<Self::Item> {
@@ -185,7 +185,7 @@
}
}
-impl<'d, F: PacketFlavor, D: DataElementDeserializer> Iterator for GenericDeIterator<'d, F, D> {
+impl<F: PacketFlavor, D: DataElementDeserializer> Iterator for GenericDeIterator<'_, F, D> {
type Item = Result<D::Deserialized<F>, DataElementDeserializeError>;
fn next(&mut self) -> Option<Self::Item> {
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests/error_conditions.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests/error_conditions.rs
index ace055d..fe5ec21 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/tests/error_conditions.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests/error_conditions.rs
@@ -13,7 +13,6 @@
// limitations under the License.
mod unencrypted {
- use crypto_provider_default::CryptoProviderImpl;
use crate::header::V0Encoding;
use crate::legacy::data_elements::actions::{ActionBits, ActionsDataElement, ActiveUnlock};
@@ -85,14 +84,11 @@
#[test]
fn iterate_multiple_de_types_same_value() {
let input = &[0x15, 0x00, 0x15, 0x00];
- let mut it = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- input,
- )
- .unwrap()
- .as_unencrypted()
- .unwrap()
- .data_elements();
+ let mut it = IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, input)
+ .unwrap()
+ .as_unencrypted()
+ .unwrap()
+ .data_elements();
// first element will be valid
let _ = it.next();
@@ -112,11 +108,8 @@
}
fn assert_deser_error(input: &[u8], err: DataElementDeserializeError) {
- let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- input,
- )
- .unwrap();
+ let contents =
+ IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, input).unwrap();
assert_eq!(
err,
@@ -165,11 +158,8 @@
let adv = builder.into_advertisement().unwrap();
- let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Ldt,
- &adv.as_slice()[1..],
- )
- .unwrap();
+ let mut contents =
+ IntermediateAdvContents::deserialize(V0Encoding::Ldt, &adv.as_slice()[1..]).unwrap();
let ldt = contents.as_ldt().unwrap();
let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
let identity_token_hmac: [u8; 32] = hkdf
@@ -329,11 +319,9 @@
let adv = builder.into_advertisement().unwrap();
let altered_adv = alter_adv(adv.as_slice().to_vec());
- let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Ldt,
- &altered_adv.as_slice()[1..],
- )
- .unwrap();
+ let mut contents =
+ IntermediateAdvContents::deserialize(V0Encoding::Ldt, &altered_adv.as_slice()[1..])
+ .unwrap();
let ldt = contents.as_ldt().unwrap();
let decrypter = build_decrypter(&key_seed, &identity_token);
assert_eq!(DecryptError::DecryptOrVerifyError, ldt.try_decrypt(&decrypter).unwrap_err());
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests/happy_path.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests/happy_path.rs
index e5ae98f..8b0f979 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/tests/happy_path.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests/happy_path.rs
@@ -19,8 +19,6 @@
use std::{prelude::rust_2021::*, vec};
use strum::IntoEnumIterator;
- use crypto_provider_default::CryptoProviderImpl;
-
use crate::{
header::V0Encoding,
legacy::{
@@ -59,11 +57,9 @@
// extra byte for version header
assert_eq!(1 + NP_MIN_ADV_CONTENT_LEN, data.len());
- let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- &data.as_slice()[1..],
- )
- .unwrap();
+ let contents =
+ IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, &data.as_slice()[1..])
+ .unwrap();
let unencrypted = contents.as_unencrypted().unwrap();
assert_eq!(
@@ -83,11 +79,9 @@
let data = builder.into_advertisement().unwrap();
assert_eq!(BLE_4_ADV_SVC_MAX_CONTENT_LEN, data.len());
- let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- &data.as_slice()[1..],
- )
- .unwrap();
+ let contents =
+ IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, &data.as_slice()[1..])
+ .unwrap();
let unencrypted = contents.as_unencrypted().unwrap();
assert_eq!(
@@ -240,11 +234,9 @@
expected: &[D::Deserialized<Plaintext>],
adv: &'a SerializedAdv,
) -> IntermediateAdvContents<'a> {
- let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- &adv.as_slice()[1..],
- )
- .unwrap();
+ let contents =
+ IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, &adv.as_slice()[1..])
+ .unwrap();
let unencrypted = contents.as_unencrypted().unwrap();
assert_eq!(
@@ -494,11 +486,8 @@
adv: &SerializedAdv,
expected: &[D::Deserialized<Ciphertext>],
) -> DecryptedAdvContents {
- let contents = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Ldt,
- &adv.as_slice()[1..],
- )
- .unwrap();
+ let mut contents =
+ IntermediateAdvContents::deserialize(V0Encoding::Ldt, &adv.as_slice()[1..]).unwrap();
let ldt = contents.as_ldt().unwrap();
let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(key_seed);
let identity_token_hmac: [u8; 32] = hkdf
@@ -530,8 +519,6 @@
mod coverage_gaming {
use alloc::format;
- use crypto_provider_default::CryptoProviderImpl;
-
use crate::header::V0Encoding;
use crate::legacy::data_elements::de_type::DataElementType;
use crate::legacy::deserialize::intermediate::{IntermediateAdvContents, LdtAdvContents};
@@ -542,11 +529,7 @@
#[test]
fn iac_debug_eq_test_helpers() {
- let iac = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- &[0xFF],
- )
- .unwrap();
+ let iac = IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, &[0xFF]).unwrap();
let _ = format!("{:?}", iac);
assert_eq!(iac, iac);
}
@@ -555,21 +538,15 @@
fn iac_test_helpers() {
assert_eq!(
None,
- IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Unencrypted,
- &[0xFF],
- )
- .unwrap()
- .as_ldt()
+ IntermediateAdvContents::deserialize(V0Encoding::Unencrypted, &[0xFF],)
+ .unwrap()
+ .as_ldt()
);
assert_eq!(
None,
- IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Ldt,
- &[0xFF; 18],
- )
- .unwrap()
- .as_unencrypted()
+ IntermediateAdvContents::deserialize(V0Encoding::Ldt, &[0xFF; 18],)
+ .unwrap()
+ .as_unencrypted()
);
}
@@ -598,7 +575,7 @@
#[test]
fn ldt_adv_contents_debug() {
- let lac = LdtAdvContents::new::<CryptoProviderImpl>([0; 2].into(), &[0; 16]).unwrap();
+ let lac = LdtAdvContents::new([0; 2].into(), &[0; 16]).unwrap();
let _ = format!("{:?}", lac);
}
}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests/mod.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests/mod.rs
index 0a016e1..af85f9a 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/tests/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests/mod.rs
@@ -42,7 +42,7 @@
let (adv_content, correct_cipher) =
build_ciphertext_adv_contents(salt, &metadata_key, correct_key_seed);
- let eac = parse_ciphertext_adv_contents(&correct_cipher, &adv_content.as_slice()[1..]);
+ let mut eac = parse_ciphertext_adv_contents(&correct_cipher, &adv_content.as_slice()[1..]);
// wrong key seed doesn't work (derives wrong ldt key, wrong hmac key)
let wrong_key_seed_cipher = {
@@ -68,7 +68,8 @@
let (adv_content, correct_cipher_config) =
build_ciphertext_adv_contents(salt, &metadata_key, correct_key_seed);
- let eac = parse_ciphertext_adv_contents(&correct_cipher_config, &adv_content.as_slice()[1..]);
+ let mut eac =
+ parse_ciphertext_adv_contents(&correct_cipher_config, &adv_content.as_slice()[1..]);
let wrong_hmac_key_cipher = {
let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&[0x10_u8; 32]);
@@ -92,7 +93,8 @@
let (adv_content, correct_cipher_config) =
build_ciphertext_adv_contents(salt, &metadata_key, correct_key_seed);
- let eac = parse_ciphertext_adv_contents(&correct_cipher_config, &adv_content.as_slice()[1..]);
+ let mut eac =
+ parse_ciphertext_adv_contents(&correct_cipher_config, &adv_content.as_slice()[1..]);
let wrong_hmac_key_cipher = {
let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&correct_key_seed);
@@ -133,11 +135,7 @@
cipher: &ldt_np_adv::AuthenticatedNpLdtDecryptCipher<CryptoProviderImpl>,
adv_content: &'b [u8],
) -> LdtAdvContents<'b> {
- let eac = match IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
- V0Encoding::Ldt,
- adv_content,
- )
- .unwrap()
+ let mut eac = match IntermediateAdvContents::deserialize(V0Encoding::Ldt, adv_content).unwrap()
{
IntermediateAdvContents::Unencrypted(_) => panic!(),
// quick confirmation that we did get something
diff --git a/nearby/presence/np_adv/src/legacy/mod.rs b/nearby/presence/np_adv/src/legacy/mod.rs
index 2bac636..0ea4675 100644
--- a/nearby/presence/np_adv/src/legacy/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/mod.rs
@@ -89,9 +89,9 @@
B: CredentialBook<'cred>,
P: CryptoProvider,
{
- match IntermediateAdvContents::deserialize::<P>(encoding, remaining)? {
+ match IntermediateAdvContents::deserialize(encoding, remaining)? {
IntermediateAdvContents::Unencrypted(p) => Ok(V0AdvertisementContents::Plaintext(p)),
- IntermediateAdvContents::Ldt(c) => {
+ IntermediateAdvContents::Ldt(mut c) => {
for (crypto_material, matched) in cred_book.v0_iter() {
let ldt = crypto_material.ldt_adv_cipher::<P>();
match c.try_decrypt(&ldt) {
diff --git a/nearby/presence/np_adv/src/lib.rs b/nearby/presence/np_adv/src/lib.rs
index 0e958fa..0b0f42e 100644
--- a/nearby/presence/np_adv/src/lib.rs
+++ b/nearby/presence/np_adv/src/lib.rs
@@ -46,16 +46,15 @@
#[cfg(test)]
mod tests;
+mod array_vec;
pub mod credential;
pub mod deserialization_arena;
pub mod extended;
pub mod filter;
-pub mod legacy;
-pub mod shared_data;
-
-mod array_vec;
mod header;
mod helpers;
+pub mod legacy;
+pub mod shared_data;
/// Canonical form of NP's service UUID.
///
diff --git a/nearby/presence/np_adv/src/tests/deser_v0_tests.rs b/nearby/presence/np_adv/src/tests/deser_v0_tests.rs
index b9dc6a8..3ec537a 100644
--- a/nearby/presence/np_adv/src/tests/deser_v0_tests.rs
+++ b/nearby/presence/np_adv/src/tests/deser_v0_tests.rs
@@ -190,7 +190,7 @@
/// Populate an advertisement with a randomly chosen identity and a DE
fn adv_random_identity<'a, R: rand::Rng>(
mut rng: &mut R,
- identities: &'a Vec<TestIdentity>,
+ identities: &'a [TestIdentity],
) -> (ArrayView<u8, { BLE_4_ADV_SVC_MAX_CONTENT_LEN }>, AdvConfig<'a>) {
let identity = identities.choose(&mut rng).unwrap();
if rng.gen_bool(0.5) {
diff --git a/nearby/presence/np_adv/src/tests/deser_v1_tests.rs b/nearby/presence/np_adv/src/tests/deser_v1_tests.rs
index 78b8dc4..3afb086 100644
--- a/nearby/presence/np_adv/src/tests/deser_v1_tests.rs
+++ b/nearby/presence/np_adv/src/tests/deser_v1_tests.rs
@@ -16,6 +16,7 @@
extern crate std;
+use std::collections::HashSet;
use std::{prelude::rust_2021::*, vec};
use rand::{
@@ -26,16 +27,17 @@
};
use array_view::ArrayView;
-use crypto_provider::{ed25519, CryptoRng};
+use crypto_provider::CryptoRng;
use crypto_provider_default::CryptoProviderImpl;
use np_hkdf::v1_salt::ExtendedV1Salt;
+use thiserror::Error;
use crate::{
credential::{book::*, matched::*, v0::*, v1::*, *},
extended::{
data_elements::GenericDataElement,
deserialize::{data_element::DataElement, section::intermediate::PlaintextSection, *},
- salt::MultiSalt,
+ salt::{DeSalt, MultiSalt, Unsalted},
serialize::*,
*,
},
@@ -64,47 +66,99 @@
}
}
+/// Potential reasons why a [`V1DeserializedSection`]
+/// does not match a given [`SectionConfig`].
+#[derive(Error, Debug)]
+enum SectionsNotEqual<'a> {
+ #[error("data elements differ,\nconfig: {0:?},\nactual: {1:?}")]
+ DataElements(&'a [GenericDataElement], Vec<GenericDataElement>),
+ #[error("plaintext metadata differs,\nconfig: {0:?},\nactual: {1:?}")]
+ PlaintextMetadata(&'a [u8], Vec<u8>),
+ #[error("plaintext section contents differ,\nconfig: {0:?},\nactual: {1:?}")]
+ PlaintextSectionContents(Vec<u8>, &'a [u8]),
+ #[error("identity tokens differ,\nconfig: {0:?},\nactual: {1:?}")]
+ IdentityToken(V1IdentityToken, V1IdentityToken),
+ #[error("verification modes differ,\nconfig: {0:?},\nactual: {1:?}")]
+ VerificationMode(VerificationMode, VerificationMode),
+}
+
fn assert_section_equals(
section_config: &SectionConfig,
section: &V1DeserializedSection<
ReferencedMatchedCredential<MetadataMatchedCredential<Vec<u8>>>,
>,
) {
+ test_section_equals(section_config, section).unwrap();
+}
+
+fn test_section_equals<'a>(
+ section_config: &'a SectionConfig,
+ section: &'a V1DeserializedSection<
+ ReferencedMatchedCredential<MetadataMatchedCredential<Vec<u8>>>,
+ >,
+) -> Result<(), SectionsNotEqual<'a>> {
match §ion_config.identity_kind {
IdentityKind::Plaintext => {
let plaintext_section = section.as_plaintext_section();
- assert_eq!(
- section_config.data_elements,
- plaintext_section
- .iter_data_elements()
- .map(|de| (&de.unwrap()).into())
- .collect::<Vec<_>>()
- )
+ let des = plaintext_section
+ .iter_data_elements()
+ .map(|de| (&de.unwrap()).into())
+ .collect::<Vec<_>>();
+ if section_config.data_elements == des {
+ Ok(())
+ } else {
+ Err(SectionsNotEqual::DataElements(§ion_config.data_elements, des))
+ }
}
IdentityKind::Encrypted { verification_mode, identity } => {
let enc_section = section.as_ciphertext_section();
let decrypted_metadata = enc_section.decrypt_metadata::<CryptoProviderImpl>().unwrap();
- assert_eq!(&identity.plaintext_metadata, &decrypted_metadata);
+
+ if identity.plaintext_metadata != decrypted_metadata {
+ return Err(SectionsNotEqual::PlaintextMetadata(
+ &identity.plaintext_metadata,
+ decrypted_metadata,
+ ));
+ }
let expected_contents =
section_config.data_elements.clone().iter().fold(Vec::new(), |mut buf, de| {
- buf.extend_from_slice(de.de_header().serialize().as_slice());
- de.write_de_contents(&mut buf).unwrap();
+ // Okay to pretend there's no salt, since `GenericDataElement` isn't
+ // affected by salts.
+ de.write_de(Unsalted, &mut buf).unwrap();
buf
});
let contents = enc_section.contents();
- assert_eq!(&expected_contents, contents.plaintext());
- assert_eq!(
- section_config.data_elements,
- contents
- .iter_data_elements()
- .map(|de| (&de.unwrap()).into())
- .collect::<Vec<GenericDataElement>>()
- );
- assert_eq!(&identity.identity_token, enc_section.contents().identity_token());
- assert_eq!(verification_mode, &enc_section.contents().verification_mode());
+ if expected_contents != contents.plaintext() {
+ return Err(SectionsNotEqual::PlaintextSectionContents(
+ expected_contents,
+ contents.plaintext(),
+ ));
+ }
+
+ let des = contents
+ .iter_data_elements()
+ .map(|de| (&de.unwrap()).into())
+ .collect::<Vec<GenericDataElement>>();
+
+ if section_config.data_elements != des {
+ return Err(SectionsNotEqual::DataElements(§ion_config.data_elements, des));
+ }
+ if &identity.identity_token != enc_section.contents().identity_token() {
+ return Err(SectionsNotEqual::IdentityToken(
+ identity.identity_token,
+ *enc_section.contents().identity_token(),
+ ));
+ }
+ if verification_mode != &enc_section.contents().verification_mode() {
+ return Err(SectionsNotEqual::VerificationMode(
+ *verification_mode,
+ enc_section.contents().verification_mode(),
+ ));
+ }
+ Ok(())
}
}
}
@@ -169,58 +223,32 @@
}
impl Distribution<VerificationMode> for Standard {
- fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> VerificationMode {
- match rng.gen_range(0..=2) {
- 0 => VerificationMode::Signature,
- _ => VerificationMode::Mic,
- }
+ fn sample<R: Rng + ?Sized>(&self, _rng: &mut R) -> VerificationMode {
+ VerificationMode::Mic
}
}
-pub(crate) fn add_sig_rand_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
+pub(crate) fn add_mic_rand_length_salt_to_adv<
+ 'a,
+ R: rand::Rng,
+ C: CryptoProvider,
+ const M: usize,
+>(
rng: &mut R,
identity: &'a TestIdentity,
adv_builder: &mut AdvBuilder,
) -> Result<SectionConfig<'a>, AddSectionError> {
- let salt: ExtendedV1Salt = rng.gen::<[u8; 16]>().into();
- add_sig_with_salt_to_adv::<_, C, M>(rng, identity, adv_builder, salt.into())
-}
-
-fn add_sig_with_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
- rng: &mut R,
- identity: &'a crate::tests::deser_v1_tests::TestIdentity,
- adv_builder: &mut AdvBuilder,
- salt: MultiSalt,
-) -> Result<crate::tests::deser_v1_tests::SectionConfig<'a>, AddSectionError> {
- let broadcast_cred = identity.broadcast_credential();
- let salt = match salt {
- MultiSalt::Short(_) => {
- panic!("Invalid salt type for signature encrpted adv")
- }
- MultiSalt::Extended(e) => e,
- };
- adv_builder.section_builder(SignedEncryptedSectionEncoder::new::<C>(salt, &broadcast_cred)).map(
- |mut s| {
- let mut sink = Vec::new();
- let (_, des) = fill_section_random_des::<_, _, M>(rng, &mut sink, &mut s);
- s.add_to_advertisement::<C>();
- SectionConfig::new(
- IdentityKind::Encrypted {
- verification_mode: VerificationMode::Signature,
- identity,
- },
- des,
- )
- },
- )
+ let short_salt = rng.gen_bool(0.5);
+ add_mic_rand_salt_to_adv::<R, C, M>(rng, identity, adv_builder, short_salt)
}
pub(crate) fn add_mic_rand_salt_to_adv<'a, R: rand::Rng, C: CryptoProvider, const M: usize>(
rng: &mut R,
identity: &'a TestIdentity,
adv_builder: &mut AdvBuilder,
+ short_salt: bool,
) -> Result<SectionConfig<'a>, AddSectionError> {
- let salt = if rng.gen_bool(0.5) {
+ let salt = if short_salt {
MultiSalt::Short(rng.gen::<[u8; 2]>().into())
} else {
MultiSalt::Extended(rng.gen::<[u8; 16]>().into())
@@ -255,16 +283,17 @@
identities: &'a TestIdentities,
adv_builder: &mut AdvBuilder,
) -> Vec<SectionConfig<'a>> {
+ let total_num_sections = rng.gen_range(1..=NP_V1_ADV_MAX_SECTION_COUNT);
+ let num_short_salt = rng.gen_range(0..=total_num_sections);
+
let mut expected = Vec::new();
- for _ in 0..rng.gen_range(1..=NP_V1_ADV_MAX_SECTION_COUNT) {
+ for section_index in 0..total_num_sections {
let identity = identities.pick_random_identity(&mut rng);
let mode: VerificationMode = random();
let res = match mode {
- VerificationMode::Signature => {
- add_sig_rand_salt_to_adv::<_, C, 0>(&mut rng, identity, adv_builder)
- }
VerificationMode::Mic => {
- add_mic_rand_salt_to_adv::<_, C, 0>(&mut rng, identity, adv_builder)
+ let short_salt = section_index < num_short_salt;
+ add_mic_rand_salt_to_adv::<_, C, 0>(&mut rng, identity, adv_builder, short_salt)
}
};
match res {
@@ -278,28 +307,26 @@
expected
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub(crate) struct TestIdentity {
key_seed: [u8; 32],
identity_token: V1IdentityToken,
- private_key: ed25519::PrivateKey,
plaintext_metadata: Vec<u8>,
}
impl TestIdentity {
/// Generate a new identity with random crypto material
- fn random<R: rand::Rng, C: CryptoProvider>(rng: &mut R) -> Self {
+ fn random<R: rand::Rng>(rng: &mut R) -> Self {
Self {
key_seed: rng.gen(),
identity_token: rng.gen(),
- private_key: ed25519::PrivateKey::generate::<C::Ed25519>(),
// varying length vec of random bytes
plaintext_metadata: (0..rng.gen_range(50..200)).map(|_| rng.gen()).collect(),
}
}
/// Returns a (simple, signed) broadcast credential using crypto material from this identity
fn broadcast_credential(&self) -> V1BroadcastCredential {
- V1BroadcastCredential::new(self.key_seed, self.identity_token, self.private_key.clone())
+ V1BroadcastCredential::new(self.key_seed, self.identity_token)
}
/// Returns a discovery credential using crypto material from this identity
fn discovery_credential<C: CryptoProvider>(&self) -> V1DiscoveryCredential {
@@ -307,11 +334,13 @@
}
}
+#[derive(Debug)]
pub(crate) struct SectionConfig<'a> {
identity_kind: IdentityKind<'a>,
data_elements: Vec<GenericDataElement>,
}
+#[derive(Debug)]
pub(crate) enum IdentityKind<'a> {
Plaintext,
Encrypted { verification_mode: VerificationMode, identity: &'a TestIdentity },
@@ -334,28 +363,46 @@
let mut expected_des = vec![];
let mut orig_des = vec![];
let mut de_ranges = vec![];
+ let mut de_salts = vec![];
+ let mut de_types = HashSet::new();
+
+ let mut generated_des = vec![];
+ // Generate some random DEs (possibly out-of-order)
for _ in 0..rng.gen_range(M..=5) {
- let de = random_de::<MAX_DE_LEN, _>(&mut rng);
+ loop {
+ let de = random_de::<MAX_DE_LEN, _>(&mut rng);
+ if de_types.insert(de.de_type()) {
+ generated_des.push(de);
+ break;
+ }
+ }
+ }
+ // Sort by DE type
+ generated_des.sort_by_key(|de| de.de_type());
- let de_clone = de.clone();
- if section_builder.add_de(|_| de_clone).is_err() {
+ // Populate `de_salts`, `de_ranges`, and `orig_des`
+ // based on what we can actually add to the section.
+ for de in generated_des.into_iter() {
+ let de_salt = section_builder.de_salt(de.de_type());
+ if section_builder.add_de(&de).is_err() {
break;
}
+ de_salts.push(de_salt);
let orig_len = sink.len();
- de.write_de_contents(sink).unwrap();
+ // `GenericDataElement` doesn't take a salt, so this is okay.
+ de.write_de_contents(Unsalted, sink).unwrap();
let contents_len = sink.len() - orig_len;
de_ranges.push(orig_len..orig_len + contents_len);
orig_des.push(de);
}
- for (index, (de, range)) in orig_des.iter().zip(de_ranges).enumerate() {
- expected_des.push(DataElement::new(
- u8::try_from(index).unwrap().into(),
- de.de_header().de_type,
- &sink[range],
- ));
+ // Populate `expected_des`.
+ for ((de, range), salt) in orig_des.iter().zip(de_ranges).zip(de_salts) {
+ let salt: Option<DeSalt> = salt.into();
+ let salt = salt.map(|de_salt| de_salt.salt);
+ expected_des.push(DataElement::new(de.de_type(), &sink[range], salt));
}
(expected_des, orig_des)
}
@@ -367,17 +414,15 @@
let data: ArrayView<u8, MAX_DE_LEN> =
ArrayView::try_from_array(array, rng.gen_range(0..=MAX_DE_LEN)).unwrap();
// skip the first few DEs that Google uses
- GenericDataElement::try_from(rng.gen_range(0_u32..1000).into(), data.as_slice()).unwrap()
+ GenericDataElement::try_from(rng.gen_range(0_u16..1000).into(), data.as_slice()).unwrap()
}
#[derive(Clone)]
pub(crate) struct TestIdentities(pub(crate) Vec<TestIdentity>);
impl TestIdentities {
- pub(crate) fn generate<const N: usize, R: rand::Rng, C: CryptoProvider>(
- rng: &mut R,
- ) -> TestIdentities {
- TestIdentities((0..N).map(|_| TestIdentity::random::<_, C>(rng)).collect::<Vec<_>>())
+ pub(crate) fn generate<const N: usize, R: rand::Rng>(rng: &mut R) -> TestIdentities {
+ TestIdentities((0..N).map(|_| TestIdentity::random(rng)).collect::<Vec<_>>())
}
fn pick_random_identity<R: rand::Rng>(&self, rng: &mut R) -> &TestIdentity {
@@ -425,11 +470,11 @@
let mut crypto_rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
for _ in 0..100 {
- let identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
+ let identities = TestIdentities::generate::<100, _>(&mut rng);
let identity = identities.pick_random_identity(&mut rng);
let book = identities.build_cred_book::<CryptoProviderImpl>();
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
let broadcast_cm = identity.broadcast_credential();
let mut section_builder =
@@ -471,15 +516,7 @@
) -> Result<(), AddSectionError> {
builder.section_builder(UnencryptedSectionEncoder).map(|mut sb| {
// use an upper bound on the De size to leave room for another section
- sb.add_de(|_| random_de::<20, _>(rng)).unwrap();
+ sb.add_de(&random_de::<20, _>(rng)).unwrap();
sb.add_to_advertisement::<CryptoProviderImpl>();
})
}
-
-fn append_mock_encrypted_section(adv: &mut Vec<u8>) {
- adv.push(0b0000_0001u8); // format
- adv.extend_from_slice(&[0xAA; 2]); // short salt
- adv.extend_from_slice(&[0xBB; 16]); // identity token
- adv.push(3); // payload length
- adv.extend_from_slice(&[0xCC; 3]); // payload contents
-}
diff --git a/nearby/presence/np_adv/src/tests/deser_v1_tests/error_condition.rs b/nearby/presence/np_adv/src/tests/deser_v1_tests/error_condition.rs
index 57292cf..8c2ad32 100644
--- a/nearby/presence/np_adv/src/tests/deser_v1_tests/error_condition.rs
+++ b/nearby/presence/np_adv/src/tests/deser_v1_tests/error_condition.rs
@@ -18,7 +18,7 @@
#[test]
fn v1_plaintext_empty_contents() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let sb = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
sb.add_to_advertisement::<CryptoProviderImpl>();
let adv = adv_builder.into_advertisement();
@@ -37,8 +37,8 @@
#[test]
fn v1_adv_no_sections() {
let mut rng = StdRng::from_entropy();
- let identities = TestIdentities::generate::<5, _, CryptoProviderImpl>(&mut rng);
- let adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let identities = TestIdentities::generate::<5, _>(&mut rng);
+ let adv_builder = AdvBuilder::new();
let adv = adv_builder.into_advertisement();
let arena = deserialization_arena!();
@@ -57,8 +57,8 @@
fn v1_no_creds_available_ciphertext() {
let mut rng = StdRng::from_entropy();
for _ in 0..100 {
- let identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let identities = TestIdentities::generate::<100, _>(&mut rng);
+ let mut adv_builder = AdvBuilder::new();
let section_configs = fill_with_encrypted_sections::<_, CryptoProviderImpl>(
&mut rng,
&identities,
@@ -78,8 +78,8 @@
fn v1_only_non_matching_identities_available_ciphertext() {
let mut rng = StdRng::from_entropy();
for _ in 0..100 {
- let mut identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut identities = TestIdentities::generate::<100, _>(&mut rng);
+ let mut adv_builder = AdvBuilder::new();
let cloned_identities = identities.clone();
let section_configs = fill_with_encrypted_sections::<_, CryptoProviderImpl>(
&mut rng,
@@ -106,36 +106,13 @@
}
}
-// TODO: this may be valid in future changes and would need to be updated
-#[test]
-fn v1_plaintext_then_encrypted_sections() {
- let mut rng = StdRng::from_entropy();
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- let identities = TestIdentities::generate::<5, _, CryptoProviderImpl>(&mut rng);
-
- add_plaintext_section(&mut rng, &mut adv_builder).unwrap();
- let mut adv = adv_builder.into_advertisement().as_slice().to_vec();
- append_mock_encrypted_section(&mut adv);
-
- let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
- let arena = deserialization_arena!();
-
- let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
- assert_eq!(
- v1_error,
- AdvDeserializationError::ParseError {
- details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
- }
- );
-}
-
#[test]
fn v1_encrypted_then_plaintext_sections() {
let mut rng = StdRng::from_entropy();
- let mut builder = AdvBuilder::new(AdvertisementType::Encrypted);
- let identities = TestIdentities::generate::<1, _, CryptoProviderImpl>(&mut rng);
+ let mut builder = AdvBuilder::new();
+ let identities = TestIdentities::generate::<1, _>(&mut rng);
- let _ = add_mic_rand_salt_to_adv::<_, CryptoProviderImpl, 0>(
+ let _ = add_mic_rand_length_salt_to_adv::<_, CryptoProviderImpl, 0>(
&mut rng,
&identities.0[0],
&mut builder,
@@ -144,7 +121,7 @@
let mut adv = builder.into_advertisement().as_slice().to_vec();
// Append plaintext section
- adv.push(V1_ENCODING_UNENCRYPTED); //Header
+ adv.push(V1_ENCODING_UNENCRYPTED.byte_value()); //Header
adv.push(3); // section len
adv.extend_from_slice(&[0xFF; 3]); //section contents
@@ -161,10 +138,57 @@
}
#[test]
+fn v1_mic_encrypted_out_of_order() {
+ let mut rng = StdRng::from_entropy();
+ let identities = TestIdentities::generate::<1, _>(&mut rng);
+ let identity = &identities.0[0];
+
+ // First, build an advertisement with a MIC section with an extended salt
+ // which contains no DEs.
+ let mut builder = AdvBuilder::new();
+ let _ = add_mic_rand_salt_to_adv::<_, CryptoProviderImpl, 0>(
+ &mut rng,
+ identity,
+ &mut builder,
+ false,
+ )
+ .unwrap();
+ let mut adv = builder.into_advertisement().as_slice().to_vec();
+
+ // Build another advertisement with a MIC section with a short salt which
+ // also contains no DEs.
+ let mut builder = AdvBuilder::new();
+ let _ = add_mic_rand_salt_to_adv::<_, CryptoProviderImpl, 0>(
+ &mut rng,
+ identity,
+ &mut builder,
+ true,
+ )
+ .unwrap();
+ let mut short_mic_section = builder.into_advertisement().as_slice()[1..].to_vec();
+
+ // Stitch 'em together
+ adv.append(&mut short_mic_section);
+ let adv = adv;
+
+ // Attempt to deserialize
+ let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+ let arena = deserialization_arena!();
+
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+ assert_eq!(
+ v1_error,
+ AdvDeserializationError::ParseError {
+ details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
+ }
+ );
+}
+
+#[test]
fn v1_encrypted_matching_identity_but_mic_mismatch() {
for _ in 0..100 {
v1_deserialize_error_test_tampered_adv(
- add_mic_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 0>,
+ add_mic_rand_length_salt_to_adv::<StdRng, CryptoProviderImpl, 0>,
|adv| {
// mangle the last 2 bytes of the suffix to invalidate the advertisement.
// the identity should still correctly match
@@ -176,35 +200,6 @@
}
}
-#[test]
-fn v1_encrypted_matching_identity_but_sig_mismatch() {
- v1_deserialize_error_test_tampered_adv(
- add_sig_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 0>,
- |adv| {
- // mangle the last 2 bytes of the suffix to invalidate the advertisement.
- // the identity should still correctly match
- let adv_len = adv.len();
- adv[adv_len - 2..].copy_from_slice(&[0xFF; 2]);
- },
- )
-}
-
-#[test]
-fn v1_encrypted_matching_identity_missing_signature() {
- v1_deserialize_error_test_tampered_adv(
- add_sig_rand_salt_to_adv::<StdRng, CryptoProviderImpl, 0>,
- |adv| {
- // only keep the last 63 byte bytes after the len, which is too short to fit a signature
- // 1 byte header + 1 byte format + 16 byte salt + 16 byte token + 1 byte len + 63 bytes of contents
- adv.truncate(1 + 1 + 16 + 16 + 1 + 63);
- // modify the length byte to account for this otherwise this will fail to parse at all
- // 1 byte header + 1 byte format + 16 byte salt + 16 byte token
- let len_byte_index = 1 + 1 + 16 + 16;
- adv[len_byte_index] = 63;
- },
- )
-}
-
fn v1_deserialize_error_test_tampered_adv(
add_to_adv: impl for<'a> Fn(
&mut StdRng,
@@ -214,8 +209,8 @@
mangle_adv: impl Fn(&mut Vec<u8>),
) {
let mut rng = StdRng::from_entropy();
- let mut builder = AdvBuilder::new(AdvertisementType::Encrypted);
- let identities = TestIdentities::generate::<1, _, CryptoProviderImpl>(&mut rng);
+ let mut builder = AdvBuilder::new();
+ let identities = TestIdentities::generate::<1, _>(&mut rng);
let _ = add_to_adv(&mut rng, &identities.0[0], &mut builder);
let mut adv = builder.into_advertisement().as_slice().to_vec();
diff --git a/nearby/presence/np_adv/src/tests/deser_v1_tests/happy_path.rs b/nearby/presence/np_adv/src/tests/deser_v1_tests/happy_path.rs
index ee0077d..3702e32 100644
--- a/nearby/presence/np_adv/src/tests/deser_v1_tests/happy_path.rs
+++ b/nearby/presence/np_adv/src/tests/deser_v1_tests/happy_path.rs
@@ -17,6 +17,7 @@
use super::*;
use crate::{deserialization_arena, extended::salt::*};
use rand::SeedableRng;
+use std::collections::HashSet;
#[test]
fn deserialize_rand_identities_single_section_finds_correct_one_mic_short_salt() {
@@ -41,18 +42,10 @@
}
#[test]
-fn deserialize_rand_identities_single_section_finds_correct_one_signed() {
- deserialize_rand_identities_finds_correct_one(
- SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>,
- VerificationMode::Signature,
- )
-}
-
-#[test]
fn v1_plaintext() {
let mut rng = StdRng::from_entropy();
for _ in 0..100 {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let section_config = fill_plaintext_adv(&mut rng, &mut adv_builder);
let adv = adv_builder.into_advertisement();
let arena = deserialization_arena!();
@@ -68,9 +61,56 @@
}
#[test]
+fn v1_encodings_forward_extensibility() {
+ let mut rng = StdRng::from_entropy();
+ for _ in 0..100 {
+ let mut adv_builder = AdvBuilder::new();
+ add_plaintext_section(&mut rng, &mut adv_builder).unwrap();
+
+ let mut adv = adv_builder.into_advertisement().as_slice().to_vec();
+ // Append a section header with a randomized encoding type which
+ // is chosen so as not to be recognized.
+ let mut encoding_type: u8 = 0;
+ while encoding_type == V1_ENCODING_UNENCRYPTED.byte_value()
+ || encoding_type == V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN.byte_value()
+ || encoding_type == V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN.byte_value()
+ {
+ encoding_type = rng.gen_range(0..=15);
+ }
+ adv.push(encoding_type);
+ // Generate random garbage for the section contents. We don't
+ // even care if the length matches, since calculation can be encoding-specific.
+ let section_contents_len = rng.gen_range(0..=127);
+ for _ in 0..section_contents_len {
+ adv.push(rng.gen());
+ }
+ // Deserialize, and ensure that we only get back the plaintext section.
+ let arena = deserialization_arena!();
+ let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+ assert_eq!(0, v1_contents.invalid_sections_count());
+
+ let sections = v1_contents.into_sections();
+ assert_eq!(1, sections.len());
+
+ if let V1DeserializedSection::Decrypted(_) = §ions[0] {
+ panic!("Expected a plaintext section");
+ }
+ // To further ensure it's not just garbage data, ensure that there's only
+ // one data element in the section, per what add_plaintext_section does
+ assert_eq!(1, sections[0].iter_data_elements().count());
+ }
+}
+
+#[test]
fn v1_multiple_plaintext_sections() {
let mut rng = StdRng::from_entropy();
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
add_plaintext_section(&mut rng, &mut adv_builder).unwrap();
// append an extra plaintext section
@@ -92,11 +132,34 @@
}
#[test]
+fn v1_plaintext_then_encrypted_sections() {
+ let mut rng = StdRng::from_entropy();
+ let mut adv_builder = AdvBuilder::new();
+ let identities = TestIdentities::generate::<1, _>(&mut rng);
+
+ add_plaintext_section(&mut rng, &mut adv_builder).unwrap();
+ let _ = add_mic_rand_length_salt_to_adv::<_, CryptoProviderImpl, 1>(
+ &mut rng,
+ &identities.0[0],
+ &mut adv_builder,
+ )
+ .unwrap();
+ let adv = adv_builder.into_advertisement().as_slice().to_vec();
+
+ let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
+ let arena = deserialization_arena!();
+
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+ assert_eq!(0, v1_contents.invalid_sections_count());
+ assert_eq!(2, v1_contents.sections().len());
+}
+
+#[test]
fn v1_all_identities_resolvable_ciphertext() {
let mut rng = StdRng::from_entropy();
for _ in 0..100 {
- let identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let identities = TestIdentities::generate::<100, _>(&mut rng);
+ let mut adv_builder = AdvBuilder::new();
let section_configs = fill_with_encrypted_sections::<_, CryptoProviderImpl>(
&mut rng,
&identities,
@@ -110,8 +173,17 @@
let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
assert_eq!(0, v1_contents.invalid_sections_count());
assert_eq!(section_configs.len(), v1_contents.sections().len());
- for (section_config, section) in section_configs.iter().zip(v1_contents.sections()) {
- assert_section_equals(section_config, section);
+ for section_config in §ion_configs {
+ let mut has_match: bool = false;
+ for section in v1_contents.sections() {
+ if test_section_equals(section_config, section).is_ok() {
+ has_match = true;
+ break;
+ }
+ }
+ if !has_match {
+ panic!("Section config does not exist in output: {:?}", section_config);
+ }
}
}
}
@@ -120,8 +192,8 @@
fn v1_only_some_matching_identities_available_ciphertext() {
let mut rng = StdRng::from_entropy();
for _ in 0..100 {
- let mut identities = TestIdentities::generate::<100, _, CryptoProviderImpl>(&mut rng);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut identities = TestIdentities::generate::<100, _>(&mut rng);
+ let mut adv_builder = AdvBuilder::new();
let cloned_identities = identities.clone();
let section_configs = fill_with_encrypted_sections::<_, CryptoProviderImpl>(
&mut rng,
@@ -147,6 +219,8 @@
res
});
+ let removed_key_seeds: HashSet<[u8; 32]> = removed.iter().map(|i| i.key_seed).collect();
+
// Need to account for how many sections were affected since the same identity could be used
// to encode multiple different sections
let affected_sections = section_configs
@@ -156,7 +230,7 @@
panic!("There are no plaintext sections")
}
IdentityKind::Encrypted { identity: si, verification_mode: _ } => {
- removed.iter().any(|i| i.key_seed == si.key_seed)
+ removed_key_seeds.contains(&si.key_seed)
}
})
.count();
@@ -165,23 +239,32 @@
let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
+ // Verify that the number of not-successfully-decrypted sections matches
+ // the number of sections for which we had no associated credentials to decrypt.
assert_eq!(affected_sections, v1_contents.invalid_sections_count());
assert_eq!(section_configs.len() - affected_sections, v1_contents.sections().len());
- for (section_config, section) in section_configs
- .iter()
- // skip sections w/ removed identities
- .filter(|sc| match sc.identity_kind {
- IdentityKind::Plaintext => {
- panic!("There are no plaintext sections")
+ for section_config in §ion_configs {
+ // Verify that each section config corresponds to _some_
+ // actual section in the output (but they could have been re-ordered.)
+ let IdentityKind::Encrypted { identity, verification_mode: _ } =
+ §ion_config.identity_kind
+ else {
+ panic!("There are no plaintext sections");
+ };
+ if removed_key_seeds.contains(&identity.key_seed) {
+ continue;
+ }
+ let mut has_match: bool = false;
+ for section in v1_contents.sections() {
+ if test_section_equals(section_config, section).is_ok() {
+ has_match = true;
+ break;
}
- IdentityKind::Encrypted { identity: si, verification_mode: _ } => {
- !removed.iter().any(|i| i.key_seed == si.key_seed)
- }
- })
- .zip(v1_contents.sections())
- {
- assert_section_equals(section_config, section);
+ }
+ if !has_match {
+ panic!("Section config does not exist in output: {:?}", section_config);
+ }
}
}
}
@@ -208,17 +291,6 @@
);
}
-#[test]
-fn v1_decrypted_sig_extended_salt_matches() {
- let mut rng = StdRng::from_entropy();
- let salt = MultiSalt::Extended(rng.gen::<[u8; 16]>().into());
- v1_decrypted_adv_salt_matches(
- &mut rng,
- salt,
- add_sig_with_salt_to_adv::<_, CryptoProviderImpl, 0>,
- );
-}
-
fn v1_decrypted_adv_salt_matches(
rng: &mut StdRng,
salt: MultiSalt,
@@ -229,8 +301,8 @@
MultiSalt,
) -> Result<SectionConfig<'a>, AddSectionError>,
) {
- let identities = TestIdentities::generate::<1, _, CryptoProviderImpl>(rng);
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let identities = TestIdentities::generate::<1, _>(rng);
+ let mut adv_builder = AdvBuilder::new();
let _ = add_to_adv(rng, &identities.0[0], &mut adv_builder, salt);
diff --git a/nearby/presence/np_adv/tests/examples_v0.rs b/nearby/presence/np_adv/tests/examples_v0.rs
index b00fd61..b27612a 100644
--- a/nearby/presence/np_adv/tests/examples_v0.rs
+++ b/nearby/presence/np_adv/tests/examples_v0.rs
@@ -11,7 +11,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::panic)]
+#![allow(
+ clippy::unwrap_used,
+ clippy::expect_used,
+ clippy::indexing_slicing,
+ clippy::panic,
+ missing_docs
+)]
use crypto_provider_default::CryptoProviderImpl;
use ldt_np_adv::*;
diff --git a/nearby/presence/np_adv/tests/examples_v1.rs b/nearby/presence/np_adv/tests/examples_v1.rs
index 7b89f8d..3c7bd5a 100644
--- a/nearby/presence/np_adv/tests/examples_v1.rs
+++ b/nearby/presence/np_adv/tests/examples_v1.rs
@@ -12,9 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::panic)]
+#![allow(
+ clippy::unwrap_used,
+ clippy::expect_used,
+ clippy::indexing_slicing,
+ clippy::panic,
+ missing_docs
+)]
-use crypto_provider::{ed25519, CryptoProvider, CryptoRng};
+use crypto_provider::{CryptoProvider, CryptoRng};
use crypto_provider_default::CryptoProviderImpl;
use np_adv::credential::matched::{
EmptyMatchedCredential, MetadataMatchedCredential, WithMatchedCredential,
@@ -30,28 +36,22 @@
deserialization_arena, deserialize_advertisement,
extended::{
data_elements::TxPowerDataElement,
+ de_type::HasDEType,
deserialize::VerificationMode,
- serialize::{
- AdvBuilder, AdvertisementType, SignedEncryptedSectionEncoder, SingleTypeDataElement,
- UnencryptedSectionEncoder,
- },
+ serialize::{AdvBuilder, MicEncryptedSectionEncoder, UnencryptedSectionEncoder},
NP_V1_ADV_MAX_SECTION_COUNT,
},
shared_data::TxPower,
AdvDeserializationError, AdvDeserializationErrorDetailsHazmat,
};
-use np_hkdf::{v1_salt, DerivedSectionKeys};
+use np_hkdf::DerivedSectionKeys;
use serde::{Deserialize, Serialize};
-type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
-
#[test]
fn v1_deser_plaintext() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
- section_builder
- .add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(6).unwrap()))
- .unwrap();
+ section_builder.add_de(&TxPowerDataElement::from(TxPower::try_from(6).unwrap())).unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
let adv = adv_builder.into_advertisement();
@@ -82,7 +82,7 @@
assert_eq!(1, data_elements.len());
let de = &data_elements[0];
- assert_eq!(v1_salt::DataElementOffset::from(0), de.offset());
+ assert_eq!(None, de.salt());
assert_eq!(TxPowerDataElement::DE_TYPE, de.de_type());
assert_eq!(&[6], de.contents());
}
@@ -114,11 +114,10 @@
let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
let token_array: [u8; 16] = rng.gen();
let identity_token = V1IdentityToken::from(token_array);
- let private_key = ed25519::PrivateKey::generate::<Ed25519ProviderImpl>();
let key_seed = rng.gen();
let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token, private_key.clone());
+ let broadcast_cred = V1BroadcastCredential::new(key_seed, identity_token);
// Serialize and encrypt some identity metadata (sender-side)
let sender_metadata = IdentityMetadata {
@@ -133,28 +132,27 @@
>(&hkdf, identity_token, &sender_metadata_bytes);
// prepare advertisement
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let mut adv_builder = AdvBuilder::new();
let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
+ .section_builder(MicEncryptedSectionEncoder::new_random_salt::<CryptoProviderImpl>(
&mut rng,
&broadcast_cred,
))
.unwrap();
- section_builder
- .add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(7).unwrap()))
- .unwrap();
+ section_builder.add_de(&TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
let adv = adv_builder.into_advertisement();
+ println!("adv: {adv:?}");
let discovery_credential = V1DiscoveryCredential::new(
key_seed,
- [0; 32],
- [0; 32], // Zeroing out MIC HMAC, since it's unused in examples here.
- hkdf.v1_signature_keys()
+ hkdf.v1_mic_short_salt_keys()
.identity_token_hmac_key()
.calculate_hmac::<CryptoProviderImpl>(identity_token.bytes()),
- private_key.derive_public_key::<Ed25519ProviderImpl>(),
+ hkdf.v1_mic_extended_salt_keys()
+ .identity_token_hmac_key()
+ .calculate_hmac::<CryptoProviderImpl>(identity_token.bytes()),
);
let credentials: [MatchableCredential<V1, MetadataMatchedCredential<_>>; 1] =
@@ -173,7 +171,7 @@
.into_v1()
.expect("Should be V1");
- assert_eq!(0, contents.invalid_sections_count());
+ assert_eq!(0, contents.invalid_sections_count(), "{contents:?}");
let sections = contents.sections().collect::<Vec<_>>();
assert_eq!(1, sections.len());
@@ -192,53 +190,21 @@
let section = matched.contents();
- assert_eq!(VerificationMode::Signature, section.verification_mode());
+ assert_eq!(VerificationMode::Mic, section.verification_mode());
assert_eq!(&identity_token, section.identity_token());
let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(1, data_elements.len());
let de = &data_elements[0];
- assert_eq!(v1_salt::DataElementOffset::from(0), de.offset());
assert_eq!(TxPowerDataElement::DE_TYPE, de.de_type());
assert_eq!(&[7], de.contents());
-
- // Uncomment if you need to regenerate C++ v1_private_identity_tests data
- // {
- // use test_helper::hex_bytes;
- // use np_adv::extended::salt::MultiSalt;
- // use np_adv_credential_matched::MatchedCredential;
- // println!("adv:\n{}", hex_bytes(adv.as_slice()));
- // println!("key seed:\n{}", hex_bytes(key_seed));
- // println!(
- // "identity token hmac:\n{}",
- // hex_bytes(
- // hkdf.v1_signature_keys()
- // .identity_token_hmac_key()
- // .calculate_hmac(identity_token.bytes())
- // )
- // );
- // println!("public key:\n{}", hex_bytes(key_pair.public().to_bytes()));
- // println!(
- // "encrypted metadata:\n{}",
- // hex_bytes(encrypted_sender_metadata.fetch_encrypted_metadata().unwrap())
- // );
- // std::println!("offset is: {:?}", de.offset());
- // let derived_salt = match section.salt() {
- // MultiSalt::Short(_) => panic!(),
- // MultiSalt::Extended(s) => {
- // s.derive::<16, CryptoProviderImpl>(Some(de.offset())).unwrap()
- // }
- // };
- // println!("DE derived salt:\n{}", hex_bytes(derived_salt));
- // panic!();
- // }
}
#[test]
fn v1_deser_no_section() {
// TODO: we shouldn't allow this invalid advertisement to be serialized
- let adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let adv_builder = AdvBuilder::new();
let adv = adv_builder.into_advertisement();
let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
0,
@@ -259,12 +225,10 @@
#[test]
fn v1_deser_plaintext_over_max_sections() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut adv_builder = AdvBuilder::new();
for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT {
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
- section_builder
- .add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(7).unwrap()))
- .unwrap();
+ section_builder.add_de(&TxPowerDataElement::from(TxPower::try_from(7).unwrap())).unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
}
let mut adv = adv_builder.into_advertisement().as_slice().to_vec();
@@ -272,7 +236,7 @@
adv.extend_from_slice(
[
0x01, // Section header
- V1_ENCODING_UNENCRYPTED,
+ V1_ENCODING_UNENCRYPTED.byte_value(),
]
.as_slice(),
);
diff --git a/nearby/presence/np_adv_dynamic/src/extended.rs b/nearby/presence/np_adv_dynamic/src/extended.rs
index c25be74..59f6a42 100644
--- a/nearby/presence/np_adv_dynamic/src/extended.rs
+++ b/nearby/presence/np_adv_dynamic/src/extended.rs
@@ -13,8 +13,8 @@
// limitations under the License.
use crypto_provider::CryptoProvider;
-use np_adv::extended::salt::MultiSalt;
-use np_adv::{extended::data_elements::*, extended::serialize::*, shared_data::*};
+use np_adv::extended::salt::{DeSalt, MultiSalt, Unsalted};
+use np_adv::{extended::de_type::*, extended::serialize::*};
use sink::Sink;
use std::fmt::{Display, Formatter};
@@ -37,9 +37,6 @@
pub enum BoxedAddSectionError {
/// An error which was generated by the underlying AdvBuilder wrapped by the BoxedAdvBuilder
Underlying(AddSectionError),
- /// An error generated when the boxed advertisement builder is unsalted, but the section
- /// identity requires salt.
- IdentityRequiresSaltError,
}
impl Display for BoxedAddSectionError {
@@ -48,9 +45,6 @@
BoxedAddSectionError::Underlying(u) => {
write!(f, "{0}", u)
}
- BoxedAddSectionError::IdentityRequiresSaltError => {
- write!(f, "Error generated when the BoxedAdvBuilder is unsalted, but the section identity requires salt.")
- }
}
}
}
@@ -106,9 +100,6 @@
BoxedEncoder::MicEncrypted(ident) => {
wrap_owning_section_builder(self.adv_builder.into_section_builder(ident))
}
- BoxedEncoder::SignedEncrypted(ident) => {
- wrap_owning_section_builder(self.adv_builder.into_section_builder(ident))
- }
}
}
@@ -130,9 +121,6 @@
BoxedEncoder::MicEncrypted(ident) => {
wrap_mut_ref_section_builder(self.adv_builder.section_builder(ident))
}
- BoxedEncoder::SignedEncrypted(ident) => {
- wrap_mut_ref_section_builder(self.adv_builder.section_builder(ident))
- }
}
}
@@ -148,8 +136,6 @@
Unencrypted,
/// An encrypted encoder leveraging MIC for verification.
MicEncrypted(MicEncryptedSectionEncoder<MultiSalt>),
- /// An encrypted encoder leveraging signatures for verification.
- SignedEncrypted(SignedEncryptedSectionEncoder),
}
/// A `SectionBuilder` whose corresponding Identity
@@ -160,18 +146,15 @@
Public(Box<SectionBuilder<R, UnencryptedSectionEncoder>>),
/// A builder for a MIC-verified section.
MicEncrypted(Box<SectionBuilder<R, MicEncryptedSectionEncoder<MultiSalt>>>),
- /// A builder for a signature-verified section.
- SignedEncrypted(Box<SectionBuilder<R, SignedEncryptedSectionEncoder>>),
}
impl BoxedSectionBuilder<AdvBuilder> {
- /// Gets the 0-based index of the section currently under construction
- /// in the context of the containing advertisement.
- pub fn section_index(&self) -> usize {
+ /// Gets the count of the sections which were added to the advertisement
+ /// prior to the creation of this section builder.
+ pub fn previously_added_section_count(&self) -> usize {
match self {
- BoxedSectionBuilder::Public(x) => x.section_index(),
- BoxedSectionBuilder::MicEncrypted(x) => x.section_index(),
- BoxedSectionBuilder::SignedEncrypted(x) => x.section_index(),
+ BoxedSectionBuilder::Public(x) => x.previously_added_section_count(),
+ BoxedSectionBuilder::MicEncrypted(x) => x.previously_added_section_count(),
}
}
/// Add this builder to the advertisement that created it,
@@ -180,19 +163,17 @@
let adv_builder = match self {
BoxedSectionBuilder::Public(x) => x.add_to_advertisement::<C>(),
BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement::<C>(),
- BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement::<C>(),
};
BoxedAdvBuilder::from(adv_builder)
}
}
-impl<'a> BoxedSectionBuilder<&'a mut AdvBuilder> {
+impl BoxedSectionBuilder<&mut AdvBuilder> {
/// Add this builder to the advertisement that created it.
pub fn add_to_advertisement<C: CryptoProvider>(self) {
match self {
BoxedSectionBuilder::Public(x) => x.add_to_advertisement::<C>(),
BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement::<C>(),
- BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement::<C>(),
}
}
}
@@ -204,50 +185,89 @@
match self {
BoxedSectionBuilder::Public(_) => false,
BoxedSectionBuilder::MicEncrypted(_) => true,
- BoxedSectionBuilder::SignedEncrypted(_) => true,
}
}
- /// Gets the derived salt of the next DE to be added to the section,
+ /// Gets the derived salt of a DE with a given type code,
/// if this section-builder corresponds to an encrypted section that can
/// provide per-DE salts.
/// Otherwise, returns nothing.
///
- /// Suitable for scenarios (like FFI) where a closure would be inappropriate
+ /// Suitable for scenarios (like FFI) where a custom DE impl would be inappropriate
/// for DE construction, and interaction with the client is preferred.
- pub fn next_de_salt(&self) -> Option<DeSalt> {
+ pub fn de_salt(&self, de_type: DeType) -> Option<DeSalt> {
match self {
BoxedSectionBuilder::Public(_) => None,
- BoxedSectionBuilder::MicEncrypted(x) => x.next_de_salt(),
- BoxedSectionBuilder::SignedEncrypted(x) => Some(x.next_de_salt()),
+ BoxedSectionBuilder::MicEncrypted(x) => x.de_salt(de_type),
}
}
+ /// Attempts to add a data element to the section.
+ pub fn add_de(&mut self, de: &dyn DynWriteDataElement) -> Result<(), BoxedAddDataElementError> {
+ // Perform a sneaky little trick to check the salt
+ // and propagate a salt mismatch error before we ever
+ // actually commit a write.
+ //
+ // To do this, we pretend as if we have an acceptable
+ // `WriteDataElement#add_de` implementation which actually
+ // invokes the `DynWriteDataElement` methods under the hood,
+ // but upon encountering a salt mismatch, it first exfiltrates
+ // the information that this exception occurred to a state
+ // variable in this calling method, and then pretends as if
+ // writing failed despite not writing anything.
+ // We then transmogrify this to the correct error or
+ // return value before returning from this method.
+ //
+ // The `RefCell` shenanigans here are panic-free due to
+ // the fact that they're all completely local to this method.
+ struct SaltErrorSmuggler<'a> {
+ de: &'a dyn DynWriteDataElement,
+ smuggled_salt_error: core::cell::RefCell<bool>,
+ }
- /// Add a data element to the section with a closure that returns a `Result`.
- ///
- /// The provided `build_de` closure will be invoked with the derived salt for this DE,
- /// if any salt has been specified for the surrounding advertisement.
- pub fn add_de_res<E>(
- &mut self,
- build_de: impl FnOnce(Option<DeSalt>) -> Result<BoxedWriteDataElement, E>,
- ) -> Result<(), AddDataElementError<E>> {
- match self {
- BoxedSectionBuilder::Public(x) => {
- let build_de_modified = |()| build_de(None);
- x.add_de_res(build_de_modified)
- }
- BoxedSectionBuilder::MicEncrypted(x) => x.add_de_res(build_de),
- BoxedSectionBuilder::SignedEncrypted(x) => {
- let build_de_modified = |de_salt: DeSalt| build_de(Some(de_salt));
- x.add_de_res(build_de_modified)
+ impl ProvidesDEType for SaltErrorSmuggler<'_> {
+ fn de_type(&self) -> DeType {
+ self.de.de_type()
}
}
- }
- /// Like add_de_res, but for infalliable closures
- pub fn add_de(
- &mut self,
- build_de: impl FnOnce(Option<DeSalt>) -> BoxedWriteDataElement,
- ) -> Result<(), AddDataElementError<()>> {
- self.add_de_res(|derived_salt| Ok::<_, ()>(build_de(derived_salt)))
+
+ impl WriteDataElement for SaltErrorSmuggler<'_> {
+ type Salt = Option<DeSalt>;
+ fn write_de_contents<S: Sink<u8>>(
+ &self,
+ salt: Option<DeSalt>,
+ sink: &mut S,
+ ) -> Option<()> {
+ let sink: &mut dyn Sink<u8> = sink;
+ match self.de.write_de_contents(salt, sink.into()) {
+ Ok(()) => Some(()),
+ Err(BoxedWriteDataElementError::InsufficientSpace) => None,
+ Err(BoxedWriteDataElementError::NoDerivedSalt) => {
+ *self.smuggled_salt_error.borrow_mut() &= true;
+ None
+ }
+ }
+ }
+ }
+ let smuggler =
+ SaltErrorSmuggler { de, smuggled_salt_error: core::cell::RefCell::new(false) };
+
+ let unmodified_ret_val = match self {
+ BoxedSectionBuilder::Public(x) => x.add_de(&smuggler),
+ BoxedSectionBuilder::MicEncrypted(x) => x.add_de(&smuggler),
+ };
+ // Unbox what we smuggled, and return what we need to.
+ let smuggled_salt_error = smuggler.smuggled_salt_error.into_inner();
+ match (unmodified_ret_val, smuggled_salt_error) {
+ (Err(AddDataElementError::InsufficientSpace), false) => {
+ Err(BoxedAddDataElementError::InsufficientSpace)
+ }
+ (Err(AddDataElementError::InsufficientSpace), true) => {
+ Err(BoxedAddDataElementError::NoDerivedSalt)
+ }
+ (Err(AddDataElementError::DuplicateDataElementTypeCode), _) => {
+ Err(BoxedAddDataElementError::DuplicateDataElementTypeCode)
+ }
+ (Ok(()), _) => Ok(()),
+ }
}
}
@@ -267,20 +287,12 @@
}
}
-impl<R: AsMut<AdvBuilder>> From<SectionBuilder<R, SignedEncryptedSectionEncoder>>
- for BoxedSectionBuilder<R>
-{
- fn from(section_builder: SectionBuilder<R, SignedEncryptedSectionEncoder>) -> Self {
- BoxedSectionBuilder::SignedEncrypted(Box::new(section_builder))
- }
-}
-
/// Mutable trait object reference to a `Sink<u8>`
pub struct DynSink<'a> {
wrapped: &'a mut dyn Sink<u8>,
}
-impl<'a> Sink<u8> for DynSink<'a> {
+impl Sink<u8> for DynSink<'_> {
fn try_extend_from_slice(&mut self, items: &[u8]) -> Option<()> {
self.wrapped.try_extend_from_slice(items)
}
@@ -295,64 +307,79 @@
}
}
+/// Errors which may be raised by [`DynWriteDataElement#write_de_contents`].
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum BoxedWriteDataElementError {
+ /// The space in the enclosing section was exhausted
+ /// and/or the data element contents are over the max DE size.
+ InsufficientSpace,
+ /// The surrounding section does not have an extended
+ /// salt, but a derived salt is expected by the data element.
+ NoDerivedSalt,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+/// Errors which may be raised by [`BoxedSectionBuilder#add_de`].
+pub enum BoxedAddDataElementError {
+ /// The space in the enclosing section was exhausted
+ /// and/or the data element contents are over the max DE size.
+ InsufficientSpace,
+ /// The surrounding section does not have an extended
+ /// salt, but a derived salt is expected by the data element.
+ NoDerivedSalt,
+ /// A data element with the same type code already
+ /// was added to the section.
+ DuplicateDataElementTypeCode,
+}
+
/// A version of the WriteDataElement trait which is object-safe
pub trait DynWriteDataElement {
- /// Gets the data-element header for the data element
- fn de_header(&self) -> DeHeader;
- /// Writes the contents of the DE payload to the given DynSink.
- /// Returns Some(()) if the write operation was successful,
- /// and None if it was unsuccessful
- fn write_de_contents(&self, sink: DynSink) -> Option<()>;
+ /// Gets the data-element type for the data element
+ fn de_type(&self) -> DeType;
+ /// Writes the contents of the DE payload to the given DynSink,
+ /// given a salt which may or may not be populated.
+ fn write_de_contents(
+ &self,
+ salt: Option<DeSalt>,
+ sink: DynSink,
+ ) -> Result<(), BoxedWriteDataElementError>;
}
-impl<T: WriteDataElement> DynWriteDataElement for T {
- fn de_header(&self) -> DeHeader {
- WriteDataElement::de_header(self)
- }
- fn write_de_contents(&self, mut sink: DynSink) -> Option<()> {
- WriteDataElement::write_de_contents(self, &mut sink)
+/// Helper trait to check whether/not dynamic conversion
+/// from an `Option<DeSalt>` to a desired salt type is possible.
+pub trait TryCoerceSalt: Sized {
+ /// Attempts to coerce a DE salt into a
+ /// salt of this type.
+ fn try_coerce(salt: Option<DeSalt>) -> Option<Self>;
+}
+
+impl TryCoerceSalt for DeSalt {
+ fn try_coerce(salt: Option<DeSalt>) -> Option<DeSalt> {
+ salt
}
}
-/// Trait object wrapper for DynWriteDataElement instances
-pub struct BoxedWriteDataElement {
- wrapped: Box<dyn DynWriteDataElement>,
-}
-
-impl BoxedWriteDataElement {
- /// Constructs a new `BoxedWriteDataElement` from a `WriteDataElement`
- /// whose trait impl is valid for a `'static` lifetime.
- pub fn new<D: WriteDataElement + 'static>(wrapped: D) -> Self {
- let wrapped = Box::new(wrapped);
- Self { wrapped }
+impl TryCoerceSalt for Unsalted {
+ fn try_coerce(_salt: Option<DeSalt>) -> Option<Unsalted> {
+ Some(Unsalted)
}
}
-impl WriteDataElement for BoxedWriteDataElement {
- fn de_header(&self) -> DeHeader {
- self.wrapped.de_header()
+impl<T: WriteDataElement> DynWriteDataElement for T
+where
+ <T as WriteDataElement>::Salt: TryCoerceSalt,
+{
+ fn de_type(&self) -> DeType {
+ ProvidesDEType::de_type(self)
}
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
- let sink: &mut dyn Sink<u8> = sink;
- let dyn_sink = DynSink::from(sink);
- self.wrapped.write_de_contents(dyn_sink)
- }
-}
-
-impl From<TxPower> for BoxedWriteDataElement {
- fn from(tx_power: TxPower) -> Self {
- BoxedWriteDataElement::new::<TxPowerDataElement>(tx_power.into())
- }
-}
-
-impl From<ContextSyncSeqNum> for BoxedWriteDataElement {
- fn from(context_sync_sequence_num: ContextSyncSeqNum) -> Self {
- BoxedWriteDataElement::new::<ContextSyncSeqNumDataElement>(context_sync_sequence_num.into())
- }
-}
-
-impl From<ActionsDataElement> for BoxedWriteDataElement {
- fn from(data: ActionsDataElement) -> Self {
- BoxedWriteDataElement::new(data)
+ fn write_de_contents(
+ &self,
+ salt: Option<DeSalt>,
+ mut sink: DynSink,
+ ) -> Result<(), BoxedWriteDataElementError> {
+ let coerced_salt = <<T as WriteDataElement>::Salt as TryCoerceSalt>::try_coerce(salt)
+ .ok_or(BoxedWriteDataElementError::NoDerivedSalt)?;
+ WriteDataElement::write_de_contents(self, coerced_salt, &mut sink)
+ .ok_or(BoxedWriteDataElementError::InsufficientSpace)
}
}
diff --git a/nearby/presence/np_c_ffi/include/c/np_c_ffi.h b/nearby/presence/np_c_ffi/include/c/np_c_ffi.h
index 2cef974..e932671 100644
--- a/nearby/presence/np_c_ffi/include/c/np_c_ffi.h
+++ b/nearby/presence/np_c_ffi/include/c/np_c_ffi.h
@@ -18,7 +18,7 @@
#pragma once
-/* Generated with cbindgen:0.26.0 */
+/* Generated with cbindgen:0.27.0 */
/*
WARNING: this file is autogenerated by cbindgen. Don't modify this manually.
@@ -132,6 +132,11 @@
* most likely a length above 127.
*/
NP_FFI_ADD_V1DE_RESULT_INVALID_DATA_ELEMENT = 3,
+ /**
+ * A data element with the same type-code was already
+ * present in the section.
+ */
+ NP_FFI_ADD_V1DE_RESULT_DUPLICATE_DATA_ELEMENT_TYPE_CODE = 4,
};
typedef uint8_t np_ffi_AddV1DEResult;
@@ -157,9 +162,8 @@
typedef uint8_t np_ffi_AddV1SectionToAdvertisementResult;
/**
- * Enum common to V0 and V1 serialization expressing
- * what kind of advertisement builder (public/encrypted)
- * is in use.
+ * Enum for V0 serialization expressing what kind of
+ * advertisement builder (public/encrypted) is in use.
*/
enum np_ffi_AdvertisementBuilderKind {
/**
@@ -225,18 +229,12 @@
*/
NP_FFI_CREATE_V1_SECTION_BUILDER_RESULT_KIND_INVALID_ADVERTISEMENT_BUILDER_HANDLE = 2,
/**
- * We're attempting to build a section with an identity
- * kind (public/encrypted) which doesn't match the kind
- * for the entire advertisement.
- */
- NP_FFI_CREATE_V1_SECTION_BUILDER_RESULT_KIND_IDENTITY_KIND_MISMATCH = 3,
- /**
* There isn't enough space for a new section, either
* because the maximum section count has been exceeded
* or because the advertisement is almost full, and
* the minimum size of a section wouldn't fit.
*/
- NP_FFI_CREATE_V1_SECTION_BUILDER_RESULT_KIND_NO_SPACE_LEFT = 4,
+ NP_FFI_CREATE_V1_SECTION_BUILDER_RESULT_KIND_NO_SPACE_LEFT = 3,
};
typedef uint8_t np_ffi_CreateV1SectionBuilderResultKind;
@@ -401,14 +399,14 @@
enum np_ffi_GetV1DE16ByteSaltResultKind {
/**
* The attempt to get the derived salt failed, possibly
- * because the passed DE offset was invalid (==255),
+ * because the passed DE type code was forbidden (0xFFFFFFFF),
* or because there was no salt included for the
* referenced advertisement section (i.e: it was
* a public advertisement section, or it was deallocated.)
*/
NP_FFI_GET_V1DE16_BYTE_SALT_RESULT_KIND_ERROR = 0,
/**
- * A 16-byte salt for the given DE offset was successfully
+ * A 16-byte salt for the given DE type-code was successfully
* derived.
*/
NP_FFI_GET_V1DE16_BYTE_SALT_RESULT_KIND_SUCCESS = 1,
@@ -475,24 +473,6 @@
typedef uint8_t np_ffi_GetV1SectionResultKind;
/**
- * Discriminant for `NextV1DE16ByteSaltResult`.
- */
-enum np_ffi_NextV1DE16ByteSaltResultKind {
- /**
- * We couldn't return a 16-byte DE salt, possibly
- * because the handle to the section builder
- * was invalid, or possibly because the section
- * builder was for a public section.
- */
- NP_FFI_NEXT_V1DE16_BYTE_SALT_RESULT_KIND_ERROR = 0,
- /**
- * A 16-byte DE salt was returned successfully.
- */
- NP_FFI_NEXT_V1DE16_BYTE_SALT_RESULT_KIND_SUCCESS = 1,
-};
-typedef uint8_t np_ffi_NextV1DE16ByteSaltResultKind;
-
-/**
* Structure for categorized reasons for why a NP C FFI call may
* be panicking.
*/
@@ -616,6 +596,25 @@
typedef uint8_t np_ffi_V0DataElementKind;
/**
+ * Discriminant for `V1DE16ByteSaltResult`.
+ */
+enum np_ffi_V1DE16ByteSaltResultKind {
+ /**
+ * We couldn't return a 16-byte DE salt, possibly
+ * because the handle to the section builder
+ * was invalid, or possibly because the section
+ * builder was for a public section, or possibly
+ * because the DE type code was forbidden (0xFFFFFFFF).
+ */
+ NP_FFI_V1DE16_BYTE_SALT_RESULT_KIND_ERROR = 0,
+ /**
+ * A 16-byte DE salt was returned successfully.
+ */
+ NP_FFI_V1DE16_BYTE_SALT_RESULT_KIND_SUCCESS = 1,
+};
+typedef uint8_t np_ffi_V1DE16ByteSaltResultKind;
+
+/**
* Information about the verification scheme used
* for verifying the integrity of the contents
* of a decrypted section.
@@ -625,10 +624,6 @@
* Message integrity code verification.
*/
NP_FFI_V1_VERIFICATION_MODE_MIC = 0,
- /**
- * Signature verification.
- */
- NP_FFI_V1_VERIFICATION_MODE_SIGNATURE = 1,
};
typedef uint8_t np_ffi_V1VerificationMode;
@@ -677,11 +672,21 @@
} np_ffi_CredentialSlab;
/**
+ * 32 byte key seed used in both V0 and V1 credentials.
+ */
+typedef struct {
+ uint8_t _0[32];
+} np_ffi_KeySeed;
+
+/**
* Cryptographic information about a particular V0 discovery credential
* necessary to match and decrypt encrypted V0 advertisements.
*/
typedef struct {
- uint8_t key_seed[32];
+ /**
+ * Key seed returned to clients for discovery credential mapping.
+ */
+ np_ffi_KeySeed key_seed;
uint8_t identity_token_hmac[32];
} np_ffi_V0DiscoveryCredential;
@@ -709,11 +714,12 @@
* necessary to match and decrypt encrypted V1 advertisement sections.
*/
typedef struct {
- uint8_t key_seed[32];
+ /**
+ * Key seed returned to clients for discovery credential mapping.
+ */
+ np_ffi_KeySeed key_seed;
uint8_t expected_mic_short_salt_identity_token_hmac[32];
uint8_t expected_mic_extended_salt_identity_token_hmac[32];
- uint8_t expected_signature_identity_token_hmac[32];
- uint8_t pub_key[32];
} np_ffi_V1DiscoveryCredential;
/**
@@ -981,6 +987,10 @@
* The 2-byte advertisement salt
*/
uint8_t salt[2];
+ /**
+ * The 32-byte key seed.
+ */
+ uint8_t key_seed[32];
} np_ffi_DeserializedV0IdentityDetails;
/**
@@ -1036,6 +1046,10 @@
* of a V1 data element.
*/
typedef struct {
+ /**
+ * DE type-code. Valid iff this is not
+ * the forbidden type-code 0xFFFFFFFF.
+ */
uint32_t code;
} np_ffi_V1DEType;
@@ -1051,16 +1065,15 @@
} np_ffi_ByteBuffer_127;
/**
- * FFI-transmissible representation of a generic V1 data-element.
+ * Represents the contents of an arbitrary type-code V1 DE whose payload
+ * is stored in a buffer which may contain up to 127 bytes,
+ * which is the maximum for any V1 DE.
+ *
* This representation is stable, and so you may directly
* reference this struct's fields if you wish.
*/
typedef struct {
/**
- * The offset of this generic data-element.
- */
- uint8_t offset;
- /**
* The DE type code of this generic data-element.
*/
np_ffi_V1DEType de_type;
@@ -1072,28 +1085,7 @@
} np_ffi_GenericV1DataElement;
/**
- * FFI-transmissible representation of a V1 data-element
- */
-typedef enum {
- /**
- * A "generic" V1 data-element, for which we have no
- * particular information about its schema (just
- * a DE type code and a byte payload.)
- */
- NP_FFI_V1_DATA_ELEMENT_GENERIC,
-} np_ffi_V1DataElement_Tag;
-
-typedef struct {
- np_ffi_V1DataElement_Tag tag;
- union {
- struct {
- np_ffi_GenericV1DataElement generic;
- };
- };
-} np_ffi_V1DataElement;
-
-/**
- * Represents the result of the `DeserializedV1Section#get_de` operation.
+ * Represents the result of the `DeserializedV1Section#get_generic_de` operation.
*/
typedef enum {
NP_FFI_GET_V1DE_RESULT_ERROR,
@@ -1104,7 +1096,7 @@
np_ffi_GetV1DEResult_Tag tag;
union {
struct {
- np_ffi_V1DataElement success;
+ np_ffi_GenericV1DataElement success;
};
};
} np_ffi_GetV1DEResult;
@@ -1128,6 +1120,10 @@
* The 16-byte metadata key.
*/
uint8_t identity_token[16];
+ /**
+ * The 32-byte key seed.
+ */
+ uint8_t key_seed[32];
} np_ffi_DeserializedV1IdentityDetails;
/**
@@ -1245,7 +1241,11 @@
*/
np_ffi_V1AdvertisementBuilder adv_builder;
/**
- * This section's index in the parent advertisement
+ * This section's logical index in the parent advertisement.
+ * Equal to the number of sections which have been previously
+ * added to the advertisement, but not suitable for identifying
+ * a section's position within an advertisement, since sections
+ * get re-ordered by encoding type as an advertisement is constructed.
*/
uint8_t section_index;
} np_ffi_V1SectionBuilder;
@@ -1257,7 +1257,6 @@
NP_FFI_CREATE_V1_SECTION_BUILDER_RESULT_SUCCESS,
NP_FFI_CREATE_V1_SECTION_BUILDER_RESULT_UNCLOSED_ACTIVE_SECTION,
NP_FFI_CREATE_V1_SECTION_BUILDER_RESULT_INVALID_ADVERTISEMENT_BUILDER_HANDLE,
- NP_FFI_CREATE_V1_SECTION_BUILDER_RESULT_IDENTITY_KIND_MISMATCH,
NP_FFI_CREATE_V1_SECTION_BUILDER_RESULT_NO_SPACE_LEFT,
} np_ffi_CreateV1SectionBuilderResult_Tag;
@@ -1272,12 +1271,11 @@
/**
* Cryptographic information about a particular V1 broadcast credential
- * necessary to encrypt V1 MIC-verified and signature-verified sections.
+ * necessary to encrypt V1 sections.
*/
typedef struct {
uint8_t key_seed[32];
uint8_t identity_token[16];
- uint8_t private_key[32];
} np_ffi_V1BroadcastCredential;
/**
@@ -1312,42 +1310,22 @@
/**
* The result of attempting to get the derived V1 DE
- * 16-byte salt for the next-added DE to the section
+ * 16-byte salt for the given type-code from the section
* builder behind the given handle.
*/
typedef enum {
- NP_FFI_NEXT_V1DE16_BYTE_SALT_RESULT_ERROR,
- NP_FFI_NEXT_V1DE16_BYTE_SALT_RESULT_SUCCESS,
-} np_ffi_NextV1DE16ByteSaltResult_Tag;
+ NP_FFI_V1DE16_BYTE_SALT_RESULT_ERROR,
+ NP_FFI_V1DE16_BYTE_SALT_RESULT_SUCCESS,
+} np_ffi_V1DE16ByteSaltResult_Tag;
typedef struct {
- np_ffi_NextV1DE16ByteSaltResult_Tag tag;
+ np_ffi_V1DE16ByteSaltResult_Tag tag;
union {
struct {
np_ffi_FixedSizeArray_16 success;
};
};
-} np_ffi_NextV1DE16ByteSaltResult;
-
-/**
- * Represents the contents of a V1 DE whose payload
- * is stored in a buffer which may contain up to 127 bytes,
- * which is the maximum for any V1 DE.
- *
- * This representation is stable, and so you may directly
- * reference this struct's fields if you wish.
- */
-typedef struct {
- /**
- * The DE type code of this generic data-element.
- */
- uint32_t de_type;
- /**
- * The raw data-element byte payload, up to
- * 127 bytes in length.
- */
- np_ffi_ByteBuffer_127 payload;
-} np_ffi_V1DE127ByteBuffer;
+} np_ffi_V1DE16ByteSaltResult;
/**
* Result type for attempting to construct a
@@ -1714,12 +1692,12 @@
np_ffi_DecryptMetadataResult np_ffi_DeserializedV1Section_decrypt_metadata(np_ffi_DeserializedV1Section section);
/**
- * Attempts to derive a 16-byte DE salt for a DE in this section with the given DE offset. This
- * operation may fail if the passed offset is 255 (causes overflow) or if the section
- * is leveraging a public identity, and hence, doesn't have an associated salt.
+ * Attempts to derive a 16-byte DE salt for a DE with the given type-code in this
+ * section. This operation may fail if the passed type-code is forbidden (0xFFFFFFFF)
+ * or if the section is leveraging a public identity, and hence, doesn't have an associated salt.
*/
-np_ffi_GetV1DE16ByteSaltResult np_ffi_DeserializedV1Section_derive_16_byte_salt_for_offset(np_ffi_DeserializedV1Section section,
- uint8_t offset);
+np_ffi_GetV1DE16ByteSaltResult np_ffi_DeserializedV1Section_derive_16_byte_salt_for_de_type(np_ffi_DeserializedV1Section section,
+ np_ffi_V1DEType de_type);
/**
* Gets the tag of a `GetV1DE16ByteSaltResult` tagged-union. On success the wrapped identity
@@ -1742,7 +1720,7 @@
* Casts a `GetV1DEResult` to the `Success` variant, panicking in the
* case where the passed value is of a different enum variant.
*/
-np_ffi_V1DataElement np_ffi_GetV1DEResult_into_SUCCESS(np_ffi_GetV1DEResult result);
+np_ffi_GenericV1DataElement np_ffi_GetV1DEResult_into_SUCCESS(np_ffi_GetV1DEResult result);
/**
* Attempts to add the given data element to the V0
@@ -1836,7 +1814,7 @@
* Creates a new V1 advertisement builder for the given advertisement
* kind (public/encrypted).
*/
-np_ffi_V1AdvertisementBuilder np_ffi_create_v1_advertisement_builder(np_ffi_AdvertisementBuilderKind kind);
+np_ffi_V1AdvertisementBuilder np_ffi_create_v1_advertisement_builder(void);
/**
* Gets the tag of a `SerializeV1AdvertisementResult` tagged-union.
@@ -1861,15 +1839,15 @@
np_ffi_V1SectionBuilder np_ffi_CreateV1SectionBuilderResult_into_SUCCESS(np_ffi_CreateV1SectionBuilderResult result);
/**
- * Gets the tag of a `NextV1DE16ByteSaltResult` tagged-union.
+ * Gets the tag of a `V1DE16ByteSaltResult` tagged-union.
*/
-np_ffi_NextV1DE16ByteSaltResultKind np_ffi_NextV1DE16ByteSaltResult_kind(np_ffi_NextV1DE16ByteSaltResult result);
+np_ffi_V1DE16ByteSaltResultKind np_ffi_V1DE16ByteSaltResult_kind(np_ffi_V1DE16ByteSaltResult result);
/**
- * Casts a `NextV1DE16ByteSaltResult` to the `Success` variant,
+ * Casts a `V1DE16ByteSaltResult` to the `Success` variant,
* panicking in the case where the passed value is of a different enum variant.
*/
-np_ffi_FixedSizeArray_16 np_ffi_NextV1DE16ByteSaltResult_into_SUCCESS(np_ffi_NextV1DE16ByteSaltResult result);
+np_ffi_FixedSizeArray_16 np_ffi_V1DE16ByteSaltResult_into_SUCCESS(np_ffi_V1DE16ByteSaltResult result);
/**
* Adds the section constructed behind the given handle to
@@ -1879,20 +1857,23 @@
np_ffi_AddV1SectionToAdvertisementResult np_ffi_V1SectionBuilder_add_to_advertisement(np_ffi_V1SectionBuilder section_builder);
/**
- * Attempts to get the derived 16-byte V1 DE salt for the next
- * DE to be added to the passed section builder. May fail if this
- * section builder handle is invalid, or if the section
- * is a public section.
+ * Attempts to get the derived 16-byte V1 DE salt for a
+ * data element with the given type code in the context
+ * of the passed section builder. May fail if this
+ * section builder handle is invalid, if the section
+ * is a public section, or if the DE type-code is
+ * forbidden (0xFFFFFFFF).
*/
-np_ffi_NextV1DE16ByteSaltResult np_ffi_V1SectionBuilder_next_de_salt(np_ffi_V1SectionBuilder section_builder);
+np_ffi_V1DE16ByteSaltResult np_ffi_V1SectionBuilder_de_salt(np_ffi_V1SectionBuilder section_builder,
+ np_ffi_V1DEType de_type);
/**
* Attempts to add the given DE to the section builder behind
* this handle. The passed DE may have a payload of up to 127
* bytes, the maximum for a V1 DE.
*/
-np_ffi_AddV1DEResult np_ffi_V1SectionBuilder_add_127_byte_buffer_de(np_ffi_V1SectionBuilder section_builder,
- np_ffi_V1DE127ByteBuffer de);
+np_ffi_AddV1DEResult np_ffi_V1SectionBuilder_add_generic_de(np_ffi_V1SectionBuilder section_builder,
+ np_ffi_GenericV1DataElement de);
/**
* Gets the tag of a `V0DataElement` tagged-union.
@@ -1990,12 +1971,6 @@
uint32_t np_ffi_V0Actions_as_u32(np_ffi_V0Actions actions);
/**
- * Converts a `V1DataElement` to a `GenericV1DataElement` which
- * only maintains information about the DE's type-code and payload.
- */
-np_ffi_GenericV1DataElement np_ffi_V1DataElement_to_generic(np_ffi_V1DataElement de);
-
-/**
* Extracts the numerical value of the given V1 DE type code as
* an unsigned 32-bit integer.
*/
diff --git a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h
index d197bc8..649837e 100644
--- a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h
+++ b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h
@@ -18,7 +18,7 @@
#pragma once
-/* Generated with cbindgen:0.26.0 */
+/* Generated with cbindgen:0.27.0 */
/*
WARNING: this file is autogenerated by cbindgen. Don't modify this manually.
@@ -265,11 +265,11 @@
/// Attempts to decrypt the metadata for the matched credential for this V0 payload (if any)
DecryptMetadataResult np_ffi_DeserializedV1Section_decrypt_metadata(DeserializedV1Section section);
-/// Attempts to derive a 16-byte DE salt for a DE in this section with the given DE offset. This
-/// operation may fail if the passed offset is 255 (causes overflow) or if the section
-/// is leveraging a public identity, and hence, doesn't have an associated salt.
-GetV1DE16ByteSaltResult np_ffi_DeserializedV1Section_derive_16_byte_salt_for_offset(DeserializedV1Section section,
- uint8_t offset);
+/// Attempts to derive a 16-byte DE salt for a DE with the given type-code in this
+/// section. This operation may fail if the passed type-code is forbidden (0xFFFFFFFF)
+/// or if the section is leveraging a public identity, and hence, doesn't have an associated salt.
+GetV1DE16ByteSaltResult np_ffi_DeserializedV1Section_derive_16_byte_salt_for_de_type(DeserializedV1Section section,
+ V1DEType de_type);
/// Gets the tag of a `GetV1DE16ByteSaltResult` tagged-union. On success the wrapped identity
/// details may be obtained via `GetV1DE16ByteSaltResult#into_success`.
@@ -284,7 +284,7 @@
/// Casts a `GetV1DEResult` to the `Success` variant, panicking in the
/// case where the passed value is of a different enum variant.
-V1DataElement np_ffi_GetV1DEResult_into_SUCCESS(GetV1DEResult result);
+GenericV1DataElement np_ffi_GetV1DEResult_into_SUCCESS(GetV1DEResult result);
/// Attempts to add the given data element to the V0
/// advertisement builder behind the passed handle.
@@ -356,7 +356,7 @@
/// Creates a new V1 advertisement builder for the given advertisement
/// kind (public/encrypted).
-V1AdvertisementBuilder np_ffi_create_v1_advertisement_builder(AdvertisementBuilderKind kind);
+V1AdvertisementBuilder np_ffi_create_v1_advertisement_builder();
/// Gets the tag of a `SerializeV1AdvertisementResult` tagged-union.
SerializeV1AdvertisementResultKind np_ffi_SerializeV1AdvertisementResult_kind(SerializeV1AdvertisementResult result);
@@ -372,29 +372,32 @@
/// panicking in the case where the passed value is of a different enum variant.
V1SectionBuilder np_ffi_CreateV1SectionBuilderResult_into_SUCCESS(CreateV1SectionBuilderResult result);
-/// Gets the tag of a `NextV1DE16ByteSaltResult` tagged-union.
-NextV1DE16ByteSaltResultKind np_ffi_NextV1DE16ByteSaltResult_kind(NextV1DE16ByteSaltResult result);
+/// Gets the tag of a `V1DE16ByteSaltResult` tagged-union.
+V1DE16ByteSaltResultKind np_ffi_V1DE16ByteSaltResult_kind(V1DE16ByteSaltResult result);
-/// Casts a `NextV1DE16ByteSaltResult` to the `Success` variant,
+/// Casts a `V1DE16ByteSaltResult` to the `Success` variant,
/// panicking in the case where the passed value is of a different enum variant.
-FixedSizeArray<16> np_ffi_NextV1DE16ByteSaltResult_into_SUCCESS(NextV1DE16ByteSaltResult result);
+FixedSizeArray<16> np_ffi_V1DE16ByteSaltResult_into_SUCCESS(V1DE16ByteSaltResult result);
/// Adds the section constructed behind the given handle to
/// a section builder to the containing advertisement it originated from.
/// After this call, the section builder handle will become invalid.
AddV1SectionToAdvertisementResult np_ffi_V1SectionBuilder_add_to_advertisement(V1SectionBuilder section_builder);
-/// Attempts to get the derived 16-byte V1 DE salt for the next
-/// DE to be added to the passed section builder. May fail if this
-/// section builder handle is invalid, or if the section
-/// is a public section.
-NextV1DE16ByteSaltResult np_ffi_V1SectionBuilder_next_de_salt(V1SectionBuilder section_builder);
+/// Attempts to get the derived 16-byte V1 DE salt for a
+/// data element with the given type code in the context
+/// of the passed section builder. May fail if this
+/// section builder handle is invalid, if the section
+/// is a public section, or if the DE type-code is
+/// forbidden (0xFFFFFFFF).
+V1DE16ByteSaltResult np_ffi_V1SectionBuilder_de_salt(V1SectionBuilder section_builder,
+ V1DEType de_type);
/// Attempts to add the given DE to the section builder behind
/// this handle. The passed DE may have a payload of up to 127
/// bytes, the maximum for a V1 DE.
-AddV1DEResult np_ffi_V1SectionBuilder_add_127_byte_buffer_de(V1SectionBuilder section_builder,
- V1DE127ByteBuffer de);
+AddV1DEResult np_ffi_V1SectionBuilder_add_generic_de(V1SectionBuilder section_builder,
+ GenericV1DataElement de);
/// Gets the tag of a `V0DataElement` tagged-union.
V0DataElementKind np_ffi_V0DataElement_kind(V0DataElement de);
@@ -459,17 +462,13 @@
/// integer, where the bit-positions correspond to individual actions.
uint32_t np_ffi_V0Actions_as_u32(V0Actions actions);
-/// Converts a `V1DataElement` to a `GenericV1DataElement` which
-/// only maintains information about the DE's type-code and payload.
-GenericV1DataElement np_ffi_V1DataElement_to_generic(V1DataElement de);
-
/// Extracts the numerical value of the given V1 DE type code as
/// an unsigned 32-bit integer.
uint32_t np_ffi_V1DEType_to_uint32_t(V1DEType de_type);
-} // extern "C"
+} // extern "C"
-} // namespace internal
-} // namespace np_ffi
+} // namespace internal
+} // namespace np_ffi
// clang-format on
diff --git a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h
index 919249d..5144478 100644
--- a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h
+++ b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h
@@ -18,7 +18,7 @@
#pragma once
-/* Generated with cbindgen:0.26.0 */
+/* Generated with cbindgen:0.27.0 */
/*
WARNING: this file is autogenerated by cbindgen. Don't modify this manually.
@@ -96,6 +96,9 @@
/// The data element itself had invalid characteristics,
/// most likely a length above 127.
InvalidDataElement = 3,
+ /// A data element with the same type-code was already
+ /// present in the section.
+ DuplicateDataElementTypeCode = 4,
};
/// Result code for [`V1SectionBuilder#add_to_advertisement`].
@@ -112,9 +115,8 @@
Success = 1,
};
-/// Enum common to V0 and V1 serialization expressing
-/// what kind of advertisement builder (public/encrypted)
-/// is in use.
+/// Enum for V0 serialization expressing what kind of
+/// advertisement builder (public/encrypted) is in use.
enum class AdvertisementBuilderKind : uint8_t {
/// The builder is for a public advertisement.
Public = 0,
@@ -151,15 +153,11 @@
UnclosedActiveSection = 1,
/// The advertisement builder handle was invalid.
InvalidAdvertisementBuilderHandle = 2,
- /// We're attempting to build a section with an identity
- /// kind (public/encrypted) which doesn't match the kind
- /// for the entire advertisement.
- IdentityKindMismatch = 3,
/// There isn't enough space for a new section, either
/// because the maximum section count has been exceeded
/// or because the advertisement is almost full, and
/// the minimum size of a section wouldn't fit.
- NoSpaceLeft = 4,
+ NoSpaceLeft = 3,
};
/// A result-type enum which tells the caller whether/not a deallocation
@@ -262,12 +260,12 @@
/// Discriminant for `GetV1DE16ByteSaltResult`.
enum class GetV1DE16ByteSaltResultKind : uint8_t {
/// The attempt to get the derived salt failed, possibly
- /// because the passed DE offset was invalid (==255),
+ /// because the passed DE type code was forbidden (0xFFFFFFFF),
/// or because there was no salt included for the
/// referenced advertisement section (i.e: it was
/// a public advertisement section, or it was deallocated.)
Error = 0,
- /// A 16-byte salt for the given DE offset was successfully
+ /// A 16-byte salt for the given DE type-code was successfully
/// derived.
Success = 1,
};
@@ -310,17 +308,6 @@
Success = 1,
};
-/// Discriminant for `NextV1DE16ByteSaltResult`.
-enum class NextV1DE16ByteSaltResultKind : uint8_t {
- /// We couldn't return a 16-byte DE salt, possibly
- /// because the handle to the section builder
- /// was invalid, or possibly because the section
- /// builder was for a public section.
- Error = 0,
- /// A 16-byte DE salt was returned successfully.
- Success = 1,
-};
-
/// Structure for categorized reasons for why a NP C FFI call may
/// be panicking.
enum class PanicReason : uint8_t {
@@ -399,14 +386,24 @@
Actions = 2,
};
+/// Discriminant for `V1DE16ByteSaltResult`.
+enum class V1DE16ByteSaltResultKind : uint8_t {
+ /// We couldn't return a 16-byte DE salt, possibly
+ /// because the handle to the section builder
+ /// was invalid, or possibly because the section
+ /// builder was for a public section, or possibly
+ /// because the DE type code was forbidden (0xFFFFFFFF).
+ Error = 0,
+ /// A 16-byte DE salt was returned successfully.
+ Success = 1,
+};
+
/// Information about the verification scheme used
/// for verifying the integrity of the contents
/// of a decrypted section.
enum class V1VerificationMode : uint8_t {
/// Message integrity code verification.
Mic = 0,
- /// Signature verification.
- Signature = 1,
};
/// Holds the count of handles currently allocated for each handle type
@@ -448,10 +445,16 @@
uint64_t handle_id;
};
+/// 32 byte key seed used in both V0 and V1 credentials.
+struct KeySeed {
+ uint8_t _0[32];
+};
+
/// Cryptographic information about a particular V0 discovery credential
/// necessary to match and decrypt encrypted V0 advertisements.
struct V0DiscoveryCredential {
- uint8_t key_seed[32];
+ /// Key seed returned to clients for discovery credential mapping.
+ KeySeed key_seed;
uint8_t identity_token_hmac[32];
};
@@ -473,11 +476,10 @@
/// Cryptographic information about a particular V1 discovery credential
/// necessary to match and decrypt encrypted V1 advertisement sections.
struct V1DiscoveryCredential {
- uint8_t key_seed[32];
+ /// Key seed returned to clients for discovery credential mapping.
+ KeySeed key_seed;
uint8_t expected_mic_short_salt_identity_token_hmac[32];
uint8_t expected_mic_extended_salt_identity_token_hmac[32];
- uint8_t expected_signature_identity_token_hmac[32];
- uint8_t pub_key[32];
};
/// Representation of a V1 credential that contains additional data to provide back to caller once it
@@ -709,6 +711,8 @@
uint8_t identity_token[14];
/// The 2-byte advertisement salt
uint8_t salt[2];
+ /// The 32-byte key seed.
+ uint8_t key_seed[32];
};
/// The result of attempting to get the identity details
@@ -760,15 +764,18 @@
/// Representation of the data-element type tag
/// of a V1 data element.
struct V1DEType {
+ /// DE type-code. Valid iff this is not
+ /// the forbidden type-code 0xFFFFFFFF.
uint32_t code;
};
-/// FFI-transmissible representation of a generic V1 data-element.
+/// Represents the contents of an arbitrary type-code V1 DE whose payload
+/// is stored in a buffer which may contain up to 127 bytes,
+/// which is the maximum for any V1 DE.
+///
/// This representation is stable, and so you may directly
/// reference this struct's fields if you wish.
struct GenericV1DataElement {
- /// The offset of this generic data-element.
- uint8_t offset;
/// The DE type code of this generic data-element.
V1DEType de_type;
/// The raw data-element byte payload, up to
@@ -776,26 +783,7 @@
ByteBuffer<127> payload;
};
-/// FFI-transmissible representation of a V1 data-element
-struct V1DataElement {
- enum class Tag {
- /// A "generic" V1 data-element, for which we have no
- /// particular information about its schema (just
- /// a DE type code and a byte payload.)
- Generic,
- };
-
- struct Generic_Body {
- GenericV1DataElement _0;
- };
-
- Tag tag;
- union {
- Generic_Body generic;
- };
-};
-
-/// Represents the result of the `DeserializedV1Section#get_de` operation.
+/// Represents the result of the `DeserializedV1Section#get_generic_de` operation.
struct GetV1DEResult {
enum class Tag {
Error,
@@ -803,7 +791,7 @@
};
struct Success_Body {
- V1DataElement _0;
+ GenericV1DataElement _0;
};
Tag tag;
@@ -823,6 +811,8 @@
int64_t cred_id;
/// The 16-byte metadata key.
uint8_t identity_token[16];
+ /// The 32-byte key seed.
+ uint8_t key_seed[32];
};
/// The result of attempting to get the identity details
@@ -910,7 +900,11 @@
struct V1SectionBuilder {
/// The parent advertisement builder for this section
V1AdvertisementBuilder adv_builder;
- /// This section's index in the parent advertisement
+ /// This section's logical index in the parent advertisement.
+ /// Equal to the number of sections which have been previously
+ /// added to the advertisement, but not suitable for identifying
+ /// a section's position within an advertisement, since sections
+ /// get re-ordered by encoding type as an advertisement is constructed.
uint8_t section_index;
};
@@ -920,7 +914,6 @@
Success,
UnclosedActiveSection,
InvalidAdvertisementBuilderHandle,
- IdentityKindMismatch,
NoSpaceLeft,
};
@@ -935,11 +928,10 @@
};
/// Cryptographic information about a particular V1 broadcast credential
-/// necessary to encrypt V1 MIC-verified and signature-verified sections.
+/// necessary to encrypt V1 sections.
struct V1BroadcastCredential {
uint8_t key_seed[32];
uint8_t identity_token[16];
- uint8_t private_key[32];
};
/// The result of attempting to serialize the contents
@@ -962,9 +954,9 @@
};
/// The result of attempting to get the derived V1 DE
-/// 16-byte salt for the next-added DE to the section
+/// 16-byte salt for the given type-code from the section
/// builder behind the given handle.
-struct NextV1DE16ByteSaltResult {
+struct V1DE16ByteSaltResult {
enum class Tag {
Error,
Success,
@@ -980,20 +972,6 @@
};
};
-/// Represents the contents of a V1 DE whose payload
-/// is stored in a buffer which may contain up to 127 bytes,
-/// which is the maximum for any V1 DE.
-///
-/// This representation is stable, and so you may directly
-/// reference this struct's fields if you wish.
-struct V1DE127ByteBuffer {
- /// The DE type code of this generic data-element.
- uint32_t de_type;
- /// The raw data-element byte payload, up to
- /// 127 bytes in length.
- ByteBuffer<127> payload;
-};
-
/// Result type for attempting to construct a
/// Tx Power from a signed byte.
struct BuildTxPowerResult {
@@ -1035,7 +1013,7 @@
};
};
-} // namespace internal
-} // namespace np_ffi
+} // namespace internal
+} // namespace np_ffi
// clang-format on
diff --git a/nearby/presence/np_c_ffi/src/credentials.rs b/nearby/presence/np_c_ffi/src/credentials.rs
index 0c8a316..4ba0d85 100644
--- a/nearby/presence/np_c_ffi/src/credentials.rs
+++ b/nearby/presence/np_c_ffi/src/credentials.rs
@@ -177,7 +177,11 @@
)
};
- let matched_credential = MatchedCredential::new(v0_cred.matched_cred.cred_id, metadata_slice);
+ let matched_credential = MatchedCredential::new(
+ v0_cred.matched_cred.cred_id,
+ metadata_slice,
+ v0_cred.discovery_cred.key_seed,
+ );
credential_slab.add_v0(v0_cred.discovery_cred, matched_credential)
}
@@ -200,7 +204,11 @@
)
};
- let matched_credential = MatchedCredential::new(v1_cred.matched_cred.cred_id, metadata_slice);
+ let matched_credential = MatchedCredential::new(
+ v1_cred.matched_cred.cred_id,
+ metadata_slice,
+ v1_cred.discovery_cred.key_seed,
+ );
credential_slab.add_v1(v1_cred.discovery_cred, matched_credential)
}
diff --git a/nearby/presence/np_c_ffi/src/deserialize/v1.rs b/nearby/presence/np_c_ffi/src/deserialize/v1.rs
index 68c0cc2..af2d494 100644
--- a/nearby/presence/np_c_ffi/src/deserialize/v1.rs
+++ b/nearby/presence/np_c_ffi/src/deserialize/v1.rs
@@ -17,6 +17,7 @@
use np_ffi_core::deserialize::v1::*;
use np_ffi_core::deserialize::DecryptMetadataResult;
use np_ffi_core::utils::FfiEnum;
+use np_ffi_core::v1::{GenericV1DataElement, V1DEType};
/// Gets the number of legible sections on a deserialized V1 advertisement.
/// Suitable as an index bound for the second argument of
@@ -85,7 +86,7 @@
section: DeserializedV1Section,
de_index: u8,
) -> GetV1DEResult {
- section.get_de(de_index)
+ section.get_generic_de(de_index)
}
/// Gets the identity details used to decrypt this V1 section, or returns an error if this payload
@@ -123,15 +124,15 @@
section.decrypt_metadata()
}
-/// Attempts to derive a 16-byte DE salt for a DE in this section with the given DE offset. This
-/// operation may fail if the passed offset is 255 (causes overflow) or if the section
-/// is leveraging a public identity, and hence, doesn't have an associated salt.
+/// Attempts to derive a 16-byte DE salt for a DE with the given type-code in this
+/// section. This operation may fail if the passed type-code is forbidden (0xFFFFFFFF)
+/// or if the section is leveraging a public identity, and hence, doesn't have an associated salt.
#[no_mangle]
-pub extern "C" fn np_ffi_DeserializedV1Section_derive_16_byte_salt_for_offset(
+pub extern "C" fn np_ffi_DeserializedV1Section_derive_16_byte_salt_for_de_type(
section: DeserializedV1Section,
- offset: u8,
+ de_type: V1DEType,
) -> GetV1DE16ByteSaltResult {
- section.derive_16_byte_salt_for_offset(offset)
+ section.derive_16_byte_salt_for_de_type(de_type)
}
/// Gets the tag of a `GetV1DE16ByteSaltResult` tagged-union. On success the wrapped identity
@@ -161,6 +162,6 @@
/// Casts a `GetV1DEResult` to the `Success` variant, panicking in the
/// case where the passed value is of a different enum variant.
#[no_mangle]
-pub extern "C" fn np_ffi_GetV1DEResult_into_SUCCESS(result: GetV1DEResult) -> V1DataElement {
+pub extern "C" fn np_ffi_GetV1DEResult_into_SUCCESS(result: GetV1DEResult) -> GenericV1DataElement {
unwrap(result.into_success(), PanicReason::EnumCastFailed)
}
diff --git a/nearby/presence/np_c_ffi/src/serialize/v1.rs b/nearby/presence/np_c_ffi/src/serialize/v1.rs
index 484425b..e193813 100644
--- a/nearby/presence/np_c_ffi/src/serialize/v1.rs
+++ b/nearby/presence/np_c_ffi/src/serialize/v1.rs
@@ -18,9 +18,8 @@
use np_ffi_core::common::{ByteBuffer, FixedSizeArray};
use np_ffi_core::credentials::V1BroadcastCredential;
use np_ffi_core::serialize::v1::*;
-use np_ffi_core::serialize::AdvertisementBuilderKind;
use np_ffi_core::utils::FfiEnum;
-use np_ffi_core::v1::V1VerificationMode;
+use np_ffi_core::v1::{GenericV1DataElement, V1DEType, V1VerificationMode};
/// Attempts to create a builder for a new public section within
/// the advertisement builder behind this handle,
@@ -75,11 +74,9 @@
/// Creates a new V1 advertisement builder for the given advertisement
/// kind (public/encrypted).
#[no_mangle]
-pub extern "C" fn np_ffi_create_v1_advertisement_builder(
- kind: AdvertisementBuilderKind,
-) -> V1AdvertisementBuilder {
+pub extern "C" fn np_ffi_create_v1_advertisement_builder() -> V1AdvertisementBuilder {
unwrap(
- create_v1_advertisement_builder(kind).into_success(),
+ create_v1_advertisement_builder().into_success(),
PanicReason::ExceededMaxHandleAllocations,
)
}
@@ -118,19 +115,19 @@
unwrap(result.into_success(), PanicReason::EnumCastFailed)
}
-/// Gets the tag of a `NextV1DE16ByteSaltResult` tagged-union.
+/// Gets the tag of a `V1DE16ByteSaltResult` tagged-union.
#[no_mangle]
-pub extern "C" fn np_ffi_NextV1DE16ByteSaltResult_kind(
- result: NextV1DE16ByteSaltResult,
-) -> NextV1DE16ByteSaltResultKind {
+pub extern "C" fn np_ffi_V1DE16ByteSaltResult_kind(
+ result: V1DE16ByteSaltResult,
+) -> V1DE16ByteSaltResultKind {
result.kind()
}
-/// Casts a `NextV1DE16ByteSaltResult` to the `Success` variant,
+/// Casts a `V1DE16ByteSaltResult` to the `Success` variant,
/// panicking in the case where the passed value is of a different enum variant.
#[no_mangle]
-pub extern "C" fn np_ffi_NextV1DE16ByteSaltResult_into_SUCCESS(
- result: NextV1DE16ByteSaltResult,
+pub extern "C" fn np_ffi_V1DE16ByteSaltResult_into_SUCCESS(
+ result: V1DE16ByteSaltResult,
) -> FixedSizeArray<16> {
unwrap(result.into_success(), PanicReason::EnumCastFailed)
}
@@ -145,24 +142,27 @@
section_builder.add_to_advertisement()
}
-/// Attempts to get the derived 16-byte V1 DE salt for the next
-/// DE to be added to the passed section builder. May fail if this
-/// section builder handle is invalid, or if the section
-/// is a public section.
+/// Attempts to get the derived 16-byte V1 DE salt for a
+/// data element with the given type code in the context
+/// of the passed section builder. May fail if this
+/// section builder handle is invalid, if the section
+/// is a public section, or if the DE type-code is
+/// forbidden (0xFFFFFFFF).
#[no_mangle]
-pub extern "C" fn np_ffi_V1SectionBuilder_next_de_salt(
+pub extern "C" fn np_ffi_V1SectionBuilder_de_salt(
section_builder: V1SectionBuilder,
-) -> NextV1DE16ByteSaltResult {
- section_builder.next_de_salt()
+ de_type: V1DEType,
+) -> V1DE16ByteSaltResult {
+ section_builder.de_salt(de_type)
}
/// Attempts to add the given DE to the section builder behind
/// this handle. The passed DE may have a payload of up to 127
/// bytes, the maximum for a V1 DE.
#[no_mangle]
-pub extern "C" fn np_ffi_V1SectionBuilder_add_127_byte_buffer_de(
+pub extern "C" fn np_ffi_V1SectionBuilder_add_generic_de(
section_builder: V1SectionBuilder,
- de: V1DE127ByteBuffer,
+ de: GenericV1DataElement,
) -> AddV1DEResult {
- section_builder.add_127_byte_buffer_de(de)
+ section_builder.add_generic_de(de)
}
diff --git a/nearby/presence/np_c_ffi/src/v0.rs b/nearby/presence/np_c_ffi/src/v0.rs
index 7bcb980..84aab95 100644
--- a/nearby/presence/np_c_ffi/src/v0.rs
+++ b/nearby/presence/np_c_ffi/src/v0.rs
@@ -15,7 +15,8 @@
//! NP Rust C FFI functionality common to V0 ser/deser flows.
use crate::{panic, panic_if_invalid, unwrap, PanicReason};
-use np_ffi_core::serialize::AdvertisementBuilderKind;
+use np_ffi_core::common::*;
+use np_ffi_core::serialize::v0::AdvertisementBuilderKind;
use np_ffi_core::utils::FfiEnum;
use np_ffi_core::v0::*;
diff --git a/nearby/presence/np_c_ffi/src/v1.rs b/nearby/presence/np_c_ffi/src/v1.rs
index 87a168e..7f4ddc0 100644
--- a/nearby/presence/np_c_ffi/src/v1.rs
+++ b/nearby/presence/np_c_ffi/src/v1.rs
@@ -14,14 +14,7 @@
//! NP Rust C FFI functionality common to V1 ser/deser flows.
-use np_ffi_core::deserialize::v1::*;
-
-/// Converts a `V1DataElement` to a `GenericV1DataElement` which
-/// only maintains information about the DE's type-code and payload.
-#[no_mangle]
-pub extern "C" fn np_ffi_V1DataElement_to_generic(de: V1DataElement) -> GenericV1DataElement {
- de.to_generic()
-}
+use np_ffi_core::v1::V1DEType;
/// Extracts the numerical value of the given V1 DE type code as
/// an unsigned 32-bit integer.
diff --git a/nearby/presence/np_cpp_ffi/benchmarks/np_cpp_benches.cc b/nearby/presence/np_cpp_ffi/benchmarks/np_cpp_benches.cc
index 392d679..1a7cf8f 100644
--- a/nearby/presence/np_cpp_ffi/benchmarks/np_cpp_benches.cc
+++ b/nearby/presence/np_cpp_ffi/benchmarks/np_cpp_benches.cc
@@ -23,7 +23,7 @@
nearby_protocol::CredentialBook CreateEmptyCredBook() {
nearby_protocol::CredentialSlab cred_slab;
nearby_protocol::CredentialBook cred_book(cred_slab);
- return std::move(cred_book);
+ return cred_book;
}
void V0Plaintext(benchmark::State &state) {
@@ -125,8 +125,10 @@
nearby_protocol::MatchedCredentialData match_data(123,
V1AdvEncryptedMetadata);
nearby_protocol::V1MatchableCredential v1_cred(
- V1AdvKeySeed, V1AdvExpectedMicExtendedSaltIdentityTokenHmac,
- V1AdvExpectedSignatureIdentityTokenHmac, V1AdvPublicKey, match_data);
+ V1AdvKeySeed,
+ V1AdvExpectedMicShortSaltIdentityTokenHmac,
+ V1AdvExpectedMicExtendedSaltIdentityTokenHmac,
+ match_data);
auto add_result = slab.AddV1Credential(v1_cred);
assert(add_result.ok());
nearby_protocol::CredentialBook cred_book(slab);
@@ -158,9 +160,8 @@
struct V1CredentialData {
uint32_t cred_id;
std::array<uint8_t, 32> key_seed;
- std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac;
- std::array<uint8_t, 32> expected_signed_metadata_key_hmac;
- std::array<uint8_t, 32> pub_key;
+ std::array<uint8_t, 32> expected_mic_short_salt_identity_token_hmac;
+ std::array<uint8_t, 32> expected_mic_extended_salt_identity_token_hmac;
std::array<uint8_t, 500> encrypted_metadata_bytes;
};
@@ -168,9 +169,8 @@
return {
.cred_id = static_cast<uint32_t>(rand()),
.key_seed = create_random_array<32>(),
- .expected_unsigned_metadata_key_hmac = create_random_array<32>(),
- .expected_signed_metadata_key_hmac = create_random_array<32>(),
- .pub_key = create_random_array<32>(),
+ .expected_mic_short_salt_identity_token_hmac = create_random_array<32>(),
+ .expected_mic_extended_salt_identity_token_hmac = create_random_array<32>(),
.encrypted_metadata_bytes = create_random_array<500>(),
};
}
@@ -198,8 +198,7 @@
nearby_protocol::MatchedCredentialData m(cred.cred_id,
cred.encrypted_metadata_bytes);
nearby_protocol::V1MatchableCredential v1_cred(
- cred.key_seed, cred.expected_unsigned_metadata_key_hmac,
- cred.expected_signed_metadata_key_hmac, cred.pub_key, m);
+ cred.key_seed, cred.expected_mic_short_salt_identity_token_hmac, cred.expected_mic_extended_salt_identity_token_hmac, m);
auto result = slab.AddV1Credential(v1_cred);
assert(result.ok());
}
diff --git a/nearby/presence/np_cpp_ffi/fuzz/deserialization_fuzzer.cc b/nearby/presence/np_cpp_ffi/fuzz/deserialization_fuzzer.cc
index 7108e0a..24614e9 100644
--- a/nearby/presence/np_cpp_ffi/fuzz/deserialization_fuzzer.cc
+++ b/nearby/presence/np_cpp_ffi/fuzz/deserialization_fuzzer.cc
@@ -14,7 +14,7 @@
#include <array>
#include <cstdint>
-#include <random>
+#include <cstdlib>
#include <span>
#include <utility>
#include <vector>
@@ -23,7 +23,6 @@
#include "fuzztest/fuzztest.h"
#include "gtest/gtest.h"
#include "nearby_protocol.h"
-#include "np_cpp_ffi_types.h"
#include "shared_test_util.h"
// redefine test data as std::vector types since fuzztest does not support
@@ -76,9 +75,8 @@
uint32_t credential_id;
std::array<uint8_t, 32> key_seed;
std::array<uint8_t, 32> legacy_metadata_key_hmac;
- std::array<uint8_t, 32> expected_unsigned_identity_token_hmac;
- std::array<uint8_t, 32> expected_signed_identity_token_hmac;
- std::array<uint8_t, 32> pub_key;
+ std::array<uint8_t, 32> expected_mic_short_salt_identity_token_hmac;
+ std::array<uint8_t, 32> expected_mic_extended_salt_identity_token_hmac;
std::vector<uint8_t> encrypted_metadata_bytes;
};
@@ -90,11 +88,10 @@
static struct IdentityData V1TestCaseIdentityData {
.credential_id = static_cast<uint32_t>(rand()), .key_seed = V1AdvKeySeed,
- .expected_unsigned_identity_token_hmac =
+ .expected_mic_short_salt_identity_token_hmac =
+ V1AdvExpectedMicShortSaltIdentityTokenHmac,
+ .expected_mic_extended_salt_identity_token_hmac =
V1AdvExpectedMicExtendedSaltIdentityTokenHmac,
- .expected_signed_identity_token_hmac =
- V1AdvExpectedSignatureIdentityTokenHmac,
- .pub_key = V1AdvPublicKey, .encrypted_metadata_bytes = V1AdvEncryptedMetadata,
};
// Now lets try feeding the fuzzer some credential data that can successfully
@@ -108,6 +105,11 @@
nearby_protocol::CredentialSlab slab;
// populate book with fuzzer generated credential data
for (auto data : identities) {
+ // Make sure the vector is not empty, as this is a prerequisite of the Rust
+ // code we call into.
+ if (data.encrypted_metadata_bytes.empty()) {
+ continue;
+ }
nearby_protocol::MatchedCredentialData match_data(
123, data.encrypted_metadata_bytes);
nearby_protocol::V0MatchableCredential v0_cred(
@@ -116,8 +118,8 @@
slab.AddV0Credential(v0_cred);
nearby_protocol::V1MatchableCredential v1_cred(
- data.key_seed, data.expected_unsigned_identity_token_hmac,
- data.expected_signed_identity_token_hmac, data.pub_key, match_data);
+ data.key_seed, data.expected_mic_short_salt_identity_token_hmac,
+ data.expected_mic_extended_salt_identity_token_hmac, match_data);
[[maybe_unused]] auto result = slab.AddV1Credential(v1_cred);
}
@@ -146,35 +148,39 @@
TEST(NpCppDeserializationFuzzers, InvalidPublicKeyInCredential) {
std::vector<IdentityData> identities;
- identities.push_back(
- {1804289383,
- {17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
- 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17},
- {136, 51, 222, 213, 77, 0, 146, 232, 128, 112, 213,
- 31, 24, 236, 34, 69, 117, 124, 36, 223, 227, 140,
- 178, 222, 119, 182, 120, 133, 252, 165, 103, 77},
- {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},
- fuzztest::ToByteArray("")});
- identities.push_back({846930886,
- {49, 67, 99, 30, 202, 232, 151, 75, 150, 80, 204,
- 28, 72, 37, 14, 129, 88, 6, 129, 81, 249, 235,
- 37, 35, 3, 212, 151, 109, 149, 25, 145, 57},
- {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},
- {28, 188, 235, 220, 23, 181, 145, 229, 7, 157, 112,
- 193, 232, 75, 204, 219, 75, 15, 118, 131, 89, 98,
- 10, 45, 85, 11, 59, 54, 164, 146, 139, 19},
- {109, 13, 182, 9, 16, 177, 83, 196, 126, 16, 22,
- 20, 156, 159, 242, 20, 15, 236, 83, 118, 227, 7,
- 217, 211, 158, 174, 231, 69, 44, 3, 236, 109},
- fuzztest::ToByteArray("")});
+ identities.push_back(IdentityData{
+ .credential_id = 1804289383,
+ .key_seed = {17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17},
+ .legacy_metadata_key_hmac = {136, 51, 222, 213, 77, 0, 146, 232,
+ 128, 112, 213, 31, 24, 236, 34, 69,
+ 117, 124, 36, 223, 227, 140, 178, 222,
+ 119, 182, 120, 133, 252, 165, 103, 77},
+ .expected_mic_short_salt_identity_token_hmac = {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},
+ .expected_mic_extended_salt_identity_token_hmac =
+ {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},
+ .encrypted_metadata_bytes = fuzztest::ToByteArray("")});
+ identities.emplace_back(IdentityData{
+ .credential_id = 846930886,
+ .key_seed = {49, 67, 99, 30, 202, 232, 151, 75, 150, 80, 204,
+ 28, 72, 37, 14, 129, 88, 6, 129, 81, 249, 235,
+ 37, 35, 3, 212, 151, 109, 149, 25, 145, 57},
+ .legacy_metadata_key_hmac = {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},
+ .expected_mic_short_salt_identity_token_hmac = {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},
+ .expected_mic_extended_salt_identity_token_hmac =
+ {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},
+ .encrypted_metadata_bytes = fuzztest::ToByteArray("")});
DeserializeWithCredentials(
identities,
@@ -228,7 +234,7 @@
void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement);
void HandleV1Section(nearby_protocol::DeserializedV1Section);
-void HandleV1DataElement(nearby_protocol::V1DataElement);
+void HandleV1DataElement(nearby_protocol::GenericV1DataElement);
void HandleAdvertisementResult(
nearby_protocol::DeserializeAdvertisementResult result) {
diff --git a/nearby/presence/np_cpp_ffi/include/nearby_protocol.h b/nearby/presence/np_cpp_ffi/include/nearby_protocol.h
index 5e064f9..08b7803 100644
--- a/nearby/presence/np_cpp_ffi/include/nearby_protocol.h
+++ b/nearby/presence/np_cpp_ffi/include/nearby_protocol.h
@@ -109,7 +109,7 @@
// V1 Classes
class DeserializedV1Advertisement;
class DeserializedV1Section;
-class V1DataElement;
+class GenericV1DataElement;
// Global static singleton class used to customize the deserialization library.
// If no values are set, then the default values will be used. In most cases the
@@ -257,12 +257,13 @@
V1MatchableCredential() = delete;
// Creates a new V1MatchableCredential from key material, its calculated hmac
- // value and some match data.
+ // values and some match data.
[[nodiscard]] V1MatchableCredential(
std::array<uint8_t, KEY_SEED_SIZE> key_seed,
- std::array<uint8_t, HMAC_TAG_SIZE> expected_unsigned_metadata_key_hmac,
- std::array<uint8_t, HMAC_TAG_SIZE> expected_signed_metadata_key_hmac,
- std::array<uint8_t, PUBLIC_SIGNING_KEY_SIZE> pub_key,
+ std::array<uint8_t, HMAC_TAG_SIZE>
+ expected_mic_short_salt_identity_token_hmac,
+ std::array<uint8_t, HMAC_TAG_SIZE>
+ expected_mic_extended_salt_identity_token_hmac,
MatchedCredentialData matched_credential_data);
private:
@@ -361,7 +362,7 @@
private:
friend class V0AdvertisementBuilder;
- friend class V1DataElement;
+ friend class GenericV1DataElement;
friend class Deserializer;
np_ffi::internal::ByteBuffer<N> internal_;
};
@@ -679,7 +680,7 @@
[[nodiscard]] DeserializedV1IdentityKind GetIdentityKind() const;
// Tries to get the data element in the section at the given index
- [[nodiscard]] absl::StatusOr<V1DataElement> TryGetDataElement(
+ [[nodiscard]] absl::StatusOr<GenericV1DataElement> TryGetDataElement(
uint8_t index) const;
// Decrypts the metadata of the credential which matched with this section
@@ -692,12 +693,13 @@
GetIdentityDetails() const;
// Attempts to derive a 16-byte DE salt for a DE in this section with the
- // given DE offset. This operation may fail if the passed offset is 255
- // (causes overflow) or if the section is leveraging a public identity, and
- // hence, doesn't have an associated salt. The offset should come from a
- // particular deserialized v1 de via `V1DataElement::GetOffset()`
+ // given DE type-code. This operation may fail if the passed DE type-code
+ // is forbidden (0xFFFFFFFF) or if the section is leveraging a public
+ // identity, and hence, doesn't have an associated salt. The type-code should
+ // come from a type-code on a particular deserialized v1 de belonging to the
+ // containing section.
[[nodiscard]] absl::StatusOr<std::array<uint8_t, DERIVED_SALT_SIZE>>
- DeriveSaltForOffset(uint8_t offset) const;
+ DeriveSaltForDeType(uint32_t de_type) const;
private:
friend class DeserializedV1Advertisement;
@@ -713,20 +715,19 @@
};
// A V1 Data Element
-class V1DataElement {
+class GenericV1DataElement {
public:
// Yields the unsigned 32-bit integer V1 DE type code
[[nodiscard]] uint32_t GetDataElementTypeCode() const;
// Yields the payload bytes of the data element
[[nodiscard]] ByteBuffer<MAX_V1_DE_PAYLOAD_SIZE> GetPayload() const;
- /// Gets the offset for this V1 data element.
- [[nodiscard]] uint8_t GetOffset() const;
private:
friend class DeserializedV1Section;
- explicit V1DataElement(np_ffi::internal::V1DataElement v1_data_element)
+ explicit GenericV1DataElement(
+ np_ffi::internal::GenericV1DataElement v1_data_element)
: v1_data_element_(v1_data_element) {}
- np_ffi::internal::V1DataElement v1_data_element_;
+ np_ffi::internal::GenericV1DataElement v1_data_element_;
};
// A builder for V0 advertisements
diff --git a/nearby/presence/np_cpp_ffi/nearby_protocol.cc b/nearby/presence/np_cpp_ffi/nearby_protocol.cc
index 510844a..471cbea 100644
--- a/nearby/presence/np_cpp_ffi/nearby_protocol.cc
+++ b/nearby/presence/np_cpp_ffi/nearby_protocol.cc
@@ -621,7 +621,7 @@
section_);
}
-absl::StatusOr<V1DataElement> DeserializedV1Section::TryGetDataElement(
+absl::StatusOr<GenericV1DataElement> DeserializedV1Section::TryGetDataElement(
const uint8_t index) const {
auto result =
np_ffi::internal::np_ffi_DeserializedV1Section_get_de(section_, index);
@@ -632,7 +632,7 @@
"Invalid data element index for this section");
}
case np_ffi::internal::GetV1DEResultKind::Success: {
- return V1DataElement(
+ return GenericV1DataElement(
np_ffi::internal::np_ffi_GetV1DEResult_into_SUCCESS(result));
}
}
@@ -667,10 +667,13 @@
}
absl::StatusOr<std::array<uint8_t, DERIVED_SALT_SIZE>>
-DeserializedV1Section::DeriveSaltForOffset(const uint8_t offset) const {
+DeserializedV1Section::DeriveSaltForDeType(const uint32_t de_type) const {
+ np_ffi::internal::V1DEType detype;
+ detype.code = de_type;
+
auto result = np_ffi::internal::
- np_ffi_DeserializedV1Section_derive_16_byte_salt_for_offset(
- this->section_, offset);
+ np_ffi_DeserializedV1Section_derive_16_byte_salt_for_de_type(
+ this->section_, detype);
auto kind = np_ffi::internal::np_ffi_GetV1DE16ByteSaltResult_kind(result);
switch (kind) {
case np_ffi::internal::GetV1DE16ByteSaltResultKind::Error: {
@@ -684,22 +687,13 @@
}
}
-uint32_t V1DataElement::GetDataElementTypeCode() const {
+uint32_t GenericV1DataElement::GetDataElementTypeCode() const {
return np_ffi::internal::np_ffi_V1DEType_to_uint32_t(
- np_ffi::internal::np_ffi_V1DataElement_to_generic(this->v1_data_element_)
- .de_type);
+ this->v1_data_element_.de_type);
}
-ByteBuffer<MAX_V1_DE_PAYLOAD_SIZE> V1DataElement::GetPayload() const {
- return ByteBuffer(
- np_ffi::internal::np_ffi_V1DataElement_to_generic(this->v1_data_element_)
- .payload);
-}
-
-uint8_t V1DataElement::GetOffset() const {
- return np_ffi::internal::np_ffi_V1DataElement_to_generic(
- this->v1_data_element_)
- .offset;
+ByteBuffer<MAX_V1_DE_PAYLOAD_SIZE> GenericV1DataElement::GetPayload() const {
+ return ByteBuffer(this->v1_data_element_.payload);
}
MatchedCredentialData::MatchedCredentialData(
@@ -717,25 +711,24 @@
const std::array<uint8_t, 32> legacy_metadata_key_hmac,
const MatchedCredentialData matched_credential_data) {
np_ffi::internal::V0DiscoveryCredential discovery_cred{};
- CopyToRawArray(discovery_cred.key_seed, key_seed);
+ CopyToRawArray(discovery_cred.key_seed._0, key_seed);
CopyToRawArray(discovery_cred.identity_token_hmac, legacy_metadata_key_hmac);
this->internal_ = {discovery_cred, matched_credential_data.data_};
}
V1MatchableCredential::V1MatchableCredential(
- const std::array<uint8_t, 32> key_seed,
- const std::array<uint8_t, 32>
+ std::array<uint8_t, KEY_SEED_SIZE> key_seed,
+ std::array<uint8_t, HMAC_TAG_SIZE>
+ expected_mic_short_salt_identity_token_hmac,
+ std::array<uint8_t, HMAC_TAG_SIZE>
expected_mic_extended_salt_identity_token_hmac,
- const std::array<uint8_t, 32> expected_signature_identity_token_hmac,
- const std::array<uint8_t, 32> pub_key,
- const MatchedCredentialData matched_credential_data) {
+ MatchedCredentialData matched_credential_data) {
np_ffi::internal::V1DiscoveryCredential discovery_cred{};
- CopyToRawArray(discovery_cred.key_seed, key_seed);
+ CopyToRawArray(discovery_cred.key_seed._0, key_seed);
+ CopyToRawArray(discovery_cred.expected_mic_short_salt_identity_token_hmac,
+ expected_mic_short_salt_identity_token_hmac);
CopyToRawArray(discovery_cred.expected_mic_extended_salt_identity_token_hmac,
expected_mic_extended_salt_identity_token_hmac);
- CopyToRawArray(discovery_cred.expected_signature_identity_token_hmac,
- expected_signature_identity_token_hmac);
- CopyToRawArray(discovery_cred.pub_key, pub_key);
this->internal_ = {discovery_cred, matched_credential_data.data_};
}
diff --git a/nearby/presence/np_cpp_ffi/sample/main.cc b/nearby/presence/np_cpp_ffi/sample/main.cc
index 8a5c044..81c2cc8 100644
--- a/nearby/presence/np_cpp_ffi/sample/main.cc
+++ b/nearby/presence/np_cpp_ffi/sample/main.cc
@@ -35,7 +35,7 @@
void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement /*adv*/);
void HandleV1Section(const nearby_protocol::DeserializedV1Section& /*section*/);
-void HandleV1DataElement(nearby_protocol::V1DataElement /*de*/);
+void HandleV1DataElement(nearby_protocol::GenericV1DataElement /*de*/);
int main() {
auto result =
@@ -285,7 +285,7 @@
}
}
-void HandleV1DataElement(nearby_protocol::V1DataElement de) {
+void HandleV1DataElement(nearby_protocol::GenericV1DataElement de) {
std::cout << "\t\t\tData Element type code: "
<< static_cast<unsigned>(de.GetDataElementTypeCode()) << "\n";
std::cout << "\t\t\tPayload bytes as hex: "
diff --git a/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc b/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc
index 58e1d20..88464a8 100644
--- a/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc
+++ b/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc
@@ -18,6 +18,9 @@
#include "nearby_protocol.h"
+using nearby_protocol::KEY_SEED_SIZE;
+using nearby_protocol::HMAC_TAG_SIZE;
+
std::string PanicReasonToString(nearby_protocol::PanicReason reason) {
switch (reason) {
case nearby_protocol::PanicReason::EnumCastFailed: {
@@ -64,13 +67,15 @@
}
nearby_protocol::V1MatchableCredential GenerateRandomCredentialV1() {
- auto key_seed = create_random_array<32>();
- auto expected_unsigned_metadata_key_hmac = create_random_array<32>();
- auto expected_signed_metadata_key_hmac = create_random_array<32>();
- auto pub_key = create_random_array<32>();
+ auto key_seed = create_random_array<KEY_SEED_SIZE>();
+ auto expected_mic_short_salt_identity_token_hmac = create_random_array<HMAC_TAG_SIZE>();
+ auto expected_mic_extended_salt_identity_token_hmac = create_random_array<HMAC_TAG_SIZE>();
auto encrypted_metadata_bytes = create_random_array<200>();
nearby_protocol::MatchedCredentialData matched_cred(rand(),
encrypted_metadata_bytes);
- return {key_seed, expected_unsigned_metadata_key_hmac,
- expected_signed_metadata_key_hmac, pub_key, matched_cred};
+ return nearby_protocol::V1MatchableCredential(
+ key_seed,
+ expected_mic_short_salt_identity_token_hmac,
+ expected_mic_extended_salt_identity_token_hmac,
+ matched_cred);
}
diff --git a/nearby/presence/np_cpp_ffi/shared/shared_test_util.h b/nearby/presence/np_cpp_ffi/shared/shared_test_util.h
index 83e79a7..0e54460 100644
--- a/nearby/presence/np_cpp_ffi/shared/shared_test_util.h
+++ b/nearby/presence/np_cpp_ffi/shared/shared_test_util.h
@@ -29,7 +29,6 @@
0x02, // section len
0x15, // Tx power value 3
0x03};
-
// V0 encrypted advertisement data - ripped out of np_adv/tests/examples_v0.rs
constexpr std::array<uint8_t, 19> V0AdvEncryptedBytes{
0x04, 0x22, 0x22, 0xD8, 0x22, 0x12, 0xEF, 0x16, 0xDB, 0xF8,
@@ -54,44 +53,43 @@
0x56, 0xA0, 0xB8, 0xEA, 0x67, 0xD1, 0x1C, 0x3E, 0x36, 0xFD};
// V1 encrypted advertisement data - ripped out of np_adv/tests/examples_v1.rs
-constexpr std::array<uint8_t, 101> V1AdvEncryptedBytes{
- 0x20, 0x03, 0x9C, 0xED, 0x67, 0x93, 0x04, 0x6A, 0xBB, 0x1B, 0x1C, 0x40,
- 0x45, 0x1F, 0x34, 0x6C, 0x03, 0xF3, 0xAA, 0x4F, 0x01, 0x8B, 0x7A, 0x5F,
- 0x8A, 0x7B, 0x06, 0x29, 0xA4, 0x12, 0x27, 0x12, 0x63, 0xB4, 0x42, 0x76,
- 0xDE, 0xE9, 0x01, 0x6D, 0xF5, 0x70, 0x1C, 0x09, 0xDF, 0xBE, 0x2E, 0x32,
- 0xE5, 0x64, 0x8A, 0xDF, 0xB0, 0xB5, 0xE0, 0xC1, 0xD9, 0x76, 0x99, 0x4C,
- 0x71, 0xFB, 0xB2, 0xBF, 0xA2, 0xB9, 0xC2, 0xEA, 0x95, 0xE6, 0x6B, 0xFD,
- 0xD7, 0x93, 0x4D, 0xB5, 0x2D, 0xC2, 0x8D, 0x1E, 0x31, 0x32, 0x00, 0x88,
- 0x38, 0xBA, 0x73, 0x39, 0x5E, 0x23, 0xC3, 0x6C, 0x2D, 0x50, 0x22, 0x5B,
- 0xB5, 0xF4, 0x9C, 0x96, 0x37};
+
+constexpr std::array<uint8_t, 53> V1AdvEncryptedBytes = {
+ 0x20, 0x02, 0x11, 0x4E, 0xB8, 0x1D, 0x0B, 0xCF, 0x6D, 0xA9, 0xD3,
+ 0x19, 0x30, 0xF3, 0xC2, 0xD8, 0xB3, 0xD1, 0x03, 0xF2, 0x46, 0x98,
+ 0x2B, 0xAF, 0x4F, 0xC5, 0x65, 0x70, 0xD2, 0x2C, 0x4F, 0x7C, 0x3E,
+ 0x42, 0x12, 0xD6, 0xCE, 0x71, 0xD9, 0x92, 0x56, 0xF1, 0x3B, 0xB1,
+ 0xD6, 0x3B, 0x77, 0xE8, 0x0E, 0x74, 0xFD, 0x82, 0xE3};
constexpr std::array<uint8_t, 32> V1AdvKeySeed = {
- 0xF1, 0xEF, 0x9E, 0x34, 0xDC, 0x28, 0xBC, 0x37, 0x5B, 0x6E, 0x4A,
- 0xC4, 0x52, 0xE6, 0x9C, 0xD3, 0x6D, 0xA9, 0xAB, 0x21, 0x5B, 0x02,
- 0xB9, 0x10, 0x27, 0xC3, 0xA9, 0x53, 0xB5, 0x29, 0x4C, 0x36};
+ 0xA0, 0xC4, 0x9E, 0xF2, 0x9D, 0xE3, 0xAF, 0x42, 0xCB, 0x71, 0xF5,
+ 0x01, 0x55, 0x7C, 0x9C, 0xD3, 0x3F, 0x76, 0x03, 0x91, 0xF8, 0xF2,
+ 0xC2, 0x72, 0xBC, 0x26, 0xEA, 0x4C, 0xF2, 0x77, 0xB8, 0xBC};
+constexpr std::array<uint8_t, 32> V1AdvExpectedMicShortSaltIdentityTokenHmac = {
+ 0x06, 0x78, 0x21, 0x6D, 0x23, 0x30, 0x8D, 0xE8, 0xA8, 0xA4, 0x29,
+ 0x9D, 0x6E, 0x1B, 0x81, 0x29, 0x63, 0xDD, 0x96, 0xE4, 0xD3, 0xE4,
+ 0x3F, 0xF0, 0xA9, 0xB5, 0x61, 0xD4, 0x16, 0x41, 0x0E, 0xA7};
constexpr std::array<uint8_t, 32>
- V1AdvExpectedMicExtendedSaltIdentityTokenHmac = {0};
-constexpr std::array<uint8_t, 32> V1AdvExpectedSignatureIdentityTokenHmac = {
- 0x10, 0x05, 0x45, 0x27, 0x8F, 0xE6, 0x86, 0x5B, 0x51, 0xB0, 0x39,
- 0xEA, 0xB8, 0xD7, 0x5F, 0x61, 0x41, 0xDD, 0x92, 0x62, 0xDD, 0x1A,
- 0x9D, 0xDA, 0x98, 0xDA, 0xF2, 0xDC, 0xA9, 0xD2, 0x45, 0x50};
-constexpr std::array<uint8_t, 32> V1AdvPublicKey = {
- 0x39, 0xA0, 0x74, 0x81, 0x70, 0x46, 0xFF, 0x72, 0x59, 0xC7, 0x78,
- 0x6C, 0x30, 0x7B, 0xC8, 0x71, 0x26, 0x34, 0xFF, 0x13, 0x61, 0x8C,
- 0xE6, 0x22, 0x46, 0x62, 0x56, 0xFD, 0xC1, 0x7A, 0x01, 0xAE};
+ V1AdvExpectedMicExtendedSaltIdentityTokenHmac = {
+ 0x1C, 0x36, 0xE9, 0x0B, 0x8D, 0xCB, 0xFB, 0x18, 0x34, 0x7D, 0xA9,
+ 0xE2, 0x89, 0x39, 0xCE, 0x88, 0x33, 0xCD, 0xBD, 0x9A, 0x0F, 0x53,
+ 0x75, 0xF1, 0x02, 0xF7, 0x96, 0x1A, 0x59, 0x55, 0x7C, 0x09};
inline std::vector<uint8_t> V1AdvEncryptedMetadata = {
- 0x7B, 0xE9, 0x66, 0x00, 0x9E, 0x14, 0x50, 0xDE, 0x96, 0xCB, 0x79,
- 0x38, 0x93, 0xC5, 0x15, 0xE9, 0xC2, 0x6B, 0xE8, 0x03, 0x9F, 0x6C,
- 0xCA, 0x94, 0xAD, 0x24, 0x13, 0x27, 0xC1, 0xDE, 0xBC, 0xC2, 0x29,
- 0x88, 0xC0, 0xA5, 0x3B, 0xCA, 0x98, 0x4A, 0x42, 0xC3, 0xDE, 0xF5,
- 0x1C, 0xAE, 0xFF, 0xC0, 0x02, 0xD2, 0xD2, 0x97, 0x8B, 0x52, 0x93,
- 0x71, 0x07, 0x39, 0x87, 0x89, 0xCB, 0xCD, 0x79, 0x0A, 0x19, 0x0E,
- 0x73, 0xEB, 0x24, 0xFA, 0x8F, 0x4C, 0xA7, 0xF3, 0x95, 0x0B, 0xED,
- 0xEF, 0x27, 0x49, 0x8D, 0xE8, 0x7B, 0x88, 0x33, 0x16, 0x83, 0xF6,
- 0xF1, 0xE6, 0x43, 0x22, 0x70, 0x70, 0xFD, 0x9B, 0xA6, 0x52, 0x35,
- 0x0B, 0xBF, 0xBA, 0x0C, 0x20, 0xA3, 0x0C, 0xE7, 0xC7, 0xD0, 0x70};
-inline std::string ExpectedV1DecryptedMetadata(
- "{\"uuid\":\"378845e1-2616-420d-86f5-674177a7504d\","
- "\"display_name\":\"Alice\",\"location\":\"Wonderland\"}");
+ 0x6D, 0x60, 0x84, 0xCD, 0xE2, 0x81, 0x6B, 0xB0, 0xE6, 0x69, 0x79,
+ 0x80, 0x4D, 0xEE, 0x8F, 0xBD, 0x37, 0x79, 0xE7, 0xB8, 0xA3, 0xF2,
+ 0x8A, 0x8A, 0xB4, 0x0F, 0x42, 0x51, 0x33, 0xB1, 0x0A, 0x1A, 0x3B,
+ 0x9C, 0xE3, 0x9C, 0xFA, 0x64, 0x4C, 0x77, 0x82, 0x94, 0x34, 0x36,
+ 0x5E, 0xE1, 0x9B, 0x18, 0xF7, 0xBF, 0x51, 0x24, 0xF5, 0x90, 0xD4,
+ 0xF1, 0x58, 0x09, 0xB2, 0x48, 0x19, 0x5F, 0x0C, 0xE9, 0x27, 0xB4,
+ 0xA1, 0xEE, 0x0C, 0x36, 0x6B, 0x02, 0x52, 0x45, 0xAE, 0x76, 0x60,
+ 0x0E, 0x8B, 0x34, 0xC2, 0xD7, 0x66, 0xCD, 0x93, 0xD9, 0xFD, 0x88,
+ 0x3F, 0xBC, 0x28, 0x49, 0x9C, 0x39, 0x70, 0xF1, 0x1F, 0x16, 0xD1,
+ 0xD3, 0x61, 0x0C, 0xEE, 0x2F, 0xF4, 0xF4, 0xFD, 0x29, 0x85, 0x2A};
+inline std::string ExpectedV1DecryptedMetadata =
+ "{\"uuid\":\"378845e1-2616-420d-86f5-674177a7504d\",\"display_name\":"
+ "\"Alice\",\"location\":\"Wonderland\"}";
+constexpr std::array<uint8_t, 16> ExpectedV1DerivedSalt = {
+ 0xFF, 0x85, 0xFF, 0x27, 0xD5, 0xE2, 0xEE, 0x77,
+ 0x06, 0x11, 0x0E, 0xD0, 0xF4, 0x2A, 0x19, 0x35};
// Data suitable for use directly through the C API (bypassing the C++ wrapper)
constexpr np_ffi::internal::RawAdvertisementPayload V0AdvMultiDeInternals{
diff --git a/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt b/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
index 80fdee0..67b7a27 100644
--- a/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
+++ b/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
@@ -20,12 +20,12 @@
deserialize_result_tests.cc
np_cpp_test.h
np_cpp_test.cc
- v0_encrypted_deserialization_tests.cc
- v0_encrypted_serialization_tests.cc
- v0_unencrypted_deserialization_tests.cc
- v0_unencrypted_serialization_tests.cc
- v1_encrypted_deserialization_tests.cc
- v1_unencrypted_deserialization_tests.cc
+ v0_encrypted_deserialization_tests.cc
+ v0_encrypted_serialization_tests.cc
+ v0_unencrypted_deserialization_tests.cc
+ v0_unencrypted_serialization_tests.cc
+ v1_encrypted_deserialization_tests.cc
+ v1_unencrypted_deserialization_tests.cc
)
target_link_libraries(
@@ -47,4 +47,4 @@
)
endif ()
-gtest_discover_tests(np_ffi_tests)
+gtest_discover_tests(np_ffi_tests PROPERTIES DISCOVERY_TIMEOUT 120)
diff --git a/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc b/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc
index ebaf874..68f195c 100644
--- a/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc
@@ -100,12 +100,11 @@
const std::span<uint8_t> metadata_span(metadata);
const nearby_protocol::MatchedCredentialData match_data(111, metadata_span);
const std::array<uint8_t, 32> key_seed{1, 2, 3};
- const std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac{1, 2, 3};
- const std::array<uint8_t, 32> expected_signed_metadata_key_hmac{1, 2, 3};
- const std::array<uint8_t, 32> pub_key{1, 2, 3};
+ const std::array<uint8_t, 32> expected_mic_short_salt_identity_token_hmac{1, 2, 3};
+ const std::array<uint8_t, 32> expected_mic_extended_salt_identity_token_hmac{1, 2, 3};
const nearby_protocol::V1MatchableCredential v1_cred(
- key_seed, expected_unsigned_metadata_key_hmac,
- expected_signed_metadata_key_hmac, pub_key, match_data);
+ key_seed, expected_mic_short_salt_identity_token_hmac,
+ expected_mic_extended_salt_identity_token_hmac, match_data);
auto add_result = slab.AddV1Credential(v1_cred);
ASSERT_EQ(add_result, absl::OkStatus());
@@ -119,12 +118,11 @@
const std::span<uint8_t> metadata_span(metadata);
const nearby_protocol::MatchedCredentialData match_data(111, metadata_span);
const std::array<uint8_t, 32> key_seed{1, 2, 3};
- const std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac{1, 2, 3};
- const std::array<uint8_t, 32> expected_signed_metadata_key_hmac{1, 2, 3};
- const std::array<uint8_t, 32> pub_key{1, 2, 3};
+ const std::array<uint8_t, 32> expected_mic_short_salt_identity_token_hmac{1, 2, 3};
+ const std::array<uint8_t, 32> expected_mic_extended_salt_identity_token_hmac{1, 2, 3};
const nearby_protocol::V1MatchableCredential v1_cred(
- key_seed, expected_unsigned_metadata_key_hmac,
- expected_signed_metadata_key_hmac, pub_key, match_data);
+ key_seed, expected_mic_short_salt_identity_token_hmac,
+ expected_mic_extended_salt_identity_token_hmac, match_data);
ASSERT_DEATH([[maybe_unused]] auto add_result = slab.AddV1Credential(v1_cred);
, "");
@@ -145,13 +143,11 @@
slab.AddV0Credential(v0_cred);
const std::array<uint8_t, 32> v1_key_seed{1, 2, 3};
- const std::array<uint8_t, 32> v1_expected_unsigned_metadata_key_hmac{1, 2,
- 3};
- const std::array<uint8_t, 32> v1_expected_signed_metadata_key_hmac{1, 2, 3};
- const std::array<uint8_t, 32> v1_pub_key{1, 2, 3};
+ const std::array<uint8_t, 32> v1_expected_mic_short_salt_identity_token_hmac{1, 2, 3};
+ const std::array<uint8_t, 32> v1_expected_mic_extended_salt_identity_token_hmac{1, 2, 3};
const nearby_protocol::V1MatchableCredential v1_cred(
- v1_key_seed, v1_expected_unsigned_metadata_key_hmac,
- v1_expected_signed_metadata_key_hmac, v1_pub_key, match_data);
+ v1_key_seed, v1_expected_mic_short_salt_identity_token_hmac,
+ v1_expected_mic_extended_salt_identity_token_hmac, match_data);
auto add_v1_result = slab.AddV1Credential(v1_cred);
ASSERT_EQ(add_v1_result, absl::OkStatus());
@@ -172,4 +168,4 @@
nearby_protocol::GlobalConfig::GetCurrentHandleAllocationCount();
ASSERT_EQ(alloc_count.cred_slab, 0U);
}
-// NOLINTEND(readability-magic-numbers)
\ No newline at end of file
+// NOLINTEND(readability-magic-numbers)
diff --git a/nearby/presence/np_cpp_ffi/tests/v1_encrypted_deserialization_tests.cc b/nearby/presence/np_cpp_ffi/tests/v1_encrypted_deserialization_tests.cc
index 69a042d..9652bc2 100644
--- a/nearby/presence/np_cpp_ffi/tests/v1_encrypted_deserialization_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/v1_encrypted_deserialization_tests.cc
@@ -29,8 +29,8 @@
const std::span<uint8_t> metadata_span(V1AdvEncryptedMetadata);
const nearby_protocol::MatchedCredentialData match_data(123, metadata_span);
const nearby_protocol::V1MatchableCredential v1_cred(
- V1AdvKeySeed, V1AdvExpectedMicExtendedSaltIdentityTokenHmac,
- V1AdvExpectedSignatureIdentityTokenHmac, V1AdvPublicKey, match_data);
+ V1AdvKeySeed, V1AdvExpectedMicShortSaltIdentityTokenHmac, V1AdvExpectedMicExtendedSaltIdentityTokenHmac,
+ match_data);
auto add_result = slab.AddV1Credential(v1_cred);
ASSERT_EQ(add_result, absl::OkStatus());
@@ -61,18 +61,15 @@
ASSERT_TRUE(identity_details.ok());
ASSERT_EQ(identity_details->cred_id, 123);
ASSERT_EQ(identity_details->verification_mode,
- nearby_protocol::V1VerificationMode::Signature);
+ nearby_protocol::V1VerificationMode::Mic);
auto de = section->TryGetDataElement(0);
ASSERT_TRUE(de.ok());
ASSERT_EQ(de->GetDataElementTypeCode(), 5U);
ASSERT_EQ(de->GetPayload().ToVector(), std::vector<uint8_t>{7});
- auto offset = de->GetOffset();
- auto derived_salt = section->DeriveSaltForOffset(offset);
+ auto derived_salt = section->DeriveSaltForDeType(de->GetDataElementTypeCode());
ASSERT_TRUE(derived_salt.ok());
- const std::array<uint8_t, 16> expected = {0xD5, 0x63, 0x47, 0x39, 0x77, 0x84,
- 0x38, 0xF2, 0x91, 0xBC, 0x24, 0x21,
- 0xAD, 0x80, 0x88, 0x16};
+ const std::array<uint8_t, 16> expected = ExpectedV1DerivedSalt;
ASSERT_EQ(*derived_salt, expected);
}
diff --git a/nearby/presence/np_ed25519/Cargo.toml b/nearby/presence/np_ed25519/Cargo.toml
deleted file mode 100644
index 5addbb1..0000000
--- a/nearby/presence/np_ed25519/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "np_ed25519"
-version.workspace = true
-edition.workspace = true
-publish.workspace = true
-license.workspace = true
-
-[lints]
-workspace = true
-
-[dependencies]
-array_view.workspace = true
-crypto_provider = { workspace = true, features = ["raw_private_key_permit"] }
-sink.workspace = true
-tinyvec.workspace = true
-
-[features]
-std = ["crypto_provider/std"]
diff --git a/nearby/presence/np_ed25519/src/lib.rs b/nearby/presence/np_ed25519/src/lib.rs
deleted file mode 100644
index b5123e1..0000000
--- a/nearby/presence/np_ed25519/src/lib.rs
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.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.
-
-//! Wrappers around NP's usage of ed25519 signatures.
-//!
-//! All of NP's usages of ed25519 signatures are performed
-//! with "context" bytes prepended to the payload to be signed
-//! or verified. These "context" bytes allow for usage of the
-//! same base key-pair for different purposes in the protocol.
-#![no_std]
-
-use array_view::ArrayView;
-use crypto_provider::ed25519::{Ed25519Provider, PrivateKey, PublicKey, Signature, SignatureError};
-use sink::{Sink, SinkWriter};
-use tinyvec::ArrayVec;
-
-/// Maximum length of the combined (context len byte) + (context bytes) + (signing payload)
-/// byte-array which an ed25519 signature will be computed over. This is deliberately
-/// chosen to be large enough to incorporate an entire v1 adv as the signing payload.
-pub const MAX_SIGNATURE_BUFFER_LEN: usize = 512;
-
-/// Sign the given message with the given context and
-/// return a digital signature. The message is represented
-/// using a [`SinkWriter`] to allow the caller to construct
-/// the payload to sign without requiring a fully-assembled
-/// payload available as a slice.
-///
-/// If the message writer writes too much data (greater than 256 bytes),
-/// this will return `None` instead of a valid signature,
-/// and so uses in `np_adv` will use `.expect` on the returned value
-/// to indicate that this length constraint has been considered.
-pub fn sign_with_context<E: Ed25519Provider, W: SinkWriter<DataType = u8>>(
- private_key: &PrivateKey,
- context: &SignatureContext,
- msg_writer: W,
-) -> Option<Signature> {
- let mut buffer = context.create_signature_buffer();
- buffer.try_extend_from_writer(msg_writer).map(|_| private_key.sign::<E>(buffer.as_ref()))
-}
-
-/// Errors yielded when attempting to verify an ed25519 signature.
-#[derive(Debug, PartialEq, Eq)]
-pub enum SignatureVerificationError {
- /// The payload that we attempted to verify the signature of was too big
- PayloadTooBig,
- /// The signature we were checking was invalid for the given payload
- SignatureInvalid,
-}
-
-impl From<SignatureError> for SignatureVerificationError {
- fn from(_: SignatureError) -> Self {
- Self::SignatureInvalid
- }
-}
-
-/// Succeeds if the signature was a valid signature created via the corresponding
-/// keypair to this public key using the given [`SignatureContext`] on the given
-/// message payload. The message payload is represented
-/// using a [`SinkWriter`] to allow the caller to construct
-/// the payload to sign without requiring a fully-assembled
-/// payload available as a slice.
-///
-/// If the message writer writes too much data (greater than 256 bytes),
-/// this will return `None` instead of a valid signature,
-/// and so uses in `np_adv` will use `.expect` on the returned value
-/// to indicate that this length constraint has been considered.
-pub fn verify_signature_with_context<E: Ed25519Provider, W: SinkWriter<DataType = u8>>(
- public_key: &PublicKey,
- context: &SignatureContext,
- msg_writer: W,
- signature: Signature,
-) -> Result<(), SignatureVerificationError> {
- let mut buffer = context.create_signature_buffer();
- let maybe_write_success = buffer.try_extend_from_writer(msg_writer);
- match maybe_write_success {
- Some(_) => {
- public_key.verify_strict::<E>(buffer.as_ref(), signature)?;
- Ok(())
- }
- None => Err(SignatureVerificationError::PayloadTooBig),
- }
-}
-
-/// Minimum length (in bytes) for a [`SignatureContext`] (which cannot be empty).
-pub const MIN_SIGNATURE_CONTEXT_LEN: usize = 1;
-
-/// Maximum length (in bytes) for a [`SignatureContext`] (which uses an 8-bit length field).
-pub const MAX_SIGNATURE_CONTEXT_LEN: usize = 255;
-
-/// (Non-empty) context bytes to use in the construction of NP's
-/// Ed25519 signatures. The context bytes should uniquely
-/// identify the component of the protocol performing the
-/// signature/verification (e.g: advertisement signing,
-/// connection signing), and should be between 1 and
-/// 255 bytes in length.
-pub struct SignatureContext {
- data: ArrayView<u8, MAX_SIGNATURE_CONTEXT_LEN>,
-}
-
-impl SignatureContext {
- /// Creates a signature buffer with size bounded by MAX_SIGNATURE_BUFFER_LEN
- /// which is pre-populated with the contents yielded by
- /// [`SignatureContext#write_length_prefixed`].
- fn create_signature_buffer(&self) -> impl Sink<u8> + AsRef<[u8]> {
- let mut buffer = ArrayVec::<[u8; MAX_SIGNATURE_BUFFER_LEN]>::new();
- #[allow(clippy::expect_used)]
- self.write_length_prefixed(&mut buffer).expect("Context should always fit into sig buffer");
- buffer
- }
-
- /// Writes the contents of this signature context, prefixed
- /// by the length of the context payload to the given byte-sink.
- /// If writing to the sink failed at some point during this operation,
- /// `None` will be returned, and the data written to the sink should
- /// be considered to be invalid.
- fn write_length_prefixed<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
- let length_byte = self.data.len() as u8;
- sink.try_push(length_byte)?;
- sink.try_extend_from_slice(self.data.as_slice())
- }
-
- /// Attempts to construct a signature context from the utf-8 bytes
- /// of the given string. Returns `None` if the passed string
- /// is invalid to use for signature context bytes.
- pub const fn from_string_bytes(data_str: &str) -> Result<Self, SignatureContextInvalidLength> {
- let data_bytes = data_str.as_bytes();
- Self::from_bytes(data_bytes)
- }
-
- #[allow(clippy::indexing_slicing)]
- const fn from_bytes(bytes: &[u8]) -> Result<Self, SignatureContextInvalidLength> {
- let num_bytes = bytes.len();
- if num_bytes < MIN_SIGNATURE_CONTEXT_LEN || num_bytes > MAX_SIGNATURE_CONTEXT_LEN {
- Err(SignatureContextInvalidLength)
- } else {
- let mut array = [0u8; MAX_SIGNATURE_CONTEXT_LEN];
- let mut i = 0;
- while i < num_bytes {
- array[i] = bytes[i];
- i += 1;
- }
- let data = ArrayView::const_from_array(array, bytes.len());
- Ok(Self { data })
- }
- }
-}
-
-/// Error raised when attempting to construct a
-/// [`SignatureContext`] out of data with an
-/// invalid length in bytes.
-#[derive(Debug)]
-pub struct SignatureContextInvalidLength;
-
-impl TryFrom<&[u8]> for SignatureContext {
- type Error = SignatureContextInvalidLength;
-
- fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
- Self::from_bytes(value)
- }
-}
diff --git a/nearby/presence/np_ffi_core/Cargo.toml b/nearby/presence/np_ffi_core/Cargo.toml
index 4faebe6..e746186 100644
--- a/nearby/presence/np_ffi_core/Cargo.toml
+++ b/nearby/presence/np_ffi_core/Cargo.toml
@@ -16,15 +16,31 @@
np_hkdf.workspace = true
handle_map.workspace = true
crypto_provider.workspace = true
-crypto_provider_default = { workspace = true, default-features = false }
+crypto_provider_default = { workspace = true, default-features = false, optional = true }
+crypto_provider_stubs = { workspace = true, optional = true }
lock_adapter.workspace = true
lazy_static.workspace = true
strum.workspace = true
strum_macros.workspace = true
+tinyvec.workspace = true
+arbitrary = { workspace = true, optional = true }
+
+[dev-dependencies]
+arbitrary.workspace = true
+derive_fuzztest.workspace = true
[features]
-default = ["rustcrypto"]
-rustcrypto = ["crypto_provider_default/rustcrypto", "crypto_provider_default/std"]
-boringssl = ["crypto_provider_default/boringssl"]
-testing = ["np_adv/testing"]
+default = []
+nocrypto = ["dep:crypto_provider_stubs"]
+rustcrypto = ["dep:crypto_provider_default", "crypto_provider_default/rustcrypto", "crypto_provider_default/std"]
+boringssl = ["dep:crypto_provider_default", "crypto_provider_default/boringssl"]
+testing = ["np_adv/testing", "dep:arbitrary"]
std = []
+
+[[test]]
+name = "cannot_add_credentials"
+required-features = ["nocrypto", "testing"]
+
+[[test]]
+name = "exhaustive_roundtrips"
+required-features = ["nocrypto"]
diff --git a/nearby/presence/np_ffi_core/fuzz/.gitignore b/nearby/presence/np_ffi_core/fuzz/.gitignore
new file mode 100644
index 0000000..b7fa0a7
--- /dev/null
+++ b/nearby/presence/np_ffi_core/fuzz/.gitignore
@@ -0,0 +1,4 @@
+/target
+/corpus
+/artifacts
+/coverage
diff --git a/nearby/presence/np_ffi_core/fuzz/Cargo.lock b/nearby/presence/np_ffi_core/fuzz/Cargo.lock
new file mode 100644
index 0000000..6b84d2c
--- /dev/null
+++ b/nearby/presence/np_ffi_core/fuzz/Cargo.lock
@@ -0,0 +1,579 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+dependencies = [
+ "derive_arbitrary",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[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 = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[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 = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "derive-fuzztest"
+version = "0.1.0"
+dependencies = [
+ "derive-fuzztest-macro",
+ "proptest",
+ "proptest-arbitrary-interop",
+]
+
+[[package]]
+name = "derive-fuzztest-macro"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "derive-where"
+version = "1.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "derive_arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[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 = "distributed-time"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "derive-where",
+ "derive_more",
+ "num-traits",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "distributed-time-fuzz"
+version = "0.0.0"
+dependencies = [
+ "arbitrary",
+ "derive-fuzztest",
+ "distributed-time",
+ "libfuzzer-sys",
+ "proptest-arb",
+]
+
+[[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.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"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[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 = "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 = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.151"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
+
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
+dependencies = [
+ "arbitrary",
+ "cc",
+ "once_cell",
+]
+
+[[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.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
+[[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 = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[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.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",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "proptest-arb"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "proptest",
+ "proptest-arb-attr-macro",
+ "proptest-arbitrary-interop",
+]
+
+[[package]]
+name = "proptest-arb-attr-macro"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "proptest-arbitrary-interop"
+version = "0.1.0"
+source = "git+https://github.com/brson/proptest-arbitrary-interop.git?branch=incorrect-format#9ae407e9805feb109b3d49cc737166bda7e698c3"
+dependencies = [
+ "arbitrary",
+ "proptest",
+]
+
+[[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.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_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.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[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.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
+dependencies = [
+ "bitflags 2.4.2",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[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 = "ryu"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
+
+[[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.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 2.0.48",
+]
+
+[[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 = "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 = "tempfile"
+version = "3.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[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.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[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.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[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.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[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.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
diff --git a/nearby/presence/np_ffi_core/fuzz/Cargo.toml b/nearby/presence/np_ffi_core/fuzz/Cargo.toml
new file mode 100644
index 0000000..985f295
--- /dev/null
+++ b/nearby/presence/np_ffi_core/fuzz/Cargo.toml
@@ -0,0 +1,46 @@
+[package]
+name = "np_ffi_core_fuzz"
+version.workspace = true
+publish = false
+edition.workspace = true
+license.workspace = true
+
+[package.metadata]
+cargo-fuzz = true
+
+[dependencies]
+arbitrary.workspace = true
+derive_fuzztest.workspace = true
+handle_map.workspace = true
+np_ffi_core.workspace = true
+
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
+
+[lints]
+workspace = true
+
+[[bin]]
+name = "nocrypto_deserialize"
+doc = false
+required-features = ["np_ffi_core/nocrypto"]
+
+[[bin]]
+name = "roundtrip_cast_id"
+doc = false
+required-features = ["np_ffi_core/nocrypto", "np_ffi_core/testing"]
+
+[[bin]]
+name = "roundtrip_dedup_hint"
+doc = false
+required-features = ["np_ffi_core/nocrypto", "np_ffi_core/testing"]
+
+[[bin]]
+name = "roundtrip_v1_actions"
+doc = false
+required-features = ["np_ffi_core/nocrypto", "np_ffi_core/testing"]
+
+[[bin]]
+name = "v1_de_deser"
+doc = false
+required-features = ["np_ffi_core/nocrypto", "np_ffi_core/testing"]
diff --git a/nearby/presence/np_ffi_core/fuzz/src/bin/nocrypto_deserialize.rs b/nearby/presence/np_ffi_core/fuzz/src/bin/nocrypto_deserialize.rs
new file mode 100644
index 0000000..538d8ae
--- /dev/null
+++ b/nearby/presence/np_ffi_core/fuzz/src/bin/nocrypto_deserialize.rs
@@ -0,0 +1,43 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that attempting to deserialize with `nocrypto` enabled doesn't panic.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use handle_map::OwnedHandle;
+use np_ffi_core::{
+ credentials::{
+ create_credential_book_from_slab, create_credential_slab, CreateCredentialBookResult,
+ CreateCredentialSlabResult, CredentialBook,
+ },
+ deserialize::deserialize_advertisement_from_slice,
+};
+
+fn make_empty_credential_book() -> OwnedHandle<CredentialBook> {
+ let CreateCredentialSlabResult::Success(slab) = create_credential_slab() else {
+ panic!("Failed to create credential slab");
+ };
+ let CreateCredentialBookResult::Success(book) = create_credential_book_from_slab(slab) else {
+ panic!("Failed to create credential book");
+ };
+ OwnedHandle::new(book)
+}
+
+#[derive_fuzztest::fuzztest]
+fn deserialize_random_advertisement(adv: Vec<u8>) {
+ let book = make_empty_credential_book();
+ let _ = deserialize_advertisement_from_slice(&adv, *book).deallocate();
+}
diff --git a/nearby/presence/np_ffi_core/fuzz/src/bin/roundtrip_cast_id.rs b/nearby/presence/np_ffi_core/fuzz/src/bin/roundtrip_cast_id.rs
new file mode 100644
index 0000000..c45dd15
--- /dev/null
+++ b/nearby/presence/np_ffi_core/fuzz/src/bin/roundtrip_cast_id.rs
@@ -0,0 +1,29 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that round-trip ser->deser of Cast Ids is the identity.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use np_ffi_core::{deserialize::v1::*, v1::*};
+
+#[derive_fuzztest::fuzztest]
+fn roundtrip_cast_id(cast_id: CastId) {
+ let de = cast_id.serialize();
+ assert_eq!(
+ de.try_deserialize(),
+ Ok(V1DataElementDeserializationResult::Success(V1DataElement::CastId(cast_id)))
+ );
+}
diff --git a/nearby/presence/np_ffi_core/fuzz/src/bin/roundtrip_dedup_hint.rs b/nearby/presence/np_ffi_core/fuzz/src/bin/roundtrip_dedup_hint.rs
new file mode 100644
index 0000000..8aa35a2
--- /dev/null
+++ b/nearby/presence/np_ffi_core/fuzz/src/bin/roundtrip_dedup_hint.rs
@@ -0,0 +1,29 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that round-trip ser->deser of deduplication hints is the identity.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use np_ffi_core::{deserialize::v1::*, v1::*};
+
+#[derive_fuzztest::fuzztest]
+fn roundtrip_dedup_hint(dedup_hint: DedupHint) {
+ let de = dedup_hint.serialize();
+ assert_eq!(
+ de.try_deserialize(),
+ Ok(V1DataElementDeserializationResult::Success(V1DataElement::DedupHint(dedup_hint)))
+ );
+}
diff --git a/nearby/presence/np_ffi_core/fuzz/src/bin/roundtrip_v1_actions.rs b/nearby/presence/np_ffi_core/fuzz/src/bin/roundtrip_v1_actions.rs
new file mode 100644
index 0000000..e754fe5
--- /dev/null
+++ b/nearby/presence/np_ffi_core/fuzz/src/bin/roundtrip_v1_actions.rs
@@ -0,0 +1,37 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that round-trip ser->deser of the V1 actions DE is the identity when restricted to
+//! serializable actions-sets.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use derive_fuzztest::TestResult;
+use np_ffi_core::{deserialize::v1::*, serialize::v1::*, v1::*};
+
+#[derive_fuzztest::fuzztest]
+fn roundtrip_v1_actions(actions: V1ActionsBitmap) -> TestResult {
+ let V1SerializeActionsBitmapResult::Success(de) = actions.try_serialize() else {
+ // We've encountered either too many or too few actions to
+ // serialize, so the rest of the test cannot procceed.
+ // We're still ensuring that failed serialization doesn't panic.
+ return TestResult::Discard;
+ };
+ assert_eq!(
+ de.try_deserialize(),
+ Ok(V1DataElementDeserializationResult::Success(V1DataElement::Actions(actions)))
+ );
+ TestResult::Passed
+}
diff --git a/nearby/presence/np_ffi_core/fuzz/src/bin/v1_de_deser.rs b/nearby/presence/np_ffi_core/fuzz/src/bin/v1_de_deser.rs
new file mode 100644
index 0000000..99f540c
--- /dev/null
+++ b/nearby/presence/np_ffi_core/fuzz/src/bin/v1_de_deser.rs
@@ -0,0 +1,25 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that deserialization of arbitrary V1 DEs never panics.
+
+#![cfg_attr(fuzzing, no_main)]
+#![allow(missing_docs)]
+
+use np_ffi_core::v1::GenericV1DataElement;
+
+#[derive_fuzztest::fuzztest]
+fn deserialization_does_not_panic(de: GenericV1DataElement) {
+ let _ = de.try_deserialize();
+}
diff --git a/nearby/presence/np_ffi_core/src/common.rs b/nearby/presence/np_ffi_core/src/common.rs
index 607a6af..53d7d1e 100644
--- a/nearby/presence/np_ffi_core/src/common.rs
+++ b/nearby/presence/np_ffi_core/src/common.rs
@@ -14,9 +14,10 @@
//! Common externally-accessible FFI constructs which are needed
//! in order to define the interfaces in this crate's various modules.
+use crate::utils::FfiEnum;
+use crate::CryptoProviderImpl;
use array_view::ArrayView;
use crypto_provider::{CryptoProvider, CryptoRng};
-use crypto_provider_default::CryptoProviderImpl;
use handle_map::HandleNotPresentError;
use lock_adapter::stdlib::{RwLock, RwLockWriteGuard};
use lock_adapter::RwLock as _;
@@ -124,16 +125,16 @@
/// for debugging, logging, and testing.
pub fn global_config_get_current_allocation_count() -> CurrentHandleAllocations {
CurrentHandleAllocations {
- cred_book: crate::credentials::credential_book::get_current_allocation_count(),
- cred_slab: crate::credentials::credential_slab::get_current_allocation_count(),
- decrypted_metadata: crate::deserialize::decrypted_metadata::get_current_allocation_count(),
- v0_payload: crate::deserialize::v0::v0_payload::get_current_allocation_count(),
+ cred_book: crate::credentials::CredentialBook::get_current_allocation_count(),
+ cred_slab: crate::credentials::CredentialSlab::get_current_allocation_count(),
+ decrypted_metadata: crate::deserialize::DecryptedMetadata::get_current_allocation_count(),
+ v0_payload: crate::deserialize::v0::V0Payload::get_current_allocation_count(),
legible_v1_sections:
- crate::deserialize::v1::legible_v1_sections::get_current_allocation_count(),
+ crate::deserialize::v1::LegibleV1Sections::get_current_allocation_count(),
v0_advertisement_builder:
- crate::serialize::v0::advertisement_builder::get_current_allocation_count(),
+ crate::serialize::v0::V0AdvertisementBuilder::get_current_allocation_count(),
v1_advertisement_builder:
- crate::serialize::v1::advertisement_builder::get_current_allocation_count(),
+ crate::serialize::v1::V1AdvertisementBuilder::get_current_allocation_count(),
}
}
@@ -179,6 +180,7 @@
/// to contain the actual payload. N is only
/// permitted to be between 0 and 255.
#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, arbitrary::Arbitrary))]
#[repr(C)]
// TODO: Once generic const exprs are stabilized,
// we could instead make N into a compile-time u8.
@@ -187,33 +189,33 @@
bytes: [u8; N],
}
-/// A FFI safe wrapper of a fixed size array
-#[derive(Clone)]
-#[repr(C)]
-pub struct FixedSizeArray<const N: usize>([u8; N]);
-
-impl<const N: usize> FixedSizeArray<N> {
- /// Constructs a byte-buffer from a Rust-side-derived owned array
- pub(crate) fn from_array(bytes: [u8; N]) -> Self {
- Self(bytes)
- }
- /// Yields a slice of the bytes
- pub fn as_slice(&self) -> &[u8] {
- self.0.as_slice()
- }
- /// De-structures this FFI-compatible fixed-size array
- /// into a bare Rust fixed size array.
- pub fn into_array(self) -> [u8; N] {
- self.0
+impl<const N: usize> Default for ByteBuffer<N> {
+ fn default() -> Self {
+ Self { len: 0, bytes: [0u8; N] }
}
}
-impl<const N: usize> From<[u8; N]> for FixedSizeArray<N> {
- fn from(arr: [u8; N]) -> Self {
- Self(arr)
+#[cfg(any(test, feature = "testing"))]
+impl<const N: usize> PartialEq for ByteBuffer<N> {
+ fn eq(&self, other: &Self) -> bool {
+ if self.len != other.len {
+ return false;
+ }
+ // Lengths equal, but not necessarily validated.
+ // If the lengths are greater than N, both
+ // are invalid structures, but they could still be equal.
+ let len = self.len as usize;
+ if len > N {
+ self.bytes == other.bytes
+ } else {
+ self.bytes[..len] == other.bytes[..len]
+ }
}
}
+#[cfg(any(test, feature = "testing"))]
+impl<const N: usize> Eq for ByteBuffer<N> {}
+
impl<const N: usize> ByteBuffer<N> {
/// Constructs a byte-buffer from a Rust-side-derived
/// ArrayView, which is assumed to be trusted to be
@@ -224,12 +226,134 @@
let len = len as u8;
Self { len, bytes }
}
+ /// Constructs a byte-buffer from a Rust-side-derived
+ /// tinyvec::ArrayVec, which is assumed to be trusted
+ /// to be properly initialized, and with a size-bound
+ /// under 255 bytes.
+ pub fn from_array_vec(array_vec: tinyvec::ArrayVec<[u8; N]>) -> Self {
+ let len = array_vec.len() as u8;
+ let bytes = array_vec.into_inner();
+ Self { len, bytes }
+ }
/// Yields a slice of the first `self.len` bytes of `self.bytes`.
- pub fn as_slice(&self) -> Option<&[u8]> {
+ pub fn as_slice(&self) -> Result<&[u8], InvalidStackDataStructure> {
if self.len as usize <= N {
- Some(&self.bytes[..(self.len as usize)])
+ Ok(&self.bytes[..(self.len as usize)])
} else {
- None
+ Err(InvalidStackDataStructure)
+ }
+ }
+}
+
+impl<const N: usize> TryFrom<ByteBuffer<N>> for tinyvec::ArrayVec<[u8; N]> {
+ type Error = InvalidStackDataStructure;
+ fn try_from(buffer: ByteBuffer<N>) -> Result<Self, Self::Error> {
+ if buffer.len as usize <= N {
+ Ok(tinyvec::ArrayVec::from_array_len(buffer.bytes, buffer.len as usize))
+ } else {
+ Err(InvalidStackDataStructure)
+ }
+ }
+}
+
+/// A FFI safe wrapper of a fixed size array
+#[derive(Clone)]
+#[repr(C)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+pub struct FixedSizeArray<const N: usize>([u8; N]);
+
+impl<const N: usize> FixedSizeArray<N> {
+ /// Yields a slice of the bytes
+ pub fn as_slice(&self) -> &[u8] {
+ self.0.as_slice()
+ }
+}
+
+impl<const N: usize> From<FixedSizeArray<N>> for [u8; N] {
+ fn from(arr: FixedSizeArray<N>) -> Self {
+ arr.0
+ }
+}
+
+impl<const N: usize> From<[u8; N]> for FixedSizeArray<N> {
+ fn from(arr: [u8; N]) -> Self {
+ Self(arr)
+ }
+}
+
+/// Discriminant for [`OptionalFixedSizeArray`].
+#[derive(Copy, Clone)]
+#[repr(u8)]
+pub enum OptionalFixedSizeArrayKind {
+ /// The optional fixed-size array isn't specified.
+ NotSpecified = 0,
+ /// The optional fixed-size array is present.
+ Present = 1,
+}
+
+/// A FFI safe wrapper of an optional fixed-size byte array.
+#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub enum OptionalFixedSizeArray<const N: usize> {
+ /// The optional fixed-size array isn't specified.
+ NotSpecified,
+ /// The optional fixed-size array is present.
+ Present(FixedSizeArray<N>),
+}
+
+impl<const N: usize> FfiEnum for OptionalFixedSizeArray<N> {
+ type Kind = OptionalFixedSizeArrayKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::NotSpecified => OptionalFixedSizeArrayKind::NotSpecified,
+ Self::Present(_) => OptionalFixedSizeArrayKind::Present,
+ }
+ }
+}
+
+impl<const N: usize> Default for OptionalFixedSizeArray<N> {
+ fn default() -> Self {
+ Self::NotSpecified
+ }
+}
+
+impl<const N: usize> OptionalFixedSizeArray<N> {
+ /// Attempts to cast `self` to the `Present` variant,
+ /// returning `None` in the case where the passed value is of a different enum variant.
+ pub fn into_present(self) -> Option<FixedSizeArray<N>> {
+ match self {
+ Self::NotSpecified => None,
+ Self::Present(x) => Some(x),
+ }
+ }
+ /// Returns true iff this `OptionalFixedSizeArray` has contents.
+ pub fn is_present(&self) -> bool {
+ match self {
+ Self::NotSpecified => false,
+ Self::Present(_) => true,
+ }
+ }
+ /// Alias for the `From` method which can save type annotations.
+ pub fn into_rust(self) -> Option<[u8; N]> {
+ self.into()
+ }
+}
+
+impl<const N: usize> From<Option<[u8; N]>> for OptionalFixedSizeArray<N> {
+ fn from(arr: Option<[u8; N]>) -> Self {
+ match arr {
+ None => Self::NotSpecified,
+ Some(x) => Self::Present(x.into()),
+ }
+ }
+}
+
+impl<const N: usize> From<OptionalFixedSizeArray<N>> for Option<[u8; N]> {
+ fn from(arr: OptionalFixedSizeArray<N>) -> Self {
+ match arr {
+ OptionalFixedSizeArray::NotSpecified => None,
+ OptionalFixedSizeArray::Present(x) => Some(x.into()),
}
}
}
@@ -265,5 +389,85 @@
/// If this kind of error is being raised, the foreign lang code must
/// be messing with stack-allocated data structures for this library
/// in an entirely unexpected way.
-#[derive(Debug)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct InvalidStackDataStructure;
+
+/// Discriminant for `BuildTxPowerResult`.
+#[repr(u8)]
+#[derive(Clone, Copy)]
+pub enum BuildTxPowerResultKind {
+ /// The transmission power was outside the
+ /// allowed -100dBm to 20dBm range.
+ OutOfRange = 0,
+ /// The transmission power was in range,
+ /// and so a `TxPower` struct was constructed.
+ Success = 1,
+}
+
+/// Result type for attempting to construct a
+/// Tx Power from a signed byte.
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum BuildTxPowerResult {
+ OutOfRange,
+ Success(TxPower),
+}
+
+impl FfiEnum for BuildTxPowerResult {
+ type Kind = BuildTxPowerResultKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::OutOfRange => BuildTxPowerResultKind::OutOfRange,
+ Self::Success(_) => BuildTxPowerResultKind::Success,
+ }
+ }
+}
+
+impl BuildTxPowerResult {
+ declare_enum_cast! {into_success, Success, TxPower}
+}
+
+/// Representation of a transmission power,
+/// as used for the Tx Power DE in V0 and V1.
+#[derive(Clone, Copy)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub struct TxPower {
+ pub(crate) tx_power: i8,
+}
+
+impl TxPower {
+ /// Attempts to construct a new TxPower from the given signed-byte value.
+ pub fn build_from_signed_byte(tx_power: i8) -> BuildTxPowerResult {
+ match np_adv::shared_data::TxPower::try_from(tx_power) {
+ Ok(_) => BuildTxPowerResult::Success(Self { tx_power }),
+ Err(_) => BuildTxPowerResult::OutOfRange,
+ }
+ }
+ /// Yields this Tx Power value as an i8.
+ pub fn as_i8(&self) -> i8 {
+ self.tx_power
+ }
+}
+
+#[cfg(any(test, feature = "testing"))]
+impl TxPower {
+ /// Constructs a new transmission power in an unchecked fashion
+ /// from an `i8`.
+ pub fn from_i8_unchecked(tx_power: i8) -> Self {
+ Self { tx_power }
+ }
+}
+
+impl From<np_adv::shared_data::TxPower> for TxPower {
+ fn from(tx_power: np_adv::shared_data::TxPower) -> Self {
+ Self { tx_power: tx_power.as_i8() }
+ }
+}
+
+impl TryFrom<TxPower> for np_adv::shared_data::TxPower {
+ type Error = InvalidStackDataStructure;
+ fn try_from(value: TxPower) -> Result<Self, InvalidStackDataStructure> {
+ np_adv::shared_data::TxPower::try_from(value.as_i8()).map_err(|_| InvalidStackDataStructure)
+ }
+}
diff --git a/nearby/presence/np_ffi_core/src/credentials.rs b/nearby/presence/np_ffi_core/src/credentials.rs
index 4f6c3cd..a0bb07b 100644
--- a/nearby/presence/np_ffi_core/src/credentials.rs
+++ b/nearby/presence/np_ffi_core/src/credentials.rs
@@ -15,19 +15,51 @@
use crate::common::*;
use crate::utils::{FfiEnum, LocksLongerThan};
-use crypto_provider::{ed25519, CryptoProvider};
-use crypto_provider_default::CryptoProviderImpl;
+use crate::CryptoProviderImpl;
+use crypto_provider::ed25519;
use handle_map::{declare_handle_map, HandleLike, HandleMapFullError, HandleMapTryAllocateError};
-use np_adv::extended;
+use np_adv::{
+ credential::{metadata, v0::V0, v1::V1},
+ extended,
+};
+
use std::sync::Arc;
-type Ed25519ProviderImpl = <CryptoProviderImpl as CryptoProvider>::Ed25519;
+/// 32 byte key seed used in both V0 and V1 credentials.
+#[cfg_attr(any(test, feature = "testing"), derive(arbitrary::Arbitrary))]
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct KeySeed([u8; 32]);
+impl From<[u8; 32]> for KeySeed {
+ fn from(value: [u8; 32]) -> Self {
+ Self(value)
+ }
+}
+
+impl KeySeed {
+ /// Returns a reference to the inner byte array
+ pub fn bytes(&self) -> &[u8; 32] {
+ &self.0
+ }
+
+ /// Returns the inner byte array
+ pub const fn into_bytes(self) -> [u8; 32] {
+ self.0
+ }
+
+ /// Returns the key seed as a slice
+ pub fn as_slice(&self) -> &[u8] {
+ &self.0
+ }
+}
/// Cryptographic information about a particular V0 discovery credential
/// necessary to match and decrypt encrypted V0 advertisements.
+#[cfg_attr(any(test, feature = "testing"), derive(Clone, Debug, arbitrary::Arbitrary))]
#[repr(C)]
pub struct V0DiscoveryCredential {
- key_seed: [u8; 32],
+ /// Key seed returned to clients for discovery credential mapping.
+ pub key_seed: KeySeed,
identity_token_hmac: [u8; 32],
}
@@ -35,53 +67,48 @@
/// Constructs a new V0 discovery credential with the given 32-byte key-seed
/// and the given 32-byte HMAC for the (14-byte) legacy metadata key.
pub fn new(key_seed: [u8; 32], identity_token_hmac: [u8; 32]) -> Self {
- Self { key_seed, identity_token_hmac }
+ Self { key_seed: KeySeed(key_seed), identity_token_hmac }
}
fn into_internal(self) -> np_adv::credential::v0::V0DiscoveryCredential {
- np_adv::credential::v0::V0DiscoveryCredential::new(self.key_seed, self.identity_token_hmac)
+ np_adv::credential::v0::V0DiscoveryCredential::new(
+ self.key_seed.into_bytes(),
+ self.identity_token_hmac,
+ )
}
}
/// Cryptographic information about a particular V1 discovery credential
/// necessary to match and decrypt encrypted V1 advertisement sections.
+#[cfg_attr(any(test, feature = "testing"), derive(Clone, Debug, arbitrary::Arbitrary))]
#[repr(C)]
pub struct V1DiscoveryCredential {
- key_seed: [u8; 32],
+ /// Key seed returned to clients for discovery credential mapping.
+ pub key_seed: KeySeed,
expected_mic_short_salt_identity_token_hmac: [u8; 32],
expected_mic_extended_salt_identity_token_hmac: [u8; 32],
- expected_signature_identity_token_hmac: [u8; 32],
- pub_key: [u8; 32],
}
impl V1DiscoveryCredential {
- /// Constructs a new V1 discovery credential with the given 32-byte key-seed,
- /// unsigned-variant HMAC of the metadata key, the signed-variant HMAC of
- /// the metadata key, and the given public key for signature verification.
+ /// Constructs a new V1 discovery credential with the given 32-byte key-seed
+ /// and the MIC HMACs of the metadata key.
pub fn new(
key_seed: [u8; 32],
expected_mic_short_salt_identity_token_hmac: [u8; 32],
expected_mic_extended_salt_identity_token_hmac: [u8; 32],
- expected_signature_identity_token_hmac: [u8; 32],
- pub_key: [u8; 32],
) -> Self {
Self {
- key_seed,
+ key_seed: KeySeed(key_seed),
expected_mic_short_salt_identity_token_hmac,
expected_mic_extended_salt_identity_token_hmac,
- expected_signature_identity_token_hmac,
- pub_key,
}
}
fn into_internal(
self,
) -> Result<np_adv::credential::v1::V1DiscoveryCredential, ed25519::InvalidPublicKeyBytes> {
- let public_key = ed25519::PublicKey::from_bytes::<Ed25519ProviderImpl>(self.pub_key)?;
Ok(np_adv::credential::v1::V1DiscoveryCredential::new(
- self.key_seed,
+ self.key_seed.into_bytes(),
self.expected_mic_short_salt_identity_token_hmac,
self.expected_mic_extended_salt_identity_token_hmac,
- self.expected_signature_identity_token_hmac,
- public_key,
))
}
}
@@ -101,25 +128,47 @@
#[derive(Debug, Clone)]
pub struct MatchedCredential {
cred_id: i64,
- encrypted_metadata_bytes: Arc<[u8]>,
+ inner: Arc<MatchedCredentialInner>,
+}
+
+/// Credential material of the matched credential.
+#[derive(Debug, Clone)]
+pub struct MatchedCredentialInner {
+ encrypted_metadata_bytes: Vec<u8>,
+ key_seed: KeySeed,
+}
+
+impl MatchedCredentialInner {
+ /// Constructs matched credential inner data wrapped in an atomic-reference-counter.
+ pub fn new(encrypted_metadata_bytes: &[u8], key_seed: KeySeed) -> Arc<Self> {
+ Arc::new(Self { encrypted_metadata_bytes: Vec::from(encrypted_metadata_bytes), key_seed })
+ }
}
impl MatchedCredential {
/// Constructs a new matched credential from the given match-id
/// (some arbitrary `i64` identifier) and encrypted metadata bytes,
/// copied from the given slice.
- pub fn new(cred_id: i64, encrypted_metadata_bytes: &[u8]) -> Self {
- Self::from_arc_bytes(cred_id, encrypted_metadata_bytes.to_vec().into())
+ pub fn new(cred_id: i64, encrypted_metadata_bytes: &[u8], key_seed: KeySeed) -> Self {
+ Self::from_arc_bytes(
+ cred_id,
+ MatchedCredentialInner::new(encrypted_metadata_bytes, key_seed),
+ )
}
/// Constructs a new matched credential from the given match-id
/// (some arbitrary `u32` identifier) and encrypted metadata bytes.
- pub fn from_arc_bytes(cred_id: i64, encrypted_metadata_bytes: Arc<[u8]>) -> Self {
- Self { cred_id, encrypted_metadata_bytes }
+ pub fn from_arc_bytes(cred_id: i64, inner: Arc<MatchedCredentialInner>) -> Self {
+ Self { cred_id, inner }
}
/// Gets the pre-specified numerical identifier for this matched-credential.
pub(crate) fn id(&self) -> i64 {
self.cred_id
}
+
+ /// Gets the key seed of the matched discovery credential.
+ pub(crate) fn key_seed(&self) -> [u8; 32] {
+ self.inner.key_seed.into_bytes()
+ }
}
impl PartialEq<MatchedCredential> for MatchedCredential {
@@ -134,7 +183,7 @@
type EncryptedMetadata = Arc<[u8]>;
type EncryptedMetadataFetchError = core::convert::Infallible;
fn fetch_encrypted_metadata(&self) -> Result<Arc<[u8]>, core::convert::Infallible> {
- Ok(self.encrypted_metadata_bytes.clone())
+ Ok(Arc::from(self.inner.encrypted_metadata_bytes.clone()))
}
}
@@ -239,10 +288,9 @@
}
declare_handle_map!(
- credential_slab,
crate::common::default_handle_map_dimensions(),
- super::CredentialSlab,
- super::CredentialSlabInternals
+ CredentialSlab,
+ CredentialSlabInternals
);
impl CredentialSlab {
@@ -253,6 +301,9 @@
discovery_credential: V0DiscoveryCredential,
match_data: MatchedCredential,
) -> AddV0CredentialToSlabResult {
+ if cfg!(feature = "nocrypto") {
+ return AddV0CredentialToSlabResult::InvalidHandle;
+ }
match self.get_mut() {
Ok(mut write_guard) => {
write_guard.add_v0(discovery_credential, match_data);
@@ -268,6 +319,9 @@
discovery_credential: V1DiscoveryCredential,
match_data: MatchedCredential,
) -> AddV1CredentialToSlabResult {
+ if cfg!(feature = "nocrypto") {
+ return AddV1CredentialToSlabResult::InvalidHandle;
+ }
match self.get_mut() {
Ok(mut write_guard) => match write_guard.add_v1(discovery_credential, match_data) {
Ok(_) => AddV1CredentialToSlabResult::Success,
@@ -321,10 +375,9 @@
}
declare_handle_map!(
- credential_book,
crate::common::default_handle_map_dimensions(),
- super::CredentialBook,
- super::CredentialBookInternals
+ CredentialBook,
+ CredentialBookInternals
);
/// Discriminant for `CreateCredentialBookResult`
@@ -409,6 +462,7 @@
/// Cryptographic information about a particular V0 broadcast credential
/// necessary to LDT-encrypt V0 advertisements.
+#[cfg_attr(any(test, feature = "testing"), derive(Clone, Debug, arbitrary::Arbitrary))]
#[repr(C)]
pub struct V0BroadcastCredential {
key_seed: [u8; 32],
@@ -432,15 +486,28 @@
self.identity_token.into(),
)
}
+
+ /// Encrypt plaintext metadata with the identity token and key seed of the owned broadcast credential.
+ ///
+ /// Note: metadata encryption should be infallible.
+ pub fn encrypt_metadata(self, plaintext_metadata: &[u8]) -> Vec<u8> {
+ let identity_token = self.identity_token.into();
+ let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&self.key_seed);
+ metadata::encrypt_metadata::<CryptoProviderImpl, V0>(
+ &hkdf,
+ identity_token,
+ plaintext_metadata,
+ )
+ }
}
/// Cryptographic information about a particular V1 broadcast credential
-/// necessary to encrypt V1 MIC-verified and signature-verified sections.
+/// necessary to encrypt V1 sections.
+#[cfg_attr(any(test, feature = "testing"), derive(Clone, Debug, arbitrary::Arbitrary))]
#[repr(C)]
pub struct V1BroadcastCredential {
key_seed: [u8; 32],
identity_token: [u8; 16],
- private_key: [u8; 32],
}
impl V1BroadcastCredential {
@@ -453,19 +520,26 @@
/// sensitive cryptographic info) over FFI, foreign-lang
/// code around how this information is maintained
/// deserves close scrutiny.
- pub const fn new(
- key_seed: [u8; 32],
- identity_token: extended::V1IdentityToken,
- private_key: [u8; 32],
- ) -> Self {
- Self { key_seed, identity_token: identity_token.into_bytes(), private_key }
+ pub const fn new(key_seed: [u8; 32], identity_token: extended::V1IdentityToken) -> Self {
+ Self { key_seed, identity_token: identity_token.into_bytes() }
}
pub(crate) fn into_internal(self) -> np_adv::credential::v1::V1BroadcastCredential {
- let permit = crypto_provider::ed25519::RawPrivateKeyPermit::default();
np_adv::credential::v1::V1BroadcastCredential::new(
self.key_seed,
self.identity_token.into(),
- crypto_provider::ed25519::PrivateKey::from_raw_private_key(self.private_key, &permit),
+ )
+ }
+
+ /// Encrypt plaintext metadata with the identity token and key seed of the owned broadcast credential.
+ ///
+ /// Note: metadata encryption should be infallible.
+ pub fn encrypt_metadata(self, plaintext_metadata: &[u8]) -> Vec<u8> {
+ let identity_token = self.identity_token.into();
+ let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&self.key_seed);
+ metadata::encrypt_metadata::<CryptoProviderImpl, V1>(
+ &hkdf,
+ identity_token,
+ plaintext_metadata,
)
}
}
diff --git a/nearby/presence/np_ffi_core/src/deserialize.rs b/nearby/presence/np_ffi_core/src/deserialize.rs
index b5e9c91..92de60a 100644
--- a/nearby/presence/np_ffi_core/src/deserialize.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize.rs
@@ -18,7 +18,7 @@
use crate::deserialize::v0::*;
use crate::deserialize::v1::*;
use crate::utils::FfiEnum;
-use crypto_provider_default::CryptoProviderImpl;
+use crate::CryptoProviderImpl;
use handle_map::{declare_handle_map, HandleLike, HandleMapFullError, HandleNotPresentError};
use np_adv::deserialization_arena;
@@ -216,10 +216,9 @@
}
declare_handle_map!(
- decrypted_metadata,
crate::common::default_handle_map_dimensions(),
- super::DecryptedMetadata,
- super::DecryptedMetadataInternals
+ DecryptedMetadata,
+ DecryptedMetadataInternals
);
/// The pointer and length of the decrypted metadata byte buffer
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v0.rs b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
index 67d798d..5209f1a 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v0.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
@@ -21,7 +21,7 @@
};
use crate::utils::{FfiEnum, LocksLongerThan};
use crate::v0::V0DataElement;
-use crypto_provider_default::CryptoProviderImpl;
+use crate::CryptoProviderImpl;
use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
use np_adv::credential::matched::HasIdentityMatch;
use np_adv::legacy;
@@ -189,6 +189,8 @@
identity_token: [u8; 14],
/// The 2-byte advertisement salt
salt: [u8; 2],
+ /// The 32-byte key seed.
+ key_seed: [u8; 32],
}
impl DeserializedV0IdentityDetails {
@@ -196,9 +198,10 @@
cred_id: i64,
salt: ldt_np_adv::V0Salt,
identity_token: ldt_np_adv::V0IdentityToken,
+ key_seed: [u8; 32],
) -> Self {
let salt = salt.bytes();
- Self { cred_id, salt, identity_token: identity_token.bytes() }
+ Self { cred_id, salt, identity_token: identity_token.bytes(), key_seed }
}
/// Returns the ID of the credential which matched the deserialized adv
pub fn cred_id(&self) -> i64 {
@@ -212,6 +215,11 @@
pub fn salt(&self) -> [u8; 2] {
self.salt
}
+
+ /// Returns the 32-byte key seed used to decrypt the advertisement.
+ pub fn key_seed(&self) -> [u8; 32] {
+ self.key_seed
+ }
}
/// Discriminant for `GetV0IdentityDetailsResult`
@@ -278,7 +286,8 @@
) -> Self {
let cred_id = match_data.matched_credential().id();
let identity_token = match_data.contents();
- let details = DeserializedV0IdentityDetails::new(cred_id, salt, *identity_token);
+ let key_seed = match_data.matched_credential().key_seed();
+ let details = DeserializedV0IdentityDetails::new(cred_id, salt, *identity_token, key_seed);
Self { details, match_data }
}
/// Gets the directly-transmissible details about
@@ -337,12 +346,7 @@
handle_id: u64,
}
-declare_handle_map!(
- v0_payload,
- crate::common::default_handle_map_dimensions(),
- super::V0Payload,
- super::V0PayloadInternals
-);
+declare_handle_map!(crate::common::default_handle_map_dimensions(), V0Payload, V0PayloadInternals);
use super::DeserializeAdvertisementError;
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v1.rs b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
index 849abab..dec454c 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v1.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
@@ -19,11 +19,11 @@
credentials::{CredentialBook, MatchedCredential},
deserialize::{allocate_decrypted_metadata_handle, DecryptMetadataResult},
utils::*,
- v1::V1VerificationMode,
+ v1::*,
+ CryptoProviderImpl,
};
use array_view::ArrayView;
use crypto_provider::CryptoProvider;
-use crypto_provider_default::CryptoProviderImpl;
use handle_map::{declare_handle_map, HandleLike};
use np_adv::{
credential::matched::WithMatchedCredential,
@@ -164,10 +164,9 @@
}
declare_handle_map!(
- legible_v1_sections,
crate::common::default_handle_map_dimensions(),
- super::LegibleV1Sections,
- super::LegibleV1SectionsInternals
+ LegibleV1Sections,
+ LegibleV1SectionsInternals
);
impl LocksLongerThan<LegibleV1Sections> for CredentialBook {}
@@ -197,7 +196,7 @@
}
}
- /// Get a data element by section index and de index. Similar to `get_section().get_de()` but
+ /// Get a data element by section index and de index. Similar to `get_section().get_generic_de()` but
/// will only lock the HandleMap once. This function uses this handle but does not take
/// ownership of it.
pub fn get_section_de(&self, legible_section_index: u8, de_index: u8) -> GetV1DEResult {
@@ -207,7 +206,7 @@
let Some(section) = sections.get_section_internals(legible_section_index) else {
return GetV1DEResult::Error;
};
- section.get_de(de_index)
+ section.get_generic_de(de_index)
}
/// Gets identity details for the legible section at the given index. Similar to
@@ -285,12 +284,12 @@
#[repr(u8)]
pub enum GetV1DE16ByteSaltResultKind {
/// The attempt to get the derived salt failed, possibly
- /// because the passed DE offset was invalid (==255),
+ /// because the passed DE type code was forbidden (0xFFFFFFFF),
/// or because there was no salt included for the
/// referenced advertisement section (i.e: it was
/// a public advertisement section, or it was deallocated.)
Error = 0,
- /// A 16-byte salt for the given DE offset was successfully
+ /// A 16-byte salt for the given DE type-code was successfully
/// derived.
Success = 1,
}
@@ -320,7 +319,7 @@
/// The internal FFI-friendly representation of a deserialized v1 section
pub struct DeserializedV1SectionInternals {
- des: Vec<V1DataElement>,
+ des: Vec<GenericV1DataElement>,
identity: Option<DeserializedV1IdentityInternals>,
}
@@ -340,7 +339,7 @@
}
/// Attempts to get the DE with the given index in this section.
- fn get_de(&self, index: u8) -> GetV1DEResult {
+ fn get_generic_de(&self, index: u8) -> GetV1DEResult {
match self.des.get(index as usize) {
Some(de) => GetV1DEResult::Success(de.clone()),
None => GetV1DEResult::Error,
@@ -370,17 +369,17 @@
}
/// Attempts to derive a 16-byte DE salt for a DE in this section
- /// with the given DE offset. This operation may fail if the
- /// passed offset is 255 (causes overflow) or if the section
+ /// with the given DE type-code. This operation may fail if the
+ /// passed type-code is the forbidden code 0xFFFFFFFF or if the section
/// is leveraging a public identity, and hence, doesn't have
/// an associated salt.
- pub(crate) fn derive_16_byte_salt_for_offset<C: CryptoProvider>(
+ pub(crate) fn derive_16_byte_salt_for_de_type<C: CryptoProvider>(
&self,
- de_offset: u8,
+ de_type: V1DEType,
) -> GetV1DE16ByteSaltResult {
self.identity
.as_ref()
- .and_then(|x| x.derive_16_byte_salt_for_offset::<C>(de_offset))
+ .and_then(|x| x.derive_16_byte_salt_for_de_type::<C>(de_type))
.map_or(GetV1DE16ByteSaltResult::Error, GetV1DE16ByteSaltResult::Success)
}
}
@@ -405,7 +404,7 @@
V1DeserializedSection::Plaintext(section) => {
let des = section
.iter_data_elements()
- .map(|r| r.map(|de| V1DataElement::from(&de)))
+ .map(|r| r.map(GenericV1DataElement::from))
.collect::<Result<Vec<_>, _>>()?;
let identity = None;
Ok(Self { des, identity })
@@ -414,7 +413,7 @@
let section = with_matched.contents();
let des = section
.iter_data_elements()
- .map(|r| r.map(|de| V1DataElement::from(&de)))
+ .map(|r| r.map(GenericV1DataElement::from))
.collect::<Result<Vec<_>, _>>()?;
let verification_mode = section.verification_mode();
@@ -463,8 +462,13 @@
) -> Self {
let cred_id = match_data.matched_credential().id();
let identity_token = match_data.contents();
- let details =
- DeserializedV1IdentityDetails::new(cred_id, verification_mode, *identity_token);
+ let key_seed = match_data.matched_credential().key_seed();
+ let details = DeserializedV1IdentityDetails::new(
+ cred_id,
+ verification_mode,
+ *identity_token,
+ key_seed,
+ );
Self { details, match_data, salt }
}
/// Gets the directly-transmissible details about
@@ -478,18 +482,18 @@
pub(crate) fn decrypt_metadata(&self) -> Option<Vec<u8>> {
self.match_data.decrypt_metadata::<CryptoProviderImpl>().ok()
}
- /// For a given data-element offset, derives a 16-byte DE salt
+ /// For a given DE type-code, derives a 16-byte DE salt
/// for a DE in that position within this section.
- pub(crate) fn derive_16_byte_salt_for_offset<C: CryptoProvider>(
+ pub(crate) fn derive_16_byte_salt_for_de_type<C: CryptoProvider>(
&self,
- de_offset: u8,
+ de_type: V1DEType,
) -> Option<FixedSizeArray<16>> {
- let de_offset = np_hkdf::v1_salt::DataElementOffset::from(de_offset);
+ let de_type = np_hkdf::v1_salt::DeType::try_from(de_type).ok()?;
match self.salt {
MultiSalt::Short(_) => None,
MultiSalt::Extended(s) => {
- s.derive::<16, C>(Some(de_offset)).map(FixedSizeArray::from_array)
+ s.derive::<16, C>(Some(de_type).into()).map(FixedSizeArray::from)
}
}
}
@@ -548,6 +552,8 @@
cred_id: i64,
/// The 16-byte metadata key.
identity_token: [u8; 16],
+ /// The 32-byte key seed.
+ key_seed: [u8; 32],
}
impl DeserializedV1IdentityDetails {
@@ -555,9 +561,10 @@
cred_id: i64,
verification_mode: np_adv::extended::deserialize::VerificationMode,
identity_token: np_adv::extended::V1IdentityToken,
+ key_seed: [u8; 32],
) -> Self {
let verification_mode = verification_mode.into();
- Self { cred_id, verification_mode, identity_token: identity_token.into_bytes() }
+ Self { cred_id, verification_mode, identity_token: identity_token.into_bytes(), key_seed }
}
/// Returns the ID of the credential which matched the deserialized section.
pub fn cred_id(&self) -> i64 {
@@ -571,6 +578,11 @@
pub fn identity_token(&self) -> [u8; 16] {
self.identity_token
}
+
+ /// Returns the 32-byte key seed used to decrypt the advertisement section.
+ pub fn key_seed(&self) -> [u8; 32] {
+ self.key_seed
+ }
}
/// Handle to a deserialized V1 section
@@ -584,7 +596,7 @@
impl DeserializedV1Section {
/// Gets the number of data elements contained in this section.
- /// Suitable as an iteration bound on `Self::get_de`.
+ /// Suitable as an iteration bound on `Self::get_generic_de`.
pub fn num_des(&self) -> u8 {
self.num_des
}
@@ -595,9 +607,9 @@
}
/// Gets the DE with the given index in this section.
- pub fn get_de(&self, de_index: u8) -> GetV1DEResult {
+ pub fn get_generic_de(&self, de_index: u8) -> GetV1DEResult {
self.apply_to_section_internals(
- move |section_ref| section_ref.get_de(de_index),
+ move |section_ref| section_ref.get_generic_de(de_index),
GetV1DEResult::Error,
)
}
@@ -618,13 +630,13 @@
DecryptMetadataResult::Error,
)
}
- /// Attempts to derive a 16-byte DE salt for a DE in this section with the given DE offset.
- /// This operation may fail if the passed offset is 255 (causes overflow) or if the section is
+ /// Attempts to derive a 16-byte DE salt for a DE in this section with the given DE type-code.
+ /// This operation may fail if the passed type-code is forbidden (0xFFFFFFFF) or if the section is
/// leveraging a public identity, and hence, doesn't have an associated salt.
- pub fn derive_16_byte_salt_for_offset(&self, de_offset: u8) -> GetV1DE16ByteSaltResult {
+ pub fn derive_16_byte_salt_for_de_type(&self, de_type: V1DEType) -> GetV1DE16ByteSaltResult {
self.apply_to_section_internals(
move |section_ref| {
- section_ref.derive_16_byte_salt_for_offset::<CryptoProviderImpl>(de_offset)
+ section_ref.derive_16_byte_salt_for_de_type::<CryptoProviderImpl>(de_type)
},
GetV1DE16ByteSaltResult::Error,
)
@@ -662,12 +674,12 @@
Success = 1,
}
-/// Represents the result of the `DeserializedV1Section#get_de` operation.
+/// Represents the result of the `DeserializedV1Section#get_generic_de` operation.
#[repr(C)]
#[allow(missing_docs)]
pub enum GetV1DEResult {
Error,
- Success(V1DataElement),
+ Success(GenericV1DataElement),
}
impl FfiEnum for GetV1DEResult {
@@ -681,92 +693,354 @@
}
impl GetV1DEResult {
- declare_enum_cast! {into_success, Success, V1DataElement}
+ declare_enum_cast! {into_success, Success, GenericV1DataElement}
}
-/// FFI-transmissible representation of a V1 data-element
-#[derive(Clone)]
-#[repr(C)]
-pub enum V1DataElement {
- /// A "generic" V1 data-element, for which we have no
- /// particular information about its schema (just
- /// a DE type code and a byte payload.)
- Generic(GenericV1DataElement),
-}
-
-impl V1DataElement {
- // Note: not using declare_enum_cast! for this one, because if V1DataElement
- // gets more variants, this will have a different internal implementation
- /// Converts a `V1DataElement` to a `GenericV1DataElement` which
- /// only maintains information about the DE's type-code and payload.
- pub fn to_generic(self) -> GenericV1DataElement {
- match self {
- V1DataElement::Generic(x) => x,
- }
- }
-}
-
-impl<'a> From<&'a np_adv::extended::deserialize::data_element::DataElement<'a>> for V1DataElement {
- fn from(de: &'a np_adv::extended::deserialize::data_element::DataElement<'a>) -> Self {
- let offset = de.offset().as_u8();
+impl<'a> From<np_adv::extended::deserialize::data_element::DataElement<'a>>
+ for GenericV1DataElement
+{
+ fn from(de: np_adv::extended::deserialize::data_element::DataElement<'a>) -> Self {
let de_type = V1DEType::from(de.de_type());
let contents_as_slice = de.contents();
//Guaranteed not to panic due DE size limit.
#[allow(clippy::unwrap_used)]
let array_view: ArrayView<u8, 127> = ArrayView::try_from_slice(contents_as_slice).unwrap();
let payload = ByteBuffer::from_array_view(array_view);
- Self::Generic(GenericV1DataElement { de_type, offset, payload })
+ Self { de_type, payload }
}
}
-/// FFI-transmissible representation of a generic V1 data-element.
-/// This representation is stable, and so you may directly
-/// reference this struct's fields if you wish.
+/// Discriminant for [`V1DataElementDeserializationResult`].
+#[derive(Clone, Copy)]
+#[repr(u8)]
+pub enum V1DataElementDeserializationResultKind {
+ /// Deserialization was successful, in that the data element
+ /// was either recognized as a well-established DE, or that
+ /// the type code of the data element wasn't recognized, and
+ /// so the data element was passed along as a "generic" DE.
+ Success = 0,
+ /// There was a Cast ID DE, but its contents
+ /// were the wrong length for such an ID.
+ CastIdWrongLength = 1,
+ /// There was an Actions DE, but its contents
+ /// could not be interpreted.
+ ActionsMalformed = 2,
+ /// There was a context sync sequence number DE,
+ /// but the payload wasn't exactly 1 byte.
+ ContextSyncSeqNumWrongLength = 3,
+ /// There was a context sync sequence number DE,
+ /// but the sequence number was not in 0-15.
+ ContextSyncSeqNumOutOfBounds = 4,
+ /// There was a deduplication hint DE, but
+ /// the payload was not exactly 8 bytes.
+ DedupHintWrongLength = 5,
+ /// There was a device type DE, but
+ /// the payload was not exactly 1 byte.
+ DeviceTypeWrongLength = 6,
+ /// There was a Tx Power DE, but
+ /// the value was not in the
+ /// accepted range of -20dBm-100dBm.
+ TxPowerOutOfBounds = 7,
+ /// There was a Tx Power DE, but the
+ /// payload did not occupy exactly 1 byte.
+ TxPowerWrongLength = 8,
+ /// There was a Connectivity Info DE, but
+ /// the ordering, uniqueness, or non-emptiness
+ /// of the components was violated.
+ ConnectivityInfoMalformed = 9,
+ /// There was a Connectivity Info DE, but
+ /// one of the components was malformed.
+ ConnectivityInfoMalformedComponent = 10,
+ /// There was a Media Deduplication ID DE, but its contents
+ /// were the wrong length for such an ID.
+ MediaDeduplicationIdWrongLength = 11,
+ /// There was a Capabilities DE, but the payload was malformed.
+ CapabilitiesMalformed = 12,
+ /// There was a Requirements DE, but the payload was malformed.
+ RequirementsMalformed = 13,
+}
+
+/// Representation of the result of attempting to deserialize
+/// a V1 data element from a generic DE to a more structured
+/// deserialized representation.
+///
+/// Only the general class of error is appropriately
+/// represented by this enum. If you want more descriptive
+/// errors, consider doing your own custom DE deserialization
+/// using the plugin architecture and/or resort to more directly
+/// leveraging the Rust crate `np_adv`.
#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
#[repr(C)]
-pub struct GenericV1DataElement {
- /// The offset of this generic data-element.
- pub offset: u8,
- /// The DE type code of this generic data-element.
- pub de_type: V1DEType,
- /// The raw data-element byte payload, up to
- /// 127 bytes in length.
- pub payload: ByteBuffer<127>,
+#[allow(missing_docs, clippy::large_enum_variant)]
+pub enum V1DataElementDeserializationResult {
+ Success(V1DataElement),
+ CastIdWrongLength,
+ ActionsMalformed,
+ ContextSyncSeqNumWrongLength,
+ ContextSyncSeqNumOutOfBounds,
+ DedupHintWrongLength,
+ DeviceTypeWrongLength,
+ TxPowerOutOfBounds,
+ TxPowerWrongLength,
+ ConnectivityInfoMalformed,
+ /// There was a Connectivity Info DE, but the component
+ /// of the specified index within the DE was malformed.
+ ConnectivityInfoMalformedComponent(u8),
+ MediaDeduplicationIdWrongLength,
+ CapabilitiesMalformed,
+ RequirementsMalformed,
+}
+
+impl FfiEnum for V1DataElementDeserializationResult {
+ type Kind = V1DataElementDeserializationResultKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::Success(_) => V1DataElementDeserializationResultKind::Success,
+ Self::CastIdWrongLength => V1DataElementDeserializationResultKind::CastIdWrongLength,
+ Self::ActionsMalformed => V1DataElementDeserializationResultKind::ActionsMalformed,
+ Self::ContextSyncSeqNumWrongLength => {
+ V1DataElementDeserializationResultKind::ContextSyncSeqNumWrongLength
+ }
+ Self::ContextSyncSeqNumOutOfBounds => {
+ V1DataElementDeserializationResultKind::ContextSyncSeqNumOutOfBounds
+ }
+ Self::DedupHintWrongLength => {
+ V1DataElementDeserializationResultKind::DedupHintWrongLength
+ }
+ Self::DeviceTypeWrongLength => {
+ V1DataElementDeserializationResultKind::DeviceTypeWrongLength
+ }
+ Self::TxPowerOutOfBounds => V1DataElementDeserializationResultKind::TxPowerOutOfBounds,
+ Self::TxPowerWrongLength => V1DataElementDeserializationResultKind::TxPowerWrongLength,
+ Self::ConnectivityInfoMalformed => {
+ V1DataElementDeserializationResultKind::ConnectivityInfoMalformed
+ }
+ Self::ConnectivityInfoMalformedComponent(_) => {
+ V1DataElementDeserializationResultKind::ConnectivityInfoMalformedComponent
+ }
+ Self::MediaDeduplicationIdWrongLength => {
+ V1DataElementDeserializationResultKind::MediaDeduplicationIdWrongLength
+ }
+ Self::CapabilitiesMalformed => {
+ V1DataElementDeserializationResultKind::CapabilitiesMalformed
+ }
+ Self::RequirementsMalformed => {
+ V1DataElementDeserializationResultKind::RequirementsMalformed
+ }
+ }
+ }
+}
+
+impl V1DataElementDeserializationResult {
+ declare_enum_cast! {into_success, Success, V1DataElement}
+ declare_enum_cast! {into_malformed_component_index, ConnectivityInfoMalformedComponent, u8}
+}
+
+impl<'a> TryFrom<&'a GenericV1DataElement>
+ for np_adv::extended::deserialize::data_element::ProtoDataElement<'a>
+{
+ type Error = InvalidStackDataStructure;
+ fn try_from(de: &'a GenericV1DataElement) -> Result<Self, Self::Error> {
+ let contents = de.payload.as_slice()?;
+ let de_type = de.de_type.try_into().map_err(|_| InvalidStackDataStructure)?;
+ Ok(Self { contents, de_type })
+ }
}
impl GenericV1DataElement {
- /// Gets the offset for this generic V1 data element.
- pub fn offset(&self) -> u8 {
- self.offset
- }
- /// Gets the DE-type of this generic V1 data element.
- pub fn de_type(&self) -> V1DEType {
- self.de_type
- }
- /// Destructures this `GenericV1DataElement` into just the DE payload byte-buffer.
- pub fn into_payload(self) -> ByteBuffer<127> {
- self.payload
+ /// Attempts to deserialize the contents of this generic V1 data element
+ /// to one of the predefined DE definitions of [`V1DataElement`], returning
+ /// an error if the format of the DE payload didn't match what was expected
+ /// for the type, and passing along the DE as a generic DE if the type-code
+ /// did not match one of the predefined DEs.
+ pub fn try_deserialize(
+ self,
+ ) -> Result<V1DataElementDeserializationResult, InvalidStackDataStructure> {
+ use np_adv::extended::data_elements::DeserializedGoogleDE;
+ use np_adv::extended::deserialize::data_element::DataElementDeserializationResult;
+
+ let proto_de = (&self).try_into()?;
+ // NOTE: Keeping the salt as [`None`] only works because our predefined
+ // data elements all have no dependence on derived DE salts for deserialization.
+ let de =
+ np_adv::extended::deserialize::data_element::DataElement::from_proto_de(proto_de, None);
+ Ok(match DeserializedGoogleDE::try_deserialize(de) {
+ Ok(DeserializedGoogleDE::CastId(Ok(cast_id))) => {
+ V1DataElementDeserializationResult::Success(V1DataElement::CastId(cast_id.into()))
+ }
+ Ok(DeserializedGoogleDE::CastId(Err(
+ np_adv::extended::data_elements::CastIdDeserializationError::WrongLength,
+ ))) => V1DataElementDeserializationResult::CastIdWrongLength,
+ Ok(DeserializedGoogleDE::Actions(Ok(actions))) => match actions.action_ids_bitmap() {
+ Ok(bitmap) => V1DataElementDeserializationResult::Success(V1DataElement::Actions(
+ V1ActionsBitmap { bitmap },
+ )),
+ Err(_) => V1DataElementDeserializationResult::ActionsMalformed,
+ },
+ Ok(DeserializedGoogleDE::Actions(Err(_))) => {
+ V1DataElementDeserializationResult::ActionsMalformed
+ }
+ Ok(DeserializedGoogleDE::TxPower(Ok(tx_power))) => {
+ V1DataElementDeserializationResult::Success(V1DataElement::TxPower(tx_power.into()))
+ }
+ Ok(DeserializedGoogleDE::TxPower(Err(
+ np_adv::extended::data_elements::TxPowerMalformed::OutOfBounds,
+ ))) => V1DataElementDeserializationResult::TxPowerOutOfBounds,
+ Ok(DeserializedGoogleDE::TxPower(Err(
+ np_adv::extended::data_elements::TxPowerMalformed::WrongLength,
+ ))) => V1DataElementDeserializationResult::TxPowerWrongLength,
+ Ok(DeserializedGoogleDE::ContextSyncSeqNum(Ok(context_sync_seq_num))) => {
+ V1DataElementDeserializationResult::Success(V1DataElement::ContextSyncSeqNum(
+ context_sync_seq_num.into(),
+ ))
+ }
+ Ok(DeserializedGoogleDE::ContextSyncSeqNum(Err(
+ np_adv::extended::data_elements::ContextSyncSeqNumMalformed::OutOfBounds,
+ ))) => V1DataElementDeserializationResult::ContextSyncSeqNumOutOfBounds,
+ Ok(DeserializedGoogleDE::ContextSyncSeqNum(Err(
+ np_adv::extended::data_elements::ContextSyncSeqNumMalformed::WrongLength,
+ ))) => V1DataElementDeserializationResult::ContextSyncSeqNumWrongLength,
+ Ok(DeserializedGoogleDE::DedupHint(Ok(dedup_hint))) => {
+ V1DataElementDeserializationResult::Success(V1DataElement::DedupHint(
+ dedup_hint.into(),
+ ))
+ }
+ Ok(DeserializedGoogleDE::DedupHint(Err(
+ np_adv::extended::data_elements::DeduplicationHintMalformed::WrongLength,
+ ))) => V1DataElementDeserializationResult::DedupHintWrongLength,
+ Ok(DeserializedGoogleDE::DeviceType(Ok(device_type))) => {
+ V1DataElementDeserializationResult::Success(V1DataElement::DeviceType(
+ device_type.into(),
+ ))
+ }
+ Ok(DeserializedGoogleDE::DeviceType(Err(
+ np_adv::extended::data_elements::DeviceTypeDeserializationError::WrongLength,
+ ))) => V1DataElementDeserializationResult::DeviceTypeWrongLength,
+ Ok(DeserializedGoogleDE::ConnectivityInfo(Ok(connectivity_info))) => {
+ V1DataElementDeserializationResult::Success(V1DataElement::ConnectivityInfo(
+ connectivity_info.into(),
+ ))
+ },
+ Ok(DeserializedGoogleDE::ConnectivityInfo(Err(
+ np_adv::extended::data_elements::ConnectivityInfoDeserializationError::ComponentParseFailure(component_index)
+ ))) => V1DataElementDeserializationResult::ConnectivityInfoMalformedComponent(component_index),
+ Ok(DeserializedGoogleDE::ConnectivityInfo(Err(_)))
+ => V1DataElementDeserializationResult::ConnectivityInfoMalformed,
+ Ok(DeserializedGoogleDE::MediaDeduplicationId(Ok(media_deduplication_id))) => {
+ V1DataElementDeserializationResult::Success(V1DataElement::MediaDeduplicationId(media_deduplication_id.into()))
+ }
+ Ok(DeserializedGoogleDE::MediaDeduplicationId(Err(
+ np_adv::extended::data_elements::MediaDeduplicationIdDeserializationError::WrongLength,
+ ))) => V1DataElementDeserializationResult::MediaDeduplicationIdWrongLength,
+ Ok(DeserializedGoogleDE::Capabilities(Ok(capabilities))) => {
+ V1DataElementDeserializationResult::Success(V1DataElement::Capabilities(
+ capabilities.into(),
+ ))
+ }
+ Ok(DeserializedGoogleDE::Capabilities(Err(_))) => V1DataElementDeserializationResult::CapabilitiesMalformed,
+ Ok(DeserializedGoogleDE::Requirements(Ok(capabilities))) => {
+ V1DataElementDeserializationResult::Success(V1DataElement::Requirements(
+ capabilities.into(),
+ ))
+ }
+ Ok(DeserializedGoogleDE::Requirements(Err(_))) => V1DataElementDeserializationResult::RequirementsMalformed,
+ Err(_) => V1DataElementDeserializationResult::Success(V1DataElement::Generic(self)),
+ })
}
}
-/// Representation of the data-element type tag
-/// of a V1 data element.
-#[derive(Clone, Copy)]
-#[repr(C)]
-pub struct V1DEType {
- code: u32,
-}
-
-impl From<np_adv::extended::de_type::DeType> for V1DEType {
- fn from(de_type: np_adv::extended::de_type::DeType) -> Self {
- let code = de_type.as_u32();
- Self { code }
+impl<'a> From<np_adv::extended::data_elements::CastIdDataElement<'a>> for CastId {
+ fn from(de: np_adv::extended::data_elements::CastIdDataElement<'a>) -> Self {
+ Self { bytes: *de.get_id_as_bytes() }
}
}
-impl V1DEType {
- /// Yields this V1 DE type code as a u32.
- pub fn to_u32(&self) -> u32 {
- self.code
+impl<'a> From<np_adv::extended::data_elements::MediaDeduplicationIdDataElement<'a>>
+ for MediaDeduplicationId
+{
+ fn from(de: np_adv::extended::data_elements::MediaDeduplicationIdDataElement<'a>) -> Self {
+ Self { bytes: *de.get_id_as_bytes() }
+ }
+}
+
+impl From<np_adv::extended::data_elements::TxPowerDataElement> for TxPower {
+ fn from(de: np_adv::extended::data_elements::TxPowerDataElement) -> Self {
+ let tx_power: np_adv::shared_data::TxPower = de.into();
+ tx_power.into()
+ }
+}
+
+impl From<np_adv::extended::data_elements::ContextSyncSeqNumDataElement> for ContextSyncSeqNum {
+ fn from(de: np_adv::extended::data_elements::ContextSyncSeqNumDataElement) -> Self {
+ let num: np_adv::shared_data::ContextSyncSeqNum = de.into();
+ num.into()
+ }
+}
+
+impl From<np_adv::extended::data_elements::DeduplicationHintDataElement> for DedupHint {
+ fn from(de: np_adv::extended::data_elements::DeduplicationHintDataElement) -> Self {
+ Self { bytes: de.as_bytes() }
+ }
+}
+
+impl From<np_adv::extended::data_elements::DeviceTypeDataElement> for DeviceType {
+ fn from(de: np_adv::extended::data_elements::DeviceTypeDataElement) -> Self {
+ let device_type = de.0;
+ Self { device_type }
+ }
+}
+
+impl From<np_adv::extended::data_elements::ConnectivityInfoDataElement> for ConnectivityInfo {
+ fn from(de: np_adv::extended::data_elements::ConnectivityInfoDataElement) -> Self {
+ let ble_info = de.ble_info.into();
+ let wifi_lan_info = de.wifi_lan_info.into();
+ Self { ble_info, wifi_lan_info }
+ }
+}
+
+impl From<np_adv::extended::data_elements::BleConnectivityInfo> for BleConnectivityInfo {
+ fn from(info: np_adv::extended::data_elements::BleConnectivityInfo) -> Self {
+ let mac_address = info.mac_address.into();
+ let gatt_service_identifier = match info.gatt_service_identifier {
+ None => ByteBuffer::default(),
+ Some(x) => ByteBuffer::from_array_vec(x),
+ };
+ let psm = info.psm.into();
+ let device_token = info.device_token.into();
+ Self { mac_address, gatt_service_identifier, psm, device_token }
+ }
+}
+
+impl From<np_adv::extended::data_elements::WifiLanConnectivityInfo> for WifiLanConnectivityInfo {
+ fn from(info: np_adv::extended::data_elements::WifiLanConnectivityInfo) -> Self {
+ let ip = info.ip.into();
+ let port = info.port.map(|x| x.to_be_bytes()).into();
+ let bssid = info.bssid.into();
+ Self { ip, port, bssid }
+ }
+}
+
+impl From<Option<core::net::IpAddr>> for OptionalIpAddress {
+ fn from(maybe_addr: Option<core::net::IpAddr>) -> Self {
+ match maybe_addr {
+ None => Self::NotPresent,
+ Some(core::net::IpAddr::V4(x)) => Self::IPv4(x.octets().into()),
+ Some(core::net::IpAddr::V6(x)) => Self::IPv6(x.octets().into()),
+ }
+ }
+}
+
+impl From<np_adv::extended::data_elements::CapabilitiesDataElement> for Capabilities {
+ fn from(de: np_adv::extended::data_elements::CapabilitiesDataElement) -> Self {
+ let capabilities = de.into();
+ Self { capabilities }
+ }
+}
+
+impl From<np_adv::extended::data_elements::RequirementsDataElement> for Requirements {
+ fn from(de: np_adv::extended::data_elements::RequirementsDataElement) -> Self {
+ let requirements = de.into();
+ Self { requirements }
}
}
diff --git a/nearby/presence/np_ffi_core/src/lib.rs b/nearby/presence/np_ffi_core/src/lib.rs
index 0fd7f1a..a035eee 100644
--- a/nearby/presence/np_ffi_core/src/lib.rs
+++ b/nearby/presence/np_ffi_core/src/lib.rs
@@ -22,3 +22,9 @@
pub mod serialize;
pub mod v0;
pub mod v1;
+
+#[cfg(feature = "nocrypto")]
+pub(crate) type CryptoProviderImpl = crypto_provider_stubs::CryptoProviderStubs;
+
+#[cfg(any(feature = "boringssl", feature = "rustcrypto"))]
+pub(crate) type CryptoProviderImpl = crypto_provider_default::CryptoProviderImpl;
diff --git a/nearby/presence/np_ffi_core/src/serialize.rs b/nearby/presence/np_ffi_core/src/serialize.rs
index 0aade0a..57f0095 100644
--- a/nearby/presence/np_ffi_core/src/serialize.rs
+++ b/nearby/presence/np_ffi_core/src/serialize.rs
@@ -15,25 +15,3 @@
pub mod v0;
pub mod v1;
-
-/// Enum common to V0 and V1 serialization expressing
-/// what kind of advertisement builder (public/encrypted)
-/// is in use.
-#[derive(Clone, Copy)]
-#[repr(u8)]
-pub enum AdvertisementBuilderKind {
- /// The builder is for a public advertisement.
- Public = 0,
- /// The builder is for an encrypted advertisement.
- Encrypted = 1,
-}
-
-impl AdvertisementBuilderKind {
- pub(crate) fn as_internal_v1(&self) -> np_adv::extended::serialize::AdvertisementType {
- use np_adv::extended::serialize::AdvertisementType;
- match self {
- Self::Public => AdvertisementType::Plaintext,
- Self::Encrypted => AdvertisementType::Encrypted,
- }
- }
-}
diff --git a/nearby/presence/np_ffi_core/src/serialize/v0.rs b/nearby/presence/np_ffi_core/src/serialize/v0.rs
index e1b8103..e4595bc 100644
--- a/nearby/presence/np_ffi_core/src/serialize/v0.rs
+++ b/nearby/presence/np_ffi_core/src/serialize/v0.rs
@@ -17,10 +17,21 @@
use crate::credentials::V0BroadcastCredential;
use crate::utils::FfiEnum;
use crate::v0::V0DataElement;
-use crypto_provider_default::CryptoProviderImpl;
+use crate::CryptoProviderImpl;
use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
use np_adv_dynamic::legacy::BoxedAdvConstructionError;
+/// Enum for V0 serialization expressing what kind of
+/// advertisement builder (public/encrypted) is in use.
+#[derive(Clone, Copy)]
+#[repr(u8)]
+pub enum AdvertisementBuilderKind {
+ /// The builder is for a public advertisement.
+ Public = 0,
+ /// The builder is for an encrypted advertisement.
+ Encrypted = 1,
+}
+
/// A `#[repr(C)]` handle to a value of type `V0AdvertisementBuilderInternals`
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq)]
@@ -29,10 +40,9 @@
}
declare_handle_map!(
- advertisement_builder,
crate::common::default_handle_map_dimensions(),
- super::V0AdvertisementBuilder,
- super::V0AdvertisementBuilderInternals
+ V0AdvertisementBuilder,
+ V0AdvertisementBuilderInternals
);
impl V0AdvertisementBuilder {
@@ -122,8 +132,11 @@
broadcast_cred: V0BroadcastCredential,
salt: FixedSizeArray<2>,
) -> CreateV0AdvertisementBuilderResult {
+ if cfg!(feature = "nocrypto") {
+ return CreateV0AdvertisementBuilderResult::NoSpaceLeft;
+ }
V0AdvertisementBuilder::allocate(move || {
- V0AdvertisementBuilderInternals::new_ldt(broadcast_cred, salt.into_array())
+ V0AdvertisementBuilderInternals::new_ldt(broadcast_cred, salt.into())
})
.into()
}
@@ -260,3 +273,11 @@
}
}
}
+
+impl TryFrom<TxPower> for np_adv_dynamic::legacy::ToBoxedSerializeDataElement {
+ type Error = InvalidStackDataStructure;
+ fn try_from(value: TxPower) -> Result<Self, InvalidStackDataStructure> {
+ let tx_power: np_adv::shared_data::TxPower = value.try_into()?;
+ Ok(tx_power.into())
+ }
+}
diff --git a/nearby/presence/np_ffi_core/src/serialize/v1.rs b/nearby/presence/np_ffi_core/src/serialize/v1.rs
index 1191ac1..498194e 100644
--- a/nearby/presence/np_ffi_core/src/serialize/v1.rs
+++ b/nearby/presence/np_ffi_core/src/serialize/v1.rs
@@ -15,12 +15,11 @@
use crate::common::*;
use crate::credentials::V1BroadcastCredential;
-use crate::serialize::AdvertisementBuilderKind;
use crate::utils::FfiEnum;
-use crate::v1::V1VerificationMode;
-use crypto_provider_default::CryptoProviderImpl;
+use crate::v1::*;
+use crate::CryptoProviderImpl;
use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
-use np_adv::extended::serialize::AdvertisementType;
+use np_adv::extended::serialize::{ProvidesDEType, WriteDataElement};
#[cfg(feature = "testing")]
use np_adv::extended::salt::MultiSalt;
@@ -33,10 +32,9 @@
}
declare_handle_map!(
- advertisement_builder,
crate::common::default_handle_map_dimensions(),
- super::V1AdvertisementBuilder,
- super::V1AdvertisementBuilderInternals
+ V1AdvertisementBuilder,
+ V1AdvertisementBuilderInternals
);
impl V1AdvertisementBuilder {
@@ -57,7 +55,7 @@
/// The identity details for the new section builder may be specified
/// via providing the broadcast credential data, the kind of encrypted
/// identity being broadcast (private/trusted/provisioned), and the
- /// verification mode (MIC/Signature) to be used for the encrypted section.
+ /// verification mode to be used for the encrypted section.
///
/// This method may fail if there is another currently-active
/// section builder for the same advertisement builder, if the
@@ -69,6 +67,9 @@
broadcast_cred: V1BroadcastCredential,
verification_mode: V1VerificationMode,
) -> CreateV1SectionBuilderResult {
+ if cfg!(feature = "nocrypto") {
+ return CreateV1SectionBuilderResult::NoSpaceLeft;
+ }
self.section_builder_internals(move |internals| {
internals.encrypted_section_builder(broadcast_cred, verification_mode)
})
@@ -185,13 +186,8 @@
/// Creates a new V1 advertisement builder for the given advertisement
/// kind (public/encrypted).
-pub fn create_v1_advertisement_builder(
- kind: AdvertisementBuilderKind,
-) -> CreateV1AdvertisementBuilderResult {
- V1AdvertisementBuilder::allocate(move || {
- V1AdvertisementBuilderInternals::new(kind.as_internal_v1())
- })
- .into()
+pub fn create_v1_advertisement_builder() -> CreateV1AdvertisementBuilderResult {
+ V1AdvertisementBuilder::allocate(V1AdvertisementBuilderInternals::new).into()
}
pub(crate) enum V1AdvertisementBuilderState {
@@ -210,10 +206,6 @@
pub(crate) enum SectionBuilderError {
/// We're currently in the middle of building a section.
UnclosedActiveSection,
- /// We're attempting to build a section with an identity
- /// kind (public/encrypted) which doesn't match the kind
- /// for the entire advertisement.
- IdentityKindMismatch,
/// There isn't enough space for a new section, either
/// because the maximum section count has been exceeded
/// or because the advertisement is almost full, and
@@ -226,10 +218,6 @@
use np_adv::extended::serialize::AddSectionError;
use np_adv_dynamic::extended::BoxedAddSectionError;
match err {
- BoxedAddSectionError::IdentityRequiresSaltError
- | BoxedAddSectionError::Underlying(AddSectionError::IncompatibleSectionType) => {
- SectionBuilderError::IdentityKindMismatch
- }
BoxedAddSectionError::Underlying(AddSectionError::InsufficientAdvSpace)
| BoxedAddSectionError::Underlying(AddSectionError::MaxSectionCountExceeded) => {
SectionBuilderError::NoSpaceLeft
@@ -288,8 +276,8 @@
}
impl V1AdvertisementBuilderInternals {
- pub(crate) fn new(adv_type: AdvertisementType) -> Self {
- let builder = np_adv::extended::serialize::AdvBuilder::new(adv_type);
+ pub(crate) fn new() -> Self {
+ let builder = np_adv::extended::serialize::AdvBuilder::new();
let builder = builder.into();
let state = Some(V1AdvertisementBuilderState::Advertisement(builder));
Self { state }
@@ -305,7 +293,7 @@
Some(V1AdvertisementBuilderState::Advertisement(adv_builder)) => {
match adv_builder.into_section_builder(identity) {
Ok(section_builder) => {
- let section_index = section_builder.section_index();
+ let section_index = section_builder.previously_added_section_count();
self.state = Some(V1AdvertisementBuilderState::Section(section_builder));
Ok(section_index)
}
@@ -345,13 +333,6 @@
>(rng, &internal_broadcast_cred);
np_adv_dynamic::extended::BoxedEncoder::MicEncrypted(encoder)
}
- V1VerificationMode::Signature => {
- let encoder =
- np_adv::extended::serialize::SignedEncryptedSectionEncoder::new_random_salt::<
- CryptoProviderImpl,
- >(rng, &internal_broadcast_cred);
- np_adv_dynamic::extended::BoxedEncoder::SignedEncrypted(encoder)
- }
};
self.section_builder_internal(encoder)
}
@@ -391,15 +372,11 @@
UnclosedActiveSection = 1,
/// The advertisement builder handle was invalid.
InvalidAdvertisementBuilderHandle = 2,
- /// We're attempting to build a section with an identity
- /// kind (public/encrypted) which doesn't match the kind
- /// for the entire advertisement.
- IdentityKindMismatch = 3,
/// There isn't enough space for a new section, either
/// because the maximum section count has been exceeded
/// or because the advertisement is almost full, and
/// the minimum size of a section wouldn't fit.
- NoSpaceLeft = 4,
+ NoSpaceLeft = 3,
}
/// The result of attempting to create a new V1 section builder.
@@ -409,7 +386,6 @@
Success(V1SectionBuilder),
UnclosedActiveSection,
InvalidAdvertisementBuilderHandle,
- IdentityKindMismatch,
NoSpaceLeft,
}
@@ -422,7 +398,6 @@
Self::InvalidAdvertisementBuilderHandle => {
CreateV1SectionBuilderResultKind::InvalidAdvertisementBuilderHandle
}
- Self::IdentityKindMismatch => CreateV1SectionBuilderResultKind::IdentityKindMismatch,
Self::NoSpaceLeft => CreateV1SectionBuilderResultKind::NoSpaceLeft,
}
}
@@ -436,7 +411,6 @@
fn from(err: SectionBuilderError) -> Self {
match err {
SectionBuilderError::UnclosedActiveSection => Self::UnclosedActiveSection,
- SectionBuilderError::IdentityKindMismatch => Self::IdentityKindMismatch,
SectionBuilderError::NoSpaceLeft => Self::NoSpaceLeft,
}
}
@@ -473,47 +447,51 @@
/// The data element itself had invalid characteristics,
/// most likely a length above 127.
InvalidDataElement = 3,
+ /// A data element with the same type-code was already
+ /// present in the section.
+ DuplicateDataElementTypeCode = 4,
}
-/// Discriminant for `NextV1DE16ByteSaltResult`.
+/// Discriminant for `V1DE16ByteSaltResult`.
#[derive(Clone, Copy)]
#[repr(u8)]
-pub enum NextV1DE16ByteSaltResultKind {
+pub enum V1DE16ByteSaltResultKind {
/// We couldn't return a 16-byte DE salt, possibly
/// because the handle to the section builder
/// was invalid, or possibly because the section
- /// builder was for a public section.
+ /// builder was for a public section, or possibly
+ /// because the DE type code was forbidden (0xFFFFFFFF).
Error = 0,
/// A 16-byte DE salt was returned successfully.
Success = 1,
}
/// The result of attempting to get the derived V1 DE
-/// 16-byte salt for the next-added DE to the section
+/// 16-byte salt for the given type-code from the section
/// builder behind the given handle.
#[derive(Clone)]
#[repr(C)]
#[allow(missing_docs)]
-pub enum NextV1DE16ByteSaltResult {
+pub enum V1DE16ByteSaltResult {
Error,
Success(FixedSizeArray<16>),
}
-impl FfiEnum for NextV1DE16ByteSaltResult {
- type Kind = NextV1DE16ByteSaltResultKind;
+impl FfiEnum for V1DE16ByteSaltResult {
+ type Kind = V1DE16ByteSaltResultKind;
fn kind(&self) -> Self::Kind {
match self {
- Self::Error => NextV1DE16ByteSaltResultKind::Error,
- Self::Success(_) => NextV1DE16ByteSaltResultKind::Success,
+ Self::Error => V1DE16ByteSaltResultKind::Error,
+ Self::Success(_) => V1DE16ByteSaltResultKind::Success,
}
}
}
-impl NextV1DE16ByteSaltResult {
- fn new_from_de_salt(salt: Option<np_adv::extended::serialize::DeSalt>) -> Self {
+impl V1DE16ByteSaltResult {
+ fn new_from_de_salt(salt: Option<np_adv::extended::salt::DeSalt>) -> Self {
match salt.and_then(|salt| salt.derive::<16, CryptoProviderImpl>()) {
- Some(salt) => NextV1DE16ByteSaltResult::Success(FixedSizeArray::from_array(salt)),
- None => NextV1DE16ByteSaltResult::Error,
+ Some(salt) => V1DE16ByteSaltResult::Success(FixedSizeArray::from(salt)),
+ None => V1DE16ByteSaltResult::Error,
}
}
@@ -527,7 +505,11 @@
pub struct V1SectionBuilder {
/// The parent advertisement builder for this section
pub adv_builder: V1AdvertisementBuilder,
- /// This section's index in the parent advertisement
+ /// This section's logical index in the parent advertisement.
+ /// Equal to the number of sections which have been previously
+ /// added to the advertisement, but not suitable for identifying
+ /// a section's position within an advertisement, since sections
+ /// get re-ordered by encoding type as an advertisement is constructed.
pub section_index: u8,
}
@@ -542,8 +524,12 @@
match state {
Some(V1AdvertisementBuilderState::Section(section_builder)) => {
// Make sure the index of the section we're trying to close
- // matches the index of the section currently under construction.
- let actual_section_index = section_builder.section_index() as u8;
+ // matches the count of sections which were previously constructed.
+ // This is done as a way to warn developers of misuses of
+ // section builders [e.g: holding multiple section builders
+ // simultaneously] in the absence of ownership semantics.
+ let actual_section_index =
+ section_builder.previously_added_section_count() as u8;
if self.section_index == actual_section_index {
let updated_adv_builder =
section_builder.add_to_advertisement::<CryptoProviderImpl>();
@@ -567,38 +553,49 @@
}
}
- /// Attempts to get the derived 16-byte V1 DE salt for the next
- /// DE to be added to this section builder. May fail if this
- /// section builder handle is invalid, or if the section
- /// is a public section.
- pub fn next_de_salt(&self) -> NextV1DE16ByteSaltResult {
- self.try_apply_to_internals(
- |section_builder| {
- NextV1DE16ByteSaltResult::new_from_de_salt(section_builder.next_de_salt())
- },
- NextV1DE16ByteSaltResult::Error,
- )
+ /// Attempts to get the derived 16-byte V1 DE salt for the
+ /// given DE type-code. May fail if this section builder
+ /// handle is invalid, if the section is a public section,
+ /// or if the DE type-code is forbidden (0xFFFFFFFF).
+ pub fn de_salt(&self, de_type: V1DEType) -> V1DE16ByteSaltResult {
+ match de_type.try_into() {
+ Ok(de_type) => self.try_apply_to_internals(
+ |section_builder| {
+ V1DE16ByteSaltResult::new_from_de_salt(section_builder.de_salt(de_type))
+ },
+ V1DE16ByteSaltResult::Error,
+ ),
+ Err(_) => V1DE16ByteSaltResult::Error,
+ }
}
/// Attempts to add the given DE to the section builder behind
/// this handle. The passed DE may have a payload of up to 127
/// bytes, the maximum for a V1 DE.
- pub fn add_127_byte_buffer_de(&self, de: V1DE127ByteBuffer) -> AddV1DEResult {
+ pub fn add_generic_de(&self, de: GenericV1DataElement) -> AddV1DEResult {
match de.into_internal() {
- Some(generic_de) => self
- .add_de_internals(np_adv_dynamic::extended::BoxedWriteDataElement::new(generic_de)),
+ Some(generic_de) => self.add_de_internals(&generic_de),
None => AddV1DEResult::InvalidDataElement,
}
}
fn add_de_internals(
&self,
- de: np_adv_dynamic::extended::BoxedWriteDataElement,
+ de: &dyn np_adv_dynamic::extended::DynWriteDataElement,
) -> AddV1DEResult {
self.try_apply_to_internals(
- move |section_builder| match section_builder.add_de(move |_| de) {
+ move |section_builder| match section_builder.add_de(de) {
Ok(_) => AddV1DEResult::Success,
- Err(_) => AddV1DEResult::InsufficientSectionSpace,
+ Err(np_adv_dynamic::extended::BoxedAddDataElementError::DuplicateDataElementTypeCode) => {
+ AddV1DEResult::DuplicateDataElementTypeCode
+ },
+ Err(np_adv_dynamic::extended::BoxedAddDataElementError::InsufficientSpace) => {
+ AddV1DEResult::InsufficientSectionSpace
+ },
+ Err(np_adv_dynamic::extended::BoxedAddDataElementError::NoDerivedSalt) => {
+ // Currently, all stock DEs don't take a salt.
+ AddV1DEResult::InvalidDataElement
+ },
},
AddV1DEResult::InvalidSectionHandle,
)
@@ -619,7 +616,8 @@
Some(V1AdvertisementBuilderState::Section(ref mut section_builder)) => {
// Check to make sure that the section index matches, otherwise
// we have an invalid handle.
- let current_section_index = section_builder.section_index() as u8;
+ let current_section_index =
+ section_builder.previously_added_section_count() as u8;
if current_section_index == self.section_index {
func(section_builder)
} else {
@@ -635,47 +633,409 @@
}
}
-/// Represents the contents of a V1 DE whose payload
-/// is stored in a buffer which may contain up to 127 bytes,
-/// which is the maximum for any V1 DE.
-///
-/// This representation is stable, and so you may directly
-/// reference this struct's fields if you wish.
-#[repr(C)]
-//TODO: Partial unification with `deserialize::v1::GenericV1DataElement`?
-pub struct V1DE127ByteBuffer {
- /// The DE type code of this generic data-element.
- pub de_type: u32,
- /// The raw data-element byte payload, up to
- /// 127 bytes in length.
- pub payload: ByteBuffer<127>,
-}
-
-impl V1DE127ByteBuffer {
+impl GenericV1DataElement {
/// Attempts to convert this FFI-friendly DE with a byte-buffer size of 127
/// to the internal representation of a generic DE. May fail in the case
- /// where the underlying payload byte-buffer has an invalid length above 127.
+ /// where the underlying payload byte-buffer has an invalid length above 127,
+ /// or if the DE type-code is in fact invalid (0xFFFFFFFF).
fn into_internal(self) -> Option<np_adv::extended::data_elements::GenericDataElement> {
- let de_type = np_adv::extended::de_type::DeType::from(self.de_type);
- self.payload.as_slice().and_then(move |payload_slice| {
+ let de_type = np_adv::extended::de_type::DeType::try_from(self.de_type.to_u32()).ok()?;
+ self.payload.as_slice().ok().and_then(move |payload_slice| {
np_adv::extended::data_elements::GenericDataElement::try_from(de_type, payload_slice)
.ok()
})
}
+ /// Given a structure which implements [`WriteDataElement`] in such a way that
+ /// the write operation is guaranteed success, writes the contents
+ /// to create a new [`GenericV1DataElement`].
+ #[allow(clippy::expect_used)]
+ fn from_write_de<W: WriteDataElement<Salt = np_adv::extended::salt::Unsalted>>(de: &W) -> Self {
+ let de_type = V1DEType::from(de.de_type());
+ let mut payload: tinyvec::ArrayVec<[u8; 127]> = tinyvec::ArrayVec::new();
+ de.write_de_contents(np_adv::extended::salt::Unsalted, &mut payload)
+ .expect("Should always be able to write supported DEs to GenericV1DataElement");
+ let payload = ByteBuffer::from_array_vec(payload);
+ Self { de_type, payload }
+ }
+}
+
+impl CastId {
+ /// Serialize this Cast ID to a generic data element.
+ pub fn serialize(&self) -> GenericV1DataElement {
+ let de = np_adv::extended::data_elements::CastIdDataElement::from(&self.bytes);
+ GenericV1DataElement::from_write_de(&de)
+ }
+}
+
+impl MediaDeduplicationId {
+ /// Serialize this Cast ID to a generic data element.
+ pub fn serialize(&self) -> GenericV1DataElement {
+ let de =
+ np_adv::extended::data_elements::MediaDeduplicationIdDataElement::from(&self.bytes);
+ GenericV1DataElement::from_write_de(&de)
+ }
+}
+
+/// The result of attempting to serialize V1 actions
+/// represented as a bitmap into a DE.
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum V1SerializeActionsBitmapResult {
+ Success(GenericV1DataElement),
+ NoActions,
+ TooManyActions,
+}
+
+impl FfiEnum for V1SerializeActionsBitmapResult {
+ type Kind = V1SerializeActionsBitmapResultKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::Success(_) => V1SerializeActionsBitmapResultKind::Success,
+ Self::NoActions => V1SerializeActionsBitmapResultKind::NoActions,
+ Self::TooManyActions => V1SerializeActionsBitmapResultKind::TooManyActions,
+ }
+ }
+}
+
+impl V1SerializeActionsBitmapResult {
+ declare_enum_cast! {into_success, Success, GenericV1DataElement }
+}
+
+/// Discriminant for `V1SerializeActionsBitmapResult`
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[repr(u8)]
+pub enum V1SerializeActionsBitmapResultKind {
+ /// The attempt succeeded. The wrapped data element
+ /// may be obtained via
+ /// `V1SerializeActionsBitmapResult#into_success`.
+ Success = 0,
+ /// The attempt to create a new actions DE failed
+ /// because there were no actions to encode.
+ NoActions = 1,
+ /// The attempt to create a new actions DE failed
+ /// because there were too many actions to encode.
+ TooManyActions = 2,
+}
+
+impl V1ActionsBitmap {
+ /// Attempts to serialize some V1 actions given in
+ /// a bitmap format into a V1 DE byte-buffer. May fail if an attempt
+ /// is made to encode more actions than can fit into a V1 actions DE,
+ /// or if no actions were included.
+ #[allow(clippy::expect_used)]
+ pub fn try_serialize(&self) -> V1SerializeActionsBitmapResult {
+ let maybe_actions_de =
+ np_adv::extended::data_elements::ActionsDataElement::try_from_actions_bitmap(
+ &self.bitmap,
+ );
+ match maybe_actions_de {
+ Ok(actions_de) => {
+ // Convert the internal DE representation to the FFI one.
+ let mut payload = tinyvec::ArrayVec::<[u8; 127]>::new();
+ actions_de
+ .write_de_contents(np_adv::extended::salt::Unsalted, &mut payload)
+ .expect("Actions DE -> 127 byte buffer should always succeed");
+ let de_type = actions_de.de_type();
+ let de_type = de_type.into();
+ let payload = ByteBuffer::from_array_vec(payload);
+ V1SerializeActionsBitmapResult::Success(GenericV1DataElement { de_type, payload })
+ }
+ Err(np_adv::extended::data_elements::ActionsDataElementError::EmptyActions) => {
+ V1SerializeActionsBitmapResult::NoActions
+ }
+ Err(_) => V1SerializeActionsBitmapResult::TooManyActions,
+ }
+ }
+}
+
+impl TxPower {
+ /// Attempts to serialize this Tx Power to a generic V1 data element.
+ pub fn try_serialize_v1(&self) -> Result<GenericV1DataElement, InvalidStackDataStructure> {
+ let tx_power = np_adv::shared_data::TxPower::try_from(self.tx_power)
+ .map_err(|_| InvalidStackDataStructure)?;
+ let de = np_adv::extended::data_elements::TxPowerDataElement::from(tx_power);
+ Ok(GenericV1DataElement::from_write_de(&de))
+ }
+}
+
+impl ContextSyncSeqNum {
+ /// Attempts to serialize this Context Sync sequence number to a generic
+ /// V1 data element.
+ pub fn try_serialize(&self) -> Result<GenericV1DataElement, InvalidStackDataStructure> {
+ let seq_num = np_adv::shared_data::ContextSyncSeqNum::try_from(*self)?;
+ let de = np_adv::extended::data_elements::ContextSyncSeqNumDataElement::from(seq_num);
+ Ok(GenericV1DataElement::from_write_de(&de))
+ }
+}
+
+impl DedupHint {
+ /// Serializes this deduplication hint to a generic V1 data element.
+ pub fn serialize(&self) -> GenericV1DataElement {
+ let de = np_adv::extended::data_elements::DeduplicationHintDataElement::from(self.bytes);
+ GenericV1DataElement::from_write_de(&de)
+ }
+}
+
+impl DeviceType {
+ /// Serializes this device type to a generic V1 data element.
+ pub fn serialize(&self) -> GenericV1DataElement {
+ use np_adv::extended::data_elements::DeviceTypeDataElement;
+ let de = DeviceTypeDataElement(self.device_type);
+ GenericV1DataElement::from_write_de(&de)
+ }
+}
+
+impl ConnectivityInfo {
+ /// Serializes this connectivity info to a generic V1 data element.
+ ///
+ /// May yield [`InvalidStackDataStructure`] if the information is actually
+ /// empty or if any of the constituent fields is a malformed stack data-structure.
+ pub fn try_serialize(&self) -> Result<GenericV1DataElement, InvalidStackDataStructure> {
+ let ble_info = self.ble_info.clone().try_into()?;
+ let wifi_lan_info = self.wifi_lan_info.clone().into();
+ let result = np_adv::extended::data_elements::ConnectivityInfoDataElement {
+ ble_info,
+ wifi_lan_info,
+ };
+ if result.is_some() {
+ Ok(GenericV1DataElement::from_write_de(&result))
+ } else {
+ Err(InvalidStackDataStructure)
+ }
+ }
+}
+
+impl TryFrom<BleConnectivityInfo> for np_adv::extended::data_elements::BleConnectivityInfo {
+ type Error = InvalidStackDataStructure;
+ fn try_from(info: BleConnectivityInfo) -> Result<Self, Self::Error> {
+ let mac_address = info.mac_address.into();
+ let gatt_service_identifier =
+ if let Ok(0) = info.gatt_service_identifier.as_slice().map(|x| x.len()) {
+ None
+ } else {
+ Some(info.gatt_service_identifier.try_into()?)
+ };
+ let psm = info.psm.into();
+ let device_token = info.device_token.into();
+ Ok(Self { mac_address, gatt_service_identifier, psm, device_token })
+ }
+}
+
+impl From<WifiLanConnectivityInfo> for np_adv::extended::data_elements::WifiLanConnectivityInfo {
+ fn from(info: WifiLanConnectivityInfo) -> Self {
+ let ip = info.ip.into();
+ let port = info.port.into_present().map(|port| u16::from_be_bytes(port.into()));
+ let bssid = info.bssid.into();
+ Self { ip, port, bssid }
+ }
+}
+
+impl From<OptionalIpAddress> for Option<core::net::IpAddr> {
+ fn from(maybe_ip: OptionalIpAddress) -> Self {
+ match maybe_ip {
+ OptionalIpAddress::NotPresent => None,
+ OptionalIpAddress::IPv4(x) => {
+ Some(core::net::IpAddr::V4(u32::from_be_bytes(x.into()).into()))
+ }
+ OptionalIpAddress::IPv6(x) => {
+ Some(core::net::IpAddr::V6(u128::from_be_bytes(x.into()).into()))
+ }
+ }
+ }
+}
+
+/// The result of attempting to serialize capabilities
+/// represented as a bitmap into a DE.
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum SerializeCapabilitiesBitmapResult {
+ Success(GenericV1DataElement),
+ ReservedBitsSet,
+}
+
+impl FfiEnum for SerializeCapabilitiesBitmapResult {
+ type Kind = SerializeCapabilitiesBitmapResultKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::Success(_) => SerializeCapabilitiesBitmapResultKind::Success,
+ Self::ReservedBitsSet => SerializeCapabilitiesBitmapResultKind::ReservedBitsSet,
+ }
+ }
+}
+
+impl SerializeCapabilitiesBitmapResult {
+ declare_enum_cast! {into_success, Success, GenericV1DataElement }
+}
+
+/// Discriminant for `SerializeCapabilitiesBitmapResult`
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[repr(u8)]
+pub enum SerializeCapabilitiesBitmapResultKind {
+ /// The attempt succeeded. The wrapped data element
+ /// may be obtained via
+ /// `SerializeCapabilitiesBitmapResult#into_success`.
+ Success = 0,
+ /// The attempt failed because reserved bits were set.
+ ReservedBitsSet = 1,
+}
+
+impl Capabilities {
+ /// Attempts to serialize some capabilities given in
+ /// a bitmap format into a DE byte-buffer. May fail if an attempt
+ /// is made to encode capabilities into reserved bits.
+ #[allow(clippy::expect_used)]
+ pub fn try_serialize(&self) -> SerializeCapabilitiesBitmapResult {
+ let maybe_capabilities_de =
+ np_adv::extended::data_elements::CapabilitiesDataElement::try_from(self.capabilities);
+ match maybe_capabilities_de {
+ Ok(capabilities_de) => {
+ // Convert the internal DE representation to the FFI one.
+ let mut payload = tinyvec::ArrayVec::<[u8; 127]>::new();
+ capabilities_de
+ .write_de_contents(np_adv::extended::salt::Unsalted, &mut payload)
+ .expect("Capabilities DE -> 1 byte buffer should always succeed");
+ let de_type = capabilities_de.de_type();
+ let de_type = de_type.into();
+ let payload = ByteBuffer::from_array_vec(payload);
+ SerializeCapabilitiesBitmapResult::Success(GenericV1DataElement {
+ de_type,
+ payload,
+ })
+ }
+ Err(
+ np_adv::extended::data_elements::CapabilityBitsetDeserializationError::ReservedBitsSet,
+ ) => SerializeCapabilitiesBitmapResult::ReservedBitsSet,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// The result of attempting to serialize requirements
+/// represented as a bitmap into a DE.
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum SerializeRequirementsBitmapResult {
+ Success(GenericV1DataElement),
+ ReservedBitsSet,
+}
+
+impl FfiEnum for SerializeRequirementsBitmapResult {
+ type Kind = SerializeRequirementsBitmapResultKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::Success(_) => SerializeRequirementsBitmapResultKind::Success,
+ Self::ReservedBitsSet => SerializeRequirementsBitmapResultKind::ReservedBitsSet,
+ }
+ }
+}
+
+impl SerializeRequirementsBitmapResult {
+ declare_enum_cast! {into_success, Success, GenericV1DataElement }
+}
+
+/// Discriminant for `SerializeRequirementsBitmapResult`
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[repr(u8)]
+pub enum SerializeRequirementsBitmapResultKind {
+ /// The attempt succeeded. The wrapped data element
+ /// may be obtained via
+ /// `SerializeRequirementsBitmapResult#into_success`.
+ Success = 0,
+ /// The attempt failed because reserved bits were set.
+ ReservedBitsSet = 1,
+}
+
+impl Requirements {
+ /// Attempts to serialize some requirements given in
+ /// a bitmap format into a DE byte-buffer. May fail if an attempt
+ /// is made to encode requirements into reserved bits.
+ #[allow(clippy::expect_used)]
+ pub fn try_serialize(&self) -> SerializeRequirementsBitmapResult {
+ let maybe_requirements_de =
+ np_adv::extended::data_elements::RequirementsDataElement::try_from(self.requirements);
+ match maybe_requirements_de {
+ Ok(requirements_de) => {
+ // Convert the internal DE representation to the FFI one.
+ let mut payload = tinyvec::ArrayVec::<[u8; 127]>::new();
+ requirements_de
+ .write_de_contents(np_adv::extended::salt::Unsalted, &mut payload)
+ .expect("Requirements DE -> 1 byte buffer should always succeed");
+ let de_type = requirements_de.de_type();
+ let de_type = de_type.into();
+ let payload = ByteBuffer::from_array_vec(payload);
+ SerializeRequirementsBitmapResult::Success(GenericV1DataElement {
+ de_type,
+ payload,
+ })
+ }
+ Err(
+ np_adv::extended::data_elements::CapabilityBitsetDeserializationError::ReservedBitsSet,
+ ) => SerializeRequirementsBitmapResult::ReservedBitsSet,
+ _ => unreachable!(),
+ }
+ }
}
#[cfg(test)]
mod tests {
use super::*;
+ #[allow(clippy::indexing_slicing)]
+ #[test]
+ fn test_serialize_64_actions() {
+ let mut actions_bitmap = V1ActionsBitmap { bitmap: [0u8; 256] };
+ for i in 0..=7 {
+ actions_bitmap.bitmap[i] = 255u8;
+ }
+ let result = actions_bitmap.try_serialize();
+ assert_eq!(result.kind(), V1SerializeActionsBitmapResultKind::Success);
+ }
+
+ #[test]
+ fn test_serialize_too_many_actions() {
+ let actions_bitmap = V1ActionsBitmap { bitmap: [255u8; 256] };
+ let result = actions_bitmap.try_serialize();
+ assert_eq!(result.kind(), V1SerializeActionsBitmapResultKind::TooManyActions);
+ }
+
+ #[test]
+ fn test_serialize_valid_capabilities() {
+ let capabilities = Capabilities { capabilities: 0b0000_1111 };
+ let result = capabilities.try_serialize();
+ assert_eq!(result.kind(), SerializeCapabilitiesBitmapResultKind::Success);
+ }
+
+ #[test]
+ fn test_serialize_capabilities_with_reserved_bits() {
+ let capabilities = Capabilities { capabilities: 0b0001_0001 };
+ let result = capabilities.try_serialize();
+ assert_eq!(result.kind(), SerializeCapabilitiesBitmapResultKind::ReservedBitsSet);
+ }
+
+ #[test]
+ fn test_serialize_valid_requirements() {
+ let requirements = Requirements { requirements: 0b0000_1111 };
+ let result = requirements.try_serialize();
+ assert_eq!(result.kind(), SerializeRequirementsBitmapResultKind::Success);
+ }
+
+ #[test]
+ fn test_serialize_requirements_with_reserved_bits() {
+ let requirements = Requirements { requirements: 0b0001_0001 };
+ let result = requirements.try_serialize();
+ assert_eq!(result.kind(), SerializeRequirementsBitmapResultKind::ReservedBitsSet);
+ }
+
+ #[cfg(not(feature = "nocrypto"))]
fn state_is_advertisement_building(adv_builder_state: &V1AdvertisementBuilderState) -> bool {
matches!(adv_builder_state, V1AdvertisementBuilderState::Advertisement(_))
}
+ #[cfg(not(feature = "nocrypto"))]
#[allow(clippy::expect_used)]
#[test]
fn test_build_section_fails_with_outstanding_section() {
- let mut adv_builder = V1AdvertisementBuilderInternals::new(AdvertisementType::Encrypted);
+ let mut adv_builder = V1AdvertisementBuilderInternals::new();
let adv_builder_state =
adv_builder.state.as_ref().expect("Adv builder state should be present.");
@@ -696,7 +1056,8 @@
assert_eq!(double_build_error, SectionBuilderError::UnclosedActiveSection);
}
+ #[cfg(not(feature = "nocrypto"))]
fn empty_broadcast_cred() -> V1BroadcastCredential {
- V1BroadcastCredential::new([0; 32], [0; 16].into(), [0; 32])
+ V1BroadcastCredential::new([0; 32], [0; 16].into())
}
}
diff --git a/nearby/presence/np_ffi_core/src/v0.rs b/nearby/presence/np_ffi_core/src/v0.rs
index 16b7b21..d87cfe8 100644
--- a/nearby/presence/np_ffi_core/src/v0.rs
+++ b/nearby/presence/np_ffi_core/src/v0.rs
@@ -15,8 +15,8 @@
//! Common externally-acessible V0 constructs for both of the
//! serialization+deserialization flows.
-use crate::common::InvalidStackDataStructure;
-use crate::serialize::AdvertisementBuilderKind;
+use crate::common::{InvalidStackDataStructure, TxPower};
+use crate::serialize::v0::AdvertisementBuilderKind;
use crate::utils::FfiEnum;
use np_adv::{
legacy::data_elements::actions::ActionsDataElement,
@@ -83,78 +83,12 @@
declare_enum_cast! {into_actions, Actions, V0Actions}
}
-/// Discriminant for `BuildTxPowerResult`.
-#[repr(u8)]
-#[derive(Clone, Copy)]
-pub enum BuildTxPowerResultKind {
- /// The transmission power was outside the
- /// allowed -100dBm to 20dBm range.
- OutOfRange = 0,
- /// The transmission power was in range,
- /// and so a `TxPower` struct was constructed.
- Success = 1,
-}
-
-/// Result type for attempting to construct a
-/// Tx Power from a signed byte.
-#[repr(C)]
-#[allow(missing_docs)]
-pub enum BuildTxPowerResult {
- OutOfRange,
- Success(TxPower),
-}
-
-impl FfiEnum for BuildTxPowerResult {
- type Kind = BuildTxPowerResultKind;
- fn kind(&self) -> Self::Kind {
- match self {
- Self::OutOfRange => BuildTxPowerResultKind::OutOfRange,
- Self::Success(_) => BuildTxPowerResultKind::Success,
- }
- }
-}
-
-impl BuildTxPowerResult {
- declare_enum_cast! {into_success, Success, TxPower}
-}
-
-/// Representation of a transmission power,
-/// as used for the Tx Power DE in V0 and V1.
-#[derive(Clone)]
-#[repr(C)]
-pub struct TxPower {
- tx_power: i8,
-}
-
-impl TxPower {
- /// Attempts to construct a new TxPower from the given signed-byte value.
- pub fn build_from_signed_byte(tx_power: i8) -> BuildTxPowerResult {
- match np_adv::shared_data::TxPower::try_from(tx_power) {
- Ok(_) => BuildTxPowerResult::Success(Self { tx_power }),
- Err(_) => BuildTxPowerResult::OutOfRange,
- }
- }
- /// Yields this Tx Power value as an i8.
- pub fn as_i8(&self) -> i8 {
- self.tx_power
- }
-}
-
impl From<np_adv_de::tx_power::TxPowerDataElement> for TxPower {
fn from(de: np_adv_de::tx_power::TxPowerDataElement) -> Self {
Self { tx_power: de.tx_power_value() }
}
}
-impl TryFrom<TxPower> for np_adv_dynamic::legacy::ToBoxedSerializeDataElement {
- type Error = InvalidStackDataStructure;
- fn try_from(value: TxPower) -> Result<Self, InvalidStackDataStructure> {
- np_adv::shared_data::TxPower::try_from(value.as_i8())
- .map_err(|_| InvalidStackDataStructure)
- .map(|x| x.into())
- }
-}
-
/// Representation of the Actions DE in V0.
#[derive(Clone, Copy)]
#[repr(C)]
diff --git a/nearby/presence/np_ffi_core/src/v1.rs b/nearby/presence/np_ffi_core/src/v1.rs
index c7b564d..14f07eb 100644
--- a/nearby/presence/np_ffi_core/src/v1.rs
+++ b/nearby/presence/np_ffi_core/src/v1.rs
@@ -15,16 +15,20 @@
//! Common externally-acessible V1 constructs for both of the
//! serialization+deserialization flows.
+use crate::common::{
+ ByteBuffer, FixedSizeArray, InvalidStackDataStructure, OptionalFixedSizeArray, TxPower,
+};
+use crate::utils::FfiEnum;
+
/// Information about the verification scheme used
/// for verifying the integrity of the contents
/// of a decrypted section.
#[derive(Clone, Copy)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
#[repr(u8)]
pub enum V1VerificationMode {
/// Message integrity code verification.
Mic = 0,
- /// Signature verification.
- Signature = 1,
}
impl From<np_adv::extended::deserialize::VerificationMode> for V1VerificationMode {
@@ -32,7 +36,468 @@
use np_adv::extended::deserialize::VerificationMode;
match verification_mode {
VerificationMode::Mic => Self::Mic,
- VerificationMode::Signature => Self::Signature,
}
}
}
+
+/// Representation of the data-element type tag
+/// of a V1 data element.
+#[derive(Clone, Copy)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub struct V1DEType {
+ /// DE type-code. Valid iff this is not
+ /// the forbidden type-code 0xFFFFFFFF.
+ code: u32,
+}
+
+impl From<np_adv::extended::de_type::DeType> for V1DEType {
+ fn from(de_type: np_adv::extended::de_type::DeType) -> Self {
+ let code = de_type.as_u32();
+ Self { code }
+ }
+}
+
+impl TryFrom<V1DEType> for np_adv::extended::de_type::DeType {
+ type Error = np_adv::extended::de_type::InvalidDeType;
+ fn try_from(de_type: V1DEType) -> Result<Self, Self::Error> {
+ de_type.code.try_into()
+ }
+}
+
+impl V1DEType {
+ /// Yields this V1 DE type code as a u32.
+ pub fn to_u32(&self) -> u32 {
+ self.code
+ }
+}
+
+/// Represents the contents of an arbitrary type-code V1 DE whose payload
+/// is stored in a buffer which may contain up to 127 bytes,
+/// which is the maximum for any V1 DE.
+///
+/// This representation is stable, and so you may directly
+/// reference this struct's fields if you wish.
+#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub struct GenericV1DataElement {
+ /// The DE type code of this generic data-element.
+ pub de_type: V1DEType,
+ /// The raw data-element byte payload, up to
+ /// 127 bytes in length.
+ pub payload: ByteBuffer<127>,
+}
+
+impl GenericV1DataElement {
+ /// Gets the DE-type of this generic V1 data element.
+ pub fn de_type(&self) -> V1DEType {
+ self.de_type
+ }
+ /// Destructures this `GenericV1DataElement` into just the DE payload byte-buffer.
+ pub fn into_payload(self) -> ByteBuffer<127> {
+ self.payload
+ }
+}
+
+/// Discriminant for [`V1DataElement`].
+#[derive(Clone, Copy)]
+#[repr(u8)]
+pub enum V1DataElementKind {
+ /// A "generic" V1 data element which may represent any V1
+ /// data element, but during the deserialization path,
+ /// this will represent a data element whose contents
+ /// could not be further deserialized into a stock-recognizable DE.
+ Generic = 0,
+ /// A Cast Id.
+ CastId = 1,
+ /// A V1 "Actions" DE.
+ Actions = 2,
+ /// A transmission power.
+ TxPower = 3,
+ /// A context sync sequence number.
+ ContextSyncSeqNum = 4,
+ /// A deduplication hint.
+ DedupHint = 5,
+ /// A device type.
+ DeviceType = 6,
+ /// Connectivity information.
+ ConnectivityInfo = 7,
+ /// A Media Deduplication Id.
+ MediaDeduplicationId = 8,
+ /// A Capabilities DE.
+ Capabilities = 9,
+ /// A Requirements DE.
+ Requirements = 10,
+}
+
+/// A V1 data element, which may be a "generic" data element
+/// or any of a number of Google-defined data elements.
+#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum V1DataElement {
+ Generic(GenericV1DataElement),
+ CastId(CastId),
+ Actions(V1ActionsBitmap),
+ TxPower(TxPower),
+ ContextSyncSeqNum(ContextSyncSeqNum),
+ DedupHint(DedupHint),
+ DeviceType(DeviceType),
+ ConnectivityInfo(ConnectivityInfo),
+ MediaDeduplicationId(MediaDeduplicationId),
+ Capabilities(Capabilities),
+ Requirements(Requirements),
+}
+
+impl FfiEnum for V1DataElement {
+ type Kind = V1DataElementKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::Generic(_) => V1DataElementKind::Generic,
+ Self::CastId(_) => V1DataElementKind::CastId,
+ Self::Actions(_) => V1DataElementKind::Actions,
+ Self::TxPower(_) => V1DataElementKind::TxPower,
+ Self::ContextSyncSeqNum(_) => V1DataElementKind::ContextSyncSeqNum,
+ Self::DedupHint(_) => V1DataElementKind::DedupHint,
+ Self::DeviceType(_) => V1DataElementKind::DeviceType,
+ Self::ConnectivityInfo(_) => V1DataElementKind::ConnectivityInfo,
+ Self::MediaDeduplicationId(_) => V1DataElementKind::MediaDeduplicationId,
+ Self::Capabilities(_) => V1DataElementKind::Capabilities,
+ Self::Requirements(_) => V1DataElementKind::Requirements,
+ }
+ }
+}
+
+impl V1DataElement {
+ declare_enum_cast! {into_generic, Generic, GenericV1DataElement}
+ declare_enum_cast! {into_cast_id, CastId, CastId}
+ declare_enum_cast! {into_actions, Actions, V1ActionsBitmap}
+ declare_enum_cast! {into_tx_power, TxPower, TxPower}
+ declare_enum_cast! {into_context_sync_seq_num, ContextSyncSeqNum, ContextSyncSeqNum}
+ declare_enum_cast! {into_dedup_hint, DedupHint, DedupHint}
+ declare_enum_cast! {into_device_type, DeviceType, DeviceType}
+ declare_enum_cast! {into_connectivity_info, ConnectivityInfo, ConnectivityInfo}
+ declare_enum_cast! {into_media_deduplication_id, MediaDeduplicationId, MediaDeduplicationId}
+ declare_enum_cast! {into_capabilities, Capabilities, Capabilities}
+ declare_enum_cast! {into_requirements, Requirements, Requirements}
+}
+
+/// Represents the contents of a V1 Actions DE as a bitmap
+/// where the absence/presence of bits represent the absence/presence
+/// of action IDs, where the bytes in the bitmap are kept in
+/// big-endian ordering and each bit-position within a byte is kept in little-endian.
+///
+/// This representation is stable, and so you may directly
+/// reference this struct's fields if you wish.
+#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub struct V1ActionsBitmap {
+ /// The underlying byte-array for the bitmap.
+ pub bitmap: [u8; 256],
+}
+
+/// A Google Cast identifier of 32 bytes.
+///
+/// This representation is stable, so you may directly
+/// reference this struct's fields if you wish.
+#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub struct CastId {
+ /// The raw bytes of the Cast Id.
+ pub bytes: [u8; 32],
+}
+
+/// A Media Deduplication identifier of 20 bytes.
+///
+/// This representation is stable, so you may directly
+/// reference this struct's fields if you wish.
+#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub struct MediaDeduplicationId {
+ /// The raw bytes of the Media Deduplication Id.
+ pub bytes: [u8; 20],
+}
+
+/// Discriminant for `BuildContextSyncSeqNumResult`.
+#[repr(u8)]
+#[derive(Clone, Copy)]
+pub enum BuildContextSyncSeqNumResultKind {
+ /// The context sync sequence number
+ /// was outside the allowed 0-15 range.
+ OutOfRange = 0,
+ /// The context sync sequence number
+ /// was in range, and so a `ContextSyncSeqNum`
+ /// struct was constructed.
+ Success = 1,
+}
+
+/// Result type for attempting to construct
+/// a context sync sequence number from an unsigned byte.
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum BuildContextSyncSeqNumResult {
+ OutOfRange,
+ Success(ContextSyncSeqNum),
+}
+
+impl FfiEnum for BuildContextSyncSeqNumResult {
+ type Kind = BuildContextSyncSeqNumResultKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::OutOfRange => BuildContextSyncSeqNumResultKind::OutOfRange,
+ Self::Success(_) => BuildContextSyncSeqNumResultKind::Success,
+ }
+ }
+}
+
+impl BuildContextSyncSeqNumResult {
+ declare_enum_cast! {into_success, Success, ContextSyncSeqNum}
+}
+
+/// A context sync sequence number.
+#[derive(Clone, Copy)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub struct ContextSyncSeqNum {
+ /// The sequence number, from 0-15.
+ seq_num: u8,
+}
+
+#[cfg(any(test, feature = "testing"))]
+impl ContextSyncSeqNum {
+ /// Constructs a new context-sync sequence number in an unchecked
+ /// fashion from the given u8.
+ pub fn from_u8_unchecked(seq_num: u8) -> Self {
+ Self { seq_num }
+ }
+}
+
+impl ContextSyncSeqNum {
+ /// Attempts to construct a new context sync sequence number
+ /// from the given unsigned-byte value.
+ pub fn build_from_unsigned_byte(seq_num: u8) -> BuildContextSyncSeqNumResult {
+ match np_adv::shared_data::ContextSyncSeqNum::try_from(seq_num) {
+ Ok(_) => BuildContextSyncSeqNumResult::Success(Self { seq_num }),
+ Err(_) => BuildContextSyncSeqNumResult::OutOfRange,
+ }
+ }
+ /// Yields this context sync sequence number as a u8.
+ pub fn as_u8(&self) -> u8 {
+ self.seq_num
+ }
+}
+
+impl From<np_adv::shared_data::ContextSyncSeqNum> for ContextSyncSeqNum {
+ fn from(seq_num: np_adv::shared_data::ContextSyncSeqNum) -> Self {
+ Self { seq_num: seq_num.as_u8() }
+ }
+}
+
+impl TryFrom<ContextSyncSeqNum> for np_adv::shared_data::ContextSyncSeqNum {
+ type Error = InvalidStackDataStructure;
+ fn try_from(seq_num: ContextSyncSeqNum) -> Result<Self, Self::Error> {
+ Self::try_from(seq_num.seq_num).map_err(|_| InvalidStackDataStructure)
+ }
+}
+
+/// A deduplication hint used to identify the same
+/// device across different broadcasting media.
+///
+/// This structure's fields are stable, so you may
+/// directly reference them if you wish.
+#[repr(C)]
+#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+pub struct DedupHint {
+ /// The raw bytes of the deduplication hint.
+ pub bytes: [u8; 8],
+}
+
+/// A description of what kind of device
+/// is broadcasting.
+///
+/// This structure's fields are stable, so you may
+/// directly reference them if you wish.
+///
+/// Consult the Nearby spec for the numeric
+/// values corresponding to particular form factors.
+#[repr(C)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[derive(Clone)]
+pub struct DeviceType {
+ /// The numerical value corresponding to a given
+ /// form factor, or "0" for the case where the
+ /// broadcasting device doesn't know what kind
+ /// of device type it is.
+ ///
+ /// Consult the Nearby spec for values
+ /// corresponding to particular form factors.
+ pub device_type: u8,
+}
+
+/// Information for connecting to a broadcasting device.
+///
+/// You should not depend on the layout details of this
+/// structure, since more fields may be added in the future.
+#[repr(C)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[derive(Clone, Default)]
+pub struct ConnectivityInfo {
+ /// Any BLE-specific connectivity info.
+ pub(crate) ble_info: BleConnectivityInfo,
+ /// Any Wifi-LAN-specific connectivity info.
+ pub(crate) wifi_lan_info: WifiLanConnectivityInfo,
+}
+
+impl ConnectivityInfo {
+ /// Gets the BLE component of this connectivity info.
+ pub fn get_ble_info(&self) -> BleConnectivityInfo {
+ self.ble_info.clone()
+ }
+ /// Gets the Wifi-LAN component of this connectivity info.
+ pub fn get_wifi_lan_info(&self) -> WifiLanConnectivityInfo {
+ self.wifi_lan_info.clone()
+ }
+ /// Creates a new connectivity info with the BLE connectivity information
+ /// of this instance replaced with the given value.
+ pub fn set_ble_info(&self, ble_info: BleConnectivityInfo) -> Self {
+ Self { ble_info, ..self.clone() }
+ }
+ /// Creates a new connectivity info with the Wifi-LAN connectivity information
+ /// of this instance replaced with the given value.
+ pub fn set_wifi_lan_info(&self, wifi_lan_info: WifiLanConnectivityInfo) -> Self {
+ Self { wifi_lan_info, ..self.clone() }
+ }
+}
+
+/// Information for connecting to a broadcasting device over BLE.
+///
+/// This structure's fields are stable, so you may
+/// directly reference them if you wish.
+#[repr(C)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[derive(Clone, Default)]
+pub struct BleConnectivityInfo {
+ /// Bluetooth MAC address.
+ pub mac_address: OptionalFixedSizeArray<6>,
+ /// An identifier for the GATT service to connect to.
+ /// This could be a UUID + instance ID (17 bytes),
+ /// an attribute handle + instance ID (3 bytes),
+ /// or just a UUID (16 bytes) or attribute handle (2 bytes),
+ /// or empty (0 bytes) for no GATT service identifier present.
+ pub gatt_service_identifier: ByteBuffer<17>,
+ /// The PSM (L2CAP) port to connect on.
+ pub psm: OptionalFixedSizeArray<2>,
+ /// Physical identifier ("device token") for the device.
+ pub device_token: OptionalFixedSizeArray<2>,
+}
+
+impl BleConnectivityInfo {
+ /// Returns true iff the contents of this BLE connectivity info component are non-empty.
+ pub fn is_present(&self) -> bool {
+ self.mac_address.is_present()
+ || self.gatt_service_identifier.as_slice().map(|x| !x.is_empty()).unwrap_or(false)
+ || self.psm.is_present()
+ || self.device_token.is_present()
+ }
+}
+
+/// Discriminant for [`OptionalIpAddress`].
+#[derive(Clone, PartialEq, Eq)]
+#[repr(u8)]
+pub enum OptionalIpAddressKind {
+ /// No IP address specified.
+ NotPresent = 0,
+ /// An IPv4 address.
+ IPv4 = 1,
+ /// An IPv6 address.
+ IPv6 = 2,
+}
+
+/// An optional IPv4 or IPv6 address, represented by big-endian bytes.
+#[repr(C)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[derive(Clone)]
+pub enum OptionalIpAddress {
+ /// No IP address specified.
+ NotPresent,
+ /// An IPv4 address (4 bytes).
+ IPv4(FixedSizeArray<4>),
+ /// An IPv6 address (16 bytes).
+ IPv6(FixedSizeArray<16>),
+}
+
+impl Default for OptionalIpAddress {
+ fn default() -> Self {
+ Self::NotPresent
+ }
+}
+
+impl FfiEnum for OptionalIpAddress {
+ type Kind = OptionalIpAddressKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::NotPresent => OptionalIpAddressKind::NotPresent,
+ Self::IPv4(_) => OptionalIpAddressKind::IPv4,
+ Self::IPv6(_) => OptionalIpAddressKind::IPv6,
+ }
+ }
+}
+
+/// Information for connecting to a broadcasting device over Wifi-LAN.
+///
+/// This structure's fields are stable, so you may
+/// directly reference them if you wish.
+#[repr(C)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[derive(Clone, Default)]
+pub struct WifiLanConnectivityInfo {
+ /// IP address.
+ pub ip: OptionalIpAddress,
+ /// Port number, as two bytes in big-endian.
+ pub port: OptionalFixedSizeArray<2>,
+ /// Six-byte BSSID.
+ pub bssid: OptionalFixedSizeArray<6>,
+}
+
+impl WifiLanConnectivityInfo {
+ /// Returns true iff the contents of this Wifi-LAN connectivity info component are non-empty.
+ pub fn is_present(&self) -> bool {
+ self.ip.kind() != OptionalIpAddressKind::NotPresent
+ || self.port.is_present()
+ || self.bssid.is_present()
+ }
+}
+
+/// Represents the contents of a Capabilities DE as a bitmap where the absence/presence of bits
+/// represent the absence/presence of device capabilities, where each bit-position within the byte
+/// is kept in little-endian.
+///
+/// This representation is stable, and so you may directly
+/// reference this struct's fields if you wish.
+#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub struct Capabilities {
+ /// The underlying byte.
+ pub capabilities: u8,
+}
+
+/// Represents the contents of a Requirements DE as a bitmap where the absence/presence of bits
+/// represent the absence/presence of required remote device capabilities, where each bit-position within the byte
+/// is kept in little-endian.
+///
+/// This representation is stable, and so you may directly
+/// reference this struct's fields if you wish.
+#[derive(Clone)]
+#[cfg_attr(any(test, feature = "testing"), derive(Debug, PartialEq, Eq, arbitrary::Arbitrary))]
+#[repr(C)]
+pub struct Requirements {
+ /// The underlying byte.
+ pub requirements: u8,
+}
diff --git a/nearby/presence/np_ffi_core/tests/cannot_add_credentials.rs b/nearby/presence/np_ffi_core/tests/cannot_add_credentials.rs
new file mode 100644
index 0000000..84ed0f0
--- /dev/null
+++ b/nearby/presence/np_ffi_core/tests/cannot_add_credentials.rs
@@ -0,0 +1,82 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Credentials should not be able to be added to a credential slab when the `nocrypto` feature is
+//! in use.
+
+use derive_fuzztest;
+use handle_map::OwnedHandle;
+use np_ffi_core::credentials::{
+ create_credential_slab, AddV0CredentialToSlabResult, AddV1CredentialToSlabResult,
+ CreateCredentialSlabResult, CredentialSlab, MatchedCredential, V0BroadcastCredential,
+ V0DiscoveryCredential, V1BroadcastCredential, V1DiscoveryCredential,
+};
+use np_ffi_core::serialize::v1::{
+ create_v1_advertisement_builder, CreateV1AdvertisementBuilderResult,
+ CreateV1SectionBuilderResult,
+};
+use np_ffi_core::v1::V1VerificationMode;
+
+fn make_slab() -> OwnedHandle<CredentialSlab> {
+ let CreateCredentialSlabResult::Success(slab) = create_credential_slab() else {
+ panic!("failed to create slab");
+ };
+ OwnedHandle::new(slab)
+}
+
+#[derive_fuzztest::proptest]
+fn add_v0_credential_to_slab(cred: V0DiscoveryCredential, cred_id: i64) {
+ let slab = make_slab();
+ let match_data = MatchedCredential::new(cred_id, &[], [0u8; 32].into());
+
+ let res = slab.add_v0(cred, match_data);
+
+ assert!(matches!(res, AddV0CredentialToSlabResult::InvalidHandle));
+}
+
+#[derive_fuzztest::proptest]
+fn add_v1_credential_to_slab(cred: V1DiscoveryCredential, cred_id: i64) {
+ let slab = make_slab();
+ let match_data = MatchedCredential::new(cred_id, &[], [0u8; 32].into());
+
+ let res = slab.add_v1(cred, match_data);
+
+ assert!(matches!(res, AddV1CredentialToSlabResult::InvalidHandle));
+}
+
+#[derive_fuzztest::proptest]
+fn create_encrypted_v0_builder(broadcast_cred: V0BroadcastCredential, salt: [u8; 2]) {
+ use np_ffi_core::serialize::v0::{
+ create_v0_encrypted_advertisement_builder, CreateV0AdvertisementBuilderResult,
+ };
+
+ let res = create_v0_encrypted_advertisement_builder(broadcast_cred, salt.into());
+ assert!(matches!(res, CreateV0AdvertisementBuilderResult::NoSpaceLeft));
+}
+
+#[derive_fuzztest::proptest]
+fn create_encrypted_v1_section_builder(
+ broadcast_cred: V1BroadcastCredential,
+ verification_mode: V1VerificationMode,
+) {
+ let CreateV1AdvertisementBuilderResult::Success(builder) = create_v1_advertisement_builder()
+ else {
+ panic!("failed to create public builder");
+ };
+ let builder = OwnedHandle::new(builder);
+
+ let res = builder.encrypted_section_builder(broadcast_cred, verification_mode);
+
+ assert!(matches!(res, CreateV1SectionBuilderResult::NoSpaceLeft));
+}
diff --git a/nearby/presence/np_ffi_core/tests/exhaustive_roundtrips.rs b/nearby/presence/np_ffi_core/tests/exhaustive_roundtrips.rs
new file mode 100644
index 0000000..3ec3f64
--- /dev/null
+++ b/nearby/presence/np_ffi_core/tests/exhaustive_roundtrips.rs
@@ -0,0 +1,65 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.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.
+
+//! Tests that round-trip ser->deser of V1 DEs whose values are exhaustively enumerable is non-panicking and maintains information.
+
+use np_ffi_core::{common::TxPower, deserialize::v1::*, v1::*};
+
+#[test]
+fn roundtrip_tx_power() {
+ for tx_power in i8::MIN..=i8::MAX {
+ let tx_power = TxPower::from_i8_unchecked(tx_power);
+
+ let Ok(de) = tx_power.try_serialize_v1() else {
+ // The transmission power was out-of-range on the stack,
+ // and we caught that, so we don't care what happens next.
+ return;
+ };
+ assert_eq!(
+ de.try_deserialize(),
+ Ok(V1DataElementDeserializationResult::Success(V1DataElement::TxPower(tx_power)))
+ );
+ }
+}
+
+#[test]
+fn roundtrip_device_type() {
+ for device_type in 0..=255u8 {
+ let device_type = DeviceType { device_type };
+ let de = device_type.serialize();
+ assert_eq!(
+ de.try_deserialize(),
+ Ok(V1DataElementDeserializationResult::Success(V1DataElement::DeviceType(device_type)))
+ );
+ }
+}
+
+#[test]
+fn roundtrip_context_sync_seq_num() {
+ for context_sync_seq_num in 0..=255u8 {
+ let context_sync_seq_num = ContextSyncSeqNum::from_u8_unchecked(context_sync_seq_num);
+
+ let Ok(de) = context_sync_seq_num.try_serialize() else {
+ // The context-sync sequence number was out-of-range on the stack,
+ // and we caught that, so we don't care what happens next.
+ return;
+ };
+ assert_eq!(
+ de.try_deserialize(),
+ Ok(V1DataElementDeserializationResult::Success(V1DataElement::ContextSyncSeqNum(
+ context_sync_seq_num
+ )))
+ );
+ }
+}
diff --git a/nearby/presence/np_hkdf/resources/test/hkdf-test-vectors.json b/nearby/presence/np_hkdf/resources/test/hkdf-test-vectors.json
index d0af65f..b22d182 100644
--- a/nearby/presence/np_hkdf/resources/test/hkdf-test-vectors.json
+++ b/nearby/presence/np_hkdf/resources/test/hkdf-test-vectors.json
@@ -11,9 +11,7 @@
"v1_mic_extended_salt_mic_hmac_key": "77FAC801B7F4BFB45B66493109E5D2B8A1811046FE1B26FDD4911DFE83F22AE6",
"v1_mic_short_salt_aes_key": "6A4EA007544EB1F1F4FC3A09733A7636",
"v1_mic_short_salt_identity_token_hmac_key": "24CCCA1794C5267D0F31BFFD05933A3056DC866AE5CCC812D0BE992E741E3C70",
- "v1_mic_short_salt_mic_hmac_key": "900CCE3E377FBF1A075F5FB96B2A670981624069DFCF8A99FF3CCB73AEDEC153",
- "v1_signature_identity_token_hmac_key": "8C707D9DCB8C7965EF0DFB90C8781312135C0A8AD10388B939082947B0EEC5CF",
- "v1_signature_section_aes_key": "EDB08293628753CF3BE7C28A65FF1A9D"
+ "v1_mic_short_salt_mic_hmac_key": "900CCE3E377FBF1A075F5FB96B2A670981624069DFCF8A99FF3CCB73AEDEC153"
},
"v0_adv_salt_hkdf": {
"adv_salt": "F0EB",
@@ -28,9 +26,9 @@
"short_salt_nonce": "38C673E98F69BE47A67CD13D"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "DA0DEC97DD1B417639F5BABAD8F3EA65",
- "derived_salt_nonce": "3D412EFE82132F641657B86BBB145803",
- "derived_salt_third_de": "2F1011C59BF1CC8A1870CB124E8C2753",
+ "derived_salt_first_de": "3D412EFE82132F641657B86BBB145803",
+ "derived_salt_nonce": "79BCED3ABDE1B348E8143036BAECD494",
+ "derived_salt_third_de": "5981AFBBA8ECE17B9831722370CDD3CF",
"section_extended_salt": "C88D4D9DF5287AE88CC991A84CDEDAA4"
}
},
@@ -46,9 +44,7 @@
"v1_mic_extended_salt_mic_hmac_key": "B97F39D53AA3497347DC3D7DDB5F6399C3EC78A97844A4B1B6CA33D62A5610CF",
"v1_mic_short_salt_aes_key": "BD67C930144A6D49052DDD3100DEAC8C",
"v1_mic_short_salt_identity_token_hmac_key": "83741493B76DCB32684A90514D76E06AEFA48BF6CCA7E5FD88F62A21457D6B7C",
- "v1_mic_short_salt_mic_hmac_key": "9C2C874DBFFA6A5DB88D8FAB3BC2E6B8453452A666407A970AB09586A729F64B",
- "v1_signature_identity_token_hmac_key": "CE915CC5776983A0FA0D9952A2DB5B9362C413351C147E7A060161B94F668AE6",
- "v1_signature_section_aes_key": "F5E624240876543285520E1E8E74F795"
+ "v1_mic_short_salt_mic_hmac_key": "9C2C874DBFFA6A5DB88D8FAB3BC2E6B8453452A666407A970AB09586A729F64B"
},
"v0_adv_salt_hkdf": {
"adv_salt": "11F3",
@@ -63,9 +59,9 @@
"short_salt_nonce": "F43CD28A64290DCCB5ED553A"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "2A5D5B1EE6E62646232D1ABECBF33804",
- "derived_salt_nonce": "2890730B9A82A68AF81B88B2880E0CCB",
- "derived_salt_third_de": "C493E8AB86C2BD5B5B7EA619D2E0E92F",
+ "derived_salt_first_de": "2890730B9A82A68AF81B88B2880E0CCB",
+ "derived_salt_nonce": "B1132CAB985A0D2420D0FC0CF9E0A26A",
+ "derived_salt_third_de": "30CCB15C8E744581389184F718CE662A",
"section_extended_salt": "3BC347425EE1B8962BF6D5D340D53C9D"
}
},
@@ -81,9 +77,7 @@
"v1_mic_extended_salt_mic_hmac_key": "972931866621E2B8DD7FB76ABB8797C18B26C74974ED471F501102645DC76ECC",
"v1_mic_short_salt_aes_key": "E240E070654F3794C276D799CB23D5CE",
"v1_mic_short_salt_identity_token_hmac_key": "9AA904713C1F1146CEACD567C6BC4BC7B84F92660426D1FD633A42ECD3D4196D",
- "v1_mic_short_salt_mic_hmac_key": "648A1D4915934BE8EA5C24CF747E2A17A2CEF1E655D98B0877F3B1DCF04441B9",
- "v1_signature_identity_token_hmac_key": "2B3210B9D391E4D2CDA95368B8B66572975348748C382588AAF4664E2EFFC2D4",
- "v1_signature_section_aes_key": "9FC3EB118E5472E3D0E1806BC8EEB6C4"
+ "v1_mic_short_salt_mic_hmac_key": "648A1D4915934BE8EA5C24CF747E2A17A2CEF1E655D98B0877F3B1DCF04441B9"
},
"v0_adv_salt_hkdf": {
"adv_salt": "BC01",
@@ -98,9 +92,9 @@
"short_salt_nonce": "E6CDA2BF1E867E1C76A878A7"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "8A985AC6265F3067E13ABAA6218D6758",
- "derived_salt_nonce": "DA89867F71A70412D2B00D8DA0454838",
- "derived_salt_third_de": "A36B229A20A2A972401A843F84FA8D33",
+ "derived_salt_first_de": "DA89867F71A70412D2B00D8DA0454838",
+ "derived_salt_nonce": "634756DD105315C48D798851E4FE74D6",
+ "derived_salt_third_de": "6BD7FB119ECCFB0834B352B43E8F3186",
"section_extended_salt": "967D99CE3EE6D79AE98B8037056760B9"
}
},
@@ -116,9 +110,7 @@
"v1_mic_extended_salt_mic_hmac_key": "025D81497467CE23085944DB9AD50A5B8BBEF3F5036E9750A8EE3801CEA18EB9",
"v1_mic_short_salt_aes_key": "10F24F181454ED4663CC803642768DAF",
"v1_mic_short_salt_identity_token_hmac_key": "6B42C12D243ABBB7F2236EAFFE0C1A8B1654E25E3CF6416677D3462DF7402062",
- "v1_mic_short_salt_mic_hmac_key": "52EECEDEEF2758E036A9748E8BF06287F8C6E70CCDB6017C9961A97E64B26FD4",
- "v1_signature_identity_token_hmac_key": "591CE684D72DA16E9168381A2A8167F409E83662AF6713F3F68D8B753DA3174B",
- "v1_signature_section_aes_key": "240792A759473821A1D8CBF99AE54AA4"
+ "v1_mic_short_salt_mic_hmac_key": "52EECEDEEF2758E036A9748E8BF06287F8C6E70CCDB6017C9961A97E64B26FD4"
},
"v0_adv_salt_hkdf": {
"adv_salt": "480A",
@@ -133,9 +125,9 @@
"short_salt_nonce": "20358E029A4BA3EF97796505"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "002B67A8C992EAEBCA8259FEC28289A4",
- "derived_salt_nonce": "F202A0CB1EE7D8FCC9BABB9EB6304EF5",
- "derived_salt_third_de": "F44C487244901DE5D96ABFD481BAC4CB",
+ "derived_salt_first_de": "F202A0CB1EE7D8FCC9BABB9EB6304EF5",
+ "derived_salt_nonce": "5A95E131E8875D799E983D388AAE8090",
+ "derived_salt_third_de": "32E4B731040FE1B8DDE2F539DCE41AB0",
"section_extended_salt": "69167A8D033BBB5E257E729AE62ED6E7"
}
},
@@ -151,9 +143,7 @@
"v1_mic_extended_salt_mic_hmac_key": "3B571385F9A85C437D7A0FDEF9193B643FB30A82C94FF4BEF03499E8FF8DE773",
"v1_mic_short_salt_aes_key": "7945B907207E26989E99CF22D75C5134",
"v1_mic_short_salt_identity_token_hmac_key": "F4C50FCBF4772A730A1A962A61DBA99993BC4E1FC823B09C591889A07B0848EF",
- "v1_mic_short_salt_mic_hmac_key": "5BB027194D3274626B9F12F33EDF3894F48C74EC8643E979171F311AE56C6196",
- "v1_signature_identity_token_hmac_key": "4606A5AE3B1A042E620FAE09883B0E2B60F2CBA0F55243C9E82BE0BCBA64AA4C",
- "v1_signature_section_aes_key": "62ED38EF19133F282C78AD3BE7493419"
+ "v1_mic_short_salt_mic_hmac_key": "5BB027194D3274626B9F12F33EDF3894F48C74EC8643E979171F311AE56C6196"
},
"v0_adv_salt_hkdf": {
"adv_salt": "D77E",
@@ -168,9 +158,9 @@
"short_salt_nonce": "FCF5569BBB730792EAC492EB"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "FDBF480AFE38C1A3B518C9E4E37DF73B",
- "derived_salt_nonce": "D0AB883D19A80B9A9B0F71FCB3BA9C4D",
- "derived_salt_third_de": "559EF43087D81A97644A1564939C902D",
+ "derived_salt_first_de": "D0AB883D19A80B9A9B0F71FCB3BA9C4D",
+ "derived_salt_nonce": "444E9426123F6C3C3785003E0BC3816D",
+ "derived_salt_third_de": "A3BC62EB7E4A316917D1400B1E8BF6FC",
"section_extended_salt": "7D6E587DCCB086EE382CDA8ECA378555"
}
},
@@ -186,9 +176,7 @@
"v1_mic_extended_salt_mic_hmac_key": "F095007E76CB193DA11718890BE7D735C9B57DD99C85E597893E9A51DD15A93E",
"v1_mic_short_salt_aes_key": "52A881282D67F6ADE798591223985FF8",
"v1_mic_short_salt_identity_token_hmac_key": "60CDFB8EDEB884516A7A3D8501E3357AFD42A77CD5FE6578D2F5957FDA03F5CB",
- "v1_mic_short_salt_mic_hmac_key": "8555D441A574D3375E50F9A450022F4A982E8D580982908C7B6D81C628312968",
- "v1_signature_identity_token_hmac_key": "E850671925AB258049D3E3A4822FD1C78A4D80F556FB92EB7D7B5DA01F5CE641",
- "v1_signature_section_aes_key": "8A1ACA177E160A79924EB4D2C9435F98"
+ "v1_mic_short_salt_mic_hmac_key": "8555D441A574D3375E50F9A450022F4A982E8D580982908C7B6D81C628312968"
},
"v0_adv_salt_hkdf": {
"adv_salt": "AFB1",
@@ -203,9 +191,9 @@
"short_salt_nonce": "9BF830CCD7DB20F7A3115702"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "668E2A14D9B304AC23D0F56CDFE0726A",
- "derived_salt_nonce": "866E7A3B3D3F0A094F8FF58C221754BE",
- "derived_salt_third_de": "137E0CE6D82B8DE9AFDFBE0DEB447B33",
+ "derived_salt_first_de": "866E7A3B3D3F0A094F8FF58C221754BE",
+ "derived_salt_nonce": "4BA0EDD516443776F79EEDEE45546007",
+ "derived_salt_third_de": "FD3BC9D082502D1C83969AA3EB0F147E",
"section_extended_salt": "4AC4230A5F3B911F69A07DCE728586E4"
}
},
@@ -221,9 +209,7 @@
"v1_mic_extended_salt_mic_hmac_key": "0E5A3BCD461378A07659FBBD5E22AD8A658D3871FDA1B94111F5F468298A8D74",
"v1_mic_short_salt_aes_key": "C3B2251C9ECEBABBF766D5185B5F18AB",
"v1_mic_short_salt_identity_token_hmac_key": "ADF35872686CF343B3CCAC85D02789B9AE1F9D30A8DDCFF90EB87B17232F928C",
- "v1_mic_short_salt_mic_hmac_key": "EE54C5BB9C1BFE150A389B0A87E4181694721FA52C7DFAD2012375D95356CDCF",
- "v1_signature_identity_token_hmac_key": "D1E4984F45D313CD9FFBC84344861B3E4695CD514BED3150C16533E8D342CDE1",
- "v1_signature_section_aes_key": "15EB95365271D866D98BD422014205B1"
+ "v1_mic_short_salt_mic_hmac_key": "EE54C5BB9C1BFE150A389B0A87E4181694721FA52C7DFAD2012375D95356CDCF"
},
"v0_adv_salt_hkdf": {
"adv_salt": "9BCE",
@@ -238,9 +224,9 @@
"short_salt_nonce": "AEFEF9A66BCC84F652A6B6F8"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "62CA3E3C25D90BC9601B292D3DBAF36B",
- "derived_salt_nonce": "DDFA02449C05C95B5D958B876CA189DE",
- "derived_salt_third_de": "40EAA779A52CE6249681C6C2A6E33839",
+ "derived_salt_first_de": "DDFA02449C05C95B5D958B876CA189DE",
+ "derived_salt_nonce": "A91C601311456B234B8139A06C6E43A1",
+ "derived_salt_third_de": "9EBEE65479F0354487CBE0440D17E2D1",
"section_extended_salt": "9054B345A1B790A68CFF7CFC52D75525"
}
},
@@ -256,9 +242,7 @@
"v1_mic_extended_salt_mic_hmac_key": "2D65B2CD24E315B3B60237770E5F14AEE994E718C023B32BBAE93F00A49160CE",
"v1_mic_short_salt_aes_key": "C886A50B76F6FA70D653F47D58E32DB5",
"v1_mic_short_salt_identity_token_hmac_key": "73D1231C70DF32D46609A2BCC11E0F1C09298F772ED3C1ED61F3473F6B0D109E",
- "v1_mic_short_salt_mic_hmac_key": "8BA22F5A56E0C7D945A43286C432875B2A8BA4D340B71FDB32A5078BACF81D05",
- "v1_signature_identity_token_hmac_key": "8F3E043B38AD640DDB32F836B000FEB140E293FEFFE22B048DABCC6AD0D77EB8",
- "v1_signature_section_aes_key": "F0B1C0499D7F735D789BF19C6B27BDA0"
+ "v1_mic_short_salt_mic_hmac_key": "8BA22F5A56E0C7D945A43286C432875B2A8BA4D340B71FDB32A5078BACF81D05"
},
"v0_adv_salt_hkdf": {
"adv_salt": "02DD",
@@ -273,9 +257,9 @@
"short_salt_nonce": "FC5A9CD5C30AE35F2439A989"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "C7C542850D51FC65A3748B637D4BF2B6",
- "derived_salt_nonce": "E7BC6A195FD35BBBBD36890C84DFD6BF",
- "derived_salt_third_de": "08B163B3C00BCF2302E50BCD54DE256C",
+ "derived_salt_first_de": "E7BC6A195FD35BBBBD36890C84DFD6BF",
+ "derived_salt_nonce": "559DD2F3D127F43E52DD15DE04199BA1",
+ "derived_salt_third_de": "D11E9E733ABB4DD44C3C85EEBC0443C8",
"section_extended_salt": "B045F1FC3AA558F7DB3436B43FE3703D"
}
},
@@ -291,9 +275,7 @@
"v1_mic_extended_salt_mic_hmac_key": "EBA1ADD68E7A7175C6EA4BAC7C9DA02408DA8B42B079CE25883E286BE7D027F0",
"v1_mic_short_salt_aes_key": "B4FBAAC9FB28B31447917346BF960A6A",
"v1_mic_short_salt_identity_token_hmac_key": "8261D61215141838D5F9BE7B561917B4BE89A7CD558403E146FD5F9FDB232308",
- "v1_mic_short_salt_mic_hmac_key": "776E5708C049603DE6CD23D46F5867BD4CFCBD68865DA033F76284C02248C5FE",
- "v1_signature_identity_token_hmac_key": "8A10585EE8E9DE84E26A0284C4C0AADD7726DC54FCE6FB2196C6314661B8D765",
- "v1_signature_section_aes_key": "1324A78CBC23F567A880B1975F0ED40C"
+ "v1_mic_short_salt_mic_hmac_key": "776E5708C049603DE6CD23D46F5867BD4CFCBD68865DA033F76284C02248C5FE"
},
"v0_adv_salt_hkdf": {
"adv_salt": "10EE",
@@ -308,9 +290,9 @@
"short_salt_nonce": "615C3235852E9D1CD0063CE4"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "6272F7C2A41C3A15288E20DF6298EAD9",
- "derived_salt_nonce": "E2F217DE0828333D9F49E122636431EA",
- "derived_salt_third_de": "74DF8645F7FFB751482B84D2B291D3DA",
+ "derived_salt_first_de": "E2F217DE0828333D9F49E122636431EA",
+ "derived_salt_nonce": "6DBC0E9B86AD4A4F19A015931F1D81C6",
+ "derived_salt_third_de": "01C00EAA731684978FA89919C7EA294F",
"section_extended_salt": "189063CFAF8E1915CCF88CB306E65B16"
}
},
@@ -326,9 +308,7 @@
"v1_mic_extended_salt_mic_hmac_key": "1F8DCC645A797A805CF72D23FD6DCF3243735737BE0E11870614BD547C56D3D7",
"v1_mic_short_salt_aes_key": "E49F8D00B07699825E9F82D7A668F8D3",
"v1_mic_short_salt_identity_token_hmac_key": "9AFBB83B9CE4C2A0A1F865B4386E87BB31273D46BC1F5C272D1F2F098F46C579",
- "v1_mic_short_salt_mic_hmac_key": "A68EDA18BF3D5CE9CFB3EEC57922F0E4EE0DA69C36F353C9302976ACA66CEF6E",
- "v1_signature_identity_token_hmac_key": "55602CC889D8C5B0C8E2F37149831B6C97DB377B2168A8A06BC667C8D017AA29",
- "v1_signature_section_aes_key": "9EC0098FC8386B2260A44033CAF969FF"
+ "v1_mic_short_salt_mic_hmac_key": "A68EDA18BF3D5CE9CFB3EEC57922F0E4EE0DA69C36F353C9302976ACA66CEF6E"
},
"v0_adv_salt_hkdf": {
"adv_salt": "FAF0",
@@ -343,9 +323,9 @@
"short_salt_nonce": "BA72171618BC1AE229E46B79"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "F3B0AFF9A87AD9F157FCB595A0400440",
- "derived_salt_nonce": "58B7445E6B36BA40A10BA36B5C1E2EE2",
- "derived_salt_third_de": "087236B6E0C93C6153A061CE6A145D2A",
+ "derived_salt_first_de": "58B7445E6B36BA40A10BA36B5C1E2EE2",
+ "derived_salt_nonce": "FA882BF72D6CA6ABEE770643FEBC63E3",
+ "derived_salt_third_de": "44A659EDAF49F53DB25CF9CCE878F4C8",
"section_extended_salt": "E574B5E9485189F792010872C55A1A16"
}
},
@@ -361,9 +341,7 @@
"v1_mic_extended_salt_mic_hmac_key": "C073CA0A8FBFA53DE6D573FF2C4DC981C8567DCD41674F666A9B6F2A3E5DDBB8",
"v1_mic_short_salt_aes_key": "3CA3A347F198B2771543DFDA16EBF066",
"v1_mic_short_salt_identity_token_hmac_key": "526F4A2BADF226817B6CFBECAC622C76ECB29F08D94E76A19E0E5AA47E60BCC6",
- "v1_mic_short_salt_mic_hmac_key": "B1BFA6EB1CF610F7ABAB39846A4096A201400C96FF365FA28DC17E21A10B4843",
- "v1_signature_identity_token_hmac_key": "CDB40BBD6E8C6F09EACB8C49F6620036AF9EDADBE2C4DDDF4F1576BD16E60D97",
- "v1_signature_section_aes_key": "89D7606A0A0A3BD01ECC6C5753B73976"
+ "v1_mic_short_salt_mic_hmac_key": "B1BFA6EB1CF610F7ABAB39846A4096A201400C96FF365FA28DC17E21A10B4843"
},
"v0_adv_salt_hkdf": {
"adv_salt": "9F8F",
@@ -378,9 +356,9 @@
"short_salt_nonce": "622C675E322ED508F3D36C63"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "18DACA43407EBFAC3F779E06A67455F9",
- "derived_salt_nonce": "F82E736F62D198165C6D347EA4E39C6E",
- "derived_salt_third_de": "23619B13FF4E31FCACFD529756674E09",
+ "derived_salt_first_de": "F82E736F62D198165C6D347EA4E39C6E",
+ "derived_salt_nonce": "ED4E319B967B57D26983E812915BA8E1",
+ "derived_salt_third_de": "110564C6D93DDD97D5195015EFC203E0",
"section_extended_salt": "BEE23DC5EBEB75C788B766ACEFA64377"
}
},
@@ -396,9 +374,7 @@
"v1_mic_extended_salt_mic_hmac_key": "07863900E27135BC852442C80BFCBA09FE418A460F91DCF30F88F194F2B15DE5",
"v1_mic_short_salt_aes_key": "E0DA5AA00A18AB165C15005E75A02F7E",
"v1_mic_short_salt_identity_token_hmac_key": "C6870BECE7AC586F61563E3FA8F460728218E93644B40E0DDDC621A01042FCC0",
- "v1_mic_short_salt_mic_hmac_key": "CCBA93DACA9023B7633E5408DAD5EFF1FBE3A00E9F76E9AC8507DE5E651A5FD4",
- "v1_signature_identity_token_hmac_key": "E6A09F1A28C2DE26841044AAAC67C4CEDF963992767C960796E204BBA6B61432",
- "v1_signature_section_aes_key": "F610CCD8C43EF4D6E738CCD0C1C24C09"
+ "v1_mic_short_salt_mic_hmac_key": "CCBA93DACA9023B7633E5408DAD5EFF1FBE3A00E9F76E9AC8507DE5E651A5FD4"
},
"v0_adv_salt_hkdf": {
"adv_salt": "1B22",
@@ -413,9 +389,9 @@
"short_salt_nonce": "D983D29159D24108F06DB7D3"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "67BC64977B228888CC5CD6466E3112A2",
- "derived_salt_nonce": "44279BC1027CA9D501947166840CF2AE",
- "derived_salt_third_de": "D656A3294C7E089F94ACF3FDFE93D463",
+ "derived_salt_first_de": "44279BC1027CA9D501947166840CF2AE",
+ "derived_salt_nonce": "C139F7EAF72F11DEC77D5120037603E1",
+ "derived_salt_third_de": "3A6C4695DAA454EE3F48667F48DC6A76",
"section_extended_salt": "D482C2A6D420C5F6993527865FD38E45"
}
},
@@ -431,9 +407,7 @@
"v1_mic_extended_salt_mic_hmac_key": "35AC887D2973BF40F73476F789E095A220919AF1C4A308A20C4B72957A6F0847",
"v1_mic_short_salt_aes_key": "18A8B80F7FFE5B3818837D04BAF23FC8",
"v1_mic_short_salt_identity_token_hmac_key": "5CD8B34230B6BDED50B42E1EAF30F46136432AA2599F66A4F7AEE7442E974BA5",
- "v1_mic_short_salt_mic_hmac_key": "91AC6D599B6D5EDDF95472D946A8C60292896C395DF3216FE6B0297BF0FBC890",
- "v1_signature_identity_token_hmac_key": "848F82B20E686C242A2D7F1933E0CD872DBBDF1E7B436856F19424BD47078DD3",
- "v1_signature_section_aes_key": "7A88551E0D0B4C248BD88C7214423C1F"
+ "v1_mic_short_salt_mic_hmac_key": "91AC6D599B6D5EDDF95472D946A8C60292896C395DF3216FE6B0297BF0FBC890"
},
"v0_adv_salt_hkdf": {
"adv_salt": "7DAA",
@@ -448,9 +422,9 @@
"short_salt_nonce": "412810F614DD5448E7BA6171"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "F613A207D445ED2EC7CBE2A5787EFE60",
- "derived_salt_nonce": "92A3862125E69F44E8E6087DD72828B6",
- "derived_salt_third_de": "54C805F3DE7A3652271EBD5E097A9D95",
+ "derived_salt_first_de": "92A3862125E69F44E8E6087DD72828B6",
+ "derived_salt_nonce": "C251941EBB238F110CE9366366A63243",
+ "derived_salt_third_de": "F287734DEDB07CD0D65B20CF88D4C259",
"section_extended_salt": "8D5F11C001A419648067D4A1966B0EED"
}
},
@@ -466,9 +440,7 @@
"v1_mic_extended_salt_mic_hmac_key": "CD6FA52AA997B94740B8645D647103C671B5795BC46F1E12B7AEFC7067641D96",
"v1_mic_short_salt_aes_key": "6B695E1C8FB3DFDF4C1964B4B3424A03",
"v1_mic_short_salt_identity_token_hmac_key": "900252ED6385B4E457CE563A5E5FC4BD39784EA4BC64958349E7CD427A68493C",
- "v1_mic_short_salt_mic_hmac_key": "72DDECC2AB00222B3C70A9B343664B901614F01CD2E7033934DEC4077D6EC14B",
- "v1_signature_identity_token_hmac_key": "05CB39213C947B9C4D82F311372A1780E4BB4AFE8480747841B0A681FF092A0D",
- "v1_signature_section_aes_key": "E130CCBF8595EEFA23A6925AAD6E394C"
+ "v1_mic_short_salt_mic_hmac_key": "72DDECC2AB00222B3C70A9B343664B901614F01CD2E7033934DEC4077D6EC14B"
},
"v0_adv_salt_hkdf": {
"adv_salt": "C2FA",
@@ -483,9 +455,9 @@
"short_salt_nonce": "9DCF2866433ED70F26A6DCE4"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "8882901B8F1F333196F1B78FC5D012C1",
- "derived_salt_nonce": "EE7A39E6A119642C002E260A972369EC",
- "derived_salt_third_de": "20B064790320BEDAC44CE80C37B4FCC4",
+ "derived_salt_first_de": "EE7A39E6A119642C002E260A972369EC",
+ "derived_salt_nonce": "AA6BC3FE67705C59B0A4D5DDD21EABFC",
+ "derived_salt_third_de": "CF005CCCC3EE42E500B80977BD5CD300",
"section_extended_salt": "1E7F5C402A5D4994A8188E52A0AD2D72"
}
},
@@ -501,9 +473,7 @@
"v1_mic_extended_salt_mic_hmac_key": "A88A2984350349201CBA4531C95928DD34D2536CDC6958D6EB77A992AE1A3815",
"v1_mic_short_salt_aes_key": "ADD9E7D962DD812FBEF7D19E09E5C6E4",
"v1_mic_short_salt_identity_token_hmac_key": "44B9B99CF2B3A3514BF22123387F8D45F2197C71348B8FC3EE885A875AE10451",
- "v1_mic_short_salt_mic_hmac_key": "62EAB192B0DD022F91733FB357D7DD912C94EBCB45B91F04AA5C0C9A9E7A85BE",
- "v1_signature_identity_token_hmac_key": "A427FEC4ED745D0353E0B24CA11D69CE4FFC5117FA26F906B2ED023AE806B854",
- "v1_signature_section_aes_key": "6A4CA8910A38C31640CD06D2045E2D0C"
+ "v1_mic_short_salt_mic_hmac_key": "62EAB192B0DD022F91733FB357D7DD912C94EBCB45B91F04AA5C0C9A9E7A85BE"
},
"v0_adv_salt_hkdf": {
"adv_salt": "0834",
@@ -518,9 +488,9 @@
"short_salt_nonce": "FAE06666F8180F410474F3F9"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "F6BF0314DB3A1C1330A8E6E9A2CE5E7A",
- "derived_salt_nonce": "7D5985FCF0EC057383CF3BAC7B113A43",
- "derived_salt_third_de": "84F6F682DE9C87BDD9AABFFCDC14A822",
+ "derived_salt_first_de": "7D5985FCF0EC057383CF3BAC7B113A43",
+ "derived_salt_nonce": "71F566B1EC33A9019D8E46FD6A0A6303",
+ "derived_salt_third_de": "E3852E0A97BFA1ACBBF16E0E28F713B2",
"section_extended_salt": "CEE571EA0F50EC647161523CBC93367E"
}
},
@@ -536,9 +506,7 @@
"v1_mic_extended_salt_mic_hmac_key": "BF34BAC8C099EFDD3BF41C051675CDEC259C27786E50CD326FFD409AC8ED6026",
"v1_mic_short_salt_aes_key": "F3482440016A1E6CA2E1C79EF3B9F3B2",
"v1_mic_short_salt_identity_token_hmac_key": "BBB7762E694FF6DC4CA9F31A537094364495BADCCB73FC69327C86D8A84FDBC7",
- "v1_mic_short_salt_mic_hmac_key": "E35BD2293E90BD3AC97E98C2983EAA7B7F106AC55B3D88D332539F928CDB8E09",
- "v1_signature_identity_token_hmac_key": "7F9ED591367A0EB8A4F23D145BA75249FD5842B59E8CF8C8BB9E69E942E555AF",
- "v1_signature_section_aes_key": "955D996A026288A647EA707B0A33C9EB"
+ "v1_mic_short_salt_mic_hmac_key": "E35BD2293E90BD3AC97E98C2983EAA7B7F106AC55B3D88D332539F928CDB8E09"
},
"v0_adv_salt_hkdf": {
"adv_salt": "B490",
@@ -553,9 +521,9 @@
"short_salt_nonce": "446675E4C5503E587A7ECD0E"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "CB1D2C8F670DE3E029FA9F91D0FC1494",
- "derived_salt_nonce": "F82D6616C9669EC7708B6C085A45E8F8",
- "derived_salt_third_de": "8ACC53A66823593B9F10479985B6D9B0",
+ "derived_salt_first_de": "F82D6616C9669EC7708B6C085A45E8F8",
+ "derived_salt_nonce": "C1067D74A38ADAF2B94D13EE90391FE3",
+ "derived_salt_third_de": "F881EB2579F5BB1220460292457F3D0E",
"section_extended_salt": "839CE97E9C32C0C81368ED8F8057FC02"
}
},
@@ -571,9 +539,7 @@
"v1_mic_extended_salt_mic_hmac_key": "26B0F39B6880F40FBB6D006D3FDF20FA75D61FE70A21AA279295303AEEAAE57C",
"v1_mic_short_salt_aes_key": "A0082298B9261FA98FF77171F7FC6D35",
"v1_mic_short_salt_identity_token_hmac_key": "DA9EEED06B73B51582DBB749CAE10B30A072B4CFFA13B3055BBB0DCFAE423D7C",
- "v1_mic_short_salt_mic_hmac_key": "28D6FDBDB521166652ABA223A3FB626DAFB5E5600D72B7EEBCE1A95E1E62CBAA",
- "v1_signature_identity_token_hmac_key": "6A82842B2EA2C3C17C26B2867FDCFDC4733F67D4486CB92E1436B282A2E63AED",
- "v1_signature_section_aes_key": "066127DEAD9D2611AFCC7B6216F8F180"
+ "v1_mic_short_salt_mic_hmac_key": "28D6FDBDB521166652ABA223A3FB626DAFB5E5600D72B7EEBCE1A95E1E62CBAA"
},
"v0_adv_salt_hkdf": {
"adv_salt": "3820",
@@ -588,9 +554,9 @@
"short_salt_nonce": "7D16B77869830EE92A4EBFC5"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "659B8A721546530DA9795AF4F85F3172",
- "derived_salt_nonce": "C86E1DF850ABF4050CFF5069AF064842",
- "derived_salt_third_de": "5409D2E3012D6087942D9C6A9AD01055",
+ "derived_salt_first_de": "C86E1DF850ABF4050CFF5069AF064842",
+ "derived_salt_nonce": "7DC134EFC0F29157C468430798662C4F",
+ "derived_salt_third_de": "7B194464D9C546AD53F7A5DC60E7BE26",
"section_extended_salt": "618434E133FE816E6B612F6CA5057231"
}
},
@@ -606,9 +572,7 @@
"v1_mic_extended_salt_mic_hmac_key": "081D31934C99EB52072EC7ED6D5DB9B238E8E9E6A1E66F612C3A53CE664895E5",
"v1_mic_short_salt_aes_key": "87B5CFFAF5E8BF3F76F901E8A8167EE1",
"v1_mic_short_salt_identity_token_hmac_key": "02926351AC11AB30159026DE19F8860825CDDEA95AE3B4054564EE4F42AAB436",
- "v1_mic_short_salt_mic_hmac_key": "E7B62E504AF1F2EF99A86EB37FB14AB5642FCAA0EAD4BB0636D65A41B455A876",
- "v1_signature_identity_token_hmac_key": "8E1E4E0D0082011E31322CF3CCF5CB6DD94F041C529255A3E96215C2EA887B86",
- "v1_signature_section_aes_key": "113BDF8FDEB7FDDC45EB8D0CBFAA35DD"
+ "v1_mic_short_salt_mic_hmac_key": "E7B62E504AF1F2EF99A86EB37FB14AB5642FCAA0EAD4BB0636D65A41B455A876"
},
"v0_adv_salt_hkdf": {
"adv_salt": "34D8",
@@ -623,9 +587,9 @@
"short_salt_nonce": "B61ACE6DA49F07333D36D94E"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "B2DB55B0EE6A19591335FD4A004AED74",
- "derived_salt_nonce": "A18DB7BB4DEB0BAC4C300C1DFF08300B",
- "derived_salt_third_de": "65FE8535564AE2CFB9D3D19CD5F1DF30",
+ "derived_salt_first_de": "A18DB7BB4DEB0BAC4C300C1DFF08300B",
+ "derived_salt_nonce": "EF88BBF79AF04718B399B21F7DBFEF11",
+ "derived_salt_third_de": "C78F13C9E1A96F0A543D1BF802FB098E",
"section_extended_salt": "FC87C8E278F6CF79DFA952B70D3804CD"
}
},
@@ -641,9 +605,7 @@
"v1_mic_extended_salt_mic_hmac_key": "6787832FCFCE8782338608C8F35419AC877A130B4AA7866A3C7CB22ABD67179D",
"v1_mic_short_salt_aes_key": "A3798AC54A640D75DE9460BFF32D5A5E",
"v1_mic_short_salt_identity_token_hmac_key": "43DADD838D1B76E2EAE5A94FB1B839173FBD84CA47BC35CC2AA508D9D3516823",
- "v1_mic_short_salt_mic_hmac_key": "F92977DD9EBAA721832F6E5D0CED0E29F940847AB0FB87494F323EC770EAF6F3",
- "v1_signature_identity_token_hmac_key": "28A9E655ECBB19E0C72E389AF86D18CC1DD99B837CA39F80EFBEF46562ED30DC",
- "v1_signature_section_aes_key": "5882688026AFC05D0A53C691D3C205AF"
+ "v1_mic_short_salt_mic_hmac_key": "F92977DD9EBAA721832F6E5D0CED0E29F940847AB0FB87494F323EC770EAF6F3"
},
"v0_adv_salt_hkdf": {
"adv_salt": "BB77",
@@ -658,9 +620,9 @@
"short_salt_nonce": "C4625EB21060C7AC6EA7734A"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "2040080121468E2437D7C4AF1AB57CBF",
- "derived_salt_nonce": "ED3C0CD523D93752269A7AE558DA70D1",
- "derived_salt_third_de": "7EF0A5B6DB0312484B90580CC5CCDBF6",
+ "derived_salt_first_de": "ED3C0CD523D93752269A7AE558DA70D1",
+ "derived_salt_nonce": "ED6A987A8E7724110981488E35F5EB01",
+ "derived_salt_third_de": "7DDB4A0A52EFF9A5B87A14DC1ADAC67D",
"section_extended_salt": "DD49906314C2165F3AA4887C7C2CBDE9"
}
},
@@ -676,9 +638,7 @@
"v1_mic_extended_salt_mic_hmac_key": "80025D8E9667BD088B5F9D297EEB6AE6323204DED5D6A5823E28896F58D703A4",
"v1_mic_short_salt_aes_key": "3FB967135677C8DDDA512EE813CB7143",
"v1_mic_short_salt_identity_token_hmac_key": "E9A7AF77D3A9368BF553BCA5721EA1F9981877501C5D999B437FCCF1E92086EA",
- "v1_mic_short_salt_mic_hmac_key": "7A93B87E26BF91DFE19D314D0D1E50B85CCE0BCB90AA127706A328F927092721",
- "v1_signature_identity_token_hmac_key": "39062B01DFC854FE594F8D6542FA644CD5C969E6F227511F4865D974CC6DEC2A",
- "v1_signature_section_aes_key": "4B1167F08673796EC85DAFE62137B809"
+ "v1_mic_short_salt_mic_hmac_key": "7A93B87E26BF91DFE19D314D0D1E50B85CCE0BCB90AA127706A328F927092721"
},
"v0_adv_salt_hkdf": {
"adv_salt": "F252",
@@ -693,9 +653,9 @@
"short_salt_nonce": "0956F02109DC68655CB62A5E"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "93BD8DE56900BCEDD84B9A1978AA94EE",
- "derived_salt_nonce": "66881A93AE754517DC4D61AD12F5E742",
- "derived_salt_third_de": "BBFD5FCD7F36BA90D88A9A092F8ECA29",
+ "derived_salt_first_de": "66881A93AE754517DC4D61AD12F5E742",
+ "derived_salt_nonce": "1FD78D5CCEF1F065A195EE71C41DDA33",
+ "derived_salt_third_de": "0D2851E10014226213319C8F7DF7C242",
"section_extended_salt": "4D1E91C6292D4A910828E53CA3F09EE3"
}
},
@@ -711,9 +671,7 @@
"v1_mic_extended_salt_mic_hmac_key": "4B68A5067578BC2F7618F8B47F1FB64F3D304FFAE521225A895555BA5D8D9340",
"v1_mic_short_salt_aes_key": "DE089E6F3B702F1BA314CACA9E7F2788",
"v1_mic_short_salt_identity_token_hmac_key": "B2B5C5D2DA5C6FF0E1AF067248893051884AC530852A1EDD47CB494FAFFE06A1",
- "v1_mic_short_salt_mic_hmac_key": "0263DBCBE0F69B101A639B2CF16346841E5ED159FAC9DCE93F92019A207D5819",
- "v1_signature_identity_token_hmac_key": "E9F139462C5CF1CCB22E216ADB16B13C04206D39DABAFAAD83C342010BDCDD78",
- "v1_signature_section_aes_key": "2DDFA0C2E734D9A8698821320CF1FE8D"
+ "v1_mic_short_salt_mic_hmac_key": "0263DBCBE0F69B101A639B2CF16346841E5ED159FAC9DCE93F92019A207D5819"
},
"v0_adv_salt_hkdf": {
"adv_salt": "3D53",
@@ -728,9 +686,9 @@
"short_salt_nonce": "85714E38445BE9A4C2D012CF"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "A7C1B418774DF806054C029FA82533F7",
- "derived_salt_nonce": "4919EFBFB04402814AB0C6A805820947",
- "derived_salt_third_de": "C55544C75B968C2EAAD88699050D4362",
+ "derived_salt_first_de": "4919EFBFB04402814AB0C6A805820947",
+ "derived_salt_nonce": "DC8D12E01885843F6BEF7B0375467EB5",
+ "derived_salt_third_de": "6EF87BE1D98F36E985CB469549D3AD90",
"section_extended_salt": "2BE31AA1F7D93AB925E36C8E96D2FCC3"
}
},
@@ -746,9 +704,7 @@
"v1_mic_extended_salt_mic_hmac_key": "59DD79D819E0C6E50E4189CC394B7FDE1D1AF17E29C9EEC63727F64430A1DE9A",
"v1_mic_short_salt_aes_key": "AD577ED2A071A7EA5BE2DBE3D9440CAE",
"v1_mic_short_salt_identity_token_hmac_key": "E6A27A0F3EA35DA104296F11B2D3D7104DCDD066FDD3930D2A8B4370C0497F27",
- "v1_mic_short_salt_mic_hmac_key": "F5D81846C62C536E3D6B19F3A33F6DAB14C708EA183E6D598DABD4D567DB2C21",
- "v1_signature_identity_token_hmac_key": "03BD502076C9F2CA53775BB46DB7D0120297DCE95C9D455EBF0D5C588A31FCA7",
- "v1_signature_section_aes_key": "8559B5ADD92D397DE221F606DA353EB6"
+ "v1_mic_short_salt_mic_hmac_key": "F5D81846C62C536E3D6B19F3A33F6DAB14C708EA183E6D598DABD4D567DB2C21"
},
"v0_adv_salt_hkdf": {
"adv_salt": "5B56",
@@ -763,9 +719,9 @@
"short_salt_nonce": "A1FC444CC695E3DA1BB17AF0"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "04AB6D4EDA19FC328F24C74CAC5C5DD8",
- "derived_salt_nonce": "9331647748783F90E401965AC5B87106",
- "derived_salt_third_de": "42CC023EEE82C7E84914B2E8190BF169",
+ "derived_salt_first_de": "9331647748783F90E401965AC5B87106",
+ "derived_salt_nonce": "FD2F8A5562CFCE12B632E8177EC5A661",
+ "derived_salt_third_de": "4703C2215165961932B78DB5FF7B1864",
"section_extended_salt": "B877CD0EC34F48EF8AA9E00297D82C64"
}
},
@@ -781,9 +737,7 @@
"v1_mic_extended_salt_mic_hmac_key": "41DE5A06B3108EFDAFD5B8072E5438D0D411EC26C46C2EF62742D31E7601442D",
"v1_mic_short_salt_aes_key": "2A3B1E230B36FCC470FD65299A7EDA9E",
"v1_mic_short_salt_identity_token_hmac_key": "CCA9DE895F08980C2709A0FD4CF1B835E915BEB5E5BF192D5D30AC918E100C75",
- "v1_mic_short_salt_mic_hmac_key": "80139B754D19E3896A1E1A5025DD04FC8E699B879484D8294A77EDF648E5643B",
- "v1_signature_identity_token_hmac_key": "C6D68149007593DD02EE8790467405844C978F9352DDF2545C813022497DA7D1",
- "v1_signature_section_aes_key": "738E9ABCFB9D5461E3F8E249C5BED287"
+ "v1_mic_short_salt_mic_hmac_key": "80139B754D19E3896A1E1A5025DD04FC8E699B879484D8294A77EDF648E5643B"
},
"v0_adv_salt_hkdf": {
"adv_salt": "77F7",
@@ -798,9 +752,9 @@
"short_salt_nonce": "7B035FDE619F1DC6DDC82276"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "CF464723DEB71D8CB55827A0DF0D10F2",
- "derived_salt_nonce": "5DB21C018408C6854005F5854F4FC0DD",
- "derived_salt_third_de": "4F8C68AF4DFC9F31706823CEC2CFC31F",
+ "derived_salt_first_de": "5DB21C018408C6854005F5854F4FC0DD",
+ "derived_salt_nonce": "D2840BCE7DD91B8D0D251165F61773BD",
+ "derived_salt_third_de": "F4E0AC0A7B6C6DDEF9B669AA826367A0",
"section_extended_salt": "F6F7A20A1113FA0CBADEE03D8737309C"
}
},
@@ -816,9 +770,7 @@
"v1_mic_extended_salt_mic_hmac_key": "020BF714AFCC97E16CFED3A9CEE5490B0C72B649DADCB7F0D31E1B9EFEBC048D",
"v1_mic_short_salt_aes_key": "C82D0BB3858BCF0818755B90B87467AD",
"v1_mic_short_salt_identity_token_hmac_key": "99700748DF89599EC8CD6A0464EC084651BD335DD8D8D82956E405D0994F2325",
- "v1_mic_short_salt_mic_hmac_key": "FC3B070CECBABBF42ADE9193735C6D66322A12A1C3703C59194B823E5BC460E7",
- "v1_signature_identity_token_hmac_key": "700DC0F4F8D9AC2B6A190EC7D041F0E6143C99CBB4C39C83419C5CB2382504CB",
- "v1_signature_section_aes_key": "1D268051F9FCA0FD696862C2DD230F97"
+ "v1_mic_short_salt_mic_hmac_key": "FC3B070CECBABBF42ADE9193735C6D66322A12A1C3703C59194B823E5BC460E7"
},
"v0_adv_salt_hkdf": {
"adv_salt": "0739",
@@ -833,9 +785,9 @@
"short_salt_nonce": "BADA73EFD61495DD37336600"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "22820FA9A35165D2ABF6C7C256681574",
- "derived_salt_nonce": "67DC58AD06F080D8A671163E36A4F0F1",
- "derived_salt_third_de": "B5DE19F288B57D8338FCB5134D7FE17D",
+ "derived_salt_first_de": "67DC58AD06F080D8A671163E36A4F0F1",
+ "derived_salt_nonce": "65D0D2211CD36B0F1BC7A21976B7FA45",
+ "derived_salt_third_de": "296FDA3D86444662B5EC6ADDA22CD804",
"section_extended_salt": "14C2765287EFFFE0153EF93894134505"
}
},
@@ -851,9 +803,7 @@
"v1_mic_extended_salt_mic_hmac_key": "C4C4E9B64AFC367473E38D66B110403284C3C03AE00F1BBADC230C93D5D97BB7",
"v1_mic_short_salt_aes_key": "7365641B35E3A6A5863106192EE1E1D1",
"v1_mic_short_salt_identity_token_hmac_key": "F4DB33EC5EB13864F6B46DCDAFA9399E0B5F17C111D275C9BD906FC46D45D38B",
- "v1_mic_short_salt_mic_hmac_key": "107A3B99DFA9D082BA9C5A52D066F0517EB0448B4CD3CE2671D6E191E2E2ED08",
- "v1_signature_identity_token_hmac_key": "67E13E77F909D2E4A54AF23DF1319098A1CF63EC9D69ADBE1C1993CDA198AD7A",
- "v1_signature_section_aes_key": "0CCE0847357476165E32DAB0D93660A5"
+ "v1_mic_short_salt_mic_hmac_key": "107A3B99DFA9D082BA9C5A52D066F0517EB0448B4CD3CE2671D6E191E2E2ED08"
},
"v0_adv_salt_hkdf": {
"adv_salt": "D429",
@@ -868,9 +818,9 @@
"short_salt_nonce": "38893FBF7D1BEE182635466F"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "70EF8C20670E91B0201666736D87FFF0",
- "derived_salt_nonce": "135C24DF13767FF03E133BB99D20B0F6",
- "derived_salt_third_de": "3B537B5FF5AE9AB51B076C50F88DF467",
+ "derived_salt_first_de": "135C24DF13767FF03E133BB99D20B0F6",
+ "derived_salt_nonce": "159C343847AF15C6B5C65CB5D7A57A29",
+ "derived_salt_third_de": "647FC22F25DDB9E91FF67756949948F8",
"section_extended_salt": "BE300446F843EC41349FC25CBE09D972"
}
},
@@ -886,9 +836,7 @@
"v1_mic_extended_salt_mic_hmac_key": "3041D70D7B869284630A49C8272CAA14F3BA2826BAD8EB8EF3FB2CCBEA2AC1F4",
"v1_mic_short_salt_aes_key": "24B3475C2818DD82B2B861890B521CA6",
"v1_mic_short_salt_identity_token_hmac_key": "17C3C1FEE32642EBAAF0B4D9F769346394FE12D0EF967535CA1AC7C9EDA28770",
- "v1_mic_short_salt_mic_hmac_key": "020D702CD9F9FA8D8D4214F4C2E326DCA16E782AB23DB36CD4A846B36312605B",
- "v1_signature_identity_token_hmac_key": "CBCD66F3A1395C1C119396056A69180BF48BFE314DC331E78E6B81C06A897168",
- "v1_signature_section_aes_key": "7630A756E997DD3EE511628239C2EB42"
+ "v1_mic_short_salt_mic_hmac_key": "020D702CD9F9FA8D8D4214F4C2E326DCA16E782AB23DB36CD4A846B36312605B"
},
"v0_adv_salt_hkdf": {
"adv_salt": "BD6A",
@@ -903,9 +851,9 @@
"short_salt_nonce": "32484DC7B135A80C39081760"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "78CE8B13F129273503E1536058A43149",
- "derived_salt_nonce": "C7F3873BDD429C39E5560D733BD3AFCC",
- "derived_salt_third_de": "9795B0AC7799E44E875429A7A24B1547",
+ "derived_salt_first_de": "C7F3873BDD429C39E5560D733BD3AFCC",
+ "derived_salt_nonce": "6BE2ADB388D90556F2F2772F02F73DD9",
+ "derived_salt_third_de": "E20697392F268ED4982E14B9C9E9F8CD",
"section_extended_salt": "F86AB59158954445BC6366F8E823155F"
}
},
@@ -921,9 +869,7 @@
"v1_mic_extended_salt_mic_hmac_key": "110D45543242D23BD5896052CB4EA44B9DA4AD1B6D6E4D5D795D7F23262FBEA6",
"v1_mic_short_salt_aes_key": "DB686DB1702E76B6A2E835D249E0143B",
"v1_mic_short_salt_identity_token_hmac_key": "4BC8DCD602C7894832F1CEC3D1C715B9EA2DA59AAD81D50108E4CEF6404A92E5",
- "v1_mic_short_salt_mic_hmac_key": "BA49A19BBC61BB50BEC6F2C102E4D099DF3D204E3C5CD6C7CEFF4F22B62B5804",
- "v1_signature_identity_token_hmac_key": "685A096405A541B4BA786E24DA79E8780FCD448563352CEDF12A7746A7808D75",
- "v1_signature_section_aes_key": "737D543B38D9BC756BA6BF8D0E345839"
+ "v1_mic_short_salt_mic_hmac_key": "BA49A19BBC61BB50BEC6F2C102E4D099DF3D204E3C5CD6C7CEFF4F22B62B5804"
},
"v0_adv_salt_hkdf": {
"adv_salt": "6328",
@@ -938,9 +884,9 @@
"short_salt_nonce": "C2533EAB891577CD94E07FDA"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "DD5E5F7DB850DB95FD63F5F6097094CA",
- "derived_salt_nonce": "40BF0CC82087F3D8EE4E1070EE4E0D91",
- "derived_salt_third_de": "D14D7004CA8C8776275296A563487325",
+ "derived_salt_first_de": "40BF0CC82087F3D8EE4E1070EE4E0D91",
+ "derived_salt_nonce": "9AFD882EC93E0ABD587ABD787243AF91",
+ "derived_salt_third_de": "4BE064C0461D4FEBCC51976C4164258C",
"section_extended_salt": "B774D26C0DA92679B9F85BA62EF47501"
}
},
@@ -956,9 +902,7 @@
"v1_mic_extended_salt_mic_hmac_key": "55BD72D9FE9ECD40946E57894549E0B468E0A62E2BEC97FD744FAE68C45783A7",
"v1_mic_short_salt_aes_key": "8240C926DD7C8887AB1D99C10D994BD1",
"v1_mic_short_salt_identity_token_hmac_key": "01F8D73775C16E0B2165023A3FBDF31F2FAB14F9269B26E0397D9F75105CB4A3",
- "v1_mic_short_salt_mic_hmac_key": "4DE142833A00DC99C9A98AB93893AC000BE38083B7F31F3FB112E93B32B7F3E2",
- "v1_signature_identity_token_hmac_key": "2D31AA445FED272EF8CC6C8BD869D30F1F7E2D3D013BC179AD19ECB59DBDA3D8",
- "v1_signature_section_aes_key": "4548FC98A94CEB5037232B78069656F1"
+ "v1_mic_short_salt_mic_hmac_key": "4DE142833A00DC99C9A98AB93893AC000BE38083B7F31F3FB112E93B32B7F3E2"
},
"v0_adv_salt_hkdf": {
"adv_salt": "BB38",
@@ -973,9 +917,9 @@
"short_salt_nonce": "8DC07DE237C5C0973E1F142A"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "53F10F9CA5E77717F922663B49169B24",
- "derived_salt_nonce": "4947E98A773F91E71B31C94603FE2A27",
- "derived_salt_third_de": "82576C8D1063FA9B9CFE825EC4BC8D44",
+ "derived_salt_first_de": "4947E98A773F91E71B31C94603FE2A27",
+ "derived_salt_nonce": "B175ED6D967DF242D51234FAA77C2A39",
+ "derived_salt_third_de": "A4C0DC3762DC34C7DC042BBB0E6E777D",
"section_extended_salt": "71D1DA2128B0D32EDA53591B8B1E2214"
}
},
@@ -991,9 +935,7 @@
"v1_mic_extended_salt_mic_hmac_key": "2827D2973DA029745965B868ED9634EB7394D442143E79E72CD09BBC9F59F0FA",
"v1_mic_short_salt_aes_key": "5013ADB5508464143F2D2A96C807E459",
"v1_mic_short_salt_identity_token_hmac_key": "859EAD154B93576E407471495FD37A54193F9CFEE0078342EF48F677E2A20AEE",
- "v1_mic_short_salt_mic_hmac_key": "2C5C56B063989DD245791D8398E146A585C4F91C8C29240814E606C0AF4CB6C3",
- "v1_signature_identity_token_hmac_key": "A7A0CF65D8121AD48101E5A10512CC09F6B50FC64142A030188B6ACA57EE2219",
- "v1_signature_section_aes_key": "2AD28911FAA0C9D0FD4295ACB09823F2"
+ "v1_mic_short_salt_mic_hmac_key": "2C5C56B063989DD245791D8398E146A585C4F91C8C29240814E606C0AF4CB6C3"
},
"v0_adv_salt_hkdf": {
"adv_salt": "C6E5",
@@ -1008,9 +950,9 @@
"short_salt_nonce": "063DDF6B3AE8F83186DF04FA"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "64F5ECC15737A6BD0CD7148884A7389B",
- "derived_salt_nonce": "FF7ACEE09C30922CC4367ACD2014FBE9",
- "derived_salt_third_de": "A4B5B9D6F05A84BC5AA915AC954CA94C",
+ "derived_salt_first_de": "FF7ACEE09C30922CC4367ACD2014FBE9",
+ "derived_salt_nonce": "B91F31313F9B41169D6DF606ECF6FA56",
+ "derived_salt_third_de": "97BF390E8B861615F714976C471BF5B2",
"section_extended_salt": "2D31BE7AB1A84EDF74D9B66B6DF3E7D4"
}
},
@@ -1026,9 +968,7 @@
"v1_mic_extended_salt_mic_hmac_key": "5F0411676B1FA1C17C08568A88BB477DA9C2016560888D6D791D342CA8C9B812",
"v1_mic_short_salt_aes_key": "AC6C5BDDE8C655C7354206D748065E45",
"v1_mic_short_salt_identity_token_hmac_key": "7F04F2F8BAA4D0F564D291E17E00A4EA547A33D22C144608D6CA64D048571502",
- "v1_mic_short_salt_mic_hmac_key": "6E2F84D1A9BE9234BBD6F8138068B7520B8E056B1B0949EEA5B720972DDA5D5E",
- "v1_signature_identity_token_hmac_key": "983863BD77F0A3A79443DE302AD320F51EDACD4E735385260CD8703414AC897A",
- "v1_signature_section_aes_key": "E62E81BBD49561DD9C20FDD414C1BF8A"
+ "v1_mic_short_salt_mic_hmac_key": "6E2F84D1A9BE9234BBD6F8138068B7520B8E056B1B0949EEA5B720972DDA5D5E"
},
"v0_adv_salt_hkdf": {
"adv_salt": "C20B",
@@ -1043,9 +983,9 @@
"short_salt_nonce": "9EC52904170660BFC9946FD0"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "D3DFF59C7AEA3C8E820B92E76B80ED03",
- "derived_salt_nonce": "16DE3412C96734F65C24B77E6A597BAA",
- "derived_salt_third_de": "73FC3E0306353363F7AA4F8910C1F86F",
+ "derived_salt_first_de": "16DE3412C96734F65C24B77E6A597BAA",
+ "derived_salt_nonce": "FC35C487A60DF7BDB6E513ED17A10595",
+ "derived_salt_third_de": "1E4EE37AD47D9F360495181240E867D1",
"section_extended_salt": "F0181D6CC67C40620D61C0377D06627A"
}
},
@@ -1061,9 +1001,7 @@
"v1_mic_extended_salt_mic_hmac_key": "9EB1CD357FA7BB0AB5B17E95661DB20F30A9AF2DE76F06CDC6FF19D4B533A15F",
"v1_mic_short_salt_aes_key": "949AE776AA4D9F7B80E23D25EC3F0CB9",
"v1_mic_short_salt_identity_token_hmac_key": "58B69B49CF4DB03F5784823D95C94E868AFE1B1AE547131F32A0410CA8B4C205",
- "v1_mic_short_salt_mic_hmac_key": "166A695C761FE5D1C0CA5577CDAE59BADC4D6B34ED6A02FF8C7C5B9F9D829D34",
- "v1_signature_identity_token_hmac_key": "B8A5017381D6CA6EC9A1CB4676F00BA3F3F345BCA33C348CB7CE60AF1A9A7279",
- "v1_signature_section_aes_key": "8BF7B8345F2B0BE8DB7CBE7E96D9E3AC"
+ "v1_mic_short_salt_mic_hmac_key": "166A695C761FE5D1C0CA5577CDAE59BADC4D6B34ED6A02FF8C7C5B9F9D829D34"
},
"v0_adv_salt_hkdf": {
"adv_salt": "30D1",
@@ -1078,9 +1016,9 @@
"short_salt_nonce": "3BE65DEA17A00BF9D49A1C38"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "2B99B7DBFD1AD40DBE93E10E259A6188",
- "derived_salt_nonce": "FA549B59FBB527C1422799C1749F0304",
- "derived_salt_third_de": "7570E91C96332E0248AB19ED32AE2A31",
+ "derived_salt_first_de": "FA549B59FBB527C1422799C1749F0304",
+ "derived_salt_nonce": "88125C143B58DFBDDAE7A24FDF2653F8",
+ "derived_salt_third_de": "5C341199A3A3856045FB160AAA6AAE9D",
"section_extended_salt": "0F3D3F0272988D59B1F3B1C6189E7E89"
}
},
@@ -1096,9 +1034,7 @@
"v1_mic_extended_salt_mic_hmac_key": "A0CB3B5F81BF78216E18957A46B0DC4E38570FE1C69B1D6E06D29C3E2D56C5CC",
"v1_mic_short_salt_aes_key": "A67440D7A821523A84FCA1633161125E",
"v1_mic_short_salt_identity_token_hmac_key": "7DB92F89448C4D4059E48D199C36ACC879F0E450B0D8EECABBEC8C387B6A160F",
- "v1_mic_short_salt_mic_hmac_key": "5378B75FE4E4A8505FAA231CA43CB0DFC1BFB53D07FFA2FC61391214AF21D293",
- "v1_signature_identity_token_hmac_key": "31F296D8BBC8E6C8D877190FF808746B522B781F5D98E234DE608194E60A928D",
- "v1_signature_section_aes_key": "C1E5CF90392F864D15390E483F08562E"
+ "v1_mic_short_salt_mic_hmac_key": "5378B75FE4E4A8505FAA231CA43CB0DFC1BFB53D07FFA2FC61391214AF21D293"
},
"v0_adv_salt_hkdf": {
"adv_salt": "82B1",
@@ -1113,9 +1049,9 @@
"short_salt_nonce": "408CD97C38B4878C593CA9FF"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "2CB4C2F731719080646D748288FD8A09",
- "derived_salt_nonce": "C65251DDEEE83B1C0C7B1B6417EBFAA3",
- "derived_salt_third_de": "E3ADDE6C9B9124A4ABDC4026178DCBAF",
+ "derived_salt_first_de": "C65251DDEEE83B1C0C7B1B6417EBFAA3",
+ "derived_salt_nonce": "EA1AA39F4E30DD10576D6EC81935B45E",
+ "derived_salt_third_de": "16A0182E4E640F81399AA1BB22CC1C59",
"section_extended_salt": "20638EE0EBF0D34A51A53EF7563C15E3"
}
},
@@ -1131,9 +1067,7 @@
"v1_mic_extended_salt_mic_hmac_key": "B151727B25FF7630AACE3803BCB85CB942EA1638E45F1DFD70663D8B8143A6FD",
"v1_mic_short_salt_aes_key": "266FAADB0647BF79A759C9F747F0236B",
"v1_mic_short_salt_identity_token_hmac_key": "F4C7A6A7F8F6FE6C77AB0A95A7A716966C7828BF5E7D1A7204984823639ECB20",
- "v1_mic_short_salt_mic_hmac_key": "3EEF8D8F921DEBFC0B38B2C969488FC7EEAD618EEF189F0D6EE427539133095F",
- "v1_signature_identity_token_hmac_key": "0B4E8908F5113876B22700FD9505474EBC4FAF369F850F6FEAB2CFFC8C218857",
- "v1_signature_section_aes_key": "BAD090C852284E25461B7152B1B4D5D5"
+ "v1_mic_short_salt_mic_hmac_key": "3EEF8D8F921DEBFC0B38B2C969488FC7EEAD618EEF189F0D6EE427539133095F"
},
"v0_adv_salt_hkdf": {
"adv_salt": "3345",
@@ -1148,9 +1082,9 @@
"short_salt_nonce": "7F613AC000317D4C02D5D869"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "C9C285BA4E31877EB2B2E031CFEA0B30",
- "derived_salt_nonce": "35A6A82C0B520C126652A1376AD58DF4",
- "derived_salt_third_de": "2EEC85204A794C280C774C8F2C037204",
+ "derived_salt_first_de": "35A6A82C0B520C126652A1376AD58DF4",
+ "derived_salt_nonce": "E8E6D685CF743FAF150E5CA386287CC4",
+ "derived_salt_third_de": "C6771927D2A972D68FBB85BDA4EDCE87",
"section_extended_salt": "EC6182A82E58AD95A9EDCACA06791FF7"
}
},
@@ -1166,9 +1100,7 @@
"v1_mic_extended_salt_mic_hmac_key": "95EE789B7EE2D188C993872CD7F7928AADCBDB063EF37B9BF4A1E42FE48BF27A",
"v1_mic_short_salt_aes_key": "FC2D104399AED84B2E04C6EC5B1D6434",
"v1_mic_short_salt_identity_token_hmac_key": "C378873BBA5D9686626014ADB68EA7DE71CC905C6212AF0D8659D6D6FF2D364B",
- "v1_mic_short_salt_mic_hmac_key": "954651192C31CDDB288CB209E9644C9BF38566DAA16B828AB8BA51B40760C5DA",
- "v1_signature_identity_token_hmac_key": "77C78656128EDC316410216D4A7ABF39E9A83F5C975B4C0672F4FBA61C855342",
- "v1_signature_section_aes_key": "AF94A29FB78A0FE335FEC5E8B5C8DB93"
+ "v1_mic_short_salt_mic_hmac_key": "954651192C31CDDB288CB209E9644C9BF38566DAA16B828AB8BA51B40760C5DA"
},
"v0_adv_salt_hkdf": {
"adv_salt": "5094",
@@ -1183,9 +1115,9 @@
"short_salt_nonce": "EF90714317133008CC791097"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "FC45A7467A362CB4A7BD1B51717012AF",
- "derived_salt_nonce": "DBDD7802D5F950078856A9182E623962",
- "derived_salt_third_de": "3C2970E4D337BA8D15FB3CEB02305027",
+ "derived_salt_first_de": "DBDD7802D5F950078856A9182E623962",
+ "derived_salt_nonce": "631C4843F5BC902D8D0F246183A821A7",
+ "derived_salt_third_de": "E17E27E10BF00973E96992FF5A3847C8",
"section_extended_salt": "6C61349ED1E575BEE8632E0704C63FA0"
}
},
@@ -1201,9 +1133,7 @@
"v1_mic_extended_salt_mic_hmac_key": "54034D6726FE5EB1B3ABE64CFDA9F59B27363AE3E31134381439AD97E9D102CF",
"v1_mic_short_salt_aes_key": "563BE2F028CCAFACB90F495AB1C6E90D",
"v1_mic_short_salt_identity_token_hmac_key": "179DC9E04EF5284CDB8226A46355E771C618934D46052339E580CFA4A1042921",
- "v1_mic_short_salt_mic_hmac_key": "65238FBDB477D87AA79E6BF2FEFE763D26C95FC2BE480C007318A1FE0A2B2F87",
- "v1_signature_identity_token_hmac_key": "8548488B07F32D86F41B584DACC3FAA5D1FBE99B85CA152CA358C585D27E8C05",
- "v1_signature_section_aes_key": "649A99512E6B524CD6AAFB9349EEC4AD"
+ "v1_mic_short_salt_mic_hmac_key": "65238FBDB477D87AA79E6BF2FEFE763D26C95FC2BE480C007318A1FE0A2B2F87"
},
"v0_adv_salt_hkdf": {
"adv_salt": "BD68",
@@ -1218,9 +1148,9 @@
"short_salt_nonce": "268B4FA03588CAAD663128E5"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "3BB392471E3DC9FD55124F895C7715FB",
- "derived_salt_nonce": "D55F2D27065CEC26BA6A3CDA4A99420C",
- "derived_salt_third_de": "F0C487B8010500EC28E1E162C3ECDA7C",
+ "derived_salt_first_de": "D55F2D27065CEC26BA6A3CDA4A99420C",
+ "derived_salt_nonce": "F03C199D903A06732B46D3E171A58770",
+ "derived_salt_third_de": "1A3216FB4A414A967E8F0362EFAD0A09",
"section_extended_salt": "B6B17B39F5C7EED0037906B686AD650C"
}
},
@@ -1236,9 +1166,7 @@
"v1_mic_extended_salt_mic_hmac_key": "88229AC78E07751043236D0963BC121E2A4EB6489D0C19008E3210F57277CD90",
"v1_mic_short_salt_aes_key": "8C3C9A5E608DDD8195590F70B852C9EF",
"v1_mic_short_salt_identity_token_hmac_key": "9D1C4285C18AA6B490C11557CAD4A909ADC0711EB5F0FE493D0A837B84DB9E1F",
- "v1_mic_short_salt_mic_hmac_key": "95550CE1FF69EAAF1BA1E8AE6560F29F2E068D0C8C78EEA78A6BB8A898B09C83",
- "v1_signature_identity_token_hmac_key": "3893648367F0710E2E1FD4608C7A7892DF05A40CE1DA124C99F7483016B2A9AC",
- "v1_signature_section_aes_key": "9E4A0F1FD50A640743596D58FDAB2A8C"
+ "v1_mic_short_salt_mic_hmac_key": "95550CE1FF69EAAF1BA1E8AE6560F29F2E068D0C8C78EEA78A6BB8A898B09C83"
},
"v0_adv_salt_hkdf": {
"adv_salt": "A14A",
@@ -1253,9 +1181,9 @@
"short_salt_nonce": "689506D55DA43C98229E35E8"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "6C28C3ABDE8FFDA08F9953CB6B24320F",
- "derived_salt_nonce": "410E75A3CA41E3C125C11FBB5FEF5698",
- "derived_salt_third_de": "7A39A6831FE1C42FD179D229AB3BA997",
+ "derived_salt_first_de": "410E75A3CA41E3C125C11FBB5FEF5698",
+ "derived_salt_nonce": "7A6B18317C363C2CBB8EC95BFFB23AB2",
+ "derived_salt_third_de": "1AC78412A9AFEF098568E496C93C9D6E",
"section_extended_salt": "50BAC8BFC5B415D834FB0E214FD0A435"
}
},
@@ -1271,9 +1199,7 @@
"v1_mic_extended_salt_mic_hmac_key": "F62B10BBE49EDB6710E99F12D0A7A5C4F76E05158905098D3B7E7221675057A4",
"v1_mic_short_salt_aes_key": "12A98E8CAE6329555EEB63400A56D627",
"v1_mic_short_salt_identity_token_hmac_key": "401BF4799CB21A9277D3F643D51C5C2E5D574E53F20D5A4D6039BF8501B4B9A4",
- "v1_mic_short_salt_mic_hmac_key": "77937075451137DA6E774BA77C5A25B51896BD85429245EB45FDF8D84D40ECD1",
- "v1_signature_identity_token_hmac_key": "2A938DF61D8F4FC208BB76E2C1509AD85C880079F1C194287CAECE0AB03EFD25",
- "v1_signature_section_aes_key": "305B71CD7CC16B254D7DB937EED3E384"
+ "v1_mic_short_salt_mic_hmac_key": "77937075451137DA6E774BA77C5A25B51896BD85429245EB45FDF8D84D40ECD1"
},
"v0_adv_salt_hkdf": {
"adv_salt": "CE71",
@@ -1288,9 +1214,9 @@
"short_salt_nonce": "C7B4FA588C9333953524A7CA"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "DE16277E7CEC4B1FACAA17BC54474A8E",
- "derived_salt_nonce": "D4FF5F07C589D8138D2FDB2A24BC0C89",
- "derived_salt_third_de": "D53C34584D2EA2FCF446979A19CD7EB4",
+ "derived_salt_first_de": "D4FF5F07C589D8138D2FDB2A24BC0C89",
+ "derived_salt_nonce": "76BA4651FA3CA88C74389A9CB4D908C3",
+ "derived_salt_third_de": "2C92D0F5A9200D68AD59E72B54A8BF27",
"section_extended_salt": "F946C81199DE69612EB07197BC151E2C"
}
},
@@ -1306,9 +1232,7 @@
"v1_mic_extended_salt_mic_hmac_key": "4E98FDB0A10DECFF15DD50201B666FD37DECD7714DC39375807CA551EA4566B2",
"v1_mic_short_salt_aes_key": "FC0DABBB4752FEC56777FB109C1FCF70",
"v1_mic_short_salt_identity_token_hmac_key": "26B67259B23EE7FF7732B63D29FB88C6EFF6B2963742314E7EE443D062D577FE",
- "v1_mic_short_salt_mic_hmac_key": "494B430E241D388B8E15EAC8B79FB531D8202019A03F171A70FEF735DEA4A839",
- "v1_signature_identity_token_hmac_key": "1BB63967B26B3A8182DB6E45CE90217C48F7C7289049669114F2F60D59A4982D",
- "v1_signature_section_aes_key": "C12D865CE3C3B92E3D86E11D31C4B7D0"
+ "v1_mic_short_salt_mic_hmac_key": "494B430E241D388B8E15EAC8B79FB531D8202019A03F171A70FEF735DEA4A839"
},
"v0_adv_salt_hkdf": {
"adv_salt": "AA4B",
@@ -1323,9 +1247,9 @@
"short_salt_nonce": "C9FDD6FB248C1AB3CFF1BEF9"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "90549FA7AC9880D8007E17CDEE74FE89",
- "derived_salt_nonce": "55FB1B0D6681696C15F559A6A0B817BD",
- "derived_salt_third_de": "DA0D6A896BC2718BAD6B2582B3EE79C7",
+ "derived_salt_first_de": "55FB1B0D6681696C15F559A6A0B817BD",
+ "derived_salt_nonce": "32EF206EB8539FFE20419B058B26C028",
+ "derived_salt_third_de": "7E5D3483FDDEF83EEB15B7B59DA7BB5B",
"section_extended_salt": "1FCA0AE82AE6340C91647C5564742C84"
}
},
@@ -1341,9 +1265,7 @@
"v1_mic_extended_salt_mic_hmac_key": "46ECCA964CC3D606C16840222CB68074FE307938809FC97EA68C5E9EAC01E754",
"v1_mic_short_salt_aes_key": "A4EB5E86AF11AB3A4F8D70BA2D37784A",
"v1_mic_short_salt_identity_token_hmac_key": "32E95A4CFC685F4F3987F8D89C13B33C43E5F8BA1A1C76518975667B7EDD5A98",
- "v1_mic_short_salt_mic_hmac_key": "80179BE73B86A9D0AFF72833F594BCF83905914646F4DB288EC11F875F1591BB",
- "v1_signature_identity_token_hmac_key": "94A64B97AA4C3A474CFD819BA6FC161F2E2865B1E2B08B041CA219490F786F6A",
- "v1_signature_section_aes_key": "8025F8AA3FC09DC5EF67F59C63D81FDB"
+ "v1_mic_short_salt_mic_hmac_key": "80179BE73B86A9D0AFF72833F594BCF83905914646F4DB288EC11F875F1591BB"
},
"v0_adv_salt_hkdf": {
"adv_salt": "28B7",
@@ -1358,9 +1280,9 @@
"short_salt_nonce": "911ACEFB50B97795D2A96537"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "5BAA0ED9252F176221DE2D9E287C731A",
- "derived_salt_nonce": "220F098C6ADCF4E24B6762E4CA4DC7A6",
- "derived_salt_third_de": "FDB6D13DB24556E6A1E926CA5080B20E",
+ "derived_salt_first_de": "220F098C6ADCF4E24B6762E4CA4DC7A6",
+ "derived_salt_nonce": "E0F6AE009AF0E329508A916D49CAAF47",
+ "derived_salt_third_de": "336AC0B9D31CEBE2276027DC693562D7",
"section_extended_salt": "C10AF6288A750A0C71B7FBB3057BBAA2"
}
},
@@ -1376,9 +1298,7 @@
"v1_mic_extended_salt_mic_hmac_key": "1BE4DF51E2A244F17793D9C3D1EE2DDC9FC992BFBE378C828DBD1D3121F4D9EC",
"v1_mic_short_salt_aes_key": "FDB274DA8F75B1DA00CEAAD2BDD6BD06",
"v1_mic_short_salt_identity_token_hmac_key": "750DFF2B2046595041B400945833A3434B39D98C23975040C6192F66959E40B7",
- "v1_mic_short_salt_mic_hmac_key": "19F78A8FE548117BA40809FD8F602F2FB9A57F6EFE01756A93A81A6BBEA0BEF6",
- "v1_signature_identity_token_hmac_key": "48FAF93A1F41C85646B0204CCEA6C145E7CEB69BA38F3FCF3979E4F9A34747BC",
- "v1_signature_section_aes_key": "34324281067F3278984109B434594C84"
+ "v1_mic_short_salt_mic_hmac_key": "19F78A8FE548117BA40809FD8F602F2FB9A57F6EFE01756A93A81A6BBEA0BEF6"
},
"v0_adv_salt_hkdf": {
"adv_salt": "9021",
@@ -1393,9 +1313,9 @@
"short_salt_nonce": "760FE743C18D50BC5915DEAA"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "66980E111A8AA401E3B96E7E0EA44FF5",
- "derived_salt_nonce": "51074A20BC98469D5E302D454A661CF1",
- "derived_salt_third_de": "0A7723EEBC848E10D5573084A5AA9326",
+ "derived_salt_first_de": "51074A20BC98469D5E302D454A661CF1",
+ "derived_salt_nonce": "DCD6CB358B33CB33402E96ABDA453544",
+ "derived_salt_third_de": "4B4630D5B804867012BD04E186020C6D",
"section_extended_salt": "3CDABDF68AAB76BA4AD395BEB668D405"
}
},
@@ -1411,9 +1331,7 @@
"v1_mic_extended_salt_mic_hmac_key": "4EFE91D05602871EF06FEE7A9B2DB2ABF8FDA8093467E2B7E5437176D24885AF",
"v1_mic_short_salt_aes_key": "6465CF64ACF1F01D2D5D9D1441B2E4B9",
"v1_mic_short_salt_identity_token_hmac_key": "F5129A6C3923C228366A93A12BA8287B1C2CDA1A6CFDEC305C7FB224D92EDE7B",
- "v1_mic_short_salt_mic_hmac_key": "30876F2D82145F3754EBD7CAF699A3BA64458F0846C9E6047485B5C209F65C1F",
- "v1_signature_identity_token_hmac_key": "AD8B825C5A010150B5F2AF5A3FF073A3EE199654BBBB073E4D902FE4D0AC5A48",
- "v1_signature_section_aes_key": "611B58E120CDE3E26F834932C36566DD"
+ "v1_mic_short_salt_mic_hmac_key": "30876F2D82145F3754EBD7CAF699A3BA64458F0846C9E6047485B5C209F65C1F"
},
"v0_adv_salt_hkdf": {
"adv_salt": "3CA1",
@@ -1428,9 +1346,9 @@
"short_salt_nonce": "72163DAF1F557C67B89BA7B0"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "84D7B207BC3AC861E317C4427A2F3301",
- "derived_salt_nonce": "498C9B6F78B4E5F68FCBDFA5FAD87C0B",
- "derived_salt_third_de": "363C6DADFC2F7B20DB94885929867F1F",
+ "derived_salt_first_de": "498C9B6F78B4E5F68FCBDFA5FAD87C0B",
+ "derived_salt_nonce": "553226292566810FA5145063CC387142",
+ "derived_salt_third_de": "36470C9FD252479124C80237CB09D7B4",
"section_extended_salt": "9435272D1BBC6EA6FB74B94D6AEB5AF9"
}
},
@@ -1446,9 +1364,7 @@
"v1_mic_extended_salt_mic_hmac_key": "D619E81CC454D9988BAEE3126A279B2E06E91555B79E736D3A148BC5562B7640",
"v1_mic_short_salt_aes_key": "7F710EBA3B7926655AB09CBE0B907506",
"v1_mic_short_salt_identity_token_hmac_key": "CD64A6D97487DFA1AB1893D5E00117807F598A2AE118654996BC1E3DE8F8B61A",
- "v1_mic_short_salt_mic_hmac_key": "3C5F8ED49E7B095E21F3DFC00E92C34A6291E6E445FB181EB5612FB45389BC9B",
- "v1_signature_identity_token_hmac_key": "FD265D72D87FBB7503D2B532DF0F03F916FB41D82758D1A6BD1E71AF1C3C99E8",
- "v1_signature_section_aes_key": "C715D287A1C5131332DE38C844EC32D9"
+ "v1_mic_short_salt_mic_hmac_key": "3C5F8ED49E7B095E21F3DFC00E92C34A6291E6E445FB181EB5612FB45389BC9B"
},
"v0_adv_salt_hkdf": {
"adv_salt": "38BA",
@@ -1463,9 +1379,9 @@
"short_salt_nonce": "F93616C2AEC28E78FF0B5D0C"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "8F505CBB29A4CC1CD037D1864D15141C",
- "derived_salt_nonce": "03A12AB647E4834E3E507F1F6EF20F1A",
- "derived_salt_third_de": "654F8A7CD26F23E82163007DD25029B9",
+ "derived_salt_first_de": "03A12AB647E4834E3E507F1F6EF20F1A",
+ "derived_salt_nonce": "9562F5EDAB6DEE92BAC998F980388B64",
+ "derived_salt_third_de": "A469E38314EE63E6A43F6A95FF4DFDBB",
"section_extended_salt": "AD43FF79BFF583BED9ECAA14CDDA4B4A"
}
},
@@ -1481,9 +1397,7 @@
"v1_mic_extended_salt_mic_hmac_key": "208EA31DA316646310DAFE2A4036E5A1556E80020D8EE582DCD9CB6DC475B625",
"v1_mic_short_salt_aes_key": "0B308555FC29F102CF5AF79AA5C5D85C",
"v1_mic_short_salt_identity_token_hmac_key": "5F89875055E105BD96055508E440A6A6B44AC0F867D642FF95EBF249D2A28BD8",
- "v1_mic_short_salt_mic_hmac_key": "54F0E228A0E4761F1694BC2717794E2C79BC9CCA99A0B368A370A83750468223",
- "v1_signature_identity_token_hmac_key": "5838D84FFD60A23225B059EFE52D7FFE71CDFDB599826D8BBCF2E254FA5B72A7",
- "v1_signature_section_aes_key": "0BA6F0BE32E0E8AF932B27A38BFF678C"
+ "v1_mic_short_salt_mic_hmac_key": "54F0E228A0E4761F1694BC2717794E2C79BC9CCA99A0B368A370A83750468223"
},
"v0_adv_salt_hkdf": {
"adv_salt": "1D76",
@@ -1498,9 +1412,9 @@
"short_salt_nonce": "ACF0BFA6CF9267C79BF71814"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "588B14F40986244A6E6E11B59DBE6004",
- "derived_salt_nonce": "8F4780C654ACC3FDC454F59459EE4806",
- "derived_salt_third_de": "433266A654D8821576954A6122E18ED5",
+ "derived_salt_first_de": "8F4780C654ACC3FDC454F59459EE4806",
+ "derived_salt_nonce": "C2EFC744B5AB6FF278FA77B9699C0900",
+ "derived_salt_third_de": "F95FEE2D4E0BB599816ADDB834EF2DE3",
"section_extended_salt": "5D5394D0EF11A3C4C83A1B3F4DD1340D"
}
},
@@ -1516,9 +1430,7 @@
"v1_mic_extended_salt_mic_hmac_key": "2ED15F9F067294394BA5F7EB2F554553CB932E33A8C10E6AC3434FF1A7F8DD2F",
"v1_mic_short_salt_aes_key": "CA73989C086C73B114AC605A79A0F8F2",
"v1_mic_short_salt_identity_token_hmac_key": "63269C17F98AA0029E06EA1B749D8DB6C28ED916906DD28F1A3A8C94E31DACF9",
- "v1_mic_short_salt_mic_hmac_key": "AD1C3A30332FA34408CA62A9C6B9A5263932EEE57829C1195DC74127B1C88671",
- "v1_signature_identity_token_hmac_key": "99B0BDB55C04EAEBA66E40815CA346C596D848044A16F8A8CD1E8F22E4B11F97",
- "v1_signature_section_aes_key": "5F2B10AD3A8C1803B080A1F99239B66C"
+ "v1_mic_short_salt_mic_hmac_key": "AD1C3A30332FA34408CA62A9C6B9A5263932EEE57829C1195DC74127B1C88671"
},
"v0_adv_salt_hkdf": {
"adv_salt": "D451",
@@ -1533,9 +1445,9 @@
"short_salt_nonce": "D13C30E6D309831E754A8B5D"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "22A24B673F12E4066B1058549ACB431A",
- "derived_salt_nonce": "EEF802FFAEBD8AF451F5275934E2F8ED",
- "derived_salt_third_de": "136F8970074C895F2195791B5636A000",
+ "derived_salt_first_de": "EEF802FFAEBD8AF451F5275934E2F8ED",
+ "derived_salt_nonce": "36D1994CAB2262D668498D1F5FD34746",
+ "derived_salt_third_de": "B02329F55BE01E807A24186410DD2511",
"section_extended_salt": "18B6A2DB75BA6F6C2CF40473B7673D48"
}
},
@@ -1551,9 +1463,7 @@
"v1_mic_extended_salt_mic_hmac_key": "1FDF6A45C585A0F1B30CB767AD84410A2B95628635124F63C05F092918768D6E",
"v1_mic_short_salt_aes_key": "2C30551B0B248E7AA0C567A9F1F588A2",
"v1_mic_short_salt_identity_token_hmac_key": "CE516307270755B243FF272BB9A38EDA69BA0E04771E1675B5CFF1218637CC36",
- "v1_mic_short_salt_mic_hmac_key": "BA808DDB430868DEF3BAA9FA53D9FF19BDA2559876360F87DEC0B4D04E6B482B",
- "v1_signature_identity_token_hmac_key": "B90E66D1CC33DB3DEED3F867C42569514764A2E4E26D6E65A5009D3C39EF0BB0",
- "v1_signature_section_aes_key": "5E4075E092E92885446D7D362B52D788"
+ "v1_mic_short_salt_mic_hmac_key": "BA808DDB430868DEF3BAA9FA53D9FF19BDA2559876360F87DEC0B4D04E6B482B"
},
"v0_adv_salt_hkdf": {
"adv_salt": "E077",
@@ -1568,9 +1478,9 @@
"short_salt_nonce": "808310562C7E7A9F797FE241"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "D063EED8AE6DA992FACCD9BEAEFF9D60",
- "derived_salt_nonce": "49FB1F75B6A395722D25D76BF3B5CA35",
- "derived_salt_third_de": "86575CCD43CFD910F9103697669DFE08",
+ "derived_salt_first_de": "49FB1F75B6A395722D25D76BF3B5CA35",
+ "derived_salt_nonce": "4BEDC1594D54BB8D11B1F15F8EC243C3",
+ "derived_salt_third_de": "CA32421825B68C129580B33C6F98B332",
"section_extended_salt": "AB63F5E51D36B4C5CC9C302876BB2C37"
}
},
@@ -1586,9 +1496,7 @@
"v1_mic_extended_salt_mic_hmac_key": "57EEBAEC158DD1DC90F3B1D8884CACB5FA32322E4E7C4EE21AA0EB175232498E",
"v1_mic_short_salt_aes_key": "CB4C2ECD3F7058A3FF10FBA90939B090",
"v1_mic_short_salt_identity_token_hmac_key": "A09E42551B882B1E6FDC075FB2A379332C66F91C3FD2DE496E16AC3C57FB2F88",
- "v1_mic_short_salt_mic_hmac_key": "4746E3679A6D724E53E049DB091AF90619DAB91369DBD18204E346B2BD69FD6E",
- "v1_signature_identity_token_hmac_key": "6944BE9FD3E6F9FE05843758B8185862A8112941E10615767A7E41A76E6B1136",
- "v1_signature_section_aes_key": "4A4A874A9EA2E43F5A9F76E7B2C20283"
+ "v1_mic_short_salt_mic_hmac_key": "4746E3679A6D724E53E049DB091AF90619DAB91369DBD18204E346B2BD69FD6E"
},
"v0_adv_salt_hkdf": {
"adv_salt": "4B85",
@@ -1603,9 +1511,9 @@
"short_salt_nonce": "224F4CDE51A4C3379B542B1E"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "4CED096908CA26C394EC58083D07668B",
- "derived_salt_nonce": "0676D12F882BBE0106CA0BB3138F0BB9",
- "derived_salt_third_de": "5A1A19BD6F8C55822C6B51538206B166",
+ "derived_salt_first_de": "0676D12F882BBE0106CA0BB3138F0BB9",
+ "derived_salt_nonce": "FA2E5ED3B4B6591262DEEEB52D9E5D8F",
+ "derived_salt_third_de": "04AA3C6185C2E6DD3B0AA650F75E9036",
"section_extended_salt": "59D1E9BDA1B9BBC808157A8EFA1D6BC2"
}
},
@@ -1621,9 +1529,7 @@
"v1_mic_extended_salt_mic_hmac_key": "6C6BB48C73B078F00191FCD9E5A6F9E51540963C3B11B242DBC6B8C4EF5DC396",
"v1_mic_short_salt_aes_key": "A75EB826BBE44DA0B11FB0E93B06F661",
"v1_mic_short_salt_identity_token_hmac_key": "3848F69057BE12FF81A53754431C32DFAE36155AB7069ADB0C59A441F3459C68",
- "v1_mic_short_salt_mic_hmac_key": "96EF80B80CB868F76C623029EBBC45793B78D584F43D955930C74EACF1256885",
- "v1_signature_identity_token_hmac_key": "1FC9D1ECDBC54491BE8F60F7663EF74B551FC5734620463D55E6A561A2AF6945",
- "v1_signature_section_aes_key": "72EEC64698FBD42D3A53F84EFB88EC3A"
+ "v1_mic_short_salt_mic_hmac_key": "96EF80B80CB868F76C623029EBBC45793B78D584F43D955930C74EACF1256885"
},
"v0_adv_salt_hkdf": {
"adv_salt": "B65D",
@@ -1638,9 +1544,9 @@
"short_salt_nonce": "3701F88A64084BBC60178848"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "7A718CFDEDF904C7A72BD13FD5DFDF47",
- "derived_salt_nonce": "53C9C943AFE5BE0EF3EEE3859B536A4B",
- "derived_salt_third_de": "42726C30FDF62DBDA0074E6FAE969CEA",
+ "derived_salt_first_de": "53C9C943AFE5BE0EF3EEE3859B536A4B",
+ "derived_salt_nonce": "A37E9E20F8C2049F8AC6F157C94D881F",
+ "derived_salt_third_de": "73FC19D76C553F75F663CF1E39D10782",
"section_extended_salt": "45E80FFC36670147E8D2CFBA526EFD42"
}
},
@@ -1656,9 +1562,7 @@
"v1_mic_extended_salt_mic_hmac_key": "A0C758A7EF5B92B5CB0EB64F5406F63C03CD395DF2D2806661023CDD29A92735",
"v1_mic_short_salt_aes_key": "85F922D62800B1716710D7347534F076",
"v1_mic_short_salt_identity_token_hmac_key": "734B82605001AA6C8380F8D020D3B66D6CC5BF6D841CB54417FF633566EE42E4",
- "v1_mic_short_salt_mic_hmac_key": "97851B6E5D32EDF9858D3261BEC3E9B6D821475F73C248EE1C82C5482C780A1D",
- "v1_signature_identity_token_hmac_key": "0A209406D33340C3CB0052E913F1E9790029DA31184175D8D8E18795251EA194",
- "v1_signature_section_aes_key": "C9385776AA1D74BC77CD1A7985329E16"
+ "v1_mic_short_salt_mic_hmac_key": "97851B6E5D32EDF9858D3261BEC3E9B6D821475F73C248EE1C82C5482C780A1D"
},
"v0_adv_salt_hkdf": {
"adv_salt": "7A7D",
@@ -1673,9 +1577,9 @@
"short_salt_nonce": "0C3874DC89E7793DA6562C3B"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "854BB53F1FFAA03F8E4A4F89B36FC75A",
- "derived_salt_nonce": "2B61315A2A80409D20DD3567EC8612E6",
- "derived_salt_third_de": "68CFF39B8B60522BB864F55909F6A07B",
+ "derived_salt_first_de": "2B61315A2A80409D20DD3567EC8612E6",
+ "derived_salt_nonce": "1B5BF6D3587FC20755E5D673E42623A6",
+ "derived_salt_third_de": "F47192FBB411577A559580D083B14CCF",
"section_extended_salt": "BE6DFEB3BC734140F9F2E7A2C514782A"
}
},
@@ -1691,9 +1595,7 @@
"v1_mic_extended_salt_mic_hmac_key": "BAEDA9CCDD8780DA6D122D777249B462D43E267A60C8AC821B6E49E52FF6E497",
"v1_mic_short_salt_aes_key": "B078CE6C132D1D5F03C7011CD1D1B739",
"v1_mic_short_salt_identity_token_hmac_key": "B2A42460F29197AA8F08367283175BDB97DDCF5716EE7F011EF7D7765336425A",
- "v1_mic_short_salt_mic_hmac_key": "C774F2182514364F7FD446D14E545C6B94DAA8292BD09286B5A06320DF0E6E31",
- "v1_signature_identity_token_hmac_key": "C41617580CDF1D4C81AE49774535BA6A622C319F6F02466905A13F6DFC34D354",
- "v1_signature_section_aes_key": "F438355B1434789B8BA63BFA2FDB4DF4"
+ "v1_mic_short_salt_mic_hmac_key": "C774F2182514364F7FD446D14E545C6B94DAA8292BD09286B5A06320DF0E6E31"
},
"v0_adv_salt_hkdf": {
"adv_salt": "F67E",
@@ -1708,9 +1610,9 @@
"short_salt_nonce": "9A22E33E298A3D2F813E947A"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "BB326463B805B3B120DF3A3C1EA440E5",
- "derived_salt_nonce": "6D533671722212A5B388B94F462B66B0",
- "derived_salt_third_de": "FCCD3C04B6AD3FBB94F5196EE5E5CB39",
+ "derived_salt_first_de": "6D533671722212A5B388B94F462B66B0",
+ "derived_salt_nonce": "DAEA78C485FB32B369B24A79B8B77A94",
+ "derived_salt_third_de": "8EE70884FF5CB56AB93F45BF500F2908",
"section_extended_salt": "E9E85B2DD811EFE01DB44C34D96245D7"
}
},
@@ -1726,9 +1628,7 @@
"v1_mic_extended_salt_mic_hmac_key": "DDF3D0BF099CD0A4AFC96E682BA82414775B21F69F7604CC4EEC3A70AD9F1909",
"v1_mic_short_salt_aes_key": "10D98E2BA375142D53279A7C8BAA18C9",
"v1_mic_short_salt_identity_token_hmac_key": "A3DC4E64B719FC69E60F600ECD6A6C61A1E2DBB03C936A716CDD23BA486BE5DD",
- "v1_mic_short_salt_mic_hmac_key": "56FBCE7F9800AE9985C591692672ADDD87F9E97DAD409DC33A428C2DAE66D4B1",
- "v1_signature_identity_token_hmac_key": "F5E639C319E632569E882D9426505E71A1E283584F4DA0CB23BE6792F4708F19",
- "v1_signature_section_aes_key": "74554872D23253B364539E0DF37FD1B5"
+ "v1_mic_short_salt_mic_hmac_key": "56FBCE7F9800AE9985C591692672ADDD87F9E97DAD409DC33A428C2DAE66D4B1"
},
"v0_adv_salt_hkdf": {
"adv_salt": "2C27",
@@ -1743,9 +1643,9 @@
"short_salt_nonce": "9967714E945DD4F451DCD5D2"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "263AC32CE6FA4D0E5F00671D461C4082",
- "derived_salt_nonce": "D5216F638FB5E3148C23E3568B02DED0",
- "derived_salt_third_de": "F5E3DBC571628A18F0832B3A78BB144D",
+ "derived_salt_first_de": "D5216F638FB5E3148C23E3568B02DED0",
+ "derived_salt_nonce": "161F43DFAF9D8D1B117710F6D16AFF82",
+ "derived_salt_third_de": "B2FBBB74A3DF7B2554FD1619CF9B2E0A",
"section_extended_salt": "50AAE9055970B5F6A79554148AFF1C67"
}
},
@@ -1761,9 +1661,7 @@
"v1_mic_extended_salt_mic_hmac_key": "3D035169BBBADA723E634DCF0E203D7D0F85ABAA4DADC4C33D1DB14908C31D1B",
"v1_mic_short_salt_aes_key": "54AF93FD026428575900009294AF21DA",
"v1_mic_short_salt_identity_token_hmac_key": "B4F6A45A822954C251F04E5F54CCAC07FAD0826B5C323F4A9D2755E0E184893F",
- "v1_mic_short_salt_mic_hmac_key": "6A8B6CAA0F30C013A40BE4535FA727FE47A832C4BAE0878E72ECE9DAC538807D",
- "v1_signature_identity_token_hmac_key": "2EBBD9FA35E24808B06361017D2ED29263F9BD857595AEFC4336740485D3AA41",
- "v1_signature_section_aes_key": "44C1F00F21E21DE548332224F191B56A"
+ "v1_mic_short_salt_mic_hmac_key": "6A8B6CAA0F30C013A40BE4535FA727FE47A832C4BAE0878E72ECE9DAC538807D"
},
"v0_adv_salt_hkdf": {
"adv_salt": "8C48",
@@ -1778,9 +1676,9 @@
"short_salt_nonce": "A8A5A0D023F982987C6C2912"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "731880CCE113A561F779D98EC3BED041",
- "derived_salt_nonce": "1C3A582B0B0E6461B150A1559F122B76",
- "derived_salt_third_de": "374E30A969CBB37DFDDE376E217E4427",
+ "derived_salt_first_de": "1C3A582B0B0E6461B150A1559F122B76",
+ "derived_salt_nonce": "DBC444950FF0803D9E777A6251B9C6C7",
+ "derived_salt_third_de": "104EE82C677F1B2B56228E59B288A558",
"section_extended_salt": "FD6B1023E870A6908EDA3CD8067004BC"
}
},
@@ -1796,9 +1694,7 @@
"v1_mic_extended_salt_mic_hmac_key": "3CED276A31CCFB412EF3E22B0E048907CD73A2BF39955B7C73EFB295689469A5",
"v1_mic_short_salt_aes_key": "9DCD2E8874EE2E6A092618AFAF397D20",
"v1_mic_short_salt_identity_token_hmac_key": "284F83827CAC37A22DC0CC2A3F10BDAC8A0E3859218C5DD5FF1F6277268FAD3D",
- "v1_mic_short_salt_mic_hmac_key": "77FAA24269AE6A5A4B61569DCAF8AA75B155C8D483F32AA26ADDAC49C3AFB3EC",
- "v1_signature_identity_token_hmac_key": "FF533E6454F471C4D5AE1CBE51A085082147480E06881A193CA0375D30305247",
- "v1_signature_section_aes_key": "E0AA39A3E2D60041A8956A6B8A3FE4E4"
+ "v1_mic_short_salt_mic_hmac_key": "77FAA24269AE6A5A4B61569DCAF8AA75B155C8D483F32AA26ADDAC49C3AFB3EC"
},
"v0_adv_salt_hkdf": {
"adv_salt": "BB3E",
@@ -1813,9 +1709,9 @@
"short_salt_nonce": "147C2526D096E43BFB972E05"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "0E396FA0163DD119AF3097362E838146",
- "derived_salt_nonce": "6B87889DA7443CA9F8D9526ED9C86B14",
- "derived_salt_third_de": "9E91F27B911BF1CF6CBE13CF1BF43338",
+ "derived_salt_first_de": "6B87889DA7443CA9F8D9526ED9C86B14",
+ "derived_salt_nonce": "276095EAC698A1BEF012C26DEFE9D5F5",
+ "derived_salt_third_de": "28D3F5B0E50D6CC557AFFB955963CA85",
"section_extended_salt": "72B71CE5E6A3534C17A0A3BDE29FBE0B"
}
},
@@ -1831,9 +1727,7 @@
"v1_mic_extended_salt_mic_hmac_key": "A2B891CA3E764C78518ECB419C76EB9696F5B5489B7DAA59246A775C175AE8CB",
"v1_mic_short_salt_aes_key": "F511DCF5E5239F6F001CCEA3582BC561",
"v1_mic_short_salt_identity_token_hmac_key": "22C4B6CB1135E483F7703A06BC9F71BE65CC7901215DD30047F86ADAAFFF2216",
- "v1_mic_short_salt_mic_hmac_key": "14D1B893A3947846911168A2AD7BF475D5B10B3AED3E16EB13B82A6AE9E3D5ED",
- "v1_signature_identity_token_hmac_key": "2818C76B7C6BBC90069219FD3C9B3630A35582813A71627B00D6C255DE831655",
- "v1_signature_section_aes_key": "AC76472156646E7D1F1E7DEB26409B8F"
+ "v1_mic_short_salt_mic_hmac_key": "14D1B893A3947846911168A2AD7BF475D5B10B3AED3E16EB13B82A6AE9E3D5ED"
},
"v0_adv_salt_hkdf": {
"adv_salt": "08A7",
@@ -1848,9 +1742,9 @@
"short_salt_nonce": "E74FA992910E3A0F20733446"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "BE63ECA3139C3A520E562C416E4BD42A",
- "derived_salt_nonce": "77699741250F48BC8D6C3C275C7129B9",
- "derived_salt_third_de": "2D57D9556AF4D0076136D20FDBBBE600",
+ "derived_salt_first_de": "77699741250F48BC8D6C3C275C7129B9",
+ "derived_salt_nonce": "1EF1B5FCB6812A1F8163FA82369E8636",
+ "derived_salt_third_de": "96390EDD2F59C309ED53818426698320",
"section_extended_salt": "CF0DCBFE60B56E6199A39F86BA47CCEE"
}
},
@@ -1866,9 +1760,7 @@
"v1_mic_extended_salt_mic_hmac_key": "8E377E6E535A84940C721E088559D3AABA4F448464DCE43227B446E8DE373E01",
"v1_mic_short_salt_aes_key": "0CDBB34EE71BF6F99ED10A8DDB0A3D2C",
"v1_mic_short_salt_identity_token_hmac_key": "C3A22D602232DF672FEC71BA616D49C882E9F6140379629A38DCA2DDD8BFB394",
- "v1_mic_short_salt_mic_hmac_key": "CE5169D9EC16AF2D039574A47BD1B892A57AB874A214BDE3C9F078CC527F9EA1",
- "v1_signature_identity_token_hmac_key": "4DF918930A3D2DC8871481B4FEB538331D57759E6F0A9407A06D15CF05809EE2",
- "v1_signature_section_aes_key": "756B3BC24AB2B332F46A02620630465D"
+ "v1_mic_short_salt_mic_hmac_key": "CE5169D9EC16AF2D039574A47BD1B892A57AB874A214BDE3C9F078CC527F9EA1"
},
"v0_adv_salt_hkdf": {
"adv_salt": "8F23",
@@ -1883,9 +1775,9 @@
"short_salt_nonce": "EC8FC7EE9EF97564C0478CFD"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "5DC4750A1046E2318D30B4B2612F4DDA",
- "derived_salt_nonce": "AC3C59C2FB8EBABD7150FD5B35F87323",
- "derived_salt_third_de": "12C1E5CB114FA19F099E4E49C729B365",
+ "derived_salt_first_de": "AC3C59C2FB8EBABD7150FD5B35F87323",
+ "derived_salt_nonce": "20AFB5E3E5E9DACB0B69E9B323A3DEA9",
+ "derived_salt_third_de": "66DAA46C97BB5D2E34F4819EA175B0F6",
"section_extended_salt": "2F130A2F720D4DA91B4C0AAC8B3F2F1F"
}
},
@@ -1901,9 +1793,7 @@
"v1_mic_extended_salt_mic_hmac_key": "F4C3B085E1887428A07A2EF526B4C54AABDC6A596864674297BD7225FC2BA7AA",
"v1_mic_short_salt_aes_key": "19FFFA58D00FE48FC03D4D5D6ABC1D4E",
"v1_mic_short_salt_identity_token_hmac_key": "31D79DD4EDB7BE05562955557ADD32358760D9AE327656F563DF31311AD4D2DA",
- "v1_mic_short_salt_mic_hmac_key": "38846B9D18CEF86540E5F2D2C94E791B1756EB5BB3EEB9D28EF5E60B32D33C8F",
- "v1_signature_identity_token_hmac_key": "2C78F12CB4F9F423FD2143027E04BEFC493FBE189AC118EC4E5BF6E2E2BC6328",
- "v1_signature_section_aes_key": "B8E3B89FE6CB6F9983469499A29084BA"
+ "v1_mic_short_salt_mic_hmac_key": "38846B9D18CEF86540E5F2D2C94E791B1756EB5BB3EEB9D28EF5E60B32D33C8F"
},
"v0_adv_salt_hkdf": {
"adv_salt": "7AF8",
@@ -1918,9 +1808,9 @@
"short_salt_nonce": "D61316F899E53429A2D50DB3"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "70972284567C448B649644F5B6EAB7EB",
- "derived_salt_nonce": "CBF23EE5038C1D5C1D3CB7B1BF408DDF",
- "derived_salt_third_de": "D99B02CA713BA66DC867CC60D2500875",
+ "derived_salt_first_de": "CBF23EE5038C1D5C1D3CB7B1BF408DDF",
+ "derived_salt_nonce": "203DB7AAE5CFFE63A1C54DB52031433E",
+ "derived_salt_third_de": "0730FBFFB78ED743AB007DE161732E4C",
"section_extended_salt": "4C5CD144852DF461550C19312FDE138C"
}
},
@@ -1936,9 +1826,7 @@
"v1_mic_extended_salt_mic_hmac_key": "726C6770FD205CD85F732FED55421A4DB1D7F861E028AE144E883FA88128E099",
"v1_mic_short_salt_aes_key": "0CF33F93B242A4095821CFF75ECB9A95",
"v1_mic_short_salt_identity_token_hmac_key": "A0E1E2FADA968696B8A421D348021783D41BF5B92A8A61CBABAA3A605706FD70",
- "v1_mic_short_salt_mic_hmac_key": "32774F6FC589A6A0F3C57D3A3FBDEAAA8C94CE31BC1BE2E71A770C29A365E8C1",
- "v1_signature_identity_token_hmac_key": "3FA4A5EE8D7CB441D33519D371B615733E82CD99CE5DF704B4BEFFF525B5D484",
- "v1_signature_section_aes_key": "A69439EEE9E503DC46CDDB7220E6C1BF"
+ "v1_mic_short_salt_mic_hmac_key": "32774F6FC589A6A0F3C57D3A3FBDEAAA8C94CE31BC1BE2E71A770C29A365E8C1"
},
"v0_adv_salt_hkdf": {
"adv_salt": "DBA9",
@@ -1953,9 +1841,9 @@
"short_salt_nonce": "EFDA8286D9A7A9B08A098C9E"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "932952F0056579BD3532A369D60A6F70",
- "derived_salt_nonce": "0F8B66EE0EF87D7B6BE39BF707378448",
- "derived_salt_third_de": "DED92B09B5BC920A8AECE3DB8C7AA0C1",
+ "derived_salt_first_de": "0F8B66EE0EF87D7B6BE39BF707378448",
+ "derived_salt_nonce": "23CB9B769A4E9A66CEA86C565BDC48C5",
+ "derived_salt_third_de": "12E4CE14AB7A0022406B96E14728148D",
"section_extended_salt": "CDA0859A2285EADBE17556BD28F0D67F"
}
},
@@ -1971,9 +1859,7 @@
"v1_mic_extended_salt_mic_hmac_key": "2B2F8575D91F2B3964DAB5721DDED08B8C12AE5C0B984DEF3363994729661130",
"v1_mic_short_salt_aes_key": "EDE131CD4C8D0F1251E92C3A3BFF7BE0",
"v1_mic_short_salt_identity_token_hmac_key": "DF5823CB7AFD4442A6BC99342B13B9A94343570D487F47F99B15B41131FDC291",
- "v1_mic_short_salt_mic_hmac_key": "729CDAF9889B1465960C519EA7C5A2D93ECDCF08A252047125284A6A4AFB8423",
- "v1_signature_identity_token_hmac_key": "DFD3C298F8D8AA5091667EA7DA56C1749259021B5D046E18FCF11515606605EC",
- "v1_signature_section_aes_key": "1F0821DA6799721FF225950245DC62A2"
+ "v1_mic_short_salt_mic_hmac_key": "729CDAF9889B1465960C519EA7C5A2D93ECDCF08A252047125284A6A4AFB8423"
},
"v0_adv_salt_hkdf": {
"adv_salt": "4BF9",
@@ -1988,9 +1874,9 @@
"short_salt_nonce": "8D12E132EB3558CB5A2A1A0F"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "A568C7960F41860E4CD438B7E09DA45C",
- "derived_salt_nonce": "82003458C32B5E1408B98668BAFCC9C4",
- "derived_salt_third_de": "BC2E3F5E799FF967924D4C31E7A8A0C7",
+ "derived_salt_first_de": "82003458C32B5E1408B98668BAFCC9C4",
+ "derived_salt_nonce": "1496A29784740BF0745C252F4C74FE6D",
+ "derived_salt_third_de": "29FF39A20399C251B5B11E0F85FDE846",
"section_extended_salt": "CCC4AE71E0147BB16F2EBCCDB9FE31A1"
}
},
@@ -2006,9 +1892,7 @@
"v1_mic_extended_salt_mic_hmac_key": "905E498FA9444EFC1C23D87B1A48DFF08CD0F71199F5D4A33B0A8D9C03B687FA",
"v1_mic_short_salt_aes_key": "D023099AA0859DBB1547E3736DA4D22F",
"v1_mic_short_salt_identity_token_hmac_key": "9446AD8F40DE7801AE8C05F4198221C4545836FAFADCFB6A9F6B6811C17320C8",
- "v1_mic_short_salt_mic_hmac_key": "2F437832F6F6055F3D9FC22E1FF89071C579F9261B200B1289E40BD1987FCE62",
- "v1_signature_identity_token_hmac_key": "08DEBDA02FDB8250E3B6DD38CB14E6423F414733847634154DA243A0A48709CF",
- "v1_signature_section_aes_key": "5AE2258B089A53E623FDE10A849660B9"
+ "v1_mic_short_salt_mic_hmac_key": "2F437832F6F6055F3D9FC22E1FF89071C579F9261B200B1289E40BD1987FCE62"
},
"v0_adv_salt_hkdf": {
"adv_salt": "45BD",
@@ -2023,9 +1907,9 @@
"short_salt_nonce": "DBC275D67654EFB6ECE3CB2A"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "F1C91B43A17919A92B1A663CD5214050",
- "derived_salt_nonce": "07D78BAAB0539B291E17C946F94AD5A3",
- "derived_salt_third_de": "9E1D2E450EAA994518580BE3F95163D4",
+ "derived_salt_first_de": "07D78BAAB0539B291E17C946F94AD5A3",
+ "derived_salt_nonce": "968C04996C0AC99DECE33D502BFAA875",
+ "derived_salt_third_de": "528271566943652BD8197619D3D66FD7",
"section_extended_salt": "98646141DF3AFB3FFBD4DA56C170AC50"
}
},
@@ -2041,9 +1925,7 @@
"v1_mic_extended_salt_mic_hmac_key": "87FA6DA8F450D40085241D55E17EE0F1FAB9B4C51841663CF13DF7D84A03684A",
"v1_mic_short_salt_aes_key": "603CDFF82688718D13120372589636A4",
"v1_mic_short_salt_identity_token_hmac_key": "743FE98375D1C4D41B02AA9A636A92837AB4C5A588664CECF00E216ABDA17DBE",
- "v1_mic_short_salt_mic_hmac_key": "1C67FC0FE2E06FE973DD46D4DB0D72A651376F61C8CEF15002F2FB511C05C303",
- "v1_signature_identity_token_hmac_key": "58B127F990B55B164990917F1C23B485F3EC847DA2E27E0D541FC6F7AD437BBC",
- "v1_signature_section_aes_key": "33F90FC36D254B78F3D0A1CCE47CDF2F"
+ "v1_mic_short_salt_mic_hmac_key": "1C67FC0FE2E06FE973DD46D4DB0D72A651376F61C8CEF15002F2FB511C05C303"
},
"v0_adv_salt_hkdf": {
"adv_salt": "CEF6",
@@ -2058,9 +1940,9 @@
"short_salt_nonce": "5DE9BC53D47908F83224673A"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "4AEE99434AD2D226A375264CBAE0497D",
- "derived_salt_nonce": "0F201B1E244712F3AE2B7C5DE056DE63",
- "derived_salt_third_de": "282638440E3212C29F5E8654C6B8B9FA",
+ "derived_salt_first_de": "0F201B1E244712F3AE2B7C5DE056DE63",
+ "derived_salt_nonce": "9696070803617A9D9339822F010F9676",
+ "derived_salt_third_de": "BD012869E2F6018A824AC87D854262AB",
"section_extended_salt": "1A80A34BAC43864CB5BCFF4547F1B88A"
}
},
@@ -2076,9 +1958,7 @@
"v1_mic_extended_salt_mic_hmac_key": "20C74AAE9D32359A0C9353267458581B4AFE60606060B16060591CB96AC68F97",
"v1_mic_short_salt_aes_key": "11381F1734226C3C2911C7D1280E55C9",
"v1_mic_short_salt_identity_token_hmac_key": "A4E6D4F2475E98ACB5E95D518DEA87E90B1EB7D9D2F5AA1456C609CD5B5351AF",
- "v1_mic_short_salt_mic_hmac_key": "AF6150F86F8AC3AE7BA32219E7B92B158E866EF8CEB36BCEDF6DB456CBBC94BD",
- "v1_signature_identity_token_hmac_key": "6D27EB4BE29E556CE5A41BB89648D91314A73D60FE884CAE79BDC811115CF08B",
- "v1_signature_section_aes_key": "E5C1080FA68F5EBDD6F3D063BBE0EFD7"
+ "v1_mic_short_salt_mic_hmac_key": "AF6150F86F8AC3AE7BA32219E7B92B158E866EF8CEB36BCEDF6DB456CBBC94BD"
},
"v0_adv_salt_hkdf": {
"adv_salt": "DAD4",
@@ -2093,9 +1973,9 @@
"short_salt_nonce": "D7A09F5405418C63649804EA"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "C94F9B608026089CA2BF81CE363295F7",
- "derived_salt_nonce": "302455E6682F71E2A813211487A3026B",
- "derived_salt_third_de": "C289C64BE651B24E7365D79679EFCC46",
+ "derived_salt_first_de": "302455E6682F71E2A813211487A3026B",
+ "derived_salt_nonce": "6A708D03515B95F6CA2F30303C8E7658",
+ "derived_salt_third_de": "2D595576E1140EF70F1CB63449D0A3C8",
"section_extended_salt": "BFB748C26DD77BA9F8C82E7572B14608"
}
},
@@ -2111,9 +1991,7 @@
"v1_mic_extended_salt_mic_hmac_key": "EF9D7AC607E5B37BB21B16409556162C6EBD3DD89144F19365698FB34E06A2B1",
"v1_mic_short_salt_aes_key": "1DFE937349A0968CEA77801B8B51E743",
"v1_mic_short_salt_identity_token_hmac_key": "9D1C138B75F9D255E62CC8806D36727DF5877CD831CF6CBFA9AA1D631D032F32",
- "v1_mic_short_salt_mic_hmac_key": "9D1EFDC00D17243D29DF77EAD25DD34EE1E10E420B047D7C147769A1EC149AF7",
- "v1_signature_identity_token_hmac_key": "E07194FE1E11686C87A325E511C7DB7DABA3B631878CBBC2018B70F5D75B843F",
- "v1_signature_section_aes_key": "34EF3FADD1EA265EA5DB45B3335862B5"
+ "v1_mic_short_salt_mic_hmac_key": "9D1EFDC00D17243D29DF77EAD25DD34EE1E10E420B047D7C147769A1EC149AF7"
},
"v0_adv_salt_hkdf": {
"adv_salt": "D65B",
@@ -2128,9 +2006,9 @@
"short_salt_nonce": "14E6E257738D5C3F8EA61A99"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "37DF3ED46159C7B50B7648454A8E3F6E",
- "derived_salt_nonce": "B19C63BCBC2C5466B7CDD1DD160BC813",
- "derived_salt_third_de": "7A52A770AC31BA84A31ABFA410398E39",
+ "derived_salt_first_de": "B19C63BCBC2C5466B7CDD1DD160BC813",
+ "derived_salt_nonce": "B82C3753A40FF9F21F999A282CEE97D8",
+ "derived_salt_third_de": "5FBC4E19495E6D960BB304DD393D4FF2",
"section_extended_salt": "02017844CC8940E3F95FE27ABF392FDA"
}
},
@@ -2146,9 +2024,7 @@
"v1_mic_extended_salt_mic_hmac_key": "23736395522DF94359771CA21DE0EAD1E27AE0833A8C02ED4407DA7BC71BB971",
"v1_mic_short_salt_aes_key": "917D6DC510048504D748A1251EAC8BBC",
"v1_mic_short_salt_identity_token_hmac_key": "D247C289A7F7C16473E7A8E5F737B2F60F1F281359A2E0E5976CA13E33D0BE48",
- "v1_mic_short_salt_mic_hmac_key": "D917D8FE9CE653BC8AB3109DB07377B95CD9A0E02028432ECF3B71F933AFB548",
- "v1_signature_identity_token_hmac_key": "A9E65F92433276507487CCFB1318EA841496C7C1DED9B963D843F56035BF2889",
- "v1_signature_section_aes_key": "714014241942077D480CBEBA5C7FDA16"
+ "v1_mic_short_salt_mic_hmac_key": "D917D8FE9CE653BC8AB3109DB07377B95CD9A0E02028432ECF3B71F933AFB548"
},
"v0_adv_salt_hkdf": {
"adv_salt": "41D7",
@@ -2163,9 +2039,9 @@
"short_salt_nonce": "2B7D6083221050162AD11C2B"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "1479B6AE3A8F99CC6A421B7654E82B65",
- "derived_salt_nonce": "DE3F081731C6B7E3B2155922FCFBA033",
- "derived_salt_third_de": "B0BF305855ED8EE892A1574BBC35F1CE",
+ "derived_salt_first_de": "DE3F081731C6B7E3B2155922FCFBA033",
+ "derived_salt_nonce": "7667DF339633605205DC92C021EB9597",
+ "derived_salt_third_de": "A1E165AB32A06FED47D9DF97A6214E7B",
"section_extended_salt": "4949EB4D92049664AA78BA61AD0A159F"
}
},
@@ -2181,9 +2057,7 @@
"v1_mic_extended_salt_mic_hmac_key": "8A46B4B1543041B78B3EB07A68F4228B439E5AB139DAD332660482C7FE902FB4",
"v1_mic_short_salt_aes_key": "C5613A5B5FE8DC12550878B35E58E2DA",
"v1_mic_short_salt_identity_token_hmac_key": "A6C66389A16F81596893F988F31B3C3EDC871378DF687B6F92C069FEC3763C56",
- "v1_mic_short_salt_mic_hmac_key": "A309E0EBD1838289BE98D58062298A7BF81743511FFE526162CD57C1A5DA7CD4",
- "v1_signature_identity_token_hmac_key": "2727662B707BBB86574B63B085950181C10A66E95E365BD5E4BE2BD42CFFCD23",
- "v1_signature_section_aes_key": "61539D6CB1E73FC4C5234E1F505EFE83"
+ "v1_mic_short_salt_mic_hmac_key": "A309E0EBD1838289BE98D58062298A7BF81743511FFE526162CD57C1A5DA7CD4"
},
"v0_adv_salt_hkdf": {
"adv_salt": "C590",
@@ -2198,9 +2072,9 @@
"short_salt_nonce": "A58A3AB41FB65068E3B2BE23"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "982466B94926A293CAFAD0D8DDB5E2A0",
- "derived_salt_nonce": "9C14D11F87D3E49D71426882A36B793E",
- "derived_salt_third_de": "53024AF2F7BEFA7D22D9F057CB75B285",
+ "derived_salt_first_de": "9C14D11F87D3E49D71426882A36B793E",
+ "derived_salt_nonce": "9708FA61DA61F274D8CDAA5A68335648",
+ "derived_salt_third_de": "5D70B901D2C8FF4EB44DE1ADEED0858B",
"section_extended_salt": "DCA57C5D334B5501EFA2C6C6CA66AC4E"
}
},
@@ -2216,9 +2090,7 @@
"v1_mic_extended_salt_mic_hmac_key": "7B8ACCDD365A02C8E2DC4D3DA7CCE6D9B7F5C4EBD65A6513DC9992B9CBD1BEAA",
"v1_mic_short_salt_aes_key": "531F84D2E14CC3266E44ECBD6D6DFD96",
"v1_mic_short_salt_identity_token_hmac_key": "03B61A2222358A41D08AFD8F242C1905994D315F544DBE92DE9243EA46F0CDEC",
- "v1_mic_short_salt_mic_hmac_key": "362E7C04F2606012DE5DC59BB8DD1F3B5F5EFE66F6C9427FABD1E5C8F4D2F76A",
- "v1_signature_identity_token_hmac_key": "C8F25EC50826357C319A9AE60C043945C9A8013FAC0E2F2C623E22977E9B76C3",
- "v1_signature_section_aes_key": "E2BDA03CF3781A80500F8ECA73C1A84E"
+ "v1_mic_short_salt_mic_hmac_key": "362E7C04F2606012DE5DC59BB8DD1F3B5F5EFE66F6C9427FABD1E5C8F4D2F76A"
},
"v0_adv_salt_hkdf": {
"adv_salt": "A7B3",
@@ -2233,9 +2105,9 @@
"short_salt_nonce": "C5FBF8210A55A6176527E35A"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "4F8C73FA3C662FC6516B4E7D3333B663",
- "derived_salt_nonce": "01D90834B89356931A7C575478271A43",
- "derived_salt_third_de": "DF71CC3039A2C9EBE8792C639573245B",
+ "derived_salt_first_de": "01D90834B89356931A7C575478271A43",
+ "derived_salt_nonce": "C2C085EB4E265E6E90ED1804AB9B97B1",
+ "derived_salt_third_de": "C6DA37C91D6EA47EE92196A512E44BC7",
"section_extended_salt": "E667569C7C35342D78598CE967DDD6EF"
}
},
@@ -2251,9 +2123,7 @@
"v1_mic_extended_salt_mic_hmac_key": "D980A878C750F1AE2C25A21C6045C8EECD81A0B45F400F10EEFB4222DCAE79BC",
"v1_mic_short_salt_aes_key": "EC807E84AF935FDCF6210EAFC261FC8F",
"v1_mic_short_salt_identity_token_hmac_key": "3683F211CF4FF6728E509372C732404FFD7A49EB6E2A9E011D8A2730A2766488",
- "v1_mic_short_salt_mic_hmac_key": "ED63CB5202AEA05D6A666A9CF66AA76A0D10E6803A7F0A13C6303FC5D149B326",
- "v1_signature_identity_token_hmac_key": "B1CB9EEB37699A13A225BDF36102A6E80610AA9A97B8A6B320E772FFDC6B5972",
- "v1_signature_section_aes_key": "58C40319B83E496188B699111FB9A659"
+ "v1_mic_short_salt_mic_hmac_key": "ED63CB5202AEA05D6A666A9CF66AA76A0D10E6803A7F0A13C6303FC5D149B326"
},
"v0_adv_salt_hkdf": {
"adv_salt": "B8C1",
@@ -2268,9 +2138,9 @@
"short_salt_nonce": "1BEE1C6648B348133DCF4E1F"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "E4EC66F0E365A49CAC1266A28F307798",
- "derived_salt_nonce": "7478C715C11452249E821EA57A9832A4",
- "derived_salt_third_de": "1A1F76CB9C0358FB86E217487B860855",
+ "derived_salt_first_de": "7478C715C11452249E821EA57A9832A4",
+ "derived_salt_nonce": "56901C8F3E071F2A1180BCF6D1393B8D",
+ "derived_salt_third_de": "D9A9C43086C327883391ACAD1ADD2492",
"section_extended_salt": "3E351E1A8DEEF7C30011566EF72CCF33"
}
},
@@ -2286,9 +2156,7 @@
"v1_mic_extended_salt_mic_hmac_key": "378010DBE79B40985A0AEA35F7BDF9791E87ECEE34DB04D82084DEFA57ADB601",
"v1_mic_short_salt_aes_key": "15EAD484945F798DA57C65107009E5CA",
"v1_mic_short_salt_identity_token_hmac_key": "8B9F442893494B0D663B669AEEB2DB66D489B58FACDCFAE8D583019C16E72BEC",
- "v1_mic_short_salt_mic_hmac_key": "B4EE0EBCBE390C849D864EFB7FBAEAC67932631186C729F76F3CA91BBC7680E9",
- "v1_signature_identity_token_hmac_key": "A1673B9103B2253E455B8AAA8C2AE84DDD02BFEA7D69E68FE5D120E0B5E57B80",
- "v1_signature_section_aes_key": "F7CABAA4EBF642DB0BCFA3C0C3EEA99B"
+ "v1_mic_short_salt_mic_hmac_key": "B4EE0EBCBE390C849D864EFB7FBAEAC67932631186C729F76F3CA91BBC7680E9"
},
"v0_adv_salt_hkdf": {
"adv_salt": "B20E",
@@ -2303,9 +2171,9 @@
"short_salt_nonce": "9DFE98ABABD5693077D90FFF"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "013B34285047C393204A70E0056B0308",
- "derived_salt_nonce": "BF792D32CBA8F57028F7EB5BF225CB6F",
- "derived_salt_third_de": "208228C6462B7671161768D2DE2FF375",
+ "derived_salt_first_de": "BF792D32CBA8F57028F7EB5BF225CB6F",
+ "derived_salt_nonce": "4DE0C8E48054D3B27403BDCBD03E7FA2",
+ "derived_salt_third_de": "8BFE26FE23A1D48A36AB5FE162D3EE98",
"section_extended_salt": "973ACE22CDA6F8F49B74ED9878CF3437"
}
},
@@ -2321,9 +2189,7 @@
"v1_mic_extended_salt_mic_hmac_key": "43950758403417002A184DDBC4991FFC5F0B2BC9CC8D2302784D6B59163546BF",
"v1_mic_short_salt_aes_key": "DC18AEDB7EF362A00A8A050D44860877",
"v1_mic_short_salt_identity_token_hmac_key": "CFB49BD705D54AC75EC449E1BC2D84B405FA970C728BCB004884DB70AB4E9C0D",
- "v1_mic_short_salt_mic_hmac_key": "2D9C3E637E1E387EDDD22E586AC029A5FE0FE380502E1F9B1070869806EDA0F9",
- "v1_signature_identity_token_hmac_key": "D8AC46D57D8F97D333705D9E37582923CC64EB9269692E044CB6F42B6EC7A1D3",
- "v1_signature_section_aes_key": "3408666AFBFBA0F7C6B1956FF696A7BE"
+ "v1_mic_short_salt_mic_hmac_key": "2D9C3E637E1E387EDDD22E586AC029A5FE0FE380502E1F9B1070869806EDA0F9"
},
"v0_adv_salt_hkdf": {
"adv_salt": "697D",
@@ -2338,9 +2204,9 @@
"short_salt_nonce": "A07118DCA162260AE3431760"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "E64BA2437291F00276F06A9686145AA7",
- "derived_salt_nonce": "0CAC865313B2A15A5080ADFB9217E5C8",
- "derived_salt_third_de": "6FD2414155FFA0177A9F6417B607C3FD",
+ "derived_salt_first_de": "0CAC865313B2A15A5080ADFB9217E5C8",
+ "derived_salt_nonce": "87AE85CDA77F0ECC8F0A061ECABF8573",
+ "derived_salt_third_de": "18FF91D4C47DF2FC5F8EDCCDBDA603BA",
"section_extended_salt": "AC9AD659EA00A4265C647D5B46D4EB70"
}
},
@@ -2356,9 +2222,7 @@
"v1_mic_extended_salt_mic_hmac_key": "9B25D3914B0CDDBDF5B73F2F95BEDBCFAAE65289D6649696D91C0BCC8926B619",
"v1_mic_short_salt_aes_key": "39C321D60F6E9871C6F0E5F08021894B",
"v1_mic_short_salt_identity_token_hmac_key": "67F7D65051D4339DCFD118B55233214FDF6A4D22104C123490267C0A68029A37",
- "v1_mic_short_salt_mic_hmac_key": "97B7E56E79C9F0E05ADE70F6E85B0104B08150A1BB253451FEE37A02AB2F607E",
- "v1_signature_identity_token_hmac_key": "019A2FF53B587F6B45139C744973D4BE7F6BAD418625181AC3F0404CE042F125",
- "v1_signature_section_aes_key": "FBAF3E389EB39C82A87E36BF26459FEE"
+ "v1_mic_short_salt_mic_hmac_key": "97B7E56E79C9F0E05ADE70F6E85B0104B08150A1BB253451FEE37A02AB2F607E"
},
"v0_adv_salt_hkdf": {
"adv_salt": "C489",
@@ -2373,9 +2237,9 @@
"short_salt_nonce": "5E4D5C6D9A148F2E2695D39F"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "EC8C3C7B3A563015AD914D065F065C42",
- "derived_salt_nonce": "40842C94F4F5046882D39A1F353F5174",
- "derived_salt_third_de": "CF99D9895D39ED5AEB35E4596C63B4FE",
+ "derived_salt_first_de": "40842C94F4F5046882D39A1F353F5174",
+ "derived_salt_nonce": "B8954D19C2705F64153E821B5A639BAD",
+ "derived_salt_third_de": "F1BEC1B77E2C704AC9A08DE96FE359F1",
"section_extended_salt": "FC4A1004C457DF7D85788332C3ABF9FE"
}
},
@@ -2391,9 +2255,7 @@
"v1_mic_extended_salt_mic_hmac_key": "73766408361E3A670635EEB7C9FF70A72116819F94576737C06FE78910063502",
"v1_mic_short_salt_aes_key": "0C8A3C2D081449F4F263E11730F59385",
"v1_mic_short_salt_identity_token_hmac_key": "E46EEBE36262EF589672F67E20DBBE24DC18B863DDF4CF9D8330DCFA3CFAA3B2",
- "v1_mic_short_salt_mic_hmac_key": "5CE78E09F37386D4554DFC39B22E4AAF28F812D335BD372583B7C3F1A1552B9C",
- "v1_signature_identity_token_hmac_key": "B947D986D221E4C06CF27FD3352674D5DE51FF5B14885E36074D363B286B28F1",
- "v1_signature_section_aes_key": "49A0920DE44EA33D0E2FDD6217492837"
+ "v1_mic_short_salt_mic_hmac_key": "5CE78E09F37386D4554DFC39B22E4AAF28F812D335BD372583B7C3F1A1552B9C"
},
"v0_adv_salt_hkdf": {
"adv_salt": "28F7",
@@ -2408,9 +2270,9 @@
"short_salt_nonce": "17F243E45941156F9CB40E66"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "A2D63A55BABCFB0F636B0D3621C4CF0E",
- "derived_salt_nonce": "7DA7DEB96787A854B3B02CD1D3D90BBC",
- "derived_salt_third_de": "4A653154FF6C376DD0EB5CE64FEA70A1",
+ "derived_salt_first_de": "7DA7DEB96787A854B3B02CD1D3D90BBC",
+ "derived_salt_nonce": "E036FE89BCB8E637BB3A4260AA2F8F64",
+ "derived_salt_third_de": "FC2D91839C6FFDAF38099BD1C930B71E",
"section_extended_salt": "8B2303C4EE99348E3B5B34EAF99E29E6"
}
},
@@ -2426,9 +2288,7 @@
"v1_mic_extended_salt_mic_hmac_key": "CAB44B647CEF4A189CF3B89428DFCF4D8D7EF74AA05252D4E2D1473472B17E29",
"v1_mic_short_salt_aes_key": "480B8E97EC00901A06850E0746BADF25",
"v1_mic_short_salt_identity_token_hmac_key": "6CDF2DF6C49DDB32051C172679F731F040549742B92758445712E5380DB052EE",
- "v1_mic_short_salt_mic_hmac_key": "751622D2D36222D292DB1FA835FAC4676610C27FE961AC28B6F19EDE4FE6D233",
- "v1_signature_identity_token_hmac_key": "DB49365A1148EFA93B856ADE848B15DC81143FFED26E29811ED4B400D3C3D880",
- "v1_signature_section_aes_key": "8376773D764F42C0C02209207DE4AE80"
+ "v1_mic_short_salt_mic_hmac_key": "751622D2D36222D292DB1FA835FAC4676610C27FE961AC28B6F19EDE4FE6D233"
},
"v0_adv_salt_hkdf": {
"adv_salt": "FF69",
@@ -2443,9 +2303,9 @@
"short_salt_nonce": "78A4DF6F4645BF6868D3B8C0"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "557438A8D86570CB0179CC69F8B1653F",
- "derived_salt_nonce": "4BB982A676DCF6715DBA55A5DA00D25A",
- "derived_salt_third_de": "F250FD05CF2307636F60750CE5E9B323",
+ "derived_salt_first_de": "4BB982A676DCF6715DBA55A5DA00D25A",
+ "derived_salt_nonce": "0C2C17B473741919BA9627952E5B3023",
+ "derived_salt_third_de": "5DBF07E34FDE980AEE2404C5FEC38B1E",
"section_extended_salt": "941E0C341EB666240EDAAD47239DABD6"
}
},
@@ -2461,9 +2321,7 @@
"v1_mic_extended_salt_mic_hmac_key": "C8C2EBA26F834A9E3C96C2B7E4F697D1A180FC8F854A6169710120DB29BF0C0F",
"v1_mic_short_salt_aes_key": "CA6577851C9BC850A88F485732FB4C52",
"v1_mic_short_salt_identity_token_hmac_key": "EB2B6628523226BE853EC1A2A23C8B8F210B1C4B40EDA72435F65BFEE3AA066D",
- "v1_mic_short_salt_mic_hmac_key": "D17C34BC36306CB1AEC52A7125B0E7D6C9A50F4B9DAC6015BBB9D6E933E7A2DF",
- "v1_signature_identity_token_hmac_key": "1527905BF57561A93D2EE2509172BFB08BEB8D1456D17B0227C3EC621766B850",
- "v1_signature_section_aes_key": "4EACAFCBAEC4739EC7726C78BC27BEA1"
+ "v1_mic_short_salt_mic_hmac_key": "D17C34BC36306CB1AEC52A7125B0E7D6C9A50F4B9DAC6015BBB9D6E933E7A2DF"
},
"v0_adv_salt_hkdf": {
"adv_salt": "581F",
@@ -2478,9 +2336,9 @@
"short_salt_nonce": "BDAD50E1174C4F1A4011335B"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "C92C5055A19F1B8A0FD1F07705B454DA",
- "derived_salt_nonce": "C2603BFA63695C6D3CF9F8C294217640",
- "derived_salt_third_de": "C3509C601D0F373B0875B6B1F7FC7D9D",
+ "derived_salt_first_de": "C2603BFA63695C6D3CF9F8C294217640",
+ "derived_salt_nonce": "46E50973E5C3BF4CED53A4A349C92B6A",
+ "derived_salt_third_de": "0B15F3860198E6B92E4BCEC7D80A5369",
"section_extended_salt": "782DBF528A022F527A00408A045CEDE2"
}
},
@@ -2496,9 +2354,7 @@
"v1_mic_extended_salt_mic_hmac_key": "15F1121625072AE546BE7E9A17CAF4C7D85F084CB0BEA894495F1CC37E97E60F",
"v1_mic_short_salt_aes_key": "6452C325F1DF8A9E0D4B6D4D9EA59A7C",
"v1_mic_short_salt_identity_token_hmac_key": "4397BF6738AC22CFF3C4272C046F488139107476E2566FE66C789E240A624803",
- "v1_mic_short_salt_mic_hmac_key": "A7FD1087175D5FD000D3FB744CA35120E232C54DC167B776F42B9EB5A8453079",
- "v1_signature_identity_token_hmac_key": "368BFF1160B108CE21133C215812D25D36FF6669B72C4B88BACA2D31A597C8A7",
- "v1_signature_section_aes_key": "351F043D8B6435E5E73684B10F79471B"
+ "v1_mic_short_salt_mic_hmac_key": "A7FD1087175D5FD000D3FB744CA35120E232C54DC167B776F42B9EB5A8453079"
},
"v0_adv_salt_hkdf": {
"adv_salt": "DE49",
@@ -2513,9 +2369,9 @@
"short_salt_nonce": "68CB8DB099FC125FE4E2AAC3"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "9DDDB28462DBFCA74F63FC476FFE3D46",
- "derived_salt_nonce": "85989FA93A8C9A85A7DA598529479045",
- "derived_salt_third_de": "FF1A21F20BCB2A86AFF9A29CB42A3C46",
+ "derived_salt_first_de": "85989FA93A8C9A85A7DA598529479045",
+ "derived_salt_nonce": "3ADE9A22162411699F073FD6E348C6DE",
+ "derived_salt_third_de": "EEB1CBD759C6621657D4B4ED808446A7",
"section_extended_salt": "FA9DB8C9A1376BA0265420D4B0588C21"
}
},
@@ -2531,9 +2387,7 @@
"v1_mic_extended_salt_mic_hmac_key": "3A3BDEEC0AC9D04A2EC9E03D8B32BB48BF7EFACEBC4D946D8FF6B814D5661234",
"v1_mic_short_salt_aes_key": "6914FF089524DD20681F6A250A3C2BC0",
"v1_mic_short_salt_identity_token_hmac_key": "0B75CD46D539DECA8A7D14F4F7ECF2F1A2618FE449722CECC2816B5D674EACB2",
- "v1_mic_short_salt_mic_hmac_key": "19D33DE899A50CE211E3BA3727669A6470EE3F52B17AC200E5FCD6FDFD000D5B",
- "v1_signature_identity_token_hmac_key": "324954DBD6C28C2DB4C1918C3C1964FADFCD0DC2CD5F3E1F8EF870DE57059F5A",
- "v1_signature_section_aes_key": "78F98654143E5EA976DC7EF3080C4E99"
+ "v1_mic_short_salt_mic_hmac_key": "19D33DE899A50CE211E3BA3727669A6470EE3F52B17AC200E5FCD6FDFD000D5B"
},
"v0_adv_salt_hkdf": {
"adv_salt": "42AC",
@@ -2548,9 +2402,9 @@
"short_salt_nonce": "6C06CC0653B5BFD0AF6DEEC2"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "F7684DEFAC951359A02D3C3478964CA4",
- "derived_salt_nonce": "89DD24B2821B57501E07BF8913BA5B23",
- "derived_salt_third_de": "381D3B6413BE8703AFFB0D17B38D372B",
+ "derived_salt_first_de": "89DD24B2821B57501E07BF8913BA5B23",
+ "derived_salt_nonce": "365E29DA7DFDDC15863A3883A41A58BA",
+ "derived_salt_third_de": "E881B3BD70143E81F989E809B8BB8F2E",
"section_extended_salt": "58A07B28332F5BEE07B5B2B6501DE060"
}
},
@@ -2566,9 +2420,7 @@
"v1_mic_extended_salt_mic_hmac_key": "FAD94CC0F43729C896A4B38B6FC314A30A6235336FB9144857F74DBA62446C43",
"v1_mic_short_salt_aes_key": "DA65AAFCBBF298770D7E4C20E8ED4015",
"v1_mic_short_salt_identity_token_hmac_key": "A67A0798A0395495824110A9ADE3520DC3E80110046C46C31AE59B133A31D8AD",
- "v1_mic_short_salt_mic_hmac_key": "A2BDDA2BF237949C6F6C25D8A5138966D2E2778FDA5692C61CC8DD0B5CE06CCF",
- "v1_signature_identity_token_hmac_key": "E046453AB6F68FEDB7A960170A2827B28037DDC8DA1E7F73D3CC8C83A15EE813",
- "v1_signature_section_aes_key": "445F1E3B3AA663DDEFE1E5C0F2AB972C"
+ "v1_mic_short_salt_mic_hmac_key": "A2BDDA2BF237949C6F6C25D8A5138966D2E2778FDA5692C61CC8DD0B5CE06CCF"
},
"v0_adv_salt_hkdf": {
"adv_salt": "9EA1",
@@ -2583,9 +2435,9 @@
"short_salt_nonce": "BAB0729453AC44CB60695F7A"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "FF510969FFE54C346C277BCD12314A23",
- "derived_salt_nonce": "C858D33D065BE6BE321F09A182E4E0B5",
- "derived_salt_third_de": "8C972C1345CF4B88552917A6C0AA0132",
+ "derived_salt_first_de": "C858D33D065BE6BE321F09A182E4E0B5",
+ "derived_salt_nonce": "7F812A69ECB4F45986F733BF51367734",
+ "derived_salt_third_de": "51C3547A95EE0B8C954EFFC7FCEB1030",
"section_extended_salt": "947F9A9E1AAC92228E70B97E686092B2"
}
},
@@ -2601,9 +2453,7 @@
"v1_mic_extended_salt_mic_hmac_key": "BF3E481EEB971717B448D4EDB765F2C9291C3165A8C021742E3B33D84474772C",
"v1_mic_short_salt_aes_key": "C30DBBFFD2A16E98895BD9EC2D7E724C",
"v1_mic_short_salt_identity_token_hmac_key": "B0B97E8EF4DCD33E40356CB2D6D7359517FCE3FFC7B8DBD4D73FAE87CE0D0CB5",
- "v1_mic_short_salt_mic_hmac_key": "7AA526E8ACBE888A47E7F042DA3751FBF8988294D8286208122C55C2322D5F25",
- "v1_signature_identity_token_hmac_key": "5EA50122D84E384D7C4B2105A182BEEC7A4D8CBE266B54C51D70F2186CC821C4",
- "v1_signature_section_aes_key": "E9E31EFF10C7F9F2634BDB89CBB407CF"
+ "v1_mic_short_salt_mic_hmac_key": "7AA526E8ACBE888A47E7F042DA3751FBF8988294D8286208122C55C2322D5F25"
},
"v0_adv_salt_hkdf": {
"adv_salt": "C543",
@@ -2618,9 +2468,9 @@
"short_salt_nonce": "FC8A1484820906156C195098"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "E4CC4A82D88EA39A5187A28FBDD40F3D",
- "derived_salt_nonce": "B5807E2B08DE99DC9EA01E9478282DDF",
- "derived_salt_third_de": "E3CC102DB32458B2EAFEEBB6113DCEC2",
+ "derived_salt_first_de": "B5807E2B08DE99DC9EA01E9478282DDF",
+ "derived_salt_nonce": "5D45AF328595815E8F0D6E4C7B47103F",
+ "derived_salt_third_de": "1D8D9A5D1006B350F479BC3EA39F7E1A",
"section_extended_salt": "2E250B90A61C0F513340D930EA6571E4"
}
},
@@ -2636,9 +2486,7 @@
"v1_mic_extended_salt_mic_hmac_key": "D115FE4B3ED3D6FF3C457411F89EEC573FB6B4FF074844250F10D923416A951F",
"v1_mic_short_salt_aes_key": "10F209765DD1E2848DC419C4268771A6",
"v1_mic_short_salt_identity_token_hmac_key": "9FD5A74F6EC544AB29AF7CDF130FEAB9D22B37A4178E7A4C7703DCCE6AEAF4C7",
- "v1_mic_short_salt_mic_hmac_key": "DB1E2E8F93CB7D9891CBC951DDF02E89B0E77F646EDB9BB4A33F936F7A04F693",
- "v1_signature_identity_token_hmac_key": "419EA078DEA8DE15D57ED34A497920F671D83425B204F2C364CA506B2A4FFAE9",
- "v1_signature_section_aes_key": "519EA77C6FFACDF488BC05BD1E45C56D"
+ "v1_mic_short_salt_mic_hmac_key": "DB1E2E8F93CB7D9891CBC951DDF02E89B0E77F646EDB9BB4A33F936F7A04F693"
},
"v0_adv_salt_hkdf": {
"adv_salt": "C2F8",
@@ -2653,9 +2501,9 @@
"short_salt_nonce": "AE407828507152FA369D8AF9"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "93A5A6F16A869EFF9F4147B3BFEE2779",
- "derived_salt_nonce": "51BA87FE075BF338688B780493445A1F",
- "derived_salt_third_de": "534680E817F3E8366D19AA564ECC2ECC",
+ "derived_salt_first_de": "51BA87FE075BF338688B780493445A1F",
+ "derived_salt_nonce": "A23AAC937BA9081501AEC3E3A9A80D8E",
+ "derived_salt_third_de": "B12CA463311ED6E710B1063B8B06CBE4",
"section_extended_salt": "58FE62A2B35E250EC7A5F7885357E659"
}
},
@@ -2671,9 +2519,7 @@
"v1_mic_extended_salt_mic_hmac_key": "4EA92D6148AA2F61D283E1DB8AE6EE1066893C4A898BA503145CE493F2AD5400",
"v1_mic_short_salt_aes_key": "83F56E946E80C574D9A6C89975DEF2DB",
"v1_mic_short_salt_identity_token_hmac_key": "5CA60D709F88F9D07556B4A6E6A4CDE9DF1DBD280BABA39BE47A13FE90E5E27D",
- "v1_mic_short_salt_mic_hmac_key": "33E1065D5BA0349E161E41D873B194379EEF0D2DEE466805D72BBE01F6349F2E",
- "v1_signature_identity_token_hmac_key": "58A6E794B4593108761562A3C74CA4EDD345EFAA08F35E661B97FF113C24B6A6",
- "v1_signature_section_aes_key": "1DCC7FBD666A58BD52FB5EB65A63251D"
+ "v1_mic_short_salt_mic_hmac_key": "33E1065D5BA0349E161E41D873B194379EEF0D2DEE466805D72BBE01F6349F2E"
},
"v0_adv_salt_hkdf": {
"adv_salt": "94D8",
@@ -2688,9 +2534,9 @@
"short_salt_nonce": "E006CD94A4539C0185D8A480"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "28AFE5F7B2249029B8C64188C46E8EA6",
- "derived_salt_nonce": "72B2C14A669C5CA1684D7E4751793E2F",
- "derived_salt_third_de": "93DA04162B18AEE23A3056DD6B53B447",
+ "derived_salt_first_de": "72B2C14A669C5CA1684D7E4751793E2F",
+ "derived_salt_nonce": "5194B6439F2AD168A7811D694F741C30",
+ "derived_salt_third_de": "62C545C7FC6E223FA188C356A58A2A41",
"section_extended_salt": "E3D5BA66BFF1542BB9E864F1718972EE"
}
},
@@ -2706,9 +2552,7 @@
"v1_mic_extended_salt_mic_hmac_key": "5D68D65D0C1926E664FB9DABDEC88B84A1151479045FF66BD520E496DA11BDE1",
"v1_mic_short_salt_aes_key": "4201A2259722BA7647492CD28EEC1AEF",
"v1_mic_short_salt_identity_token_hmac_key": "B827390AAFDFFE4121E501DD5AB0F63FE99812B0AA19EE5D3CCE8371BA8D8565",
- "v1_mic_short_salt_mic_hmac_key": "14E7069440F3314D316424A4C05B14781FB8057BF555056B4DCC1CCC01CEA5F6",
- "v1_signature_identity_token_hmac_key": "CEF4CC2BC66B6DA8CDE0BDC33CDB0544A482E693D0AE8E4776D2C50EB3141EDA",
- "v1_signature_section_aes_key": "26D03E938464532CBED815DE327C0E53"
+ "v1_mic_short_salt_mic_hmac_key": "14E7069440F3314D316424A4C05B14781FB8057BF555056B4DCC1CCC01CEA5F6"
},
"v0_adv_salt_hkdf": {
"adv_salt": "5883",
@@ -2723,9 +2567,9 @@
"short_salt_nonce": "725F8B5D5A48F5BF15805851"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "ECE9232AEB13F080BBA9110F115C1C06",
- "derived_salt_nonce": "B067DB848C67AB62F7D9647AEF6350DD",
- "derived_salt_third_de": "0E171CFAF26BE107633093C5FBE09795",
+ "derived_salt_first_de": "B067DB848C67AB62F7D9647AEF6350DD",
+ "derived_salt_nonce": "02B20DE9FC2A60AA489FF762F0ED849E",
+ "derived_salt_third_de": "95E342A2240A9AE4F172A3748149A03D",
"section_extended_salt": "98564150CD7F85177E2FBCD32D1991AB"
}
},
@@ -2741,9 +2585,7 @@
"v1_mic_extended_salt_mic_hmac_key": "7175D3D814EC85A2D0425C56E3AE38B7380FFF95C51104BAA187095753BA574C",
"v1_mic_short_salt_aes_key": "50628611E98D88D30CF256444B776072",
"v1_mic_short_salt_identity_token_hmac_key": "D8347CF2051CCCE1900812130C256F539172A7937BB9F024C101C61BD11C0767",
- "v1_mic_short_salt_mic_hmac_key": "46D08AF025C2B6D0B27D54ACC927D0FB0CE944D38A5611BB15DC7FB71864D468",
- "v1_signature_identity_token_hmac_key": "8B4C710326841E386DB89A9E34140A3129F740253121E150E59F86AC03B7BE9A",
- "v1_signature_section_aes_key": "F72965CB64E9365256119F29F5355A04"
+ "v1_mic_short_salt_mic_hmac_key": "46D08AF025C2B6D0B27D54ACC927D0FB0CE944D38A5611BB15DC7FB71864D468"
},
"v0_adv_salt_hkdf": {
"adv_salt": "D584",
@@ -2758,9 +2600,9 @@
"short_salt_nonce": "011AB27EBDDCF76BECAC83E1"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "9E43D77377463D53AC57DFA860AC64F2",
- "derived_salt_nonce": "CDABCA09C3FE20F0BFD2F0855C16709B",
- "derived_salt_third_de": "2A7BB2161625A9FA4C944A03780FE0F7",
+ "derived_salt_first_de": "CDABCA09C3FE20F0BFD2F0855C16709B",
+ "derived_salt_nonce": "36FB8E21A9EBECCA5F945D7F32B557A9",
+ "derived_salt_third_de": "8CADB29B7451DF58F0C78099492C8DD9",
"section_extended_salt": "C3AD1D6CF13D0ED5D4B52933E4BE9923"
}
},
@@ -2776,9 +2618,7 @@
"v1_mic_extended_salt_mic_hmac_key": "279FD3C9459F9752565AC221A02805DCB2860DEE54FC5FB6787F3ED26C327463",
"v1_mic_short_salt_aes_key": "9A62AE0DDB8B45D2E6B1A5C34576D29B",
"v1_mic_short_salt_identity_token_hmac_key": "D0FBCE82CFCE356C430CB45A87CAB367C0F43F2D512F996854A1E0CF1AA230F8",
- "v1_mic_short_salt_mic_hmac_key": "35DD70B190738EE311AD7A9169E9831C6DE663A038BC30F744B73686264D868E",
- "v1_signature_identity_token_hmac_key": "57683A7ECCAF59E1233B5972B29A681EFB5C5CCC7841A59F200562F43B604955",
- "v1_signature_section_aes_key": "3992116A5B3CB5E2909EBA148CA3D0C0"
+ "v1_mic_short_salt_mic_hmac_key": "35DD70B190738EE311AD7A9169E9831C6DE663A038BC30F744B73686264D868E"
},
"v0_adv_salt_hkdf": {
"adv_salt": "3484",
@@ -2793,9 +2633,9 @@
"short_salt_nonce": "D060E783D2A0CE06655119EC"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "2B2E1D1168C638E5F96A01EFE95E9198",
- "derived_salt_nonce": "42F31D89F5FF91B8CC141B2555261E94",
- "derived_salt_third_de": "9ECC9D55DFF824E87A7C10427EDB7D26",
+ "derived_salt_first_de": "42F31D89F5FF91B8CC141B2555261E94",
+ "derived_salt_nonce": "C6E20790947281DDA6F1CB22F9363B88",
+ "derived_salt_third_de": "B8C8FC283ED8D8989730939790F1FBA1",
"section_extended_salt": "9CF4B88393E26CA09669F2A24F0893F2"
}
},
@@ -2811,9 +2651,7 @@
"v1_mic_extended_salt_mic_hmac_key": "1FFDBECE1FB5B98AC89B7AC112608F8E7705C1C98F2434DEE5529232CFA16BC7",
"v1_mic_short_salt_aes_key": "3A7608329B686BEEA3B22FE3358B1310",
"v1_mic_short_salt_identity_token_hmac_key": "4B4C530D8FD05FB35D04618172CC0CB522F1DB01D91665941DD62B9179642D3D",
- "v1_mic_short_salt_mic_hmac_key": "386F86CA17343F3A66426BD8B626682D9F4AFDF77AB93AB964FAA3708E9100F2",
- "v1_signature_identity_token_hmac_key": "9D9FBDEAEFA4D07C54302BDC107B47530E461D428A4DCBA4C20B4339053FFBF1",
- "v1_signature_section_aes_key": "D8BB6D91F80D467B55F348501483688A"
+ "v1_mic_short_salt_mic_hmac_key": "386F86CA17343F3A66426BD8B626682D9F4AFDF77AB93AB964FAA3708E9100F2"
},
"v0_adv_salt_hkdf": {
"adv_salt": "1CE3",
@@ -2828,9 +2666,9 @@
"short_salt_nonce": "F4BD7CB2CE01511C1F18A53E"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "541E5A0A4D7EBD30C3366C4628971F0A",
- "derived_salt_nonce": "6D0DE80696F619D2B4CBA1D9DD0B7DE2",
- "derived_salt_third_de": "DC761244E373DB2F195ADA9CCB849BF0",
+ "derived_salt_first_de": "6D0DE80696F619D2B4CBA1D9DD0B7DE2",
+ "derived_salt_nonce": "E848D19F51B760FCECCFD112E0FAB74D",
+ "derived_salt_third_de": "BCF6CFAC7DFB98581E7842166B0682A2",
"section_extended_salt": "829E9AC22DC10ACB10F1FE7EFE014CDF"
}
},
@@ -2846,9 +2684,7 @@
"v1_mic_extended_salt_mic_hmac_key": "EBB43E51498E6753A1877E2C996330A2DF880E41887DDDED4FE7FF6D144CF4DA",
"v1_mic_short_salt_aes_key": "40119DA2ADBBF3E491019863938942E3",
"v1_mic_short_salt_identity_token_hmac_key": "9E82B5C1E723F1B578FE9A8519819B4A5B0D0F6DDAD0147E78BF48F778A38CA0",
- "v1_mic_short_salt_mic_hmac_key": "2EA118DF0241382FDA2AA66599346986048260DACC8B1CA8EB6AF745224DB3FE",
- "v1_signature_identity_token_hmac_key": "F31EB8FBF7E0170E352059413B935E7ADC5C8262D97DE0D81CEBF1176D270352",
- "v1_signature_section_aes_key": "E780DDAB066C5FC692736EBE821C6FF8"
+ "v1_mic_short_salt_mic_hmac_key": "2EA118DF0241382FDA2AA66599346986048260DACC8B1CA8EB6AF745224DB3FE"
},
"v0_adv_salt_hkdf": {
"adv_salt": "8118",
@@ -2863,9 +2699,9 @@
"short_salt_nonce": "E9582F01D2CBE7A7F61C39E8"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "47A9E3DDC054A4E93C11018129263A61",
- "derived_salt_nonce": "04023B2ED6EDFD2654E91BB8604CFCB9",
- "derived_salt_third_de": "FE7BEE522D169D527312F2EA7DCDCC1A",
+ "derived_salt_first_de": "04023B2ED6EDFD2654E91BB8604CFCB9",
+ "derived_salt_nonce": "1FE8E9E2B611876F8EED29E7F098B4F9",
+ "derived_salt_third_de": "27BDB48E9F78B0D247FCDCF8FC2B01C0",
"section_extended_salt": "F9696D41F224F85BF8A5CDB62C171144"
}
},
@@ -2881,9 +2717,7 @@
"v1_mic_extended_salt_mic_hmac_key": "B87017DB360AD3F66B74E6F8779C485573CECE93A9B058E9197E08DE19DC233E",
"v1_mic_short_salt_aes_key": "C1802BB67FA9966A675459E93BB18E7C",
"v1_mic_short_salt_identity_token_hmac_key": "B9BF21CAEA29FDC085FBDD4D26DF28FE548C1B945952FCA25799D13999C1CD30",
- "v1_mic_short_salt_mic_hmac_key": "9CBC02F56E1DE42FA95DABD085E014E5B1FAC3282A37B23D497F49DA81EF42A2",
- "v1_signature_identity_token_hmac_key": "F556B76B0E1EE952A87246820380A53ADFC7928AE9A497F3CDEDCC72CB15F2F3",
- "v1_signature_section_aes_key": "419C27C2E8899D21456C8586BB234079"
+ "v1_mic_short_salt_mic_hmac_key": "9CBC02F56E1DE42FA95DABD085E014E5B1FAC3282A37B23D497F49DA81EF42A2"
},
"v0_adv_salt_hkdf": {
"adv_salt": "218D",
@@ -2898,9 +2732,9 @@
"short_salt_nonce": "B726D470FB3C62E5AEE6F3AB"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "AFBAA77C37E2F2E4C4BEBD7A7A40E8E8",
- "derived_salt_nonce": "666C9A35AA6DA090BEE448E331D2201D",
- "derived_salt_third_de": "89EBB1D894E43156911A709CE348F664",
+ "derived_salt_first_de": "666C9A35AA6DA090BEE448E331D2201D",
+ "derived_salt_nonce": "C2F4985C33BE2A1255E4DA3C037DB5B2",
+ "derived_salt_third_de": "127F3ABDE1874E6D38B50253ABDCFBDC",
"section_extended_salt": "606E4773F79F85943AD21CE692D8F5BC"
}
},
@@ -2916,9 +2750,7 @@
"v1_mic_extended_salt_mic_hmac_key": "1EF4654DD267EF8ABF0C7B6401F0A108AB7FF39D11593C1CB836C1345312F92B",
"v1_mic_short_salt_aes_key": "F7906C1B9382A8AA9E30D58ED937126F",
"v1_mic_short_salt_identity_token_hmac_key": "F26237F030DABB04BCCDAB8B927C86C6C5853C2608C39FC574283140E7CD3D65",
- "v1_mic_short_salt_mic_hmac_key": "387BAD7E4C92B13271493360484B2AD24C8C1DE146CB4AAFD8CB2F9046E57058",
- "v1_signature_identity_token_hmac_key": "E31153728A02DCA8CA2D2F697F0772E77A92C4483B7CB99E61542B79FCDDA123",
- "v1_signature_section_aes_key": "AA93AC9530BC4E822430C4A79A898D03"
+ "v1_mic_short_salt_mic_hmac_key": "387BAD7E4C92B13271493360484B2AD24C8C1DE146CB4AAFD8CB2F9046E57058"
},
"v0_adv_salt_hkdf": {
"adv_salt": "2914",
@@ -2933,9 +2765,9 @@
"short_salt_nonce": "9B0F55835CCBB056C630C3D6"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "5B7A957680DBE5A18F6D47490EBF2839",
- "derived_salt_nonce": "FA772F27245390D3125821597FB8D0F9",
- "derived_salt_third_de": "62B187FEFED14F6E4B292E06BC94A19B",
+ "derived_salt_first_de": "FA772F27245390D3125821597FB8D0F9",
+ "derived_salt_nonce": "C862E33821C3EB820E829C6041880AED",
+ "derived_salt_third_de": "0A1265D0ECDD10D6B761C7D4B69C012E",
"section_extended_salt": "157D4AD5BC9A70486F2E15EFFBA52500"
}
},
@@ -2951,9 +2783,7 @@
"v1_mic_extended_salt_mic_hmac_key": "5CE6F847C64DEAA632CBDA32769F18763E757E80B1746AAB191F638E0833C32E",
"v1_mic_short_salt_aes_key": "0544AFBF786E3F4C01EC50AB2AD04DA2",
"v1_mic_short_salt_identity_token_hmac_key": "A1DB27DB83EE9FD2C756C67CC8FB993286CDAA5895E7C3012E540F32D46EC046",
- "v1_mic_short_salt_mic_hmac_key": "000B1436329CFB45BFAEF8BD5A0DB872D2951E54FF56BEAEFE4063407BB02F5C",
- "v1_signature_identity_token_hmac_key": "A768835197F34ED548D340389920D4128507BE77BC0E13556EF41E95C34CEC3F",
- "v1_signature_section_aes_key": "D23318460ABBDFF8B79B7B7977564BCB"
+ "v1_mic_short_salt_mic_hmac_key": "000B1436329CFB45BFAEF8BD5A0DB872D2951E54FF56BEAEFE4063407BB02F5C"
},
"v0_adv_salt_hkdf": {
"adv_salt": "5EC8",
@@ -2968,9 +2798,9 @@
"short_salt_nonce": "0D5A7220B52241C711F8A7B7"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "7A4EC90B70FA9ACC4A37F691C8A62363",
- "derived_salt_nonce": "90646E267796BB4D06D9DA6332CFC0B8",
- "derived_salt_third_de": "C76CC4B946155D059264B51412240813",
+ "derived_salt_first_de": "90646E267796BB4D06D9DA6332CFC0B8",
+ "derived_salt_nonce": "78CE9A09DDF1400019CDD9A18FF92A99",
+ "derived_salt_third_de": "39CC942CBB6BE7814698ACE3BDBAE76E",
"section_extended_salt": "8B7B2570D21C35A39687CB7725B1D8DC"
}
},
@@ -2986,9 +2816,7 @@
"v1_mic_extended_salt_mic_hmac_key": "D6BF2E0C532A2297799A495F52F5830C47E8D121B07A7600A12ED6DBB6CD1167",
"v1_mic_short_salt_aes_key": "8C05F37E25BBE33BFAD40E3BFFB4EFC1",
"v1_mic_short_salt_identity_token_hmac_key": "5E3DC31AF514DBE21286E86009435844BD96CC31F501B48E0CDCFCE0032D42AA",
- "v1_mic_short_salt_mic_hmac_key": "41C70F6FE4E862553196A69B4B66E32B15384159A6865464EB50824E1AC399DB",
- "v1_signature_identity_token_hmac_key": "7885A9B7E84FB130334AD9AB8BFF1643B3D33D68DE2CD208977656EB5CADA2EE",
- "v1_signature_section_aes_key": "D559EC95E7D9D905C43582B528204FE6"
+ "v1_mic_short_salt_mic_hmac_key": "41C70F6FE4E862553196A69B4B66E32B15384159A6865464EB50824E1AC399DB"
},
"v0_adv_salt_hkdf": {
"adv_salt": "D1C8",
@@ -3003,9 +2831,9 @@
"short_salt_nonce": "FC424C642B26FDAE820AF1AB"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "F352BAC3D04AAB1663A78964ACD27F3D",
- "derived_salt_nonce": "BEB93BCF36574D428781A04841770194",
- "derived_salt_third_de": "21E4F9FB37F092DD846235297297FBED",
+ "derived_salt_first_de": "BEB93BCF36574D428781A04841770194",
+ "derived_salt_nonce": "19C8AF0C686239DB7D13C3D04BF5107E",
+ "derived_salt_third_de": "347FD7A132A3C83FDB13CC6F50BCCC8E",
"section_extended_salt": "A20F1A436E3CD07AF356935EA412F986"
}
},
@@ -3021,9 +2849,7 @@
"v1_mic_extended_salt_mic_hmac_key": "DD85470E8F3EDAF23FE25157B8FBD1A7C241E7B1C31EC2A198F345D750D57A32",
"v1_mic_short_salt_aes_key": "88A8BC6F73692B9E4BA3D932F34A1C12",
"v1_mic_short_salt_identity_token_hmac_key": "06E05118607B678C75BE26876B469D29C7E039EA5BF642B2339CA4D30EFF432F",
- "v1_mic_short_salt_mic_hmac_key": "B24E520B572C5A64C652D5F7E95A58FD937569779213CD79DD435503B05F8678",
- "v1_signature_identity_token_hmac_key": "F75DDF545736C151BD76F0AF6D952DF525D7F53F0393F42346B6DE51D410F6F8",
- "v1_signature_section_aes_key": "972451D672CCDB9E7CA5D68C7CA291CD"
+ "v1_mic_short_salt_mic_hmac_key": "B24E520B572C5A64C652D5F7E95A58FD937569779213CD79DD435503B05F8678"
},
"v0_adv_salt_hkdf": {
"adv_salt": "AA55",
@@ -3038,9 +2864,9 @@
"short_salt_nonce": "41FC0F8BC4B6ADD56E767CA0"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "8CA3B5B9AD5A244C7C81A4CECDFBA881",
- "derived_salt_nonce": "B19D427E9CB92F7F2D1F433BBFC1C484",
- "derived_salt_third_de": "5EE54CE1EE6359617F847ACF515237C4",
+ "derived_salt_first_de": "B19D427E9CB92F7F2D1F433BBFC1C484",
+ "derived_salt_nonce": "0180B3FEFF5DF0A0EAC789F07A4FC4DA",
+ "derived_salt_third_de": "6C82C46B961ED9B08C5F1C5416B04F06",
"section_extended_salt": "05FE726BE045EDBE53C924C94DCC01FF"
}
},
@@ -3056,9 +2882,7 @@
"v1_mic_extended_salt_mic_hmac_key": "F7F9A514455AE831234376BDFBC64A3639D7E2C4274851EBCB7EFB14D3392FD7",
"v1_mic_short_salt_aes_key": "2385AEC92C92345F560C4D292D3C4C98",
"v1_mic_short_salt_identity_token_hmac_key": "45B7159DC874A72B0F232DB37EEABFC00B801453BBE78B57A5C08B2FB8AE626A",
- "v1_mic_short_salt_mic_hmac_key": "0352B8CEE18F5D2A75B5D055806611BEF436A2F2172447236E6C929B41C00FF5",
- "v1_signature_identity_token_hmac_key": "789B7C2BBEA40F1E9D8C35CA4F6A80734F4A0EC11973F49EEA988DAC0EF10877",
- "v1_signature_section_aes_key": "9AE1BC3E1532FC968486276BE96AEB6C"
+ "v1_mic_short_salt_mic_hmac_key": "0352B8CEE18F5D2A75B5D055806611BEF436A2F2172447236E6C929B41C00FF5"
},
"v0_adv_salt_hkdf": {
"adv_salt": "0733",
@@ -3073,9 +2897,9 @@
"short_salt_nonce": "4B223F4BE10514D1B8407002"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "4BF6F87B0CD12A0AE66AB6C80C976E33",
- "derived_salt_nonce": "3AFAF9158C6986EC11F22D14ECD21D29",
- "derived_salt_third_de": "EF28C13335BB6321FAB46EF2A859DD58",
+ "derived_salt_first_de": "3AFAF9158C6986EC11F22D14ECD21D29",
+ "derived_salt_nonce": "15F95BD4DC60A06BC8FAD859149922D3",
+ "derived_salt_third_de": "0FE0D08D88F3F1BD96D6F14751E456AB",
"section_extended_salt": "0DB9FC8CF99C093927DFE9CC092C008D"
}
},
@@ -3091,9 +2915,7 @@
"v1_mic_extended_salt_mic_hmac_key": "8CB11E1B229A46BF2620483B959201465A44A31F6FB9E675539B7F27439C8B0E",
"v1_mic_short_salt_aes_key": "2C9851D5CD39BB2E1FEC593F79F23085",
"v1_mic_short_salt_identity_token_hmac_key": "70315F0D044A3F478184A76892BB4204A5CC1E4542CDA99595196323C0770B31",
- "v1_mic_short_salt_mic_hmac_key": "FCAD5AB2AF31EC2C2649B9DEFFCCA9865B74E332E81474E8FBD7356D33F88DE6",
- "v1_signature_identity_token_hmac_key": "80F9B9285D8DBF0666839F92E2D2752EF249FCB127503BFF41A629DFFAA1CB45",
- "v1_signature_section_aes_key": "B519FDA43EE7B072ACB8A855A0820016"
+ "v1_mic_short_salt_mic_hmac_key": "FCAD5AB2AF31EC2C2649B9DEFFCCA9865B74E332E81474E8FBD7356D33F88DE6"
},
"v0_adv_salt_hkdf": {
"adv_salt": "0F09",
@@ -3108,9 +2930,9 @@
"short_salt_nonce": "C2DA1EC8766C69498CB6EB47"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "E732FDC269D45C4213635D2D35B4A8C1",
- "derived_salt_nonce": "FF25222D3D493186CBEA2C203733BA86",
- "derived_salt_third_de": "02BD9E3B04444BF78D059BB4336D1D14",
+ "derived_salt_first_de": "FF25222D3D493186CBEA2C203733BA86",
+ "derived_salt_nonce": "2B399736365DB535FF13F1B8C8FB8064",
+ "derived_salt_third_de": "2B76F7B38F137E5C3005E0F83EFA40A8",
"section_extended_salt": "BF79E23A91CD05105EDB13CCE3CB6B16"
}
},
@@ -3126,9 +2948,7 @@
"v1_mic_extended_salt_mic_hmac_key": "73494C1A3BF1D355985E61D009736941FFB39A3251D915D7EB3E555E887117BA",
"v1_mic_short_salt_aes_key": "622187A9DC8AF81E033888DDFE504F77",
"v1_mic_short_salt_identity_token_hmac_key": "0BD43C55C16C95BDA2A6DFF39A9FCAE21CDF87DE842C35694698B2B53D1FDFAE",
- "v1_mic_short_salt_mic_hmac_key": "9900746A6567048FAC292563E40333B948F5D3EDA89BB8D8E5B159441D5EECE6",
- "v1_signature_identity_token_hmac_key": "9A2793927FD494FB1AFB2EB7F7B91319A231EC88B1A22B3E42293FC1EE81E388",
- "v1_signature_section_aes_key": "0F5A12959E5210EC994C97724D3E2A96"
+ "v1_mic_short_salt_mic_hmac_key": "9900746A6567048FAC292563E40333B948F5D3EDA89BB8D8E5B159441D5EECE6"
},
"v0_adv_salt_hkdf": {
"adv_salt": "2B8A",
@@ -3143,9 +2963,9 @@
"short_salt_nonce": "2183C1252F128A480A53CCDF"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "1033FF7BE57DFD280D3F1AAF407831C0",
- "derived_salt_nonce": "E6D9C880D2182F448A6615F7FE10BE69",
- "derived_salt_third_de": "63A9C49B9F470EA354637612725729A0",
+ "derived_salt_first_de": "E6D9C880D2182F448A6615F7FE10BE69",
+ "derived_salt_nonce": "14158D5D5894E474C6891EBD74668B7B",
+ "derived_salt_third_de": "1BFEF72C28BFE52B7348DFD91086DE4A",
"section_extended_salt": "9AFB8618127F357D0060618E781CA599"
}
},
@@ -3161,9 +2981,7 @@
"v1_mic_extended_salt_mic_hmac_key": "5D193196A34E4437DB7034795266C7C62DA3605D9F5818C2D433292E610A3762",
"v1_mic_short_salt_aes_key": "B29FFF8F001C4726749A2FE7047A82D1",
"v1_mic_short_salt_identity_token_hmac_key": "F1A90E829ED67B49C445D7DEBDAFC88E3BCF1974F0ED1EB9643730BF6EB3B90D",
- "v1_mic_short_salt_mic_hmac_key": "BAB6DA7601191FDEB4344FAA163E818C9BF9B1D5B0AB1EE8D9F4A1C1D30A86E7",
- "v1_signature_identity_token_hmac_key": "8C6832DB6955803AB934B98378B7F81F3710C65947A8B2C6132525B163F62CD6",
- "v1_signature_section_aes_key": "8916CE7332C1F924DCC17E68D999EE7D"
+ "v1_mic_short_salt_mic_hmac_key": "BAB6DA7601191FDEB4344FAA163E818C9BF9B1D5B0AB1EE8D9F4A1C1D30A86E7"
},
"v0_adv_salt_hkdf": {
"adv_salt": "C270",
@@ -3178,9 +2996,9 @@
"short_salt_nonce": "7803F0E069FF172CCF47AA05"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "6D93C32275239B6A82508FEFDFEC112A",
- "derived_salt_nonce": "D2C45BD5079FE6B2D69E4F5EA192CE07",
- "derived_salt_third_de": "2CF3FE0C1E6689361B50D2B558FCA09B",
+ "derived_salt_first_de": "D2C45BD5079FE6B2D69E4F5EA192CE07",
+ "derived_salt_nonce": "27627442B7C08B0654B9691430A555EF",
+ "derived_salt_third_de": "FF65B3D3F44314D9DA6A113FD547AAF3",
"section_extended_salt": "23FF70E4526E9961792744D2C7534BFE"
}
},
@@ -3196,9 +3014,7 @@
"v1_mic_extended_salt_mic_hmac_key": "693EB813C40F33650699205BD038EC0D09989F8BA0A6B50741EC8FE1211DFDFE",
"v1_mic_short_salt_aes_key": "ADCB2F8D667D5DBABAACD28F72C2E11D",
"v1_mic_short_salt_identity_token_hmac_key": "0501458FF851D1C3C83C1BB65F55495E75C24778AA9B8FF0488FFB7F119B574E",
- "v1_mic_short_salt_mic_hmac_key": "2ABF2E7A68FC09E88AC1122710BC7DD9F57FB8E150F699D5DC0DA5A560C76044",
- "v1_signature_identity_token_hmac_key": "A267DB5025D3F87D12966C3853FB9449AE1FC4B654428CFAD44AB3AF6020D572",
- "v1_signature_section_aes_key": "9E6F27E4E0118B8507146871CCF84E2B"
+ "v1_mic_short_salt_mic_hmac_key": "2ABF2E7A68FC09E88AC1122710BC7DD9F57FB8E150F699D5DC0DA5A560C76044"
},
"v0_adv_salt_hkdf": {
"adv_salt": "81AB",
@@ -3213,9 +3029,9 @@
"short_salt_nonce": "8F039D3FA934BC8560918D45"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "FC34E69B7FA34B4341D2D33D5D591D46",
- "derived_salt_nonce": "9BF73251EFD5E11020390188ADFF72C6",
- "derived_salt_third_de": "C80634B6E96C0D7EA25C5C47F7389B01",
+ "derived_salt_first_de": "9BF73251EFD5E11020390188ADFF72C6",
+ "derived_salt_nonce": "5FDDE9E2452C27E07187F78EEB0C8789",
+ "derived_salt_third_de": "2B2E00F9A6CB1B6EAD4FC460A9CE3E7D",
"section_extended_salt": "DF9DAD95D8384321FED7873DFC750B66"
}
},
@@ -3231,9 +3047,7 @@
"v1_mic_extended_salt_mic_hmac_key": "DD1077C4A05C4701B7EE34561CCC9D4D46C80E522414CC1053A2A3C3F918E24E",
"v1_mic_short_salt_aes_key": "EBDF2DEB57C82EC7633A337346495321",
"v1_mic_short_salt_identity_token_hmac_key": "B5A4831B58E71C5EDF3206D25CD378D2E569EC32E8E10E173C82527AF7DE59E5",
- "v1_mic_short_salt_mic_hmac_key": "FD94C8A62DD00C251DAD6ED9DC459E1C06C2E625C24D3EA902122BE1B1EAFC13",
- "v1_signature_identity_token_hmac_key": "40BACB42A697D9B7322FFC8196CCE5ADCA84758196F9211572C51D1E96683536",
- "v1_signature_section_aes_key": "ECC4297E2036A147E17C48550B32C9C6"
+ "v1_mic_short_salt_mic_hmac_key": "FD94C8A62DD00C251DAD6ED9DC459E1C06C2E625C24D3EA902122BE1B1EAFC13"
},
"v0_adv_salt_hkdf": {
"adv_salt": "D77E",
@@ -3248,9 +3062,9 @@
"short_salt_nonce": "DFDD3C550621ED62C9452D36"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "CF6F2269AE6601323D59347A6B698B6E",
- "derived_salt_nonce": "8E899705639BD06B26439FA131EE2A2C",
- "derived_salt_third_de": "B00A28308187B862E7F971387CA44CC4",
+ "derived_salt_first_de": "8E899705639BD06B26439FA131EE2A2C",
+ "derived_salt_nonce": "871958BBD6565508CD570B6EB075EE88",
+ "derived_salt_third_de": "B285A1EA4996C46C35EA5B5F8396C37B",
"section_extended_salt": "B543154433E370E6D7A97B46A73F5652"
}
},
@@ -3266,9 +3080,7 @@
"v1_mic_extended_salt_mic_hmac_key": "893E8AFB307BF4C011CDA36140152CF892F2356DCB8B87AB8D3B0CEFAE7D23CB",
"v1_mic_short_salt_aes_key": "33EB408FAD37D1FF76A27B2CF7D421F8",
"v1_mic_short_salt_identity_token_hmac_key": "9EEE04B64C468770682AF45F96D7DCA1D6EF53D4C592A2AE7A172A0DEBF10E21",
- "v1_mic_short_salt_mic_hmac_key": "74655FA0D30D1E2DF0DA9747A3966C9111693B8E00704DC61D2156009D9F26BD",
- "v1_signature_identity_token_hmac_key": "801294C7CA501C3BF20F57BA2B0E2FA09F13722B3655F625AE8E17C8A79B1154",
- "v1_signature_section_aes_key": "44D3F5A168DD048901D7FDA07952634C"
+ "v1_mic_short_salt_mic_hmac_key": "74655FA0D30D1E2DF0DA9747A3966C9111693B8E00704DC61D2156009D9F26BD"
},
"v0_adv_salt_hkdf": {
"adv_salt": "ADEB",
@@ -3283,9 +3095,9 @@
"short_salt_nonce": "A4E6F88E3225FACE356BFDA4"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "3885509BA1045121AE5CED396AA039EA",
- "derived_salt_nonce": "679DF301EEF3F51C43FFC1701A184685",
- "derived_salt_third_de": "441CDFC937345A037284C3E35698448A",
+ "derived_salt_first_de": "679DF301EEF3F51C43FFC1701A184685",
+ "derived_salt_nonce": "8B4063B4DDC226BF01A547B773260452",
+ "derived_salt_third_de": "758E6DE8B331CF70607DA50056C2D02C",
"section_extended_salt": "B43C581ACFC8A74540852A7DC924867C"
}
},
@@ -3301,9 +3113,7 @@
"v1_mic_extended_salt_mic_hmac_key": "DB771C4FBA0936936876181EDB642A4C3C018561845F35B3D0DC07D7EE4840F6",
"v1_mic_short_salt_aes_key": "AF688DBBE5AE004BAD0D5E49B6F7D234",
"v1_mic_short_salt_identity_token_hmac_key": "F09E67D34142371B649BC5A06BF027125CAC81971B962F469FB2BAAF1F077B1C",
- "v1_mic_short_salt_mic_hmac_key": "DCE2001156BB5C30969FA012D8FE16DB5C599574CC46B61FBA232AE2B184C1F9",
- "v1_signature_identity_token_hmac_key": "9E7CA4743B0670FB181CDA233640CBADE4A81E38D4EB4F4BE6188B5548EDC765",
- "v1_signature_section_aes_key": "2523471EBF1ED605399CD39AF0B624B0"
+ "v1_mic_short_salt_mic_hmac_key": "DCE2001156BB5C30969FA012D8FE16DB5C599574CC46B61FBA232AE2B184C1F9"
},
"v0_adv_salt_hkdf": {
"adv_salt": "16D1",
@@ -3318,9 +3128,9 @@
"short_salt_nonce": "483CBFACEC1D1468DBCD4FDA"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "03E5070187884CD433FBD176EA5E4A63",
- "derived_salt_nonce": "D116BF3B37136C23AB77038C81C6FD87",
- "derived_salt_third_de": "2ADBC8DF751DBB23172BBC87F1E6E77E",
+ "derived_salt_first_de": "D116BF3B37136C23AB77038C81C6FD87",
+ "derived_salt_nonce": "E2955A1A94D3C8F4902EAD1F71A142FB",
+ "derived_salt_third_de": "3D15FF1B1BD78D4FCB4FB66BA048B652",
"section_extended_salt": "87CA5497D5CE54D75C0B0DBD6CF07B39"
}
},
@@ -3336,9 +3146,7 @@
"v1_mic_extended_salt_mic_hmac_key": "4FDE5BCD4A8CD9B8B0212BCA23A7A9EA49D7C039EBC90AABFECD864F594C5203",
"v1_mic_short_salt_aes_key": "8D5372501B32F7A568349C722CB0F695",
"v1_mic_short_salt_identity_token_hmac_key": "596A1BE90D3806E242B88A5CCE32BC8A2C4FAE2E17BF48B06AEAAD2C5E122032",
- "v1_mic_short_salt_mic_hmac_key": "B00D35A3F49F08FBDAFDABB7ECA0A04297850A433A6D11C9FF8F6819F8C39C2B",
- "v1_signature_identity_token_hmac_key": "7509D7C29027BC944390DD51AD6F3804D429C3EFBBFE24AD1AFF25FF40C6A8DE",
- "v1_signature_section_aes_key": "EC1FA7F03A5E90DBABFFF5EC2495AB5E"
+ "v1_mic_short_salt_mic_hmac_key": "B00D35A3F49F08FBDAFDABB7ECA0A04297850A433A6D11C9FF8F6819F8C39C2B"
},
"v0_adv_salt_hkdf": {
"adv_salt": "9F21",
@@ -3353,9 +3161,9 @@
"short_salt_nonce": "02C4AC240A3874E831DF38DB"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "FB3EA084403FA66ECB1C15F9D0AC8396",
- "derived_salt_nonce": "4443A4454CF2F491410423CD5F2AE9D0",
- "derived_salt_third_de": "90A8D37E587090B7E6D636C6F8D27114",
+ "derived_salt_first_de": "4443A4454CF2F491410423CD5F2AE9D0",
+ "derived_salt_nonce": "08C1CFB262121A7A634BC18894C4AC77",
+ "derived_salt_third_de": "6FE14A26FC7FBC443A0ABC2EC983BC59",
"section_extended_salt": "38DA154EAF77D994EBBF3CD6FFDE825C"
}
},
@@ -3371,9 +3179,7 @@
"v1_mic_extended_salt_mic_hmac_key": "5F5436261C25D3E5FA446D513B409D5A0C4C27297CBE9F4EE741749154019E21",
"v1_mic_short_salt_aes_key": "B03494A82C3848A67453FB367532112C",
"v1_mic_short_salt_identity_token_hmac_key": "59D6749A1D6AFA4DB973AEAF088FCC0E7BE7D859A2814D641799BB1590496924",
- "v1_mic_short_salt_mic_hmac_key": "D3B73C61E28AB288C60D7240B501A074C29A5E328D9BBD8716566B83AB1217A3",
- "v1_signature_identity_token_hmac_key": "F85D6CA0DA816F073831FDDF3A0E0FCA9C888FB820762CCD6477DAFB90547E8F",
- "v1_signature_section_aes_key": "807F93D9C345DC3AB3D98D43D6FBEAB9"
+ "v1_mic_short_salt_mic_hmac_key": "D3B73C61E28AB288C60D7240B501A074C29A5E328D9BBD8716566B83AB1217A3"
},
"v0_adv_salt_hkdf": {
"adv_salt": "765B",
@@ -3388,9 +3194,9 @@
"short_salt_nonce": "8B511172378BCB1BF0CB674C"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "1E962EC586FE218A8F3A736BA5677B7E",
- "derived_salt_nonce": "7399E4535A366989EF412F3EA0F8CE45",
- "derived_salt_third_de": "46F2B7D99127C01B68C8B7C8A45CD479",
+ "derived_salt_first_de": "7399E4535A366989EF412F3EA0F8CE45",
+ "derived_salt_nonce": "712C1711BF548DB26A68142097B04A44",
+ "derived_salt_third_de": "EA668CEAEA9ED252C092DAF0B181AA1E",
"section_extended_salt": "6BC0DFA6EBFA267952CB7305F05AD8F2"
}
},
@@ -3406,9 +3212,7 @@
"v1_mic_extended_salt_mic_hmac_key": "CDD5D25349B3AB7D4885AD124A050704116A5911CE5F379BE998CC7E92DE01AB",
"v1_mic_short_salt_aes_key": "405DCC958551CDD1EC90C4A91D1B87AE",
"v1_mic_short_salt_identity_token_hmac_key": "D06923425C4A028710861654BBFACA9F2FB6B3488B70E9C3C1898152A8DAFA6E",
- "v1_mic_short_salt_mic_hmac_key": "421A413608C4344BF7A0E250324337C9ED374636D38D2B35969B6EF434D0D30F",
- "v1_signature_identity_token_hmac_key": "D4FADAACB47CC260DEEDB1BBD56131D4FAB54270ACBBFAE975D77079A8554E53",
- "v1_signature_section_aes_key": "1F5F9803F64C4A6DCF4BFB0C4FDC800A"
+ "v1_mic_short_salt_mic_hmac_key": "421A413608C4344BF7A0E250324337C9ED374636D38D2B35969B6EF434D0D30F"
},
"v0_adv_salt_hkdf": {
"adv_salt": "CDFF",
@@ -3423,9 +3227,9 @@
"short_salt_nonce": "1B68E2C018314F5C61591D06"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "443DBEF6EA8FBB7E1AED17C651902D8F",
- "derived_salt_nonce": "C832DD69A51C4466CC7E4C8FF2C8D830",
- "derived_salt_third_de": "996A7355DADC94B04ED4DD5BFF713D10",
+ "derived_salt_first_de": "C832DD69A51C4466CC7E4C8FF2C8D830",
+ "derived_salt_nonce": "EBF773536DC522FD77A821E569A352D4",
+ "derived_salt_third_de": "96E770AFE17ADA57F24DE0E3BB499E23",
"section_extended_salt": "86042432C5C8BFA2ADFEF07E52F76CE1"
}
},
@@ -3441,9 +3245,7 @@
"v1_mic_extended_salt_mic_hmac_key": "2F7EFAC31BC19C6414DBFE1E151CA5A9BB2D7FF23816E50DB8D3DBCEF0F651D6",
"v1_mic_short_salt_aes_key": "72EED4197BDD9429F3B227EEEF8D9003",
"v1_mic_short_salt_identity_token_hmac_key": "060C6B4E277154EF8E2AACD29FA7FD561D89EE1B55FE75804AC900B2A14FC6AC",
- "v1_mic_short_salt_mic_hmac_key": "9E5E30433992ECC1D0E897740DE7380195AD9D393B172626A04EB4EC1540441D",
- "v1_signature_identity_token_hmac_key": "6F8D65A2FDF4A76C612A1CE055862D4E1F3BC758FA9DEA8171281F387C89E89F",
- "v1_signature_section_aes_key": "9E1A2442E091482C1AD358CB46EBB449"
+ "v1_mic_short_salt_mic_hmac_key": "9E5E30433992ECC1D0E897740DE7380195AD9D393B172626A04EB4EC1540441D"
},
"v0_adv_salt_hkdf": {
"adv_salt": "6BC4",
@@ -3458,9 +3260,9 @@
"short_salt_nonce": "B1B266BC940E1CB4C0DDB7C6"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "EC10AA256E69F3BFC61E03E02DAC5572",
- "derived_salt_nonce": "C5CD7C8AF453AA0A2E0CFB92B4DEC297",
- "derived_salt_third_de": "E72061FB97353A6B46800EAEA02E86A2",
+ "derived_salt_first_de": "C5CD7C8AF453AA0A2E0CFB92B4DEC297",
+ "derived_salt_nonce": "09E5F0FF9F513E11E212E7396FAC1961",
+ "derived_salt_third_de": "F8B0319CCB323A6AF277F811D9E32695",
"section_extended_salt": "DC5E1856188626F6FCAF7DEBCD91F83B"
}
},
@@ -3476,9 +3278,7 @@
"v1_mic_extended_salt_mic_hmac_key": "2F8F91E46219709D1389613489A418FA8D806A1FB9D9519D0C5D677D55883E5F",
"v1_mic_short_salt_aes_key": "B8A40538DB14E9CCE0157DF49441CDAC",
"v1_mic_short_salt_identity_token_hmac_key": "08569465F0C66A011A7D4FFF67B0FC97690ECE6868E62DF94767E7E9436467D1",
- "v1_mic_short_salt_mic_hmac_key": "19D0469CCCA27EE200930615C3D35C27ABE9AAB808752DA5D205E4003A617543",
- "v1_signature_identity_token_hmac_key": "FCBBD61A230D9BE84D5E57E4DE4C641F3F9733867290614BE54F33E381EAF517",
- "v1_signature_section_aes_key": "39C03BE4FCCB838D975F0B10F2F27FA6"
+ "v1_mic_short_salt_mic_hmac_key": "19D0469CCCA27EE200930615C3D35C27ABE9AAB808752DA5D205E4003A617543"
},
"v0_adv_salt_hkdf": {
"adv_salt": "DF27",
@@ -3493,9 +3293,9 @@
"short_salt_nonce": "7DB3F25CD050360CEB4DC7D0"
},
"v1_section_extended_salt_hkdf": {
- "derived_salt_first_de": "ADACCCB5226C9A33368DCA3CDB62BBA1",
- "derived_salt_nonce": "00AA84F4608746BEC8A17749584F7229",
- "derived_salt_third_de": "C925E34CF796B07300BFDA0B91172D86",
+ "derived_salt_first_de": "00AA84F4608746BEC8A17749584F7229",
+ "derived_salt_nonce": "734E24882CE091951BB2324C24D2DA76",
+ "derived_salt_third_de": "8F3957367B7BFEFB520A1A0CCAF96875",
"section_extended_salt": "65D6AA69B72031C537A1EBD7D1F16B7F"
}
}
diff --git a/nearby/presence/np_hkdf/src/lib.rs b/nearby/presence/np_hkdf/src/lib.rs
index bf82169..8e07576 100644
--- a/nearby/presence/np_hkdf/src/lib.rs
+++ b/nearby/presence/np_hkdf/src/lib.rs
@@ -117,8 +117,6 @@
}
/// AES-GCM nonce used when decrypting metadata.
- ///
- /// Shared between signed and unsigned since they use the same credential.
#[allow(clippy::expect_used)]
pub fn v1_metadata_nonce(&self) -> <C::Aes128Gcm as Aead>::Nonce {
self.hkdf.derive_array(b"V1 Metadata nonce").expect("Nonce is a valid length")
@@ -133,11 +131,6 @@
pub fn v1_mic_extended_salt_keys(&self) -> MicExtendedSaltSectionKeys<'_, C> {
MicExtendedSaltSectionKeys { hkdf: &self.hkdf }
}
-
- /// Derived keys for MIC signature sections
- pub fn v1_signature_keys(&self) -> SignatureSectionKeys<'_, C> {
- SignatureSectionKeys { hkdf: &self.hkdf }
- }
}
/// Derived keys for MIC short salt sections
@@ -145,14 +138,14 @@
hkdf: &'a NpHkdf<C>,
}
-impl<'a, C: CryptoProvider> MicShortSaltSectionKeys<'a, C> {
+impl<C: CryptoProvider> MicShortSaltSectionKeys<'_, C> {
/// HMAC-SHA256 key used when verifying a section's ciphertext
pub fn mic_hmac_key(&self) -> NpHmacSha256Key {
self.hkdf.derive_hmac_sha256_key(b"MIC Section short salt HMAC key")
}
}
-impl<'a, C: CryptoProvider> DerivedSectionKeys<C> for MicShortSaltSectionKeys<'a, C> {
+impl<C: CryptoProvider> DerivedSectionKeys<C> for MicShortSaltSectionKeys<'_, C> {
fn aes_key(&self) -> Aes128Key {
self.hkdf.derive_aes128_key(b"MIC Section short salt AES key")
}
@@ -167,14 +160,14 @@
hkdf: &'a NpHkdf<C>,
}
-impl<'a, C: CryptoProvider> MicExtendedSaltSectionKeys<'a, C> {
+impl<C: CryptoProvider> MicExtendedSaltSectionKeys<'_, C> {
/// HMAC-SHA256 key used when verifying a section's ciphertext
pub fn mic_hmac_key(&self) -> NpHmacSha256Key {
self.hkdf.derive_hmac_sha256_key(b"MIC Section extended salt HMAC key")
}
}
-impl<'a, C: CryptoProvider> DerivedSectionKeys<C> for MicExtendedSaltSectionKeys<'a, C> {
+impl<C: CryptoProvider> DerivedSectionKeys<C> for MicExtendedSaltSectionKeys<'_, C> {
fn aes_key(&self) -> Aes128Key {
self.hkdf.derive_aes128_key(b"MIC Section extended salt AES key")
}
@@ -184,21 +177,6 @@
}
}
-/// Derived keys for Signature sections
-pub struct SignatureSectionKeys<'a, C: CryptoProvider> {
- hkdf: &'a NpHkdf<C>,
-}
-
-impl<'a, C: CryptoProvider> DerivedSectionKeys<C> for SignatureSectionKeys<'a, C> {
- fn aes_key(&self) -> Aes128Key {
- self.hkdf.derive_aes128_key(b"Signature Section AES key")
- }
-
- fn identity_token_hmac_key(&self) -> NpHmacSha256Key {
- self.hkdf.derive_hmac_sha256_key(b"Signature Section identity token HMAC key")
- }
-}
-
/// Derived keys for encrypted V1 sections
pub trait DerivedSectionKeys<C: CryptoProvider> {
/// AES128 key used when encrypting a section's ciphertext
diff --git a/nearby/presence/np_hkdf/src/v1_salt.rs b/nearby/presence/np_hkdf/src/v1_salt.rs
index cfa1f51..dd4d44e 100644
--- a/nearby/presence/np_hkdf/src/v1_salt.rs
+++ b/nearby/presence/np_hkdf/src/v1_salt.rs
@@ -14,6 +14,7 @@
//! Salt used in a V1 advertisement.
use crate::np_salt_hkdf;
+use core::cmp::Ordering;
use crypto_provider::{hkdf::Hkdf, CryptoProvider, CryptoRng, FromCryptoRng};
/// Length of a V1 extended salt
@@ -29,29 +30,21 @@
}
impl ExtendedV1Salt {
- /// Derive a salt for a particular DE, if applicable.
+ /// Derive a salt for a particular DE, or for a section's encoder implementation
+ /// (`OptionDeType::NONE`).
///
- /// Returns none if the requested size is larger than HKDF allows or if offset arithmetic
- /// overflows.
+ /// Returns `None` if the requested size is larger than HKDF allows (8192 bytes).
pub fn derive<const N: usize, C: CryptoProvider>(
&self,
- de: Option<DataElementOffset>,
+ maybe_de_type: OptionDeType,
) -> Option<[u8; N]> {
let hkdf = np_salt_hkdf::<C>(&self.data);
let mut arr = [0_u8; N];
- // 0-based offsets -> 1-based indices w/ 0 indicating not present
- hkdf.expand_multi_info(
- &[
- b"V1 derived salt",
- &de.and_then(|d| d.offset.checked_add(1))
- .map(|o| o.into())
- .unwrap_or(0_u32)
- .to_be_bytes(),
- ],
- &mut arr,
- )
- .map(|_| arr)
- .ok()
+ // `OptionDeType` uses `FORBIDDEN_DE_TYPE_CODE` for `None`, which matches the method
+ // contract against the advertisement specification correctly.
+ hkdf.expand_multi_info(&[b"V1 derived salt", &maybe_de_type.code.to_le_bytes()], &mut arr)
+ .map(|_| arr)
+ .ok()
}
/// Returns the salt bytes as a slice
@@ -82,32 +75,122 @@
}
}
-/// Offset of a data element in its containing section, used with [ExtendedV1Salt].
-#[derive(PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
-pub struct DataElementOffset {
- /// 0-based offset of the DE in the advertisement
- offset: u8,
+/// Error type indicating that a value used in an
+/// attempt to construct a [`DeType`] is
+/// a forbidden value out of the range of
+/// allowable DE type codes.
+#[derive(Debug)]
+pub struct InvalidDeType;
+
+/// Reserved `u32` which may not be employed for data element
+/// type codes, but is employed in deriving section-level
+/// salts used for e.g: MIC encrypted sections.
+pub const FORBIDDEN_DE_TYPE_CODE: u32 = 0xFFFFFFFF;
+
+/// Minimal-size representation of an `Option<DeType>`, exploiting
+/// the [`FORBIDDEN_DE_TYPE_CODE`] as a niche to store `None` in.
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+pub struct OptionDeType {
+ code: u32,
}
-impl DataElementOffset {
- /// The zero offset
- pub const ZERO: DataElementOffset = Self { offset: 0 };
+impl OptionDeType {
+ /// [`OptionDeType`] corresponding to `Option::None`.
+ pub const NONE: Self = Self { code: FORBIDDEN_DE_TYPE_CODE };
+}
- /// Returns the offset as a usize
- pub fn as_u8(&self) -> u8 {
- self.offset
- }
-
- /// Returns the next offset.
- ///
- /// Does not handle overflow as there can't be more than 2^8 DEs in a section.
- pub const fn incremented(&self) -> Self {
- Self { offset: self.offset + 1 }
+impl Default for OptionDeType {
+ fn default() -> Self {
+ Self::NONE
}
}
-impl From<u8> for DataElementOffset {
- fn from(num: u8) -> Self {
- Self { offset: num }
+impl From<DeType> for OptionDeType {
+ fn from(de_type: DeType) -> Self {
+ Self { code: de_type.code }
+ }
+}
+
+impl From<Option<DeType>> for OptionDeType {
+ fn from(maybe_de_type: Option<DeType>) -> Self {
+ maybe_de_type.map(OptionDeType::from).unwrap_or(Self::NONE)
+ }
+}
+
+impl From<OptionDeType> for Option<DeType> {
+ fn from(option_de_type: OptionDeType) -> Self {
+ if option_de_type.code == FORBIDDEN_DE_TYPE_CODE {
+ None
+ } else {
+ Some(DeType { code: option_de_type.code })
+ }
+ }
+}
+
+impl PartialOrd for OptionDeType {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for OptionDeType {
+ fn cmp(&self, other: &Self) -> Ordering {
+ match (self.code, other.code) {
+ (FORBIDDEN_DE_TYPE_CODE, FORBIDDEN_DE_TYPE_CODE) => Ordering::Equal,
+ (_, FORBIDDEN_DE_TYPE_CODE) => Ordering::Greater,
+ (FORBIDDEN_DE_TYPE_CODE, _) => Ordering::Less,
+ (my_de_type, other_de_type) => my_de_type.cmp(&other_de_type),
+ }
+ }
+}
+
+/// Data element types for extended advertisements
+#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Copy)]
+pub struct DeType {
+ // 4 billion type codes should be enough for anybody
+ code: u32,
+}
+
+impl DeType {
+ /// A `const` equivalent to `From<u32>` since trait methods can't yet be const.
+ pub const fn const_from(value: u32) -> Self {
+ if value == FORBIDDEN_DE_TYPE_CODE {
+ panic!("Invalid DeType.");
+ }
+ Self { code: value }
+ }
+
+ /// Returns the type as a u32
+ pub fn as_u32(&self) -> u32 {
+ self.code
+ }
+}
+
+impl From<u8> for DeType {
+ fn from(value: u8) -> Self {
+ DeType { code: value.into() }
+ }
+}
+
+impl From<u16> for DeType {
+ fn from(value: u16) -> Self {
+ DeType { code: value.into() }
+ }
+}
+
+impl TryFrom<u32> for DeType {
+ type Error = InvalidDeType;
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ if value == FORBIDDEN_DE_TYPE_CODE {
+ Err(InvalidDeType)
+ } else {
+ Ok(DeType { code: value })
+ }
+ }
+}
+
+impl From<DeType> for u32 {
+ fn from(value: DeType) -> Self {
+ value.code
}
}
diff --git a/nearby/presence/np_hkdf/tests/hmac.rs b/nearby/presence/np_hkdf/tests/hmac.rs
index 9cb0357..9530a0a 100644
--- a/nearby/presence/np_hkdf/tests/hmac.rs
+++ b/nearby/presence/np_hkdf/tests/hmac.rs
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#![allow(missing_docs)]
+
use crypto_provider::hmac::MacError;
use crypto_provider_default::CryptoProviderImpl;
use np_hkdf::*;
diff --git a/nearby/presence/np_hkdf/tests/test_vectors.rs b/nearby/presence/np_hkdf/tests/test_vectors.rs
index a00e747..8fbb999 100644
--- a/nearby/presence/np_hkdf/tests/test_vectors.rs
+++ b/nearby/presence/np_hkdf/tests/test_vectors.rs
@@ -12,12 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::indexing_slicing, clippy::unwrap_used)]
+#![allow(clippy::indexing_slicing, clippy::unwrap_used, missing_docs)]
use anyhow::anyhow;
use crypto_provider::aes::AesKey;
use crypto_provider_default::CryptoProviderImpl;
-use np_hkdf::{v1_salt::ExtendedV1Salt, *};
+use np_hkdf::{
+ v1_salt::{DeType, ExtendedV1Salt, OptionDeType},
+ *,
+};
use serde_json::json;
use std::{fs, io::Read as _};
use test_helper::extract_key_array;
@@ -80,14 +83,6 @@
&extract_key_array::<32>(group, "v1_mic_extended_salt_mic_hmac_key"),
hkdf.v1_mic_extended_salt_keys().mic_hmac_key().as_bytes()
);
- assert_eq!(
- &extract_key_array::<32>(group, "v1_signature_identity_token_hmac_key"),
- hkdf.v1_signature_keys().identity_token_hmac_key().as_bytes()
- );
- assert_eq!(
- extract_key_array::<16>(group, "v1_signature_section_aes_key"),
- *hkdf.v1_signature_keys().aes_key().as_array()
- );
}
{
@@ -114,15 +109,17 @@
let salt = ExtendedV1Salt::from(ikm);
assert_eq!(
extract_key_array::<16>(group, "derived_salt_nonce"),
- salt.derive::<16, CryptoProviderImpl>(None).unwrap(),
+ salt.derive::<16, CryptoProviderImpl>(OptionDeType::NONE).unwrap(),
);
assert_eq!(
extract_key_array::<16>(group, "derived_salt_first_de"),
- salt.derive::<16, CryptoProviderImpl>(Some(0.into())).unwrap(),
+ salt.derive::<16, CryptoProviderImpl>(OptionDeType::from(DeType::from(0u8)))
+ .unwrap(),
);
assert_eq!(
extract_key_array::<16>(group, "derived_salt_third_de"),
- salt.derive::<16, CryptoProviderImpl>(Some(2.into())).unwrap(),
+ salt.derive::<16, CryptoProviderImpl>(OptionDeType::from(DeType::from(2u8)))
+ .unwrap(),
);
}
@@ -179,8 +176,6 @@
"v1_mic_extended_salt_identity_token_hmac_key": hex::encode_upper(key_seed_hkdf.v1_mic_extended_salt_keys().identity_token_hmac_key().as_bytes()),
"v1_mic_extended_salt_aes_key": hex::encode_upper(key_seed_hkdf.v1_mic_extended_salt_keys().aes_key().as_array()),
"v1_mic_extended_salt_mic_hmac_key": hex::encode_upper(key_seed_hkdf.v1_mic_extended_salt_keys().mic_hmac_key().as_bytes()),
- "v1_signature_identity_token_hmac_key": hex::encode_upper(key_seed_hkdf.v1_signature_keys().identity_token_hmac_key().as_bytes()),
- "v1_signature_section_aes_key": hex::encode_upper(key_seed_hkdf.v1_signature_keys().aes_key().as_array()),
},
"v0_adv_salt_hkdf": {
"adv_salt": hex::encode_upper(v0_adv_salt),
@@ -194,9 +189,9 @@
"v1_section_extended_salt_hkdf": {
"section_extended_salt": hex::encode_upper(v1_extended_salt.bytes()),
// 0-based offsets -> 1-based indexing
- "derived_salt_nonce": hex::encode_upper(v1_extended_salt.derive::<16, CryptoProviderImpl>(None).unwrap()),
- "derived_salt_first_de": hex::encode_upper(v1_extended_salt.derive::<16, CryptoProviderImpl>(Some(0.into())).unwrap()),
- "derived_salt_third_de": hex::encode_upper(v1_extended_salt.derive::<16, CryptoProviderImpl>(Some(2.into())).unwrap()),
+ "derived_salt_nonce": hex::encode_upper(v1_extended_salt.derive::<16, CryptoProviderImpl>(OptionDeType::NONE).unwrap()),
+ "derived_salt_first_de": hex::encode_upper(v1_extended_salt.derive::<16, CryptoProviderImpl>(OptionDeType::from(DeType::from(0u8))).unwrap()),
+ "derived_salt_third_de": hex::encode_upper(v1_extended_salt.derive::<16, CryptoProviderImpl>(OptionDeType::from(DeType::from(2u8))).unwrap()),
},
"v1_mic_section_short_salt_hkdf": {
"section_short_salt": hex::encode_upper(v1_mic_short_salt),
diff --git a/nearby/presence/np_java_ffi/.gitignore b/nearby/presence/np_java_ffi/.gitignore
index cafbf51..bf2b546 100644
--- a/nearby/presence/np_java_ffi/.gitignore
+++ b/nearby/presence/np_java_ffi/.gitignore
@@ -6,3 +6,6 @@
# Ignore IDEA dir
/.idea
+
+# Ignore vscode-gradle build output directory
+/bin
diff --git a/nearby/presence/np_java_ffi/Cargo.toml b/nearby/presence/np_java_ffi/Cargo.toml
index 134dfbb..c16073d 100644
--- a/nearby/presence/np_java_ffi/Cargo.toml
+++ b/nearby/presence/np_java_ffi/Cargo.toml
@@ -9,6 +9,8 @@
workspace = true
[features]
+# The testing feature enables test-only APIs that implement native methods in
+# the test/ library.
testing = ["np_ffi_core/testing"]
[dependencies]
@@ -18,7 +20,7 @@
np_ffi_core.workspace = true
pourover.workspace = true
pourover_macro.workspace = true
-crypto_provider_default.workspace = true # for setting features from cmdline
+tinyvec.workspace = true
jni.workspace = true
diff --git a/nearby/presence/np_java_ffi/README.md b/nearby/presence/np_java_ffi/README.md
new file mode 100644
index 0000000..2ae2201
--- /dev/null
+++ b/nearby/presence/np_java_ffi/README.md
@@ -0,0 +1,21 @@
+# np_adv Java FFI
+
+This package defines a Java interface for the `np_adv` library.
+
+This package is split into directories:
+* `java/` - The Java library with `native` methods.
+* `src/` - The Rust library that implements the `native` Java methods.
+ - `classes/` - Contains Rust files matching Java classes from `java/`. Code in these files will implement the `native` methods for the associated Java class.
+* `test/` - Java tests that test the Java library and underlying native code.
+
+The build is controlled by the following files:
+* `build.gradle.kts` - Defines the builds for `java/` and `test/` along with Java dependencies for the Java library.
+ - `settings.gradle.kts` - Defines where local Java dependencies are located.
+* `Cargo.toml` - Defines the Rust .so binary that is loaded by the Java library.
+
+This library defines the following build_scripts actions:
+* `run-np-java-ffi-tests` - Builds the package and runs the Java test suite.
+
+Additional configs for external use:
+* `AndroidManifest.xml` - Defines the Android `minSdkVersion` required by the Java library.
+* `used_by_native.pgcfg` - Defines proguard exclusions for the `@UsedByNative` annotation, so that native code can access Java structures.
diff --git a/nearby/presence/np_java_ffi/build.gradle.kts b/nearby/presence/np_java_ffi/build.gradle.kts
index 6fe9e82..40f3845 100644
--- a/nearby/presence/np_java_ffi/build.gradle.kts
+++ b/nearby/presence/np_java_ffi/build.gradle.kts
@@ -18,6 +18,7 @@
plugins {
`java-library`
+ // For static analysis
id("net.ltgt.errorprone") version "4.0.0"
}
@@ -33,10 +34,14 @@
}
dependencies {
+ // Static analysis annnotations
implementation("androidx.annotation:annotation:1.6.0")
implementation("com.google.errorprone:error_prone_core:2.28.0")
implementation("org.checkerframework:checker-qual:3.45.0")
+ // Local dependencies
+ implementation(project(":cooperative_cleaner"))
+
// JUnit Test Support
testImplementation("junit:junit:4.13")
testImplementation("com.google.truth:truth:1.1.4")
@@ -62,13 +67,72 @@
useJUnit()
jvmArgs = mutableListOf(
// libnp_java_ffi.so
+ // This is the Cargo workspace's build output directory
"-Djava.library.path=$projectDir/../../target/debug",
// ByteBuddy agent for mocks
"-XX:+EnableDynamicAgentLoading"
)
}
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath("com.guardsquare:proguard-gradle:7.7.0") {
+ exclude("com.android.tools.build")
+ }
+ }
+}
+
+// Task to take built test classes and package them into a .jar
+// which may then be fed as input to proguard.
+tasks.register<Jar>("jarTestClasses") {
+ from("build/classes/java/test")
+ archiveFileName.set("tests.jar")
+ destinationDirectory.set(file("build/testjars"))
+ dependsOn("compileTestJava")
+}
+
+// Task to obfuscate the library to np-java-ffi-proguarded.jar
+tasks.register<proguard.gradle.ProGuardTask>("proguardForTests") {
+ injars(listOf("build/libs/np-java-ffi.jar", "build/testjars/tests.jar"))
+ outjars("build/libs/np-java-ffi-proguarded-tests.jar")
+ configuration("proguarded_unit_tests.pgcfg")
+ dependsOn("jarTestClasses")
+ dependsOn("jar")
+}
+
+// Expands the proguarded .jar into a directory structure with
+// .class files.
+tasks.register<Copy>("unzipProguardedTestsJar") {
+ from(zipTree("build/libs/np-java-ffi-proguarded-tests.jar"))
+ into("build/libs/unzippedproguardedtests")
+ dependsOn("proguardForTests")
+}
+
+tasks.register<Test>("proguardedTest") {
+ useJUnit()
+ jvmArgs = mutableListOf(
+ // libnp_java_ffi.so
+ // This is the Cargo workspace's build output directory
+ "-Djava.library.path=$projectDir/../../target/debug",
+ // ByteBuddy agent for mocks
+ "-XX:+EnableDynamicAgentLoading"
+ )
+ // Ignore the usual built classes
+ classpath -= files("build/classes/java/main")
+ classpath -= files("build/classes/java/test")
+ // Use the proguarded classes instead
+ classpath += files("build/libs/unzippedproguardedtests")
+ dependsOn("unzipProguardedTestsJar")
+ // Exclude test-vector checks, since that
+ // uses reflection, which won't play nice with proguard.
+ exclude("**/*TestVectors*")
+}
+
tasks.withType<JavaCompile>().configureEach {
+ // Configure static analysis passes to match g3 if possible
options.errorprone {
error("CheckReturnValue")
error("UnnecessaryStaticImport")
@@ -80,5 +144,6 @@
error("CheckedExceptionNotThrown")
error("NonFinalStaticField")
error("InvalidLink")
+ error("ThrowsUncheckedException")
}
}
diff --git a/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.properties b/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.properties
index f398c33..171d876 100644
--- a/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.properties
+++ b/nearby/presence/np_java_ffi/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializationException.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializationException.java
index 68db2c9..b7eeef7 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializationException.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializationException.java
@@ -23,13 +23,17 @@
super(message);
}
+ @UsedByNative
public static final class InvalidHeaderException extends DeserializationException {
+ @UsedByNative
public InvalidHeaderException() {
super("Invalid advertisement header");
}
}
+ @UsedByNative
public static final class InvalidFormatException extends DeserializationException {
+ @UsedByNative
public InvalidFormatException() {
super("Invalid advertisement format");
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializeResult.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializeResult.java
index 5be7f61..31fba80 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializeResult.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializeResult.java
@@ -31,6 +31,7 @@
* reasons. That condition is reported on the advertisement object itself since a V1 advertisement
* may be partially legible (some sections are legible, not all).
*/
+@UsedByNative
public final class DeserializeResult<M extends CredentialBook.MatchedMetadata>
implements AutoCloseable {
@@ -41,11 +42,12 @@
Kind.V1_ADVERTISEMENT,
})
@Retention(SOURCE)
+ @UsedByNative
public @interface Kind {
- public static final int UNKNOWN_ERROR = -1;
+ @UsedByNative public static final int UNKNOWN_ERROR = -1;
- public static final int V0_ADVERTISEMENT = 1;
- public static final int V1_ADVERTISEMENT = 2;
+ @UsedByNative public static final int V0_ADVERTISEMENT = 1;
+ @UsedByNative public static final int V1_ADVERTISEMENT = 2;
}
/** Checks if a {@link Kind} represents and error or not. */
@@ -57,6 +59,7 @@
@Nullable private final DeserializedAdvertisement advertisement;
/** Create a DeserializeResult containing an error code */
+ @UsedByNative
/* package */ DeserializeResult(@Kind int errorKind) {
if (!isErrorKind(errorKind)) {
throw new IllegalArgumentException(
@@ -67,12 +70,14 @@
}
/** Create a DeserializeResult containing a V0 advertisement */
+ @UsedByNative
/* package */ DeserializeResult(DeserializedV0Advertisement<M> advertisement) {
this.kind = Kind.V0_ADVERTISEMENT;
this.advertisement = advertisement;
}
/** Create a DeserializeResult containing a V1 advertisement */
+ @UsedByNative
/* package */ DeserializeResult(DeserializedV1Advertisement<M> advertisement) {
this.kind = Kind.V1_ADVERTISEMENT;
this.advertisement = advertisement;
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV0Advertisement.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV0Advertisement.java
index 7df838e..bbfdb2d 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV0Advertisement.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV0Advertisement.java
@@ -26,6 +26,7 @@
* handle. If this class is closed then the underlying handle will be closed too. Methods on this
* class should not be called if {@link #close()} has already been called.
*/
+@UsedByNative
public final class DeserializedV0Advertisement<M extends CredentialBook.MatchedMetadata>
extends DeserializedAdvertisement {
public static boolean isLegibleIdentity(@IdentityKind int identity) {
@@ -38,6 +39,7 @@
@Nullable private final CredentialBook<M> credentialBook;
/** Create an illegible instance with the given error identity. */
+ @UsedByNative
/* package */ DeserializedV0Advertisement(@IdentityKind int illegibleIdentity) {
if (isLegibleIdentity(illegibleIdentity)) {
throw new IllegalArgumentException(
@@ -66,6 +68,7 @@
* This is a helper to be called from native code to avoid needing to construct {@code V0Payload}
* on the native side.
*/
+ @UsedByNative
/* package */ DeserializedV0Advertisement(
int numDataElements,
long payload,
@@ -151,6 +154,18 @@
}
/**
+ * Gets the key seed for the credential this advertisement was decrypted with. This will return
+ * {@code null} if this advertisement is not an encrypted advertisement.
+ */
+ @Nullable
+ public byte[] getKeySeed() {
+ ensureLegible("get key seed");
+
+ V0Payload.IdentityDetails details = payload.getIdentityDetails();
+ return (details != null) ? details.getKeySeed() : null;
+ }
+
+ /**
* Gets the salt this advertisement was encrypted with. This will return {@code null} if this
* advertisement is not an encrypted advertisement.
*/
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Advertisement.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Advertisement.java
index cbe855a..a8e4c89 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Advertisement.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Advertisement.java
@@ -24,6 +24,7 @@
* LegibleV1Sections} handle. If this class is closed then the underlying handle will be closed too.
* Methods on this class should not be called if {@link #close()} has already been called.
*/
+@UsedByNative
public final class DeserializedV1Advertisement<M extends CredentialBook.MatchedMetadata>
extends DeserializedAdvertisement {
@@ -33,6 +34,7 @@
private final CredentialBook<M> credentialBook;
/** Create a legible instance with the given information. */
+ @UsedByNative
/* package */ DeserializedV1Advertisement(
int numLegibleSections,
int numUndecryptableSections,
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Section.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Section.java
index 3c26e9e..d74f133 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Section.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Section.java
@@ -25,6 +25,7 @@
* containing advertisement object is valid; it is backed by the same {@link LegibleV1Sections}
* instance.
*/
+@UsedByNative
public final class DeserializedV1Section<M extends CredentialBook.MatchedMetadata> {
private final LegibleV1Sections legibleSections;
@@ -33,6 +34,7 @@
@IdentityKind private final int identityTag;
private final CredentialBook<M> credentialBook;
+ @UsedByNative
/* package */ DeserializedV1Section(
LegibleV1Sections legibleSections,
int legibleSectionsIndex,
@@ -67,17 +69,10 @@
}
/** Gets all the data elements for iteration. */
- public Iterable<V1DataElement> getDataElements() {
+ public Iterable<V1DataElement.Generic> getDataElements() {
return () -> new DataElementIterator(legibleSections, legibleSectionsIndex, numDataElements);
}
- /** Visits all the data elements with the given visitor. */
- public void visitDataElements(V1DataElement.Visitor v) {
- for (V1DataElement de : getDataElements()) {
- de.visit(v);
- }
- }
-
/**
* Gets the identity token for the credential this advertisement was deserialized with. This will
* return {@code null} if this advertisement is not an encrypted advertisement.
@@ -90,6 +85,17 @@
}
/**
+ * Gets the key seed for the credential this advertisement was decrypted with. This will return
+ * {@code null} if this advertisement is not an encrypted advertisement.
+ */
+ @Nullable
+ public byte[] getKeySeed() {
+ LegibleV1Sections.IdentityDetails details =
+ legibleSections.getSectionIdentityDetails(legibleSectionsIndex);
+ return (details != null) ? details.getKeySeed() : null;
+ }
+
+ /**
* Gets the verification mode for this advertisement. This will return {@code null} if this
* advertisement is not an encrypted advertisement.
*/
@@ -125,7 +131,7 @@
return legibleSections.getSectionDecryptedMetadata(legibleSectionsIndex);
}
- private static final class DataElementIterator implements Iterator<V1DataElement> {
+ private static final class DataElementIterator implements Iterator<V1DataElement.Generic> {
private final LegibleV1Sections legibleSections;
private final int legibleSectionsIndex;
private final int numDataElements;
@@ -145,7 +151,7 @@
}
@Override
- public V1DataElement next() {
+ public V1DataElement.Generic next() {
return legibleSections.getSectionDataElement(legibleSectionsIndex, position++);
}
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/Handle.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/Handle.java
index 537098b..1cba854 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/Handle.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/Handle.java
@@ -24,10 +24,13 @@
* <p>This may be a handle to an object that has already been deallocated. In that case, any native
* uses of this handle should throw {@link Handle.InvalidHandleException}.
*/
+@UsedByNative
public abstract class Handle {
/** Thrown when an invalid handle is used */
+ @UsedByNative
public static final class InvalidHandleException extends RuntimeException {
+ @UsedByNative
public InvalidHandleException() {
super("The given handle is no longer valid");
}
@@ -39,6 +42,7 @@
this.handleId = handleId;
}
+ @UsedByNative
public long getId() {
return handleId;
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/IdentityKind.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/IdentityKind.java
index 9b69851..09dc890 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/IdentityKind.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/IdentityKind.java
@@ -31,13 +31,14 @@
IdentityKind.DECRYPTED,
})
@Retention(SOURCE)
+@UsedByNative
public @interface IdentityKind {
/** An encrypted identity that we do not have a credential for. */
- public static final int NO_MATCHING_CREDENTIALS = -1;
+ @UsedByNative public static final int NO_MATCHING_CREDENTIALS = -1;
/** A plaintext identity. */
- public static final int PLAINTEXT = 1;
+ @UsedByNative public static final int PLAINTEXT = 1;
/** An encrypted identity that we have a credential for. */
- public static final int DECRYPTED = 2;
+ @UsedByNative public static final int DECRYPTED = 2;
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/LegibleV1Sections.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/LegibleV1Sections.java
index a5ac931..7108050 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/LegibleV1Sections.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/LegibleV1Sections.java
@@ -17,17 +17,16 @@
package com.google.android.nearby.presence.rust;
import androidx.annotation.Nullable;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
import java.util.Arrays;
/** Internal handle for a V1 deserialized advertisement. */
+@UsedByNative
public final class LegibleV1Sections extends OwnedHandle {
- static {
- System.loadLibrary(NpAdv.LIBRARY_NAME);
- }
-
/** Internal data class to pass identity data back from native code. */
+ @UsedByNative
public static final class IdentityDetails {
@VerificationMode private final int verificationMode;
@@ -38,11 +37,18 @@
private final byte[] identityToken;
+ private final byte[] keySeed;
+
+ @UsedByNative
public IdentityDetails(
- @VerificationMode int verificationMode, int credentialId, byte[] identityToken) {
+ @VerificationMode int verificationMode,
+ int credentialId,
+ byte[] identityToken,
+ byte[] keySeed) {
this.verificationMode = verificationMode;
this.credentialId = credentialId;
this.identityToken = Arrays.copyOf(identityToken, identityToken.length);
+ this.keySeed = Arrays.copyOf(keySeed, keySeed.length);
}
@VerificationMode
@@ -57,13 +63,18 @@
public byte[] getIdentityToken() {
return identityToken;
}
+
+ public byte[] getKeySeed() {
+ return keySeed;
+ }
}
/**
* Create a LegibleV1Sections handle from the raw handle id. This will use the default cleaner
* form {@code NpAdv#getCleaner()}. This is expected to be called from native code.
*/
- /* package-visible */ LegibleV1Sections(long handleId) {
+ @UsedByNative
+ /* package */ LegibleV1Sections(long handleId) {
this(handleId, NpAdv.getCleaner());
}
@@ -96,8 +107,8 @@
* @throws IndexOutOfBoundsException if either index is out of range for this advertisement
* @return The data element found at {@code deIndex} in the section at {@code sectionIndex}
*/
- public V1DataElement getSectionDataElement(int sectionIndex, int deIndex) {
- V1DataElement de = nativeGetSectionDataElement(sectionIndex, deIndex);
+ public V1DataElement.Generic getSectionDataElement(int sectionIndex, int deIndex) {
+ V1DataElement.Generic de = nativeGetSectionDataElement(sectionIndex, deIndex);
if (de == null) {
throw new IndexOutOfBoundsException();
}
@@ -119,7 +130,7 @@
DeserializedV1Section<M> nativeGetSection(int index, CredentialBook<M> credentialBook);
@Nullable
- private native V1DataElement nativeGetSectionDataElement(int sectionIndex, int deIndex);
+ private native V1DataElement.Generic nativeGetSectionDataElement(int sectionIndex, int deIndex);
@Nullable
private native IdentityDetails nativeGetSectionIdentityDetails(int sectionIndex);
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NativeLibrary.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NativeLibrary.java
new file mode 100644
index 0000000..35a144a
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NativeLibrary.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.nearby.presence.rust;
+
+/**
+ * Represents a native library to be loaded. Call {@link #ensureLoaded()} to ensure that the library
+ * has been loaded.
+ */
+public class NativeLibrary {
+ private final String libraryName;
+
+ /**
+ * Create an instance. {@code libraryName} should be the same name that would be passed to {@link
+ * System#loadLibrary(String)}.
+ */
+ public NativeLibrary(String libraryName) {
+ this.libraryName = libraryName;
+ }
+
+ /** Ensure the library is loaded by loading it if needed. */
+ public void ensureLoaded() {
+ System.loadLibrary(this.libraryName);
+ }
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NpAdv.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NpAdv.java
index b7639ab..1cc9a6d 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NpAdv.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NpAdv.java
@@ -17,7 +17,9 @@
package com.google.android.nearby.presence.rust;
import androidx.annotation.Nullable;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
+import com.google.android.nearby.presence.rust.credential.V0BroadcastCredential;
/**
* The main entrypoint to the library.
@@ -25,26 +27,30 @@
* <h3>Supported Features:</h3>
*
* <ul>
- * <li>Create credential books: {@link CredentialBook#builder()}
+ * <li>Create credential books: {@link #newCredentialBookBuilder()}
* <li>Deserialize advertisements: {@link #deserializeAdvertisement}
- * <li>Serialize advertisements: {@link
- * com.google.android.nearby.presence.rust.V0AdvertisementBuilder}
+ * <li>Serialize V0 advertisements: {@link #newPublicV0Builder}, {@link #newEncryptedV0Builder}
+ * <li>Serialize V1 advertisements: {@link #newV1Builder}
* </ul>
*/
-public final class NpAdv {
+public final class NpAdv extends NativeLibrary {
- public static final String LIBRARY_NAME = "np_java_ffi";
-
- static {
- System.loadLibrary(LIBRARY_NAME);
- }
+ public static final String DEFAULT_LIBRARY_NAME = "np_java_ffi";
// This is effectively an injected variable, but without depending on a DI implementation.
@SuppressWarnings("NonFinalStaticField")
@Nullable
private static CooperativeCleaner cleaner = null;
- private NpAdv() {}
+ /** Create an instance of this library with a custom library name. */
+ public NpAdv(String libraryName) {
+ super(libraryName);
+ }
+
+ /** Create an instance of this library with the default library name {@code "np_java_ffi"}. */
+ public NpAdv() {
+ this(DEFAULT_LIBRARY_NAME);
+ }
/**
* Deserialize a Nearby Presence advertisement from its service data bytes.
@@ -53,9 +59,10 @@
* @param credentialBook The credential book to use to decrypt.
* @return A result containing the advertisement if it was able to be deserialized.
*/
- public static <M extends CredentialBook.MatchedMetadata>
- DeserializeResult<M> deserializeAdvertisement(
- byte[] serviceData, CredentialBook<M> credentialBook) {
+ public <M extends CredentialBook.MatchedMetadata> DeserializeResult<M> deserializeAdvertisement(
+ byte[] serviceData, CredentialBook<M> credentialBook) {
+ this.ensureLoaded();
+
DeserializeResult<M> result = nativeDeserializeAdvertisement(serviceData, credentialBook);
if (result == null) {
result = new DeserializeResult<M>(DeserializeResult.Kind.UNKNOWN_ERROR);
@@ -63,6 +70,33 @@
return result;
}
+ /** Create a {@link CredentialBook} builder. */
+ public <M extends CredentialBook.MatchedMetadata>
+ CredentialBook.Builder<M> newCredentialBookBuilder() {
+ return CredentialBook.<M>builder(this);
+ }
+
+ /** Create an empty {@link CredentialBook}. */
+ public CredentialBook<CredentialBook.NoMetadata> newEmptyCredentialBook() {
+ return CredentialBook.empty(this);
+ }
+
+ /** Create an advertisement builder for a public V0 advertisement. */
+ public V0AdvertisementBuilder newPublicV0Builder() {
+ return V0AdvertisementBuilder.newPublic(this);
+ }
+
+ /** Create an advertisement builder for an encrypted V0 advertisement. */
+ public V0AdvertisementBuilder newEncryptedV0Builder(
+ V0BroadcastCredential credential, byte[] salt) {
+ return V0AdvertisementBuilder.newEncrypted(this, credential, salt);
+ }
+
+ /** Create an advertisement builder for a V1 advertisement. */
+ public V1AdvertisementBuilder newV1Builder() {
+ return V1AdvertisementBuilder.newBuilder(this);
+ }
+
/**
* Get the currently configured cleaner. If a cleaner is not configured, a new one will be
* created.
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/OwnedHandle.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/OwnedHandle.java
index a1c1a79..8fa6f00 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/OwnedHandle.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/OwnedHandle.java
@@ -18,6 +18,7 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
/**
* A handle to natively-allocated object with lifetime control. This is a {@code Handle} that also
@@ -46,7 +47,9 @@
}
/** Thrown when a new handle cannot be allocated due to lack of space */
+ @UsedByNative
public static final class NoSpaceLeftException extends RuntimeException {
+ @UsedByNative
public NoSpaceLeftException() {
super("No space remaining in the associated HandleMap");
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/SerializationException.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/SerializationException.java
index 741b030..359ffd7 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/SerializationException.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/SerializationException.java
@@ -23,32 +23,44 @@
super(message);
}
+ @UsedByNative
+ public static final class DuplicateDataElementTypeCodeException extends RuntimeException {
+ @UsedByNative
+ public DuplicateDataElementTypeCodeException() {
+ super(
+ "Attempted to add a data element with a type-code which is already present in the"
+ + " section.");
+ }
+ }
+
+ @UsedByNative
public static final class InvalidDataElementException extends RuntimeException {
+ @UsedByNative
public InvalidDataElementException(String reason) {
super(String.format("Data element is invalid: %s", reason));
}
}
+ @UsedByNative
public static final class UnclosedActiveSectionException extends RuntimeException {
+ @UsedByNative
public UnclosedActiveSectionException() {
super(
"There is currently an active section builder which must be finished before continuing");
}
}
- public static final class InvalidSectionKindException extends RuntimeException {
- public InvalidSectionKindException() {
- super("A section of this kind is not allowed in this advertisement");
- }
- }
-
+ @UsedByNative
public static final class InsufficientSpaceException extends SerializationException {
+ @UsedByNative
public InsufficientSpaceException() {
super("There isn't enough space remaining in the advertisement");
}
}
+ @UsedByNative
public static final class LdtEncryptionException extends SerializationException {
+ @UsedByNative
public LdtEncryptionException() {
super(
"Serializing the advertisement to bytes failed because the data in the advertisement"
@@ -60,7 +72,9 @@
* Advertisement has an invalid length. This means it's empty; the length requirement is {@code
* length >= 1 byte}.
*/
+ @UsedByNative
public static final class UnencryptedSizeException extends SerializationException {
+ @UsedByNative
public UnencryptedSizeException() {
super(
"Serializing an unencrypted adv failed because the adv data didn't meet the length"
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/UsedByNative.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/UsedByNative.java
new file mode 100644
index 0000000..3d77253
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/UsedByNative.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.nearby.presence.rust;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/** An annotation used to mark APIs that are used by native code. */
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR})
+public @interface UsedByNative {}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0AdvertisementBuilder.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0AdvertisementBuilder.java
index 0061fff..b707df4 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0AdvertisementBuilder.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0AdvertisementBuilder.java
@@ -16,6 +16,7 @@
package com.google.android.nearby.presence.rust;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
import com.google.android.nearby.presence.rust.SerializationException.InsufficientSpaceException;
import com.google.android.nearby.presence.rust.SerializationException.InvalidDataElementException;
import com.google.android.nearby.presence.rust.V0DataElement.TxPower;
@@ -31,23 +32,26 @@
public final class V0AdvertisementBuilder implements AutoCloseable {
/** Create a builder for a public advertisement. */
- public static V0AdvertisementBuilder newPublic() {
- return newPublic(NpAdv.getCleaner());
+ public static V0AdvertisementBuilder newPublic(NpAdv npAdv) {
+ return newPublic(npAdv, NpAdv.getCleaner());
}
/** Create a builder for a public advertisement with a specific cleaner. */
- private static V0AdvertisementBuilder newPublic(CooperativeCleaner cleaner) {
+ private static V0AdvertisementBuilder newPublic(NpAdv npAdv, CooperativeCleaner cleaner) {
+ npAdv.ensureLoaded();
return new V0AdvertisementBuilder(new V0BuilderHandle(cleaner));
}
/** Create a builder for an encrypted advertisement. */
- public static V0AdvertisementBuilder newEncrypted(V0BroadcastCredential credential, byte[] salt) {
- return newEncrypted(NpAdv.getCleaner(), credential, salt);
+ public static V0AdvertisementBuilder newEncrypted(
+ NpAdv npAdv, V0BroadcastCredential credential, byte[] salt) {
+ return newEncrypted(npAdv, NpAdv.getCleaner(), credential, salt);
}
/** Create a builder for an encrypted advertisement with a specific cleaner. */
private static V0AdvertisementBuilder newEncrypted(
- CooperativeCleaner cleaner, V0BroadcastCredential credential, byte[] salt) {
+ NpAdv npAdv, CooperativeCleaner cleaner, V0BroadcastCredential credential, byte[] salt) {
+ npAdv.ensureLoaded();
return new V0AdvertisementBuilder(new V0BuilderHandle(cleaner, credential, salt));
}
@@ -90,11 +94,8 @@
}
/** Internal builder handle object. */
+ @UsedByNative
private static final class V0BuilderHandle extends OwnedHandle {
- static {
- System.loadLibrary(NpAdv.LIBRARY_NAME);
- }
-
/** Create a public builder. */
public V0BuilderHandle(CooperativeCleaner cleaner) {
super(allocatePublic(), cleaner, V0BuilderHandle::deallocate);
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0DataElement.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0DataElement.java
index 0ee2157..2c3c4ed 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0DataElement.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0DataElement.java
@@ -41,10 +41,17 @@
public abstract void visit(Visitor v);
/** Contains the TxPower information. See the spec for more information. */
+ @UsedByNative
public static final class TxPower extends V0DataElement {
- private final int txPower;
+ @UsedByNative private final int txPower;
- public TxPower(int txPower) {
+ public TxPower(NpAdv npAdv, int txPower) {
+ this(txPower);
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private TxPower(int txPower) {
this.txPower = txPower;
}
@@ -79,13 +86,11 @@
}
/** The Actions data element. See the spec for more information. */
+ @UsedByNative
public static final class V0Actions extends V0DataElement {
- static {
- System.loadLibrary(NpAdv.LIBRARY_NAME);
- }
- @IdentityKind private final int identityKind;
- private final int actionBits;
+ @UsedByNative @IdentityKind private final int identityKind;
+ @UsedByNative private final int actionBits;
/**
* Create an actions data element.
@@ -96,11 +101,14 @@
* @throws IllegalStateException when an action is not a valid action for the given identity
* kind.
*/
- public V0Actions(@IdentityKind int identityKind, @V0ActionType int... actions) {
- this(identityKind, nativeMergeActions(identityKind, actions));
+ public V0Actions(NpAdv npAdv, @IdentityKind int identityKind, @V0ActionType int... actions) {
+ npAdv.ensureLoaded();
+ this.identityKind = identityKind;
+ this.actionBits = nativeMergeActions(identityKind, actions);
}
/** Used by native code. This must be private to avoid being confused with the above method. */
+ @UsedByNative
private V0Actions(@IdentityKind int identityKind, int actionBits) {
this.identityKind = identityKind;
this.actionBits = actionBits;
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0Payload.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0Payload.java
index 588a2ca..767bd77 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0Payload.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0Payload.java
@@ -17,6 +17,7 @@
package com.google.android.nearby.presence.rust;
import androidx.annotation.Nullable;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
import java.util.Arrays;
/**
@@ -25,11 +26,8 @@
*/
public final class V0Payload extends OwnedHandle {
- static {
- System.loadLibrary(NpAdv.LIBRARY_NAME);
- }
-
/** Internal data class to pass identity data back from native code. */
+ @UsedByNative
public static final class IdentityDetails {
/**
* @see com.google.android.nearby.presence.rust.credential.CredentialBook.Builder
@@ -38,11 +36,14 @@
private final byte[] identityToken;
private final byte[] salt;
+ private final byte[] keySeed;
- public IdentityDetails(int credentialId, byte[] identityToken, byte[] salt) {
+ @UsedByNative
+ public IdentityDetails(int credentialId, byte[] identityToken, byte[] salt, byte[] keySeed) {
this.credentialId = credentialId;
this.identityToken = Arrays.copyOf(identityToken, identityToken.length);
this.salt = Arrays.copyOf(salt, salt.length);
+ this.keySeed = Arrays.copyOf(keySeed, keySeed.length);
}
public int getCredentialId() {
@@ -56,6 +57,10 @@
public byte[] getSalt() {
return salt;
}
+
+ public byte[] getKeySeed() {
+ return keySeed;
+ }
}
/**
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1AdvertisementBuilder.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1AdvertisementBuilder.java
index 058494b..b765ab3 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1AdvertisementBuilder.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1AdvertisementBuilder.java
@@ -17,6 +17,7 @@
package com.google.android.nearby.presence.rust;
import androidx.annotation.VisibleForTesting;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
import com.google.android.nearby.presence.rust.SerializationException.InsufficientSpaceException;
import com.google.android.nearby.presence.rust.credential.V1BroadcastCredential;
@@ -33,23 +34,14 @@
public final class V1AdvertisementBuilder implements AutoCloseable {
/** Create a builder for a public advertisement. */
- public static V1AdvertisementBuilder newPublic() {
- return newPublic(NpAdv.getCleaner());
+ public static V1AdvertisementBuilder newBuilder(NpAdv npAdv) {
+ return newBuilder(npAdv, NpAdv.getCleaner());
}
/** Create a builder for a public advertisement with a specific cleaner. */
- private static V1AdvertisementBuilder newPublic(CooperativeCleaner cleaner) {
- return new V1AdvertisementBuilder(new V1BuilderHandle(cleaner, false));
- }
-
- /** Create a builder for an encrypted advertisement. */
- public static V1AdvertisementBuilder newEncrypted() {
- return newEncrypted(NpAdv.getCleaner());
- }
-
- /** Create a builder for an encrypted advertisement with a specific cleaner. */
- private static V1AdvertisementBuilder newEncrypted(CooperativeCleaner cleaner) {
- return new V1AdvertisementBuilder(new V1BuilderHandle(cleaner, true));
+ private static V1AdvertisementBuilder newBuilder(NpAdv npAdv, CooperativeCleaner cleaner) {
+ npAdv.ensureLoaded();
+ return new V1AdvertisementBuilder(new V1BuilderHandle(cleaner));
}
@VisibleForTesting final V1BuilderHandle builder;
@@ -63,8 +55,6 @@
*
* @throws UnclosedActiveSectionException when there is already an active section builder.
* @throws InvalidHandleException if this builder has already been closed.
- * @throws InvalidSectionKindException if this advertisement builder is not a public advertisement
- * builder.
* @throws InsufficientSpaceException if this advertisement does not have enough space for a new
* section
*/
@@ -78,8 +68,6 @@
*
* @throws UnclosedActiveSectionException when there is already an active section builder.
* @throws InvalidHandleException if this builder has already been closed.
- * @throws InvalidSectionKindException if this advertisement builder is not an encrypted
- * advertisement builder.
* @throws InsufficientSpaceException if this advertisement does not have enough space for a new
* section
*/
@@ -105,13 +93,10 @@
}
/** Internal builder handle object. */
+ @UsedByNative
static final class V1BuilderHandle extends OwnedHandle {
- static {
- System.loadLibrary(NpAdv.LIBRARY_NAME);
- }
-
- public V1BuilderHandle(CooperativeCleaner cleaner, boolean encrypted) {
- super(allocate(encrypted), cleaner, V1BuilderHandle::deallocate);
+ public V1BuilderHandle(CooperativeCleaner cleaner) {
+ super(allocate(), cleaner, V1BuilderHandle::deallocate);
}
public V1SectionBuilder addPublicSection() throws InsufficientSpaceException {
@@ -124,46 +109,9 @@
return nativeAddEncryptedSection(credential, verificationMode);
}
- private class AddDataElementVisitor implements V1DataElement.Visitor {
- private final int section;
-
- private AddDataElementVisitor(int section) {
- this.section = section;
- }
-
- @Override
- public void visitGeneric(V1DataElement.Generic generic) {
- try {
- nativeAddGenericDataElement(section, generic);
- } catch (InsufficientSpaceException ise) {
- throw new SmuggledInsufficientSpaceException(ise);
- }
- }
- }
-
- /**
- * Helper to smuggle {@link InsufficientSpaceException} (a checked exception) through APIs that
- * don't support checked exceptions.
- */
- private static class SmuggledInsufficientSpaceException extends RuntimeException {
- private final InsufficientSpaceException ise;
-
- public SmuggledInsufficientSpaceException(InsufficientSpaceException ise) {
- this.ise = ise;
- }
-
- public void throwChecked() throws InsufficientSpaceException {
- throw ise;
- }
- }
-
- public void addDataElement(int section, V1DataElement dataElement)
+ public void addDataElement(int section, V1DataElement.Generic dataElement)
throws InsufficientSpaceException {
- try {
- dataElement.visit(new AddDataElementVisitor(section));
- } catch (SmuggledInsufficientSpaceException ex) {
- ex.throwChecked();
- }
+ nativeAddGenericDataElement(section, dataElement);
}
public void finishSection(int section) {
@@ -176,7 +124,7 @@
return nativeBuild();
}
- private static native long allocate(boolean encrypted);
+ private static native long allocate();
private native V1SectionBuilder nativeAddPublicSection() throws InsufficientSpaceException;
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1DataElement.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1DataElement.java
index e7d921e..5eb47b9 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1DataElement.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1DataElement.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Google LLC
+ * Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,14 @@
package com.google.android.nearby.presence.rust;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import java.lang.annotation.Retention;
import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Objects;
/** Base class for V1 data element types. */
public abstract class V1DataElement {
@@ -27,6 +34,26 @@
*/
public interface Visitor {
void visitGeneric(Generic generic);
+
+ void visitTxPower(TxPower txPower);
+
+ void visitCastId(CastId castId);
+
+ void visitActions(Actions actions);
+
+ void visitContextSyncSeqNum(ContextSyncSeqNum seqNum);
+
+ void visitDedupHint(DedupHint dedupHint);
+
+ void visitDeviceType(DeviceType deviceType);
+
+ void visitConnectivityInfo(ConnectivityInfo connectivityInfo);
+
+ void visitMediaDeduplicationId(MediaDeduplicationId mediaDeduplicationId);
+
+ void visitCapabilities(Capabilities capabilities);
+
+ void visitRequirements(Requirements requirements);
}
// All subclasses should be in this file
@@ -36,22 +63,82 @@
public abstract void visit(Visitor v);
/**
+ * Serializes this data element to a generic DE.
+ *
+ * @throws IllegalStateException in cases where the data element which is being serialized has an
+ * invalid internal state, such as a Tx Power out of bounds or an Actions DE containing no
+ * actions (since none have been added yet). While most DEs have guards against this kind of
+ * exception by being immutable and having appropriate checking done in constructors, mutable,
+ * "unfinished" DEs may also throw this exception.
+ * @throws TooMuchDataException in cases where the data element cannot be serialized due to
+ * running out of space in an ordinary V1 DE's payload (max 127 bytes.)
+ */
+ public abstract Generic trySerialize();
+
+ /**
+ * An exception raised when attempting to deserialize a DE from a `Generic` DE into a
+ * representation for some more specific DE type.
+ */
+ @UsedByNative
+ public static class DeserializationException extends Exception {
+ protected DeserializationException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * An exception raised when attempting to serialize a DE, but the contents of the DE would wind up
+ * being too large to fit within the up-to-127-byte payload of a V1 data element.
+ */
+ @UsedByNative
+ public static class TooMuchDataException extends RuntimeException {
+ protected TooMuchDataException() {
+ super("DE has too much data to fit within a V1 DE");
+ }
+ }
+
+ /**
* A generic data element. This is a data element which has a type that is not known by this
* library (e.g. a vendor-specific data element).
*/
+ @UsedByNative
public static final class Generic extends V1DataElement {
private final long type;
private final byte[] data;
- public Generic(long type, byte[] data) {
- this.type = type;
- this.data = Arrays.copyOf(data, data.length);
+ /**
+ * Constructs a new generic DE with the given type-code and payload.
+ *
+ * @throws IllegalArgumentException if the DE payload contained more than 127 bytes.
+ */
+ public Generic(NpAdv npAdv, long type, byte[] data) {
+ this(type, data);
+ npAdv.ensureLoaded();
}
+ /**
+ * Constructs a new generic DE with the given type-code and payload.
+ *
+ * @throws IllegalArgumentException if the DE payload contained more than 127 bytes.
+ */
+ @UsedByNative
+ private Generic(long type, byte[] data) {
+ this.type = type;
+ this.data = Arrays.copyOf(data, data.length);
+ if (type == 0xFFFFFFFF) {
+ throw new IllegalArgumentException("Generic DE has a forbidden type-code!");
+ }
+ if (data.length > 127) {
+ throw new IllegalArgumentException("Generic DE is too long!");
+ }
+ }
+
+ @UsedByNative
public long getType() {
return type;
}
+ @UsedByNative
public byte[] getData() {
return Arrays.copyOf(data, data.length);
}
@@ -60,5 +147,1262 @@
public void visit(Visitor v) {
v.visitGeneric(this);
}
+
+ /**
+ * Attempts to deserialize the contents of this generic DE into a more specific data element
+ * type.
+ *
+ * @return - If the DE type was unrecognized, the input will simply be returned unaltered. - If
+ * the DE type was recognized and the contents were valid for an instance of that DE type,
+ * returns an instance of the appropriate non-`Generic` inheriting subclass of
+ * `V1DataElement`.
+ * @throws DeserializationException if the contents of the passed DE were invalid for the DE's
+ * type code.
+ */
+ public V1DataElement tryDeserialize() throws DeserializationException {
+ return nativeTryDeserialize(this);
+ }
+
+ private static native V1DataElement nativeTryDeserialize(Generic generic)
+ throws DeserializationException;
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private static native Generic nativeTrySerialize(Generic generic);
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (other instanceof Generic) {
+ Generic otherGeneric = (Generic) other;
+ return this.type == otherGeneric.type && Arrays.equals(this.data, otherGeneric.data);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return ((int) (31 * this.type)) + Arrays.hashCode(this.data);
+ }
+
+ @Override
+ public String toString() {
+ return "GenericDE { type_code: "
+ + this.type
+ + ", contents: "
+ + formatByteArrayToString(this.data, ByteValuePrintFormat.HEX);
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize a Tx Power which is out of the valid bounds
+ * ([-20dBm-100dBm]) for a transmission power
+ */
+ @UsedByNative
+ public static final class TxPowerOutOfBoundsException extends DeserializationException {
+ @UsedByNative
+ public TxPowerOutOfBoundsException() {
+ super("Tx Power out of bounds");
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize a Tx Power which fails to occupy exactly one
+ * byte.
+ */
+ @UsedByNative
+ public static final class TxPowerWrongLengthException extends DeserializationException {
+ @UsedByNative
+ public TxPowerWrongLengthException() {
+ super("Tx Power payload doesn't occupy exactly one byte");
+ }
+ }
+
+ /** A transmission power from -20dBm-100dBm. */
+ @UsedByNative
+ public static final class TxPower extends V1DataElement {
+ @UsedByNative private final int txPower;
+
+ /**
+ * Constructs a new V1 Tx Power DE.
+ *
+ * @throws IllegalArgumentException if the transmission power was not in the allowable [-100,
+ * 20] range.
+ */
+ public TxPower(NpAdv npAdv, int txPower) {
+ this(txPower);
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private TxPower(int txPower) {
+ this.txPower = txPower;
+ if (txPower > 20 || txPower < -100) {
+ throw new IllegalArgumentException("Tx power is out of range");
+ }
+ }
+
+ @UsedByNative
+ public int getTxPower() {
+ return txPower;
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitTxPower(this);
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private static native Generic nativeTrySerialize(TxPower txPower);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof TxPower) {
+ TxPower other = (TxPower) o;
+ return other.txPower == this.txPower;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.txPower;
+ }
+
+ @Override
+ public String toString() {
+ return "V1TxPower(" + this.txPower + ")";
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize a Cast ID which fails to occupy exactly 32
+ * bytes.
+ */
+ @UsedByNative
+ public static final class CastIdWrongLengthException extends DeserializationException {
+ @UsedByNative
+ public CastIdWrongLengthException() {
+ super("Cast ID payload doesn't occupy exactly 32 bytes");
+ }
+ }
+
+ /** A 32-byte Cast ID. */
+ @UsedByNative
+ public static final class CastId extends V1DataElement {
+ @UsedByNative private final byte[] castId;
+
+ /**
+ * Constructs a new Cast ID DE.
+ *
+ * @throws IllegalArgumentException if the raw bytes of the ID were a length other than 32.
+ */
+ public CastId(NpAdv npAdv, byte[] castId) {
+ this(castId);
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private CastId(byte[] castId) {
+ this.castId = castId;
+ if (castId.length != 32) {
+ throw new IllegalArgumentException("Cast Id is not of length 32");
+ }
+ }
+
+ @UsedByNative
+ public byte[] getCastId() {
+ return Arrays.copyOf(castId, castId.length);
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitCastId(this);
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private native Generic nativeTrySerialize(CastId castId);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof CastId) {
+ CastId other = (CastId) o;
+ return Arrays.equals(this.castId, other.castId);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(this.castId);
+ }
+
+ @Override
+ public String toString() {
+ return "CastIdDE(" + formatByteArrayToString(this.castId, ByteValuePrintFormat.HEX) + ")";
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize a Cast ID which fails to occupy exactly 32
+ * bytes.
+ */
+ @UsedByNative
+ public static final class MediaDeduplicationIdWrongLengthException
+ extends DeserializationException {
+ @UsedByNative
+ public MediaDeduplicationIdWrongLengthException() {
+ super("Media deduplication ID payload doesn't occupy exactly 20 bytes");
+ }
+ }
+
+ /** A 20-byte Media Deduplication ID. */
+ @UsedByNative
+ public static final class MediaDeduplicationId extends V1DataElement {
+ @UsedByNative private final byte[] mediaDeduplicationId;
+
+ /**
+ * Constructs a new Cast ID DE.
+ *
+ * @throws IllegalArgumentException if the raw bytes of the ID were a length other than 32.
+ */
+ public MediaDeduplicationId(NpAdv npAdv, byte[] mediaDeduplicationId) {
+ this(mediaDeduplicationId);
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private MediaDeduplicationId(byte[] mediaDeduplicationId) {
+ this.mediaDeduplicationId = mediaDeduplicationId;
+ if (mediaDeduplicationId.length != 20) {
+ throw new IllegalArgumentException("Media deduplication ID is not of length 20");
+ }
+ }
+
+ @UsedByNative
+ public byte[] getMediaDeduplicationId() {
+ return Arrays.copyOf(mediaDeduplicationId, mediaDeduplicationId.length);
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitMediaDeduplicationId(this);
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private native Generic nativeTrySerialize(MediaDeduplicationId mediaDeduplicationId);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof MediaDeduplicationId) {
+ MediaDeduplicationId other = (MediaDeduplicationId) o;
+ return Arrays.equals(this.mediaDeduplicationId, other.mediaDeduplicationId);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(this.mediaDeduplicationId);
+ }
+
+ @Override
+ public String toString() {
+ return "MediaDeduplicationIdDE("
+ + formatByteArrayToString(this.mediaDeduplicationId, ByteValuePrintFormat.HEX)
+ + ")";
+ }
+ }
+
+ /**
+ * Blanket exception raised when the payload of an Actions DE was unable to be interpreted as a
+ * set of actions upon deserialization.
+ */
+ @UsedByNative
+ public static final class ActionsMalformedException extends DeserializationException {
+ @UsedByNative
+ public ActionsMalformedException() {
+ super("Actions DE was malformed");
+ }
+ }
+
+ /** A collection of one or more "actions" supported by the broadcaster. */
+ @UsedByNative
+ public static final class Actions extends V1DataElement {
+ private final BitSet actionIds;
+
+ /** Constructs an (initially-empty) set of V1 actions. */
+ public Actions(NpAdv npAdv) {
+ this.actionIds = new BitSet();
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private Actions(byte[] rawBitSet) {
+ this.actionIds = BitSet.valueOf(rawBitSet);
+ }
+
+ /** Gets a copy of the underlying bit-set of actions. */
+ public BitSet getBitSet() {
+ return (BitSet) this.actionIds.clone();
+ }
+
+ /**
+ * Gets a length-256 byte-array which represents a bit set stored in big-endian byte order and
+ * little-endian bit order within each byte.
+ */
+ @UsedByNative
+ private byte[] getRawBitSet() {
+ return Arrays.copyOf(this.actionIds.toByteArray(), 256);
+ }
+
+ /**
+ * Determines whether or not the action with the given ID is set within this set of actions.
+ *
+ * @throws IndexOutOfBoundsException If the action ID is not in the range [0, 2047].
+ */
+ public boolean hasActionWithId(int actionId) {
+ if (actionId < 0 || actionId > 2047) {
+ throw new IndexOutOfBoundsException("V1 Action with ID " + actionId + " is out of bounds.");
+ }
+ return this.actionIds.get(actionId);
+ }
+
+ /**
+ * Adds the action with the given ID to this set of actions if it was not already present.
+ *
+ * @throws IndexOutOfBoundsException If the action ID is not in the range [0, 2047].
+ */
+ public void addActionWithId(int actionId) {
+ if (actionId < 0 || actionId > 2047) {
+ throw new IndexOutOfBoundsException("V1 Action with ID " + actionId + " is out of bounds.");
+ }
+ this.actionIds.set(actionId);
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitActions(this);
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private static native Generic nativeTrySerialize(Actions actions);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof Actions) {
+ Actions other = (Actions) o;
+ return this.actionIds.equals(other.actionIds);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.actionIds.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "V1Actions(" + this.actionIds + ")";
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize a context sync sequence number whose payload is
+ * not exactly one byte.
+ */
+ @UsedByNative
+ public static class ContextSyncSeqNumWrongLengthException extends DeserializationException {
+ @UsedByNative
+ public ContextSyncSeqNumWrongLengthException() {
+ super("Context sync sequence number didn't have a one-byte payload");
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize a context sync sequence number whose value is
+ * not within the supported 0-15 range.
+ */
+ @UsedByNative
+ public static class ContextSyncSeqNumOutOfBoundsException extends DeserializationException {
+ @UsedByNative
+ public ContextSyncSeqNumOutOfBoundsException() {
+ super("Context sync sequence number was not in 0-15");
+ }
+ }
+
+ /** A context sync sequence number between 0 and 15, inclusive. */
+ @UsedByNative
+ public static class ContextSyncSeqNum extends V1DataElement {
+ @UsedByNative private final int seqNum;
+
+ /**
+ * Constructs a new context sync sequence number DE.
+ *
+ * @throws IllegalArgumentException if the context sync sequence number was not in the range
+ * 0-15.
+ */
+ public ContextSyncSeqNum(NpAdv npAdv, int seqNum) {
+ this(seqNum);
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private ContextSyncSeqNum(int seqNum) {
+ this.seqNum = seqNum;
+ if (seqNum < 0 || seqNum > 15) {
+ throw new IllegalArgumentException("Context sync sequence number is out of bounds");
+ }
+ }
+
+ @UsedByNative
+ public int getSeqNum() {
+ return seqNum;
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitContextSyncSeqNum(this);
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private static native Generic nativeTrySerialize(ContextSyncSeqNum seqNum);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof ContextSyncSeqNum) {
+ ContextSyncSeqNum other = (ContextSyncSeqNum) o;
+ return this.seqNum == other.seqNum;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.seqNum;
+ }
+
+ @Override
+ public String toString() {
+ return "ContextSyncSeqNum(" + this.seqNum + ")";
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize a deduplication hint whose payload is not
+ * exactly 8 bytes.
+ */
+ @UsedByNative
+ public static class DedupHintWrongLengthException extends DeserializationException {
+ @UsedByNative
+ public DedupHintWrongLengthException() {
+ super("Dedup hint payload was not exactly 8 bytes");
+ }
+ }
+
+ /**
+ * A deduplication hint used for determining whether/not an advertisement received across multiple
+ * mediums came from the same source.
+ */
+ @UsedByNative
+ public static class DedupHint extends V1DataElement {
+ @UsedByNative private final byte[] dedupHint;
+
+ /**
+ * Constructs a new dedup hint DE.
+ *
+ * @throws IllegalArgumentException if the byte payload was not exactly 8 bytes.
+ */
+ public DedupHint(NpAdv npAdv, byte[] dedupHint) {
+ this(dedupHint);
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private DedupHint(byte[] dedupHint) {
+ this.dedupHint = dedupHint;
+ if (dedupHint.length != 8) {
+ throw new IllegalArgumentException("Dedup hint payload was not exactly 8 bytes.");
+ }
+ }
+
+ @UsedByNative
+ public byte[] getBytes() {
+ return Arrays.copyOf(dedupHint, dedupHint.length);
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitDedupHint(this);
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private static native Generic nativeTrySerialize(DedupHint dedupHint);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof DedupHint) {
+ DedupHint other = (DedupHint) o;
+ return Arrays.equals(this.dedupHint, other.dedupHint);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(this.dedupHint);
+ }
+
+ @Override
+ public String toString() {
+ return "DedupHint(" + formatByteArrayToString(this.dedupHint, ByteValuePrintFormat.HEX) + ")";
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize a device type whose payload is not exactly 1
+ * byte.
+ */
+ @UsedByNative
+ public static class DeviceTypeWrongLengthException extends DeserializationException {
+ @UsedByNative
+ public DeviceTypeWrongLengthException() {
+ super("Device type payload was not exactly 1 byte");
+ }
+ }
+
+ /** The type of device performing the broadcast. */
+ public static class DeviceType extends V1DataElement {
+ @IntDef({
+ Value.UNKNOWN,
+ Value.PHONE,
+ Value.TABLET,
+ Value.DISPLAY,
+ Value.LAPTOP,
+ Value.TV,
+ Value.WATCH,
+ Value.CHROMEOS,
+ Value.FOLDABLE,
+ Value.AUTOMOTIVE,
+ Value.SPEAKER,
+ })
+ @Retention(SOURCE)
+ public @interface Value {
+ public static final int UNKNOWN = 0;
+ public static final int PHONE = 1;
+ public static final int TABLET = 2;
+ public static final int DISPLAY = 3;
+ public static final int LAPTOP = 4;
+ public static final int TV = 5;
+ public static final int WATCH = 6;
+ public static final int CHROMEOS = 7;
+ public static final int FOLDABLE = 8;
+ public static final int AUTOMOTIVE = 9;
+ public static final int SPEAKER = 10;
+ }
+
+ @UsedByNative private final int deviceType;
+
+ /**
+ * Constructs a new device type DE.
+ *
+ * @throws IllegalArgumentException if the device type was outside of the recognized range.
+ */
+ public DeviceType(NpAdv npAdv, int deviceType) {
+ this(deviceType);
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private DeviceType(int deviceType) {
+ this.deviceType = deviceType;
+ if (deviceType < 0 || deviceType > 255) {
+ throw new IllegalArgumentException("Device type is out of the allowable range.");
+ }
+ }
+
+ @UsedByNative
+ public int getValue() {
+ return deviceType;
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitDeviceType(this);
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private static native Generic nativeTrySerialize(DeviceType deviceType);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof DeviceType) {
+ DeviceType other = (DeviceType) o;
+ return this.deviceType == other.deviceType;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.deviceType;
+ }
+
+ @Override
+ public String toString() {
+ String name = "";
+ switch (this.deviceType) {
+ case 0:
+ name = "UNKNOWN";
+ break;
+ case 1:
+ name = "PHONE";
+ break;
+ case 2:
+ name = "TABLET";
+ break;
+ case 3:
+ name = "DISPLAY";
+ break;
+ case 4:
+ name = "LAPTOP";
+ break;
+ case 5:
+ name = "TV";
+ break;
+ case 6:
+ name = "WATCH";
+ break;
+ case 7:
+ name = "CHROMEOS";
+ break;
+ case 8:
+ name = "FOLDABLE";
+ break;
+ case 9:
+ name = "AUTOMOTIVE";
+ break;
+ case 10:
+ name = "SPEAKER";
+ break;
+ default:
+ return "DeviceType(" + this.deviceType + ")";
+ }
+ return "DeviceType(" + name + ")";
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize connectivity info whose component ordering,
+ * uniqueness, or non-emptiness is violated.
+ */
+ @UsedByNative
+ public static class ConnectivityInfoMalformedException extends DeserializationException {
+ @UsedByNative
+ public ConnectivityInfoMalformedException() {
+ super("Connectivity info had a malformed payload.");
+ }
+ }
+
+ /**
+ * Exception raised upon attempting to deserialize connectivity info with a malformed component.
+ */
+ @UsedByNative
+ public static class ConnectivityInfoComponentMalformedException extends DeserializationException {
+ @UsedByNative
+ public ConnectivityInfoComponentMalformedException(int index) {
+ super("Connectivity info had a malformed payload component at index: " + index);
+ }
+ }
+
+ /** Information for connecting to a broadcasting device. */
+ @UsedByNative
+ public static class ConnectivityInfo extends V1DataElement {
+ @Nullable private BleConnectivityInfo bleInfo = null;
+ @Nullable private WifiLanConnectivityInfo wifiLanInfo = null;
+
+ @UsedByNative
+ private ConnectivityInfo(BleConnectivityInfo bleInfo, WifiLanConnectivityInfo wifiLanInfo) {
+ this.bleInfo = bleInfo;
+ this.wifiLanInfo = wifiLanInfo;
+ }
+
+ public ConnectivityInfo(NpAdv npAdv) {
+ npAdv.ensureLoaded();
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitConnectivityInfo(this);
+ }
+
+ /** Sets BLE connectivity info, with null indicating that it isn't present. */
+ public ConnectivityInfo setBleInfo(@Nullable BleConnectivityInfo bleInfo) {
+ this.bleInfo = bleInfo;
+ return this;
+ }
+
+ /** Sets Wifi-LAN connectivity info, with null indicating that it isn't present. */
+ public ConnectivityInfo setWifiLanInfo(@Nullable WifiLanConnectivityInfo wifiLanInfo) {
+ this.wifiLanInfo = wifiLanInfo;
+ return this;
+ }
+
+ /** Gets BLE connectivity info, with null indicating that it isn't present. */
+ @UsedByNative
+ @Nullable
+ public BleConnectivityInfo getBleInfo() {
+ return this.bleInfo;
+ }
+
+ /** Gets Wifi-LAN connectivity info, with null indicating that it isn't present. */
+ @UsedByNative
+ @Nullable
+ public WifiLanConnectivityInfo getWifiLanInfo() {
+ return this.wifiLanInfo;
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private static native Generic nativeTrySerialize(ConnectivityInfo connectivityInfo);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof ConnectivityInfo) {
+ ConnectivityInfo other = (ConnectivityInfo) o;
+ return Objects.equals(this.bleInfo, other.bleInfo)
+ && Objects.equals(this.wifiLanInfo, other.wifiLanInfo);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.bleInfo, this.wifiLanInfo);
+ }
+
+ @Override
+ public String toString() {
+ String result = "ConnectivityInfo(";
+ if (this.bleInfo != null) {
+ result += this.bleInfo.toString();
+ }
+ if (this.bleInfo != null && this.wifiLanInfo != null) {
+ result += ", ";
+ }
+ if (this.wifiLanInfo != null) {
+ result += this.wifiLanInfo.toString();
+ }
+ return result;
+ }
+ }
+
+ /** Bluetooth Low Energy (BLE) component of the connectivity info DE. */
+ @UsedByNative
+ public static class BleConnectivityInfo {
+ /** Bluetooth MAC address */
+ @Nullable private byte[] macAddress = null;
+
+ /**
+ * An identifier for the GATT service to connect to.
+ *
+ * <p>This could be a UUID + instance ID (17 bytes), an attribute handle + instance ID (3
+ * bytes), or just a UUID (16 bytes) or attribute handle (2 bytes).
+ */
+ @Nullable private byte[] gattServiceIdentifier = null;
+
+ /** Two-byte PSM (L2CAP) port to connect on */
+ @Nullable private byte[] psm = null;
+
+ /** Two-byte physical identifier ("device token") for the device. */
+ @Nullable private byte[] deviceToken = null;
+
+ @UsedByNative
+ private BleConnectivityInfo(
+ @Nullable byte[] macAddress,
+ @Nullable byte[] gattServiceIdentifier,
+ @Nullable byte[] psm,
+ @Nullable byte[] deviceToken) {
+ this.macAddress = macAddress;
+ this.gattServiceIdentifier = gattServiceIdentifier;
+ this.psm = psm;
+ this.deviceToken = deviceToken;
+ }
+
+ public BleConnectivityInfo() {}
+
+ /**
+ * Sets the 6-byte BLE MAC address.
+ *
+ * @throws IllegalArgumentException If the passed byte-array was non-null, but of the wrong
+ * length.
+ */
+ public BleConnectivityInfo setMacAddress(@Nullable byte[] macAddress) {
+ if (macAddress != null && macAddress.length != 6) {
+ throw new IllegalArgumentException("BLE MAC address should be 6 bytes");
+ }
+ this.macAddress = macAddress;
+ return this;
+ }
+
+ /**
+ * Sets the 17-bytes-or-less GATT service identifier.
+ *
+ * @throws IllegalArgumentException If the passed byte-array was non-null, but of the wrong
+ * length.
+ */
+ public BleConnectivityInfo setGattServiceIdentifier(@Nullable byte[] gattServiceIdentifier) {
+ if (gattServiceIdentifier != null && gattServiceIdentifier.length > 17) {
+ throw new IllegalArgumentException("GATT service identifier should be under 17 bytes");
+ }
+ this.gattServiceIdentifier = gattServiceIdentifier;
+ return this;
+ }
+
+ /**
+ * Sets the 2-byte PSM.
+ *
+ * @throws IllegalArgumentException If the passed byte-array was non-null, but of the wrong
+ * length.
+ */
+ public BleConnectivityInfo setPsm(@Nullable byte[] psm) {
+ if (psm != null && psm.length != 2) {
+ throw new IllegalArgumentException("PSM should be 2 bytes");
+ }
+ this.psm = psm;
+ return this;
+ }
+
+ /**
+ * Sets the 2-byte device token.
+ *
+ * @throws IllegalArgumentException If the passed byte-array was non-null, but of the wrong
+ * length.
+ */
+ public BleConnectivityInfo setDeviceToken(@Nullable byte[] deviceToken) {
+ if (deviceToken != null && deviceToken.length != 2) {
+ throw new IllegalArgumentException("Device Token should be 2 bytes");
+ }
+ this.deviceToken = deviceToken;
+ return this;
+ }
+
+ /** Gets the 6-byte BLE Mac address */
+ @UsedByNative
+ @Nullable
+ public byte[] getMacAddress() {
+ return this.macAddress;
+ }
+
+ /** Gets the sub-17-byte GATT service identifier. */
+ @UsedByNative
+ @Nullable
+ public byte[] getGattServiceIdentifier() {
+ return this.gattServiceIdentifier;
+ }
+
+ /** Gets the 2-byte PSM. */
+ @UsedByNative
+ @Nullable
+ public byte[] getPsm() {
+ return this.psm;
+ }
+
+ /** Gets the 2-byte device token. */
+ @UsedByNative
+ @Nullable
+ public byte[] getDeviceToken() {
+ return this.deviceToken;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof BleConnectivityInfo) {
+ BleConnectivityInfo other = (BleConnectivityInfo) o;
+ return Arrays.equals(this.macAddress, other.macAddress)
+ && Arrays.equals(this.gattServiceIdentifier, other.gattServiceIdentifier)
+ && Arrays.equals(this.psm, other.psm)
+ && Arrays.equals(this.deviceToken, other.deviceToken);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.deepHashCode(
+ new Object[] {this.macAddress, this.gattServiceIdentifier, this.psm, this.deviceToken});
+ }
+
+ @Override
+ public String toString() {
+ return "BleConnectivityInfo(MAC="
+ + formatByteArrayToString(this.macAddress, ByteValuePrintFormat.HEX)
+ + ", GattServiceIdentifier="
+ + formatByteArrayToString(this.gattServiceIdentifier, ByteValuePrintFormat.HEX)
+ + ", PSM="
+ + formatByteArrayToString(this.psm, ByteValuePrintFormat.HEX)
+ + ", deviceToken="
+ + formatByteArrayToString(this.deviceToken, ByteValuePrintFormat.HEX)
+ + ")";
+ }
+ }
+
+ /** Wifi-LAN component of the connectivity info DE. */
+ @UsedByNative
+ public static class WifiLanConnectivityInfo {
+ /** IPv4 (4 bytes) or IPv6 (16 bytes) address. */
+ @Nullable private byte[] ipAddress = null;
+
+ /** Two-byte big-endian port number */
+ @Nullable private byte[] port = null;
+
+ /** 6-byte BSSID */
+ @Nullable private byte[] bssid = null;
+
+ @UsedByNative
+ private WifiLanConnectivityInfo(
+ @Nullable byte[] ipAddress, @Nullable byte[] port, @Nullable byte[] bssid) {
+ this.ipAddress = ipAddress;
+ this.port = port;
+ this.bssid = bssid;
+ }
+
+ public WifiLanConnectivityInfo() {}
+
+ /**
+ * Sets the 4 (IPv4) or 16-byte (IPv6) IP address.
+ *
+ * @throws IllegalArgumentException If the passed byte-array was non-null, but of the wrong
+ * length.
+ */
+ public WifiLanConnectivityInfo setIpAddress(@Nullable byte[] ipAddress) {
+ if (ipAddress != null && !(ipAddress.length == 4 || ipAddress.length == 16)) {
+ throw new IllegalArgumentException("IP address should be 4 or 16 bytes in length");
+ }
+ this.ipAddress = ipAddress;
+ return this;
+ }
+
+ /**
+ * Sets the 2-byte big-endian port number.
+ *
+ * @throws IllegalArgumentException If the passed byte-array was non-null, but of the wrong
+ * length.
+ */
+ public WifiLanConnectivityInfo setPort(@Nullable byte[] port) {
+ if (port != null && port.length != 2) {
+ throw new IllegalArgumentException("Port should be 2 bytes in length");
+ }
+ this.port = port;
+ return this;
+ }
+
+ /**
+ * Sets the 6-byte BSSID.
+ *
+ * @throws IllegalArgumentException If the passed byte-array was non-null, but of the wrong
+ * length.
+ */
+ public WifiLanConnectivityInfo setBssid(@Nullable byte[] bssid) {
+ if (bssid != null && bssid.length != 6) {
+ throw new IllegalArgumentException("BSSID should be 6 bytes in length");
+ }
+ this.bssid = bssid;
+ return this;
+ }
+
+ /** Gets the 4-byte (IPV4) or 16-byte (IPV4) IP address. */
+ @UsedByNative
+ @Nullable
+ public byte[] getIpAddress() {
+ return this.ipAddress;
+ }
+
+ /** Gets the 2-byte big endian port number */
+ @UsedByNative
+ @Nullable
+ public byte[] getPort() {
+ return this.port;
+ }
+
+ /** Gets the 6-byte BSSID. */
+ @UsedByNative
+ @Nullable
+ public byte[] getBssid() {
+ return this.bssid;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof WifiLanConnectivityInfo) {
+ WifiLanConnectivityInfo other = (WifiLanConnectivityInfo) o;
+ return Arrays.equals(this.ipAddress, other.ipAddress)
+ && Arrays.equals(this.port, other.port)
+ && Arrays.equals(this.bssid, other.bssid);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.deepHashCode(new Object[] {this.ipAddress, this.port, this.bssid});
+ }
+
+ @Override
+ public String toString() {
+ return "WifiLanConnectivityInfo(IP="
+ + formatByteArrayToString(this.ipAddress, ByteValuePrintFormat.DECIMAL)
+ + ", Port="
+ + formatByteArrayToString(this.port, ByteValuePrintFormat.HEX)
+ + ", BSSID="
+ + formatByteArrayToString(this.bssid, ByteValuePrintFormat.HEX)
+ + ")";
+ }
+ }
+
+ /**
+ * Blanket exception raised when the payload of a Capabilities DE was unable to be interpreted as
+ * a set of capabilities upon deserialization.
+ */
+ @UsedByNative
+ public static final class CapabilitiesMalformedException extends DeserializationException {
+ @UsedByNative
+ public CapabilitiesMalformedException() {
+ super("Capabilities DE was malformed");
+ }
+ }
+
+ /** A collection of one or more device capabilities supported by the broadcaster. */
+ @UsedByNative
+ public static final class Capabilities extends V1DataElement {
+ private final BitSet deviceCapabilities;
+
+ /** Constructs an (initially-empty) set of V1 device capabilities. */
+ public Capabilities(NpAdv npAdv) {
+ this.deviceCapabilities = new BitSet();
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private Capabilities(byte capabilities) {
+ if (capabilities < 0 || capabilities > 15) {
+ throw new IllegalArgumentException("Capabilities is using reserved bits.");
+ }
+ this.deviceCapabilities = BitSet.valueOf(new byte[] {capabilities});
+ }
+
+ /** Gets a copy of the underlying bit-set of device capabilities. */
+ public BitSet getBitSet() {
+ return (BitSet) this.deviceCapabilities.clone();
+ }
+
+ @UsedByNative
+ private byte getValue() {
+ return this.deviceCapabilities.toByteArray()[0];
+ }
+
+ /**
+ * Adds the device capability to this set of capabilities if it was not already present.
+ *
+ * @throws IndexOutOfBoundsException If the device capability is not in the range [0, 3].
+ */
+ public void addCapability(int deviceCapability) {
+ if (deviceCapability < 0 || deviceCapability > 3) {
+ throw new IndexOutOfBoundsException(
+ "Device Capability: " + deviceCapability + " is out of bounds.");
+ }
+ this.deviceCapabilities.set(deviceCapability);
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitCapabilities(this);
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private static native Generic nativeTrySerialize(Capabilities actions);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof Capabilities) {
+ Capabilities other = (Capabilities) o;
+ return this.deviceCapabilities.equals(other.deviceCapabilities);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.deviceCapabilities.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Capabilities(" + this.deviceCapabilities + ")";
+ }
+ }
+
+ /**
+ * Blanket exception raised when the payload of a Requirements DE was unable to be interpreted as
+ * a set of required capabilities upon deserialization.
+ */
+ @UsedByNative
+ public static final class RequirementsMalformedException extends DeserializationException {
+ @UsedByNative
+ public RequirementsMalformedException() {
+ super("Requirements DE was malformed");
+ }
+ }
+
+ /** A collection of one or more capabilities of a remote device required by the broadcaster. */
+ @UsedByNative
+ public static final class Requirements extends V1DataElement {
+ private final BitSet requirements;
+
+ /** Constructs an (initially-empty) set of requirements. */
+ public Requirements(NpAdv npAdv) {
+ this.requirements = new BitSet();
+ npAdv.ensureLoaded();
+ }
+
+ @UsedByNative
+ private Requirements(byte requirements) {
+ if (requirements < 0 || requirements > 15) {
+ throw new IllegalArgumentException("Requirements is using reserved bits.");
+ }
+ this.requirements = BitSet.valueOf(new byte[] {requirements});
+ }
+
+ /** Gets a copy of the underlying bit-set of requirements. */
+ public BitSet getBitSet() {
+ return (BitSet) this.requirements.clone();
+ }
+
+ @UsedByNative
+ private byte getValue() {
+ return this.requirements.toByteArray()[0];
+ }
+
+ /**
+ * Adds the capability to this set of required capabilities if it was not already present.
+ *
+ * @throws IndexOutOfBoundsException If the capability is not in the range [0, 3].
+ */
+ public void addCapability(int capability) {
+ if (capability < 0 || capability > 3) {
+ throw new IndexOutOfBoundsException("Capability: " + capability + " is out of bounds.");
+ }
+ this.requirements.set(capability);
+ }
+
+ @Override
+ public void visit(Visitor v) {
+ v.visitRequirements(this);
+ }
+
+ @Override
+ public Generic trySerialize() {
+ return nativeTrySerialize(this);
+ }
+
+ private static native Generic nativeTrySerialize(Requirements actions);
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof Requirements) {
+ Requirements other = (Requirements) o;
+ return this.requirements.equals(other.requirements);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.requirements.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Requirements(" + this.requirements + ")";
+ }
+ }
+
+ private enum ByteValuePrintFormat {
+ HEX,
+ DECIMAL,
+ }
+
+ /** Formats an array of bytes as a list of hexadecimal octet values */
+ private static String formatByteArrayToString(
+ @Nullable byte[] array, ByteValuePrintFormat format) {
+ if (array == null) {
+ return "null";
+ }
+ if (array.length == 0) {
+ return "[]";
+ }
+ boolean first = true;
+ // Reserve enough space for up to 5 characters per element (up to 3 digits
+ // or up to two hex characters followed by a comma and a space)
+ // with the two '[]' brackets at the ends.
+ StringBuilder result = new StringBuilder(array.length * 5 + 2);
+ for (byte val : array) {
+ if (first) {
+ result.append("[");
+ first = false;
+ } else {
+ result.append(", ");
+ }
+ switch (format) {
+ case HEX:
+ result.append(String.format("%02x", Byte.toUnsignedInt(val)));
+ break;
+ case DECIMAL:
+ result.append("" + Byte.toUnsignedInt(val));
+ break;
+ default:
+ break;
+ }
+ }
+ result.append("]");
+ return result.toString();
}
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1SectionBuilder.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1SectionBuilder.java
index d2d62cc..01df828 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1SectionBuilder.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1SectionBuilder.java
@@ -16,6 +16,7 @@
package com.google.android.nearby.presence.rust;
+import com.google.android.nearby.presence.rust.SerializationException.DuplicateDataElementTypeCodeException;
import com.google.android.nearby.presence.rust.SerializationException.InsufficientSpaceException;
import com.google.android.nearby.presence.rust.V1AdvertisementBuilder.V1BuilderHandle;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
@@ -25,6 +26,7 @@
* V1AdvertisementBuilder#addPublicSection()} for a public section or {@link
* V1AdvertisementBuilder#addEncryptedSection()} for an encrypted section.
*/
+@UsedByNative
public final class V1SectionBuilder implements AutoCloseable {
private final V1BuilderHandle builder;
@@ -36,8 +38,8 @@
*/
private boolean finishCalled = false;
- // Native used
@SuppressWarnings("UnusedMethod")
+ @UsedByNative
private V1SectionBuilder(V1BuilderHandle builder, int section) {
this.builder = builder;
this.section = section;
@@ -49,10 +51,12 @@
*
* @throws InvalidDataElementException when the given data element is not valid (e.g. value out of
* range, too large, etc.)
+ * @throws DuplicateDataElementTypeCodeException when the given data element has a type code
+ * identical to a data element which has already been added to the section.
* @throws InsufficientSpaceException when the data element will not fit in the remaining space.
*/
@CanIgnoreReturnValue
- public V1SectionBuilder addDataElement(V1DataElement dataElement)
+ public V1SectionBuilder addDataElement(V1DataElement.Generic dataElement)
throws InsufficientSpaceException {
builder.addDataElement(section, dataElement);
return this;
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/VerificationMode.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/VerificationMode.java
index ba7953b..39922e7 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/VerificationMode.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/VerificationMode.java
@@ -21,9 +21,9 @@
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
-@IntDef({VerificationMode.MIC, VerificationMode.SIGNATURE})
+@IntDef({VerificationMode.MIC})
@Retention(SOURCE)
+@UsedByNative
public @interface VerificationMode {
- public static final int MIC = 0;
- public static final int SIGNATURE = 1;
+ @UsedByNative public static final int MIC = 0;
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialBook.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialBook.java
index e264f13..0fafb34 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialBook.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialBook.java
@@ -17,23 +17,21 @@
package com.google.android.nearby.presence.rust.credential;
import androidx.annotation.Nullable;
-import com.google.android.nearby.presence.rust.CooperativeCleaner;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
import com.google.android.nearby.presence.rust.NpAdv;
import com.google.android.nearby.presence.rust.OwnedHandle;
+import com.google.android.nearby.presence.rust.UsedByNative;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.ArrayList;
/**
* A set of credentials used for deserialization. Create a credential book using the builder from
- * {@link #builder()}. An empty credential book ({@link #empty()}) can be used to deserialize public
- * advertisements and sections.
+ * {@link NpAdv#newCredentialBookBuilder()}. An empty credential book ({@link
+ * NpAdv#newEmptyCredentialBook()}) can be used to deserialize public advertisements and sections.
*/
+@UsedByNative
public final class CredentialBook<M extends CredentialBook.MatchedMetadata> extends OwnedHandle {
- static {
- System.loadLibrary(NpAdv.LIBRARY_NAME);
- }
-
/** Metadata that is associated with a Credential. */
public interface MatchedMetadata {
/**
@@ -56,7 +54,9 @@
}
/** Thrown when a cryptographic key is given and those key bytes are not valid. */
+ @UsedByNative
public static final class InvalidPublicKeyException extends RuntimeException {
+ @UsedByNative
public InvalidPublicKeyException() {
super(
"The provided public key bytes do not actually represent a valid \"edwards y\" format or"
@@ -131,7 +131,8 @@
}
/** Create a credential book builder with the default cleaner from {@link NpAdv#getCleaner()}. */
- public static <M extends MatchedMetadata> Builder<M> builder() {
+ public static <M extends MatchedMetadata> Builder<M> builder(NpAdv npAdv) {
+ npAdv.ensureLoaded();
return new Builder<M>(NpAdv.getCleaner());
}
@@ -139,7 +140,8 @@
* Create an empty credential book. This is useful for when only plaintext advertisements are
* being deserialized.
*/
- public static CredentialBook<NoMetadata> empty() {
+ public static CredentialBook<NoMetadata> empty(NpAdv npAdv) {
+ npAdv.ensureLoaded();
return new Builder<NoMetadata>(NpAdv.getCleaner()).build();
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialSlab.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialSlab.java
index 29f2a08..1fba6ca 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialSlab.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialSlab.java
@@ -16,8 +16,7 @@
package com.google.android.nearby.presence.rust.credential;
-import com.google.android.nearby.presence.rust.CooperativeCleaner;
-import com.google.android.nearby.presence.rust.NpAdv;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
import com.google.android.nearby.presence.rust.OwnedHandle;
/**
@@ -25,10 +24,6 @@
* CredentialBook}. Clients should use {@link CredentialSlab.Builder} instead of this class.
*/
final class CredentialSlab extends OwnedHandle {
- static {
- System.loadLibrary(NpAdv.LIBRARY_NAME);
- }
-
/** Create a new {@code CredentialSlab} with the given {@code cleaner}. */
public CredentialSlab(CooperativeCleaner cleaner) {
super(allocate(), cleaner, CredentialSlab::deallocate);
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0BroadcastCredential.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0BroadcastCredential.java
index d47a15a..273d80e 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0BroadcastCredential.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0BroadcastCredential.java
@@ -18,11 +18,15 @@
import static com.google.android.nearby.presence.rust.credential.Utils.copyBytes;
+import com.google.android.nearby.presence.rust.NpAdv;
+import com.google.android.nearby.presence.rust.UsedByNative;
+
/** A V0 broadcast credential in a format that is ready to be passed to native code. */
@SuppressWarnings("UnusedVariable")
+@UsedByNative
public final class V0BroadcastCredential {
- private final byte[] keySeed;
- private final byte[] identityToken;
+ @UsedByNative private final byte[] keySeed;
+ @UsedByNative private final byte[] identityToken;
/**
* Create the credential. {@code keySeed} is exactly 32 bytes. {@code identityToken} is exactly 14
@@ -32,4 +36,17 @@
this.keySeed = copyBytes(keySeed, 32);
this.identityToken = copyBytes(identityToken, 14);
}
+
+ /**
+ * Encrypt plaintext metadata with a V0BroadcastCredential. {@code credential} is a
+ * V0BroadcastCredential. {@code metadata} is plaintext metadata.
+ */
+ public static byte[] encryptMetadata(
+ NpAdv npAdv, V0BroadcastCredential credential, byte[] metadata) {
+ npAdv.ensureLoaded();
+ return nativeEncryptMetadata(credential, metadata);
+ }
+
+ private static native byte[] nativeEncryptMetadata(
+ V0BroadcastCredential credential, byte[] metadata);
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential.java
index f2d1a41..e8bd267 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential.java
@@ -18,11 +18,14 @@
import static com.google.android.nearby.presence.rust.credential.Utils.copyBytes;
+import com.google.android.nearby.presence.rust.UsedByNative;
+
/** A V0 discovery credential in a format that is ready to be passed to native code. */
@SuppressWarnings("UnusedVariable")
+@UsedByNative
public final class V0DiscoveryCredential {
- private final byte[] keySeed;
- private final byte[] identityTokenHmac;
+ @UsedByNative private final byte[] keySeed;
+ @UsedByNative private final byte[] identityTokenHmac;
/** Create the credential. Each array is exactly 32 bytes. */
public V0DiscoveryCredential(byte[] keySeed, byte[] identityTokenHmac) {
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1BroadcastCredential.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1BroadcastCredential.java
index d9108a3..5f307ab 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1BroadcastCredential.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1BroadcastCredential.java
@@ -18,20 +18,35 @@
import static com.google.android.nearby.presence.rust.credential.Utils.copyBytes;
+import com.google.android.nearby.presence.rust.NpAdv;
+import com.google.android.nearby.presence.rust.UsedByNative;
+
/** A V1 broadcast credential in a format that is ready to be passed to native code. */
@SuppressWarnings("UnusedVariable")
+@UsedByNative
public final class V1BroadcastCredential {
- private final byte[] keySeed;
- private final byte[] identityToken;
- private final byte[] privateKey;
+ @UsedByNative private final byte[] keySeed;
+ @UsedByNative private final byte[] identityToken;
/**
- * Create the credential. {@code keySeed} and {@code privateKey} are exactly 32 bytes. {@code
- * identityToken} is exactly 16 bytes
+ * Create the credential. {@code keySeed} is exactly 32 bytes. {@code identityToken} is exactly 16
+ * bytes
*/
- public V1BroadcastCredential(byte[] keySeed, byte[] identityToken, byte[] privateKey) {
+ public V1BroadcastCredential(byte[] keySeed, byte[] identityToken) {
this.keySeed = copyBytes(keySeed, 32);
this.identityToken = copyBytes(identityToken, 16);
- this.privateKey = copyBytes(privateKey, 32);
}
+
+ /**
+ * Encrypt plaintext metadata with a V1BroadcastCredential. {@code credential} is a
+ * V1BroadcastCredential. {@code metadata} is plaintext metadata.
+ */
+ public static byte[] encryptMetadata(
+ NpAdv npAdv, V1BroadcastCredential credential, byte[] metadata) {
+ npAdv.ensureLoaded();
+ return nativeEncryptMetadata(credential, metadata);
+ }
+
+ private static native byte[] nativeEncryptMetadata(
+ V1BroadcastCredential credential, byte[] metadata);
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential.java
index 53cfcfe..c9f9ece 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential.java
@@ -18,28 +18,25 @@
import static com.google.android.nearby.presence.rust.credential.Utils.copyBytes;
+import com.google.android.nearby.presence.rust.UsedByNative;
+
/** A V1 discovery credential in a format that is ready to be passed to native code. */
@SuppressWarnings("UnusedVariable")
+@UsedByNative
public final class V1DiscoveryCredential {
- private final byte[] keySeed;
- private final byte[] expectedMicShortSaltIdentityTokenHmac;
- private final byte[] expectedMicExtendedSaltIdentityTokenHmac;
- private final byte[] expectedSignatureIdentityTokenHmac;
- private final byte[] pubKey;
+ @UsedByNative private final byte[] keySeed;
+ @UsedByNative private final byte[] expectedMicShortSaltIdentityTokenHmac;
+ @UsedByNative private final byte[] expectedMicExtendedSaltIdentityTokenHmac;
/** Create the credential. Each array is exactly 32 bytes. */
public V1DiscoveryCredential(
byte[] keySeed,
byte[] expectedMicShortSaltIdentityTokenHmac,
- byte[] expectedMicExtendedSaltIdentityTokenHmac,
- byte[] expectedSignatureIdentityTokenHmac,
- byte[] pubKey) {
+ byte[] expectedMicExtendedSaltIdentityTokenHmac) {
this.keySeed = copyBytes(keySeed, 32);
this.expectedMicShortSaltIdentityTokenHmac =
copyBytes(expectedMicShortSaltIdentityTokenHmac, 32);
this.expectedMicExtendedSaltIdentityTokenHmac =
copyBytes(expectedMicExtendedSaltIdentityTokenHmac, 32);
- this.expectedSignatureIdentityTokenHmac = copyBytes(expectedSignatureIdentityTokenHmac, 32);
- this.pubKey = copyBytes(pubKey, 32);
}
}
diff --git a/nearby/presence/np_java_ffi/proguarded_unit_tests.pgcfg b/nearby/presence/np_java_ffi/proguarded_unit_tests.pgcfg
new file mode 100644
index 0000000..bcf8ddb
--- /dev/null
+++ b/nearby/presence/np_java_ffi/proguarded_unit_tests.pgcfg
@@ -0,0 +1,42 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.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.
+-include used_by_native.pgcfg
+
+-dontskipnonpubliclibraryclasses
+-dontskipnonpubliclibraryclassmembers
+-allowaccessmodification
+
+# This prevents JUnit-relevant constructs from being obfuscated
+# in test builds. These should not be present in release builds.
+-keepclasseswithmembers @org.junit.runner.RunWith class * { *; }
+-keepclasseswithmembers class androidx.test.** { *; }
+-keepclasseswithmembers class org.junit.** { *; }
+-keepclasseswithmembers class junit.** { *; }
+
+-keep @org.junit.Test class *
+
+-keepclasseswithmembers class * {
+ @org.junit.Test *;
+}
+
+-keepclasseswithmembers class com.google.android.nearby.presence.rust.*Test { *; }
+-keep class com.google.android.nearby.presence.rust.*Test { *; }
+
+-keepattributes AnnotationDefault, Test
+
+-dontwarn org.junit.**
+-dontwarn com.google.gson.**
+-dontwarn org.mockito.**
+-dontwarn com.google.common.**
+-libraryjars <java.home>/jmods/java.base.jmod(!**.jar;!module-info.class)
diff --git a/nearby/presence/np_java_ffi/settings.gradle.kts b/nearby/presence/np_java_ffi/settings.gradle.kts
index 0c9915a..68675c4 100644
--- a/nearby/presence/np_java_ffi/settings.gradle.kts
+++ b/nearby/presence/np_java_ffi/settings.gradle.kts
@@ -15,3 +15,6 @@
*/
rootProject.name = "np-java-ffi"
+
+include("cooperative_cleaner")
+project(":cooperative_cleaner").projectDir = File("../../../common/cooperative_cleaner/lib")
diff --git a/nearby/presence/np_java_ffi/src/class.rs b/nearby/presence/np_java_ffi/src/class.rs
index d8742cb..f707c30 100644
--- a/nearby/presence/np_java_ffi/src/class.rs
+++ b/nearby/presence/np_java_ffi/src/class.rs
@@ -66,7 +66,7 @@
pub use legible_v1_sections::LegibleV1Sections;
pub use owned_handle::NoSpaceLeftException;
pub use serialization_exception::{
- InsufficientSpaceException, InvalidDataElementException, InvalidSectionKindException,
+ DuplicateDataElementTypeCodeException, InsufficientSpaceException, InvalidDataElementException,
LdtEncryptionException, UnclosedActiveSectionException, UnencryptedSizeException,
};
pub use v0_broadcast_credential::V0BroadcastCredential;
diff --git a/nearby/presence/np_java_ffi/src/class/credential_slab.rs b/nearby/presence/np_java_ffi/src/class/credential_slab.rs
index d22dea1..f84f720 100644
--- a/nearby/presence/np_java_ffi/src/class/credential_slab.rs
+++ b/nearby/presence/np_java_ffi/src/class/credential_slab.rs
@@ -85,9 +85,10 @@
let slab = CredentialSlab::from_handle(Handle::from_id(handle_id as u64));
let core_cred = credential.get_as_core(&mut env)?;
- let match_data = MatchedCredential::from_arc_bytes(
+ let match_data = MatchedCredential::new(
cred_id as i64,
- env.convert_byte_array(&encrypted_metadata_bytes)?.into(),
+ env.convert_byte_array(&encrypted_metadata_bytes)?.as_slice(),
+ core_cred.key_seed,
);
Ok::<_, jni::errors::Error>(match slab.add_v0(core_cred, match_data) {
@@ -121,9 +122,10 @@
let slab = CredentialSlab::from_handle(Handle::from_id(handle_id as u64));
let core_cred = credential.get_as_core(&mut env)?;
- let match_data = MatchedCredential::from_arc_bytes(
+ let match_data = MatchedCredential::new(
cred_id as i64,
- env.convert_byte_array(&encrypted_metadata_bytes)?.into(),
+ env.convert_byte_array(&encrypted_metadata_bytes)?.as_slice(),
+ core_cred.key_seed,
);
Ok::<_, jni::errors::Error>(match slab.add_v1(core_cred, match_data) {
diff --git a/nearby/presence/np_java_ffi/src/class/deserialization_exception.rs b/nearby/presence/np_java_ffi/src/class/deserialization_exception.rs
index a48c515..a69d15a 100644
--- a/nearby/presence/np_java_ffi/src/class/deserialization_exception.rs
+++ b/nearby/presence/np_java_ffi/src/class/deserialization_exception.rs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::exception_wrapper;
use jni::{
objects::{JObject, JThrowable},
JNIEnv,
@@ -19,59 +20,13 @@
use np_adv::AdvDeserializationError;
use pourover::desc::ClassDesc;
-static INVALID_HEADER_CLASS: ClassDesc = ClassDesc::new(
- "com/google/android/nearby/presence/rust/DeserializationException$InvalidHeaderException",
-);
+exception_wrapper!(
+ /// Rust representation of `class DeserializationException.InvalidHeaderException`.
+ InvalidHeaderException, "com/google/android/nearby/presence/rust/DeserializationException$InvalidHeaderException");
-/// Rust representation of `class DeserializationException.InvalidHeaderException`.
-#[repr(transparent)]
-pub struct InvalidHeaderException<Obj>(pub Obj);
-
-impl<'local> InvalidHeaderException<JObject<'local>> {
- /// Create a new instance.
- pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
- pourover::call_constructor!(env, &INVALID_HEADER_CLASS, "()V").map(Self)
- }
-
- /// Create a new instance and throw it.
- pub fn throw_new(env: &mut JNIEnv<'local>) -> jni::errors::Result<()> {
- Self::construct(env)?.throw(env)
- }
-}
-
-impl<'local, Obj: AsRef<JObject<'local>>> InvalidHeaderException<Obj> {
- /// Throw this exception.
- pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
- env.throw(<&JThrowable>::from(self.0.as_ref()))
- }
-}
-
-static INVALID_FORMAT_CLASS: ClassDesc = ClassDesc::new(
- "com/google/android/nearby/presence/rust/DeserializationException$InvalidFormatException",
-);
-
-/// Rust representation of `class DeserializationException.InvalidFormatException`.
-#[repr(transparent)]
-pub struct InvalidFormatException<Obj>(pub Obj);
-
-impl<'local> InvalidFormatException<JObject<'local>> {
- /// Create a new instance.
- pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
- pourover::call_constructor!(env, &INVALID_FORMAT_CLASS, "()V").map(Self)
- }
-
- /// Create a new instance and throw it.
- pub fn throw_new(env: &mut JNIEnv<'local>) -> jni::errors::Result<()> {
- Self::construct(env)?.throw(env)
- }
-}
-
-impl<'local, Obj: AsRef<JObject<'local>>> InvalidFormatException<Obj> {
- /// Throw this exception.
- pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
- env.throw(<&JThrowable>::from(self.0.as_ref()))
- }
-}
+exception_wrapper!(
+ /// Rust representation of `class DeserializationException.InvalidFormatException`.
+ InvalidFormatException, "com/google/android/nearby/presence/rust/DeserializationException$InvalidFormatException");
/// Allow AdvDeserializationError to be thrown as a Java exception.
impl super::ToJavaException for AdvDeserializationError {
diff --git a/nearby/presence/np_java_ffi/src/class/legible_v1_sections.rs b/nearby/presence/np_java_ffi/src/class/legible_v1_sections.rs
index 0e089ab..5804964 100644
--- a/nearby/presence/np_java_ffi/src/class/legible_v1_sections.rs
+++ b/nearby/presence/np_java_ffi/src/class/legible_v1_sections.rs
@@ -22,7 +22,7 @@
deserialize::{
v1::{
DeserializedV1IdentityDetails, GetV1DEResult, GetV1IdentityDetailsResult,
- GetV1SectionResult, LegibleV1Sections as LegibleSectionsHandle, V1DataElement,
+ GetV1SectionResult, LegibleV1Sections as LegibleSectionsHandle,
},
DecryptMetadataResult,
},
@@ -96,6 +96,7 @@
details.verification_mode(),
details.cred_id(),
details.identity_token(),
+ details.key_seed(),
)
}
@@ -104,18 +105,20 @@
verification_mode: V1VerificationMode,
credential_id: i64,
identity_token: [u8; 16],
+ key_seed: [u8; 32],
) -> jni::errors::Result<Self> {
let verification_mode = VerificationMode::from(verification_mode).to_java(env)?;
let credential_id = credential_id as jint;
- let identity_token = env.byte_array_from_slice(&identity_token)?;
-
+ let identity_token = env.auto_local(env.byte_array_from_slice(&identity_token)?);
+ let key_seed = env.auto_local(env.byte_array_from_slice(&key_seed)?);
pourover::call_constructor!(
env,
&IDENTITY_DETAILS_CLASS,
- "(II[B)V",
+ "(II[B[B)V",
verification_mode,
credential_id,
- identity_token
+ identity_token.as_ref(),
+ key_seed.as_ref(),
)
.map(Self)
}
@@ -176,20 +179,12 @@
return JObject::null();
};
- let ret = match de {
- V1DataElement::Generic(generic) => {
- let de_type = jlong::from(generic.de_type().to_u32());
- let Some(slice) = generic.payload.as_slice() else {
- return JObject::null();
- };
-
- env.byte_array_from_slice(slice)
- .and_then(|data| Generic::construct(&mut env, de_type, data))
- .map(|obj| obj.0)
- }
- };
-
- ret.unwrap_or_else(|_err| JObject::null())
+ match Generic::construct(&mut env, de) {
+ Ok(Some(generic)) => generic.0,
+ _ => JObject::null(), // Occurs if the stack data structure was malformed,
+ // which it really shouldn't be, because we just got it,
+ // or if there was an unrecoverable JNI error.
+ }
}
#[jni_method(package = "com.google.android.nearby.presence.rust", class = "LegibleV1Sections")]
diff --git a/nearby/presence/np_java_ffi/src/class/serialization_exception.rs b/nearby/presence/np_java_ffi/src/class/serialization_exception.rs
index e021927..e46f2c3 100644
--- a/nearby/presence/np_java_ffi/src/class/serialization_exception.rs
+++ b/nearby/presence/np_java_ffi/src/class/serialization_exception.rs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::exception_wrapper;
use jni::{
objects::{JObject, JString, JThrowable},
JNIEnv,
@@ -57,45 +58,15 @@
}
}
-/// Helper to generate wrapper types for exception classes with no-arg constructors
-macro_rules! exception_wrapper {
- ($(#[$docs:meta])* $name:ident, $cls:literal) => {
- $(#[$docs])*
- #[repr(transparent)]
- pub struct $name<Obj>(pub Obj);
-
- impl<'local> $name<JObject<'local>> {
- /// Create a new instance.
- pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
- static CLS: ClassDesc = ClassDesc::new($cls);
- pourover::call_constructor!(env, &CLS, "()V",).map(Self)
- }
-
- /// Create a new instance and throw it.
- pub fn throw_new(env: &mut JNIEnv<'local>) -> jni::errors::Result<()> {
- Self::construct(env)?.throw(env)
- }
- }
-
- impl<'local, Obj: AsRef<JObject<'local>>> $name<Obj> {
- /// Throw this exception.
- pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
- env.throw(<&JThrowable>::from(self.0.as_ref()))
- }
- }
-
- }
-}
+exception_wrapper!(
+ /// Rust representation of `class SerializationException.DuplicateDataElementTypeCodeException`.
+ DuplicateDataElementTypeCodeException, "com/google/android/nearby/presence/rust/SerializationException$DuplicateDataElementTypeCodeException");
exception_wrapper!(
/// Rust representation of `class SerializationException.UnclosedActiveSectionException`.
UnclosedActiveSectionException, "com/google/android/nearby/presence/rust/SerializationException$UnclosedActiveSectionException");
exception_wrapper!(
- /// Rust representation of `class SerializationException.InvalidSectionKindException`.
- InvalidSectionKindException, "com/google/android/nearby/presence/rust/SerializationException$InvalidSectionKindException");
-
-exception_wrapper!(
/// Rust representation of `class SerializationException.InsufficientSpaceException`.
InsufficientSpaceException, "com/google/android/nearby/presence/rust/SerializationException$InsufficientSpaceException");
diff --git a/nearby/presence/np_java_ffi/src/class/v0_advertisement_builder.rs b/nearby/presence/np_java_ffi/src/class/v0_advertisement_builder.rs
index ccb3a9e..96d56ec 100644
--- a/nearby/presence/np_java_ffi/src/class/v0_advertisement_builder.rs
+++ b/nearby/presence/np_java_ffi/src/class/v0_advertisement_builder.rs
@@ -19,12 +19,13 @@
JNIEnv,
};
use np_ffi_core::{
+ common::BuildTxPowerResult,
serialize::v0::{
create_v0_encrypted_advertisement_builder, create_v0_public_advertisement_builder,
AddV0DEResult, CreateV0AdvertisementBuilderResult, SerializeV0AdvertisementResult,
V0AdvertisementBuilder,
},
- v0::{BuildTxPowerResult, V0DataElement},
+ v0::V0DataElement,
};
use pourover::{desc::ClassDesc, jni_method};
diff --git a/nearby/presence/np_java_ffi/src/class/v0_broadcast_credential.rs b/nearby/presence/np_java_ffi/src/class/v0_broadcast_credential.rs
index 2da6553..b9ddb00 100644
--- a/nearby/presence/np_java_ffi/src/class/v0_broadcast_credential.rs
+++ b/nearby/presence/np_java_ffi/src/class/v0_broadcast_credential.rs
@@ -13,13 +13,14 @@
// limitations under the License.
use jni::{
- objects::{JByteArray, JObject},
+ objects::{JByteArray, JClass, JObject},
signature::ReturnType,
JNIEnv,
};
use np_ffi_core::credentials::V0BroadcastCredential as CoreV0BroadcastCredential;
use pourover::desc::{ClassDesc, FieldDesc};
+use pourover::jni_method;
static V0_DISCOVERY_CREDENTIAL_CLS: ClassDesc =
ClassDesc::new("com/google/android/nearby/presence/rust/credential/V0BroadcastCredential");
@@ -69,3 +70,45 @@
))
}
}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust.credential",
+ class = "V0BroadcastCredential"
+)]
+extern "system" fn nativeEncryptMetadata<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ credential: V0BroadcastCredential<JObject<'local>>,
+ plaintext_metadata: JByteArray<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_plaintext_metadata) = optional_byte_array_from_jni(&mut env, plaintext_metadata)
+ else {
+ return JObject::null();
+ };
+ let plaintext_metadata = match maybe_plaintext_metadata {
+ Some(data) => data,
+ None => return JObject::null(),
+ };
+
+ let Ok(credential) = credential.get_as_core(&mut env) else {
+ return JObject::null();
+ };
+
+ let encrypted_metadata_bytes = credential.encrypt_metadata(plaintext_metadata.as_slice());
+ env.byte_array_from_slice(&encrypted_metadata_bytes)
+ .map(|java_byte_array| java_byte_array.into())
+ .unwrap_or_else(|_jni_err| JObject::null())
+}
+
+/// Reads a `byte[]` into an `Option<Vec<u8>>`, where `None` represents `null`.
+fn optional_byte_array_from_jni<'local>(
+ env: &mut JNIEnv<'local>,
+ array: impl AsRef<JByteArray<'local>>,
+) -> jni::errors::Result<Option<Vec<u8>>> {
+ if env.is_same_object(array.as_ref(), JObject::null())? {
+ Ok(None)
+ } else {
+ let bytes = env.convert_byte_array(array)?;
+ Ok(Some(bytes))
+ }
+}
diff --git a/nearby/presence/np_java_ffi/src/class/v0_data_element.rs b/nearby/presence/np_java_ffi/src/class/v0_data_element.rs
index 4d05a6a..4082406 100644
--- a/nearby/presence/np_java_ffi/src/class/v0_data_element.rs
+++ b/nearby/presence/np_java_ffi/src/class/v0_data_element.rs
@@ -22,8 +22,8 @@
JNIEnv,
};
use np_ffi_core::{
- common::InvalidStackDataStructure, deserialize::v0::DeserializedV0IdentityKind,
- serialize::AdvertisementBuilderKind, v0,
+ common, common::InvalidStackDataStructure, deserialize::v0::DeserializedV0IdentityKind,
+ serialize::v0::AdvertisementBuilderKind, v0,
};
use pourover::desc::{ClassDesc, FieldDesc};
@@ -42,15 +42,6 @@
}
impl<'local, Obj: AsRef<JObject<'local>>> TxPower<Obj> {
- /// Cast the given Java object to `TxPower` if it is an instance of the type. Returns `None` if
- /// the object's type does not match.
- pub fn checked_cast<'other_local>(
- env: &mut JNIEnv<'other_local>,
- obj: Obj,
- ) -> jni::errors::Result<Option<Self>> {
- Ok(env.is_instance_of(obj.as_ref(), &TX_POWER_CLASS)?.then(|| Self(obj)))
- }
-
/// Gets the value of the `int txPower` field.
pub fn get_tx_power<'env_local>(
&self,
@@ -69,12 +60,12 @@
pub fn get_as_core<'env>(
&self,
env: &mut JNIEnv<'env>,
- ) -> jni::errors::Result<v0::BuildTxPowerResult> {
+ ) -> jni::errors::Result<common::BuildTxPowerResult> {
let power = self.get_tx_power(env)?;
let Ok(power) = i8::try_from(power) else {
- return Ok(v0::BuildTxPowerResult::OutOfRange);
+ return Ok(common::BuildTxPowerResult::OutOfRange);
};
- Ok(v0::TxPower::build_from_signed_byte(power))
+ Ok(common::TxPower::build_from_signed_byte(power))
}
}
@@ -100,15 +91,6 @@
}
impl<'local, Obj: AsRef<JObject<'local>>> V0Actions<Obj> {
- /// Cast the given Java object to `V0Actions` if it is an instance of the type. Returns `None` if
- /// the object's type does not match.
- pub fn checked_cast<'other_local>(
- env: &mut JNIEnv<'other_local>,
- obj: Obj,
- ) -> jni::errors::Result<Option<Self>> {
- Ok(env.is_instance_of(obj.as_ref(), &V0_ACTIONS_CLASS)?.then(|| Self(obj)))
- }
-
/// Get the `int identityKind` field from the Java object.
pub fn get_identity_kind<'env_local>(
&self,
diff --git a/nearby/presence/np_java_ffi/src/class/v0_payload.rs b/nearby/presence/np_java_ffi/src/class/v0_payload.rs
index c54de0d..ec6af1d 100644
--- a/nearby/presence/np_java_ffi/src/class/v0_payload.rs
+++ b/nearby/presence/np_java_ffi/src/class/v0_payload.rs
@@ -37,7 +37,13 @@
env: &mut JNIEnv<'local>,
details: DeserializedV0IdentityDetails,
) -> jni::errors::Result<Self> {
- Self::construct_from_parts(env, details.cred_id(), details.identity_token(), details.salt())
+ Self::construct_from_parts(
+ env,
+ details.cred_id(),
+ details.identity_token(),
+ details.salt(),
+ details.key_seed(),
+ )
}
/// Create an IdentityDetails instance
@@ -46,18 +52,20 @@
credential_id: i64,
identity_token: [u8; 14],
salt: [u8; 2],
+ key_seed: [u8; 32],
) -> jni::errors::Result<Self> {
let credential_id = credential_id as jint;
- let identity_token = env.byte_array_from_slice(&identity_token)?;
- let salt = env.byte_array_from_slice(&salt)?;
-
+ let identity_token = env.auto_local(env.byte_array_from_slice(&identity_token)?);
+ let salt = env.auto_local(env.byte_array_from_slice(&salt)?);
+ let key_seed = env.auto_local(env.byte_array_from_slice(&key_seed)?);
pourover::call_constructor!(
env,
&IDENTITY_DETAILS_CLASS,
- "(I[B[B)V",
+ "(I[B[B[B)V",
credential_id,
- identity_token,
- salt
+ identity_token.as_ref(),
+ salt.as_ref(),
+ key_seed.as_ref(),
)
.map(Self)
}
diff --git a/nearby/presence/np_java_ffi/src/class/v1_advertisement_builder.rs b/nearby/presence/np_java_ffi/src/class/v1_advertisement_builder.rs
index ba1a3ed..f6d066d 100644
--- a/nearby/presence/np_java_ffi/src/class/v1_advertisement_builder.rs
+++ b/nearby/presence/np_java_ffi/src/class/v1_advertisement_builder.rs
@@ -15,28 +15,24 @@
use handle_map::{Handle, HandleLike};
use jni::{
objects::{JClass, JObject},
- sys::{jboolean, jint, jlong, JNI_TRUE},
+ sys::{jint, jlong},
JNIEnv,
};
-use np_ffi_core::{
- serialize::v1::{
- self, create_v1_advertisement_builder, AddV1DEResult, AddV1SectionToAdvertisementResult,
- CreateV1AdvertisementBuilderResult, CreateV1SectionBuilderResult,
- SerializeV1AdvertisementResult, V1AdvertisementBuilder,
- },
- serialize::AdvertisementBuilderKind,
+use np_ffi_core::serialize::v1::{
+ self, create_v1_advertisement_builder, AddV1DEResult, AddV1SectionToAdvertisementResult,
+ CreateV1AdvertisementBuilderResult, CreateV1SectionBuilderResult,
+ SerializeV1AdvertisementResult, V1AdvertisementBuilder,
};
use pourover::{desc::ClassDesc, jni_method};
use crate::class::{
- v1_data_element::Generic, InsufficientSpaceException, InvalidDataElementException,
- InvalidHandleException, InvalidSectionKindException, NoSpaceLeftException,
+ v1_data_element::Generic, DuplicateDataElementTypeCodeException, InsufficientSpaceException,
+ InvalidDataElementException, InvalidHandleException, NoSpaceLeftException,
UnclosedActiveSectionException, V1BroadcastCredential, VerificationMode,
};
-static V1_BUILDER_HANDLE_CLASS: ClassDesc = ClassDesc::new(
- "com/google/android/nearby/presence/rust/V1AdvertisementBuilder$V1BuilderHandle",
-);
+static HANDLE_BASE_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/Handle");
/// Rust representation of `class V1AdvertisementBuilder.V1BuilderHandle`.
#[repr(transparent)]
@@ -62,7 +58,7 @@
&self,
env: &mut JNIEnv<'env_local>,
) -> jni::errors::Result<jlong> {
- use V1_BUILDER_HANDLE_CLASS as CLS;
+ use HANDLE_BASE_CLASS as CLS;
pourover::call_method!(env, &CLS, "getId", "()J", self.as_obj())
}
}
@@ -96,17 +92,8 @@
package = "com.google.android.nearby.presence.rust",
class = "V1AdvertisementBuilder.V1BuilderHandle"
)]
-extern "system" fn allocate<'local>(
- mut env: JNIEnv<'local>,
- _cls: JClass<'local>,
- encrypted: jboolean,
-) -> jlong {
- let kind = if encrypted == JNI_TRUE {
- AdvertisementBuilderKind::Encrypted
- } else {
- AdvertisementBuilderKind::Public
- };
- match create_v1_advertisement_builder(kind) {
+extern "system" fn allocate<'local>(mut env: JNIEnv<'local>, _cls: JClass<'local>) -> jlong {
+ match create_v1_advertisement_builder() {
CreateV1AdvertisementBuilderResult::Success(builder) => {
builder.get_as_handle().get_id() as jlong
}
@@ -139,10 +126,6 @@
let _ = InvalidHandleException::throw_new(&mut env);
return JObject::null();
}
- CreateV1SectionBuilderResult::IdentityKindMismatch => {
- let _ = InvalidSectionKindException::throw_new(&mut env);
- return JObject::null();
- }
CreateV1SectionBuilderResult::NoSpaceLeft => {
let _ = InsufficientSpaceException::throw_new(&mut env);
return JObject::null();
@@ -202,10 +185,6 @@
let _ = InvalidHandleException::throw_new(&mut env);
return JObject::null();
}
- CreateV1SectionBuilderResult::IdentityKindMismatch => {
- let _ = InvalidSectionKindException::throw_new(&mut env);
- return JObject::null();
- }
CreateV1SectionBuilderResult::NoSpaceLeft => {
let _ = InsufficientSpaceException::throw_new(&mut env);
return JObject::null();
@@ -235,7 +214,7 @@
mut env: JNIEnv<'local>,
this: V1BuilderHandle<JObject<'local>>,
section_index: jint,
- generic_de: Generic<JObject<'local>>,
+ generic_de: Generic<'local>,
) {
let Ok(adv_builder) = this.as_rust_handle(&mut env) else {
return;
@@ -247,7 +226,7 @@
let section_builder = v1::V1SectionBuilder { adv_builder, section_index };
- let de = match generic_de.as_core_byte_buffer_de(&mut env) {
+ let de = match generic_de.as_core_generic_de(&mut env) {
Ok(Some(de)) => de,
Ok(None) => {
let _ = env
@@ -261,7 +240,7 @@
}
};
- match section_builder.add_127_byte_buffer_de(de) {
+ match section_builder.add_generic_de(de) {
AddV1DEResult::Success => {}
AddV1DEResult::InvalidSectionHandle => {
let _ = InvalidHandleException::throw_new(&mut env);
@@ -275,6 +254,9 @@
.map(|string| env.auto_local(string))
.and_then(|reason| InvalidDataElementException::throw_new(&mut env, &reason));
}
+ AddV1DEResult::DuplicateDataElementTypeCode => {
+ let _ = DuplicateDataElementTypeCodeException::throw_new(&mut env);
+ }
}
}
diff --git a/nearby/presence/np_java_ffi/src/class/v1_broadcast_credential.rs b/nearby/presence/np_java_ffi/src/class/v1_broadcast_credential.rs
index 01fd30f..29dc0c9 100644
--- a/nearby/presence/np_java_ffi/src/class/v1_broadcast_credential.rs
+++ b/nearby/presence/np_java_ffi/src/class/v1_broadcast_credential.rs
@@ -13,13 +13,14 @@
// limitations under the License.
use jni::{
- objects::{JByteArray, JObject},
+ objects::{JByteArray, JClass, JObject},
signature::ReturnType,
JNIEnv,
};
use np_ffi_core::credentials::V1BroadcastCredential as CoreV1BroadcastCredential;
use pourover::desc::{ClassDesc, FieldDesc};
+use pourover::jni_method;
static V1_DISCOVERY_CREDENTIAL_CLS: ClassDesc =
ClassDesc::new("com/google/android/nearby/presence/rust/credential/V1BroadcastCredential");
@@ -58,12 +59,6 @@
self.get_array(env, &IDENTITY_TOKEN)
}
- /// Get the private key.
- pub fn get_private_key<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<[u8; 32]> {
- static PRIVATE_KEY: FieldDesc = V1_DISCOVERY_CREDENTIAL_CLS.field("privateKey", "[B");
- self.get_array(env, &PRIVATE_KEY)
- }
-
/// Convert this to the `np_ffi_core` representation.
pub fn get_as_core<'env>(
&self,
@@ -72,7 +67,58 @@
Ok(CoreV1BroadcastCredential::new(
self.get_key_seed(env)?,
self.get_identity_token(env)?.into(),
- self.get_private_key(env)?,
))
}
+
+ /// Encrypt plaintext metadata with the identity token and key seed of this broadcast credential.
+ pub fn encrypt_metadata<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ plaintext_metadata: &[u8],
+ ) -> jni::errors::Result<Vec<u8>> {
+ let core_credential = self.get_as_core(env)?;
+ Ok(core_credential.encrypt_metadata(plaintext_metadata))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust.credential",
+ class = "V1BroadcastCredential"
+)]
+extern "system" fn nativeEncryptMetadata<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ credential: V1BroadcastCredential<JObject<'local>>,
+ plaintext_metadata: JByteArray<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_plaintext_metadata) = optional_byte_array_from_jni(&mut env, plaintext_metadata)
+ else {
+ return JObject::null();
+ };
+ let plaintext_metadata = match maybe_plaintext_metadata {
+ Some(data) => data,
+ None => return JObject::null(),
+ };
+
+ let Ok(credential) = credential.get_as_core(&mut env) else {
+ return JObject::null();
+ };
+
+ let encrypted_metadata_bytes = credential.encrypt_metadata(plaintext_metadata.as_slice());
+ env.byte_array_from_slice(&encrypted_metadata_bytes)
+ .map(|java_byte_array| java_byte_array.into())
+ .unwrap_or_else(|_jni_err| JObject::null())
+}
+
+/// Reads a `byte[]` into an `Option<Vec<u8>>`, where `None` represents `null`.
+fn optional_byte_array_from_jni<'local>(
+ env: &mut JNIEnv<'local>,
+ array: impl AsRef<JByteArray<'local>>,
+) -> jni::errors::Result<Option<Vec<u8>>> {
+ if env.is_same_object(array.as_ref(), JObject::null())? {
+ Ok(None)
+ } else {
+ let bytes = env.convert_byte_array(array)?;
+ Ok(Some(bytes))
+ }
}
diff --git a/nearby/presence/np_java_ffi/src/class/v1_data_element.rs b/nearby/presence/np_java_ffi/src/class/v1_data_element.rs
index c404502..1e77e68 100644
--- a/nearby/presence/np_java_ffi/src/class/v1_data_element.rs
+++ b/nearby/presence/np_java_ffi/src/class/v1_data_element.rs
@@ -14,46 +14,135 @@
//! Data Elements for v1 advertisements. See `class V1DataElement`.
+use crate::exception_wrapper;
use array_view::ArrayView;
use jni::{
- objects::{JByteArray, JObject},
- sys::jlong,
+ objects::{JByteArray, JClass, JObject, JThrowable},
+ sys::{jbyte, jint, jlong},
JNIEnv,
};
-use np_ffi_core::{common::ByteBuffer, serialize::v1::V1DE127ByteBuffer};
+use np_ffi_core::{common::ByteBuffer, v1::GenericV1DataElement};
use pourover::desc::ClassDesc;
+use pourover::jni_method;
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.TooMuchDataException`
+ TooMuchDataException, "com/google/android/nearby/presence/rust/V1DataElement$TooMuchDataException");
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.TxPowerOutOfBoundsException`
+ TxPowerOutOfBoundsException, "com/google/android/nearby/presence/rust/V1DataElement$TxPowerOutOfBoundsException");
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.TxPowerWrongLengthException`
+ TxPowerWrongLengthException, "com/google/android/nearby/presence/rust/V1DataElement$TxPowerWrongLengthException");
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.CastIdWrongLengthException`
+ CastIdWrongLengthException, "com/google/android/nearby/presence/rust/V1DataElement$CastIdWrongLengthException");
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.ActionsMalformedException`
+ ActionsMalformedException, "com/google/android/nearby/presence/rust/V1DataElement$ActionsMalformedException");
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.ContextSyncSeqNumWrongLengthException`
+ ContextSyncSeqNumWrongLengthException, "com/google/android/nearby/presence/rust/V1DataElement$ContextSyncSeqNumWrongLengthException");
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.ContextSyncSeqNumOutOfBoundsException`
+ ContextSyncSeqNumOutOfBoundsException, "com/google/android/nearby/presence/rust/V1DataElement$ContextSyncSeqNumOutOfBoundsException");
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.DedupHintWrongLengthException`
+ DedupHintWrongLengthException, "com/google/android/nearby/presence/rust/V1DataElement$DedupHintWrongLengthException");
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.DeviceTypeWrongLengthException`
+ DeviceTypeWrongLengthException, "com/google/android/nearby/presence/rust/V1DataElement$DeviceTypeWrongLengthException");
+
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.ConnectivityInfoMalformedException`
+ ConnectivityInfoMalformedException, "com/google/android/nearby/presence/rust/V1DataElement$ConnectivityInfoMalformedException");
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.MediaDeduplicationIdWrongLengthException`
+ MediaDeduplicationIdWrongLengthException, "com/google/android/nearby/presence/rust/V1DataElement$MediaDeduplicationIdWrongLengthException");
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.CapabilitiesMalformedException`
+ CapabilitiesMalformedException, "com/google/android/nearby/presence/rust/V1DataElement$CapabilitiesMalformedException");
+exception_wrapper!(
+ /// Rust representation of `class V1DataElement.RequirementsMalformedException`
+ RequirementsMalformedException, "com/google/android/nearby/presence/rust/V1DataElement$RequirementsMalformedException");
+
+static CONNECTIVITY_INFO_MALFORMED_COMPONENT_EXCEPTION_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$ConnectivityInfoComponentMalformedException");
static GENERIC_CLASS: ClassDesc =
ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$Generic");
+static TX_POWER_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$TxPower");
+
+static CAST_ID_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$CastId");
+
+static MEDIA_DEDUPLICATION_ID_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$MediaDeduplicationId");
+
+static ACTIONS_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$Actions");
+
+static CONTEXT_SYNC_SEQ_NUM_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$ContextSyncSeqNum");
+
+static DEDUP_HINT_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$DedupHint");
+
+static DEVICE_TYPE_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$DeviceType");
+
+static CONNECTIVITY_INFO_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$ConnectivityInfo");
+
+static BLE_CONNECTIVITY_INFO_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$BleConnectivityInfo");
+
+static WIFI_LAN_CONNECTIVITY_INFO_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$WifiLanConnectivityInfo");
+
+static CAPABILITIES_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$Capabilities");
+
+static REQUIREMENTS_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1DataElement$Requirements");
+
/// Rust representation of `class V1DataElement.Generic`.
#[repr(transparent)]
-pub struct Generic<Obj>(pub Obj);
+pub struct Generic<'local>(pub JObject<'local>);
-impl<'local> Generic<JObject<'local>> {
- /// Create a new Java instance from the given data element info.
- pub fn construct<'data>(
+impl<'local> Generic<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::GenericV1DataElement`.
+ pub fn construct(
env: &mut JNIEnv<'local>,
- de_type: jlong,
- data: JByteArray<'data>,
- ) -> jni::errors::Result<Self> {
- pourover::call_constructor!(env, &GENERIC_CLASS, "(J[B)V", de_type, data).map(Self)
+ generic_de: np_ffi_core::v1::GenericV1DataElement,
+ ) -> jni::errors::Result<Option<Self>> {
+ let de_type = jlong::from(generic_de.de_type().to_u32());
+ let Ok(slice) = generic_de.payload.as_slice() else {
+ // No JNI errors, but the stack data structure was invalid.
+ return Ok(None);
+ };
+
+ let payload = env.auto_local(env.byte_array_from_slice(slice)?);
+ let result =
+ pourover::call_constructor!(env, &GENERIC_CLASS, "(J[B)V", de_type, payload.as_ref())?;
+ Ok(Some(Self(result)))
}
}
-impl<'local, Obj: AsRef<JObject<'local>>> Generic<Obj> {
- /// Cast the given Java object to `Generic` if it is an instance of the type. Returns `None` if
- /// the object's type does not match.
- pub fn checked_cast<'other_local>(
- env: &mut JNIEnv<'other_local>,
- obj: Obj,
- ) -> jni::errors::Result<Option<Self>> {
- Ok(env.is_instance_of(obj.as_ref(), &GENERIC_CLASS)?.then(|| Self(obj)))
- }
-
+impl<'local> Generic<'local> {
/// Get a reference to the inner `jni` crate [`JObject`].
pub fn as_obj(&self) -> &JObject<'local> {
- self.0.as_ref()
+ &self.0
}
/// Get the data element's type
@@ -83,15 +172,1022 @@
}
}
- /// Get the data element as a `np_ffi_core` byte buffer. Returns `None` if the data element is
+ /// Get the data element as a `np_ffi_core` generic DE. Returns `None` if the data element is
/// not valid.
- pub fn as_core_byte_buffer_de<'env>(
+ pub fn as_core_generic_de<'env>(
&self,
env: &mut JNIEnv<'env>,
- ) -> jni::errors::Result<Option<V1DE127ByteBuffer>> {
+ ) -> jni::errors::Result<Option<GenericV1DataElement>> {
let Some(payload) = self.get_data(env)? else {
return Ok(None);
};
- Ok(Some(V1DE127ByteBuffer { de_type: self.get_type(env)? as u32, payload }))
+ let de_type = self.get_type(env)? as u32;
+ let Ok(de_type): Result<np_adv::extended::de_type::DeType, _> = de_type.try_into() else {
+ return Ok(None);
+ };
+ let de_type = de_type.into();
+ Ok(Some(GenericV1DataElement { de_type, payload }))
+ }
+}
+
+fn translate_deserialized_de<'local>(
+ env: &mut JNIEnv<'local>,
+ de: np_ffi_core::v1::V1DataElement,
+) -> JObject<'local> {
+ use np_ffi_core::v1::V1DataElement;
+ let result: jni::errors::Result<Option<JObject<'local>>> = match de {
+ V1DataElement::Generic(generic) => Generic::construct(env, generic).map(|x| x.map(|y| y.0)),
+ V1DataElement::CastId(cast_id) => CastId::construct(env, cast_id).map(|x| Some(x.0)),
+ V1DataElement::MediaDeduplicationId(media_deduplication_id) => {
+ MediaDeduplicationId::construct(env, media_deduplication_id).map(|x| Some(x.0))
+ }
+ V1DataElement::Actions(actions) => Actions::construct(env, actions).map(|x| Some(x.0)),
+ V1DataElement::TxPower(tx_power) => TxPower::construct(env, tx_power).map(|x| Some(x.0)),
+ V1DataElement::ContextSyncSeqNum(seq_num) => {
+ ContextSyncSeqNum::construct(env, seq_num).map(|x| Some(x.0))
+ }
+ V1DataElement::DedupHint(dedup_hint) => {
+ DedupHint::construct(env, dedup_hint).map(|x| Some(x.0))
+ }
+ V1DataElement::DeviceType(device_type) => {
+ DeviceType::construct(env, device_type).map(|x| Some(x.0))
+ }
+ V1DataElement::ConnectivityInfo(connectivity_info) => {
+ ConnectivityInfo::construct(env, connectivity_info).map(|x| Some(x.0))
+ }
+ V1DataElement::Capabilities(capabilities) => {
+ Capabilities::construct(env, capabilities).map(|x| Some(x.0))
+ }
+ V1DataElement::Requirements(requirements) => {
+ Requirements::construct(env, requirements).map(|x| Some(x.0))
+ }
+ };
+ match result {
+ Ok(Some(result)) => result,
+ _ => {
+ // Either the deser'ed DE was a malformed stack data structure
+ // (shouldn't happen), or there was an unrecoverable JNI error.
+ JObject::null()
+ }
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.Generic",
+ method_name = "nativeTryDeserialize"
+)]
+extern "system" fn try_deserialize_generic_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: Generic<'local>,
+) -> JObject<'local> {
+ let generic_de = match this.as_core_generic_de(&mut env) {
+ Ok(Some(generic_de)) => generic_de,
+ Err(_) => {
+ // Internal JNI error
+ return JObject::null();
+ }
+ Ok(None) => {
+ // Invalid Generic DE Java structure
+ let _ = env.throw_new(
+ "java/lang/IllegalStateException",
+ "Invalid Generic DE structure, payload likely too long.",
+ );
+ return JObject::null();
+ }
+ };
+ let Ok(result) = generic_de.try_deserialize() else {
+ // Invalid stack data structure, but this
+ // shouldn't happen if the Java structure was ok.
+ return JObject::null();
+ };
+ use np_ffi_core::deserialize::v1::V1DataElementDeserializationResult;
+ match result {
+ V1DataElementDeserializationResult::Success(de) => {
+ return translate_deserialized_de(&mut env, de);
+ }
+ V1DataElementDeserializationResult::TxPowerOutOfBounds => {
+ let _ = TxPowerOutOfBoundsException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::TxPowerWrongLength => {
+ let _ = TxPowerWrongLengthException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::CastIdWrongLength => {
+ let _ = CastIdWrongLengthException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::ActionsMalformed => {
+ let _ = ActionsMalformedException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::ContextSyncSeqNumWrongLength => {
+ let _ = ContextSyncSeqNumWrongLengthException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::ContextSyncSeqNumOutOfBounds => {
+ let _ = ContextSyncSeqNumOutOfBoundsException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::DedupHintWrongLength => {
+ let _ = DedupHintWrongLengthException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::DeviceTypeWrongLength => {
+ let _ = DeviceTypeWrongLengthException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::ConnectivityInfoMalformed => {
+ let _ = ConnectivityInfoMalformedException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::ConnectivityInfoMalformedComponent(index) => {
+ let Ok(ex) = pourover::call_constructor!(
+ &mut env,
+ &CONNECTIVITY_INFO_MALFORMED_COMPONENT_EXCEPTION_CLASS,
+ "(I)V",
+ index as jni::sys::jint
+ ) else {
+ // JNI be buggin'
+ return JObject::null();
+ };
+ let _ = env.throw(<&JThrowable>::from(ex.as_ref()));
+ }
+ V1DataElementDeserializationResult::MediaDeduplicationIdWrongLength => {
+ let _ = MediaDeduplicationIdWrongLengthException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::CapabilitiesMalformed => {
+ let _ = CapabilitiesMalformedException::throw_new(&mut env);
+ }
+ V1DataElementDeserializationResult::RequirementsMalformed => {
+ let _ = RequirementsMalformedException::throw_new(&mut env);
+ }
+ }
+ // Catch-all return value for thrown errors.
+ JObject::null()
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.Generic",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_generic_de<'local>(
+ _env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: Generic<'local>,
+) -> Generic<'local> {
+ this
+}
+
+/// Rust representation of `class V1DataElement.TxPower`.
+#[repr(transparent)]
+pub struct TxPower<'local>(pub JObject<'local>);
+
+impl<'local> TxPower<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::common::TxPower`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ tx_power: np_ffi_core::common::TxPower,
+ ) -> jni::errors::Result<Self> {
+ let tx_power = tx_power.as_i8() as jint;
+ pourover::call_constructor!(env, &TX_POWER_CLASS, "(I)V", tx_power).map(Self)
+ }
+ /// Attempts to convert a Java instance into a `np_ffi_core::common::TxPower`.
+ /// Since the Java representation is not range-restricted, this method returns
+ /// a wrapped `None` in the case where the value was out of the accepted range.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::common::TxPower>> {
+ let tx_power = pourover::call_method!(env, &TX_POWER_CLASS, "getTxPower", "()I", &self.0)?;
+ let Ok(tx_power) = i8::try_from(tx_power) else {
+ return Ok(None);
+ };
+ let np_ffi_core::common::BuildTxPowerResult::Success(tx_power) =
+ np_ffi_core::common::TxPower::build_from_signed_byte(tx_power)
+ else {
+ return Ok(None);
+ };
+ Ok(Some(tx_power))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.TxPower",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_tx_power_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: TxPower<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_tx_power) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ let maybe_generic_de = maybe_tx_power.and_then(|tx_power| tx_power.try_serialize_v1().ok());
+ try_construct_generic_de_from_serialized(&mut env, maybe_generic_de, "Invalid Tx Power DE")
+}
+
+/// Given a possibly-successfully-serialized `GenericV1DataElement`
+/// stemming from some other DE, return it as a `class DataElement.Generic`,
+/// or throw an `IllegalStateException` if serialization failed due
+/// to the original structure being ill-formed.
+fn try_construct_generic_de_from_serialized<'local>(
+ env: &mut JNIEnv<'local>,
+ maybe_serialized_de: Option<GenericV1DataElement>,
+ malformed_data_structure_message: &str,
+) -> JObject<'local> {
+ match maybe_serialized_de {
+ None => {
+ let _ =
+ env.throw_new("java/lang/IllegalStateException", malformed_data_structure_message);
+ JObject::null()
+ }
+ Some(generic_de) => {
+ let Ok(Some(generic_de)) = Generic::construct(env, generic_de) else {
+ // Either a serialization routine yields borked stack data structures,
+ // or the JNI has hit an internal error.
+ return JObject::null();
+ };
+ generic_de.0
+ }
+ }
+}
+
+/// Rust representation of `class V1DataElement.CastId`.
+#[repr(transparent)]
+pub struct CastId<'local>(pub JObject<'local>);
+
+impl<'local> CastId<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::CastId`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ cast_id: np_ffi_core::v1::CastId,
+ ) -> jni::errors::Result<Self> {
+ let cast_id = env.auto_local(env.byte_array_from_slice(&cast_id.bytes)?);
+ pourover::call_constructor!(env, &CAST_ID_CLASS, "([B)V", cast_id.as_ref()).map(Self)
+ }
+ /// Attempts to convert a Java instance into a `np_ffi_core::v1::CastId`.
+ /// Since the Java representation is not length-restricted, this method
+ /// returns a wrapped `None` in the case where the value did not have
+ /// an acceptable length.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::v1::CastId>> {
+ let bytes = pourover::call_method!(env, &CAST_ID_CLASS, "getCastId", "()[B", &self.0)?;
+ let bytes = env.convert_byte_array(bytes)?;
+ let Ok(bytes) = <[u8; 32]>::try_from(bytes) else {
+ return Ok(None);
+ };
+ Ok(Some(np_ffi_core::v1::CastId { bytes }))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.CastId",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_cast_id_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: CastId<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_cast_id) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ try_construct_generic_de_from_serialized(
+ &mut env,
+ maybe_cast_id.map(|cast_id| cast_id.serialize()),
+ "Invalid Cast Id DE",
+ )
+}
+
+/// Rust representation of `class V1DataElement.MediaDeduplicationId`.
+#[repr(transparent)]
+pub struct MediaDeduplicationId<'local>(pub JObject<'local>);
+
+impl<'local> MediaDeduplicationId<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::MediaDeduplicationId`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ media_deduplication_id: np_ffi_core::v1::MediaDeduplicationId,
+ ) -> jni::errors::Result<Self> {
+ let media_deduplication_id =
+ env.auto_local(env.byte_array_from_slice(&media_deduplication_id.bytes)?);
+ pourover::call_constructor!(
+ env,
+ &MEDIA_DEDUPLICATION_ID_CLASS,
+ "([B)V",
+ media_deduplication_id.as_ref()
+ )
+ .map(Self)
+ }
+ /// Attempts to convert a Java instance into a `np_ffi_core::v1::MediaDeduplicationId`.
+ /// Since the Java representation is not length-restricted, this method
+ /// returns a wrapped `None` in the case where the value did not have
+ /// an acceptable length.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::v1::MediaDeduplicationId>> {
+ let bytes = pourover::call_method!(
+ env,
+ &MEDIA_DEDUPLICATION_ID_CLASS,
+ "getMediaDeduplicationId",
+ "()[B",
+ &self.0
+ )?;
+ let bytes = env.convert_byte_array(bytes)?;
+ let Ok(bytes) = <[u8; 20]>::try_from(bytes) else {
+ return Ok(None);
+ };
+ Ok(Some(np_ffi_core::v1::MediaDeduplicationId { bytes }))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.MediaDeduplicationId",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_media_deduplication_id_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: MediaDeduplicationId<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_media_deduplication_id) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ try_construct_generic_de_from_serialized(
+ &mut env,
+ maybe_media_deduplication_id
+ .map(|media_deduplication_id| media_deduplication_id.serialize()),
+ "Invalid Media Deduplication Id DE",
+ )
+}
+
+/// Rust representation of `class V1DataElement.Actions`.
+#[repr(transparent)]
+pub struct Actions<'local>(pub JObject<'local>);
+
+impl<'local> Actions<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::V1ActionsBitmap`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ bitmap: np_ffi_core::v1::V1ActionsBitmap,
+ ) -> jni::errors::Result<Self> {
+ let bitmap = env.auto_local(env.byte_array_from_slice(&bitmap.bitmap)?);
+ pourover::call_constructor!(env, &ACTIONS_CLASS, "([B)V", bitmap.as_ref()).map(Self)
+ }
+ /// Attempts to convert a Java instance into a `np_ffi_core::v1::Actions`.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::v1::V1ActionsBitmap>> {
+ let bitmap = pourover::call_method!(env, &ACTIONS_CLASS, "getRawBitSet", "()[B", &self.0)?;
+ let bitmap = env.convert_byte_array(bitmap)?;
+ let Ok(bitmap) = <[u8; 256]>::try_from(bitmap) else {
+ return Ok(None);
+ };
+ Ok(Some(np_ffi_core::v1::V1ActionsBitmap { bitmap }))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.Actions",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_actions_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: Actions<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_actions) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ let Some(actions) = maybe_actions else {
+ // Malformed Java data structure for actions.
+ let _ =
+ env.throw_new("java/lang/IllegalStateException", "Malformed Actions DE in Java impl");
+ return JObject::null();
+ };
+ use np_ffi_core::serialize::v1::V1SerializeActionsBitmapResult;
+ match actions.try_serialize() {
+ V1SerializeActionsBitmapResult::Success(generic_de) => {
+ let Ok(Some(generic_de)) = Generic::construct(&mut env, generic_de) else {
+ // Either a serialization routine yields borked stack data structures,
+ // or the JNI has hit an internal error.
+ return JObject::null();
+ };
+ generic_de.0
+ }
+ V1SerializeActionsBitmapResult::NoActions => {
+ let _ = env.throw_new(
+ "java/lang/IllegalStateException",
+ "No actions were present for DE serialization",
+ );
+ JObject::null()
+ }
+ V1SerializeActionsBitmapResult::TooManyActions => {
+ let _ = TooMuchDataException::throw_new(&mut env);
+ JObject::null()
+ }
+ }
+}
+
+/// Rust representation of `class V1DataElement.ContextSyncSeqNum`.
+#[repr(transparent)]
+pub struct ContextSyncSeqNum<'local>(pub JObject<'local>);
+
+impl<'local> ContextSyncSeqNum<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::ContextSyncSeqNum`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ seq_num: np_ffi_core::v1::ContextSyncSeqNum,
+ ) -> jni::errors::Result<Self> {
+ let seq_num = seq_num.as_u8() as jint;
+ pourover::call_constructor!(env, &CONTEXT_SYNC_SEQ_NUM_CLASS, "(I)V", seq_num).map(Self)
+ }
+ /// Attempts to convert a Java instance into a `np_ffi_core::v1::ContextSyncSeqNum`.
+ /// Since the Java representation is not range-restricted, this method
+ /// returns a wrapped `None` in the case where the value was out of the accepted range.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::v1::ContextSyncSeqNum>> {
+ let seq_num =
+ pourover::call_method!(env, &CONTEXT_SYNC_SEQ_NUM_CLASS, "getSeqNum", "()I", &self.0)?;
+ let Ok(seq_num) = u8::try_from(seq_num) else {
+ return Ok(None);
+ };
+ let np_ffi_core::v1::BuildContextSyncSeqNumResult::Success(seq_num) =
+ np_ffi_core::v1::ContextSyncSeqNum::build_from_unsigned_byte(seq_num)
+ else {
+ return Ok(None);
+ };
+ Ok(Some(seq_num))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.ContextSyncSeqNum",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_context_sync_seq_num_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: ContextSyncSeqNum<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_seq_num) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ try_construct_generic_de_from_serialized(
+ &mut env,
+ maybe_seq_num.and_then(|seq_num| seq_num.try_serialize().ok()),
+ "Invalid ContextSyncSeqNum DE",
+ )
+}
+
+/// Rust representation of `class V1DataElement.DedupHint`.
+#[repr(transparent)]
+pub struct DedupHint<'local>(pub JObject<'local>);
+
+impl<'local> DedupHint<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::DedupHint`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ dedup_hint: np_ffi_core::v1::DedupHint,
+ ) -> jni::errors::Result<Self> {
+ let dedup_hint = env.auto_local(env.byte_array_from_slice(&dedup_hint.bytes)?);
+ pourover::call_constructor!(env, &DEDUP_HINT_CLASS, "([B)V", dedup_hint.as_ref()).map(Self)
+ }
+ /// Attempts to convert a Java instance into a `np_ffi_core::v1::DedupHint`.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::v1::DedupHint>> {
+ let bytes = pourover::call_method!(env, &DEDUP_HINT_CLASS, "getBytes", "()[B", &self.0)?;
+ let bytes = env.convert_byte_array(bytes)?;
+ let Ok(bytes) = <[u8; 8]>::try_from(bytes) else {
+ return Ok(None);
+ };
+ Ok(Some(np_ffi_core::v1::DedupHint { bytes }))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.DedupHint",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_dedup_hint_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: DedupHint<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_dedup_hint) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ try_construct_generic_de_from_serialized(
+ &mut env,
+ maybe_dedup_hint.map(|dedup_hint| dedup_hint.serialize()),
+ "Invalid Dedup Hint DE",
+ )
+}
+
+/// Rust representation of `class V1DataElement.DeviceType`.
+#[repr(transparent)]
+pub struct DeviceType<'local>(pub JObject<'local>);
+
+impl<'local> DeviceType<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::DeviceType`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ device_type: np_ffi_core::v1::DeviceType,
+ ) -> jni::errors::Result<Self> {
+ let device_type = device_type.device_type as jint;
+ pourover::call_constructor!(env, &DEVICE_TYPE_CLASS, "(I)V", device_type).map(Self)
+ }
+ /// Attempts to convert a Java instance into a `np_ffi_core::v1::DeviceType`.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::v1::DeviceType>> {
+ let device_type =
+ pourover::call_method!(env, &DEVICE_TYPE_CLASS, "getValue", "()I", &self.0)?;
+ let Ok(device_type) = u8::try_from(device_type) else {
+ return Ok(None);
+ };
+ Ok(Some(np_ffi_core::v1::DeviceType { device_type }))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.DeviceType",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_device_type_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: DeviceType<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_device_type) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ try_construct_generic_de_from_serialized(
+ &mut env,
+ maybe_device_type.map(|device_type| device_type.serialize()),
+ "Invalid Device Type DE",
+ )
+}
+
+/// Rust representation of `class V1DataElement.ConnectivityInfo`.
+#[repr(transparent)]
+pub struct ConnectivityInfo<'local>(pub JObject<'local>);
+
+impl<'local> ConnectivityInfo<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::ConnectivityInfo`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ info: np_ffi_core::v1::ConnectivityInfo,
+ ) -> jni::errors::Result<Self> {
+ let ble_info = info.get_ble_info();
+ let ble_info = if ble_info.is_present() {
+ let mac_address = optional_array_to_jni(env, ble_info.mac_address.into_rust())?;
+ let psm = optional_array_to_jni(env, ble_info.psm.into_rust())?;
+ let device_token = optional_array_to_jni(env, ble_info.device_token.into_rust())?;
+
+ let Ok(gatt_service_identifier) = ble_info.gatt_service_identifier.as_slice() else {
+ // This should be unreachable in practice due to the fact that
+ // `construct` is only called on data element deserialization, but if
+ // we get here, it's equivalent to `InvalidStackDataStructure` in ffi-core
+ // or an `IllegalStateException` in Java-land.
+ let _ = env.throw_new(
+ "java/lang/IllegalStateException",
+ "ffi-core GATT service identifier array is a malformed stack data-structure",
+ );
+ return Ok(Self(JObject::null()));
+ };
+ // Always translate "empty" to null to keep things standardized.
+ let gatt_service_identifier = if gatt_service_identifier.is_empty() {
+ JObject::null().into()
+ } else {
+ optional_array_to_jni(env, Some(gatt_service_identifier))?
+ };
+
+ pourover::call_constructor!(
+ env,
+ &BLE_CONNECTIVITY_INFO_CLASS,
+ "([B[B[B[B)V",
+ &mac_address,
+ &gatt_service_identifier,
+ &psm,
+ &device_token
+ )?
+ } else {
+ JObject::null()
+ };
+
+ let wifi_lan_info = info.get_wifi_lan_info();
+ let wifi_lan_info = if wifi_lan_info.is_present() {
+ let ip = match wifi_lan_info.ip {
+ np_ffi_core::v1::OptionalIpAddress::NotPresent => None,
+ np_ffi_core::v1::OptionalIpAddress::IPv4(ip) => Some(ip.as_slice().to_vec()),
+ np_ffi_core::v1::OptionalIpAddress::IPv6(ip) => Some(ip.as_slice().to_vec()),
+ };
+ let ip = optional_array_to_jni(env, ip)?;
+
+ let port = optional_array_to_jni(env, wifi_lan_info.port.into_rust())?;
+ let bssid = optional_array_to_jni(env, wifi_lan_info.bssid.into_rust())?;
+
+ pourover::call_constructor!(
+ env,
+ &WIFI_LAN_CONNECTIVITY_INFO_CLASS,
+ "([B[B[B)V",
+ &ip,
+ &port,
+ &bssid
+ )?
+ } else {
+ JObject::null()
+ };
+ pourover::call_constructor!(
+ env,
+ &CONNECTIVITY_INFO_CLASS,
+ "(Lcom/google/android/nearby/presence/rust/V1DataElement$BleConnectivityInfo;\
+ Lcom/google/android/nearby/presence/rust/V1DataElement$WifiLanConnectivityInfo;)V",
+ &ble_info,
+ &wifi_lan_info
+ )
+ .map(Self)
+ }
+ /// Attempts to convert a Java instance into a `np_ffi_core::v1::ConnectivityInfo`.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::v1::ConnectivityInfo>> {
+ let mut result = np_ffi_core::v1::ConnectivityInfo::default();
+
+ let java_ble_info = pourover::call_method!(
+ env,
+ &CONNECTIVITY_INFO_CLASS,
+ "getBleInfo",
+ "()Lcom/google/android/nearby/presence/rust/V1DataElement$BleConnectivityInfo;",
+ &self.0
+ )?;
+
+ let java_wifi_lan_info = pourover::call_method!(
+ env,
+ &CONNECTIVITY_INFO_CLASS,
+ "getWifiLanInfo",
+ "()Lcom/google/android/nearby/presence/rust/V1DataElement$WifiLanConnectivityInfo;",
+ &self.0
+ )?;
+
+ if !env.is_same_object(&java_ble_info, JObject::null())? {
+ let mut ble_info = result.get_ble_info();
+
+ let maybe_mac_address = pourover::call_method!(
+ env,
+ &BLE_CONNECTIVITY_INFO_CLASS,
+ "getMacAddress",
+ "()[B",
+ &java_ble_info
+ )?;
+ match optional_fixed_size_array_from_jni(env, maybe_mac_address)? {
+ Ok(maybe_mac_address) => {
+ ble_info.mac_address = maybe_mac_address;
+ }
+ Err(_) => return Ok(None),
+ }
+ let maybe_gatt_service_identifier = pourover::call_method!(
+ env,
+ &BLE_CONNECTIVITY_INFO_CLASS,
+ "getGattServiceIdentifier",
+ "()[B",
+ &java_ble_info
+ )?;
+ match optionally_empty_byte_buffer_from_jni(env, maybe_gatt_service_identifier)? {
+ Ok(maybe_gatt_service_identifier) => {
+ ble_info.gatt_service_identifier = maybe_gatt_service_identifier;
+ }
+ Err(_) => return Ok(None),
+ }
+ let maybe_psm = pourover::call_method!(
+ env,
+ &BLE_CONNECTIVITY_INFO_CLASS,
+ "getPsm",
+ "()[B",
+ &java_ble_info
+ )?;
+ match optional_fixed_size_array_from_jni(env, maybe_psm)? {
+ Ok(maybe_psm) => {
+ ble_info.psm = maybe_psm;
+ }
+ Err(_) => return Ok(None),
+ }
+ let maybe_device_token = pourover::call_method!(
+ env,
+ &BLE_CONNECTIVITY_INFO_CLASS,
+ "getDeviceToken",
+ "()[B",
+ &java_ble_info
+ )?;
+ match optional_fixed_size_array_from_jni(env, maybe_device_token)? {
+ Ok(maybe_device_token) => {
+ ble_info.device_token = maybe_device_token;
+ }
+ Err(_) => return Ok(None),
+ }
+
+ result = result.set_ble_info(ble_info);
+ }
+
+ if !env.is_same_object(&java_wifi_lan_info, JObject::null())? {
+ let mut wifi_lan_info = result.get_wifi_lan_info();
+
+ let maybe_ip = pourover::call_method!(
+ env,
+ &WIFI_LAN_CONNECTIVITY_INFO_CLASS,
+ "getIpAddress",
+ "()[B",
+ &java_wifi_lan_info
+ )?;
+ let maybe_ip = optional_byte_array_from_jni(env, maybe_ip)?;
+ wifi_lan_info.ip = match maybe_ip {
+ Some(ip) => {
+ #[allow(clippy::unwrap_used)]
+ match ip.len() {
+ 4 => np_ffi_core::v1::OptionalIpAddress::IPv4(
+ TryInto::<[u8; 4]>::try_into(ip).unwrap().into(),
+ ),
+ 16 => np_ffi_core::v1::OptionalIpAddress::IPv6(
+ TryInto::<[u8; 16]>::try_into(ip).unwrap().into(),
+ ),
+ _ => {
+ // Invalid length.
+ return Ok(None);
+ }
+ }
+ }
+ None => np_ffi_core::v1::OptionalIpAddress::NotPresent,
+ };
+
+ let maybe_port = pourover::call_method!(
+ env,
+ &WIFI_LAN_CONNECTIVITY_INFO_CLASS,
+ "getPort",
+ "()[B",
+ &java_wifi_lan_info
+ )?;
+ match optional_fixed_size_array_from_jni(env, maybe_port)? {
+ Ok(maybe_port) => {
+ wifi_lan_info.port = maybe_port;
+ }
+ Err(_) => return Ok(None),
+ }
+
+ let maybe_bssid = pourover::call_method!(
+ env,
+ &WIFI_LAN_CONNECTIVITY_INFO_CLASS,
+ "getBssid",
+ "()[B",
+ &java_wifi_lan_info
+ )?;
+ match optional_fixed_size_array_from_jni(env, maybe_bssid)? {
+ Ok(maybe_bssid) => {
+ wifi_lan_info.bssid = maybe_bssid;
+ }
+ Err(_) => return Ok(None),
+ }
+ result = result.set_wifi_lan_info(wifi_lan_info);
+ }
+ Ok(Some(result))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.ConnectivityInfo",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_connectivity_info_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: ConnectivityInfo<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_connectivity_info) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ try_construct_generic_de_from_serialized(
+ &mut env,
+ maybe_connectivity_info
+ .and_then(|connectivity_info| connectivity_info.try_serialize().ok()),
+ "Attempt to Serialize Invalid Connectivity Info DE",
+ )
+}
+
+/// Rust representation of `class V1DataElement.Capabilities`.
+#[repr(transparent)]
+pub struct Capabilities<'local>(pub JObject<'local>);
+
+impl<'local> Capabilities<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::Capabilities`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ capabilities: np_ffi_core::v1::Capabilities,
+ ) -> jni::errors::Result<Self> {
+ let capabilities = capabilities.capabilities as jbyte;
+ pourover::call_constructor!(env, &CAPABILITIES_CLASS, "(B)V", capabilities).map(Self)
+ }
+
+ /// Attempts to convert a Java instance into a `np_ffi_core::v1::Capabilities`.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::v1::Capabilities>> {
+ let bitmap = pourover::call_method!(env, &CAPABILITIES_CLASS, "getValue", "()B", &self.0)?;
+ let Ok(capabilities) = u8::try_from(bitmap) else {
+ return Ok(None);
+ };
+ Ok(Some(np_ffi_core::v1::Capabilities { capabilities }))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.Capabilities",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_capabilities_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: Capabilities<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_capabilities) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ let Some(capabilities) = maybe_capabilities else {
+ // Malformed Java data structure for capabilities.
+ let _ = env
+ .throw_new("java/lang/IllegalStateException", "Malformed Capabilities DE in Java impl");
+ return JObject::null();
+ };
+ use np_ffi_core::serialize::v1::SerializeCapabilitiesBitmapResult;
+ match capabilities.try_serialize() {
+ SerializeCapabilitiesBitmapResult::Success(generic_de) => {
+ let Ok(Some(generic_de)) = Generic::construct(&mut env, generic_de) else {
+ // Either a serialization routine yields borked stack data structures,
+ // or the JNI has hit an internal error.
+ return JObject::null();
+ };
+ generic_de.0
+ }
+ SerializeCapabilitiesBitmapResult::ReservedBitsSet => {
+ let _ = env.throw_new(
+ "java/lang/IllegalStateException",
+ "Reserved bits set in Capabilities DE serialization",
+ );
+ JObject::null()
+ }
+ }
+}
+
+/// Rust representation of `class V1DataElement.Requirements`.
+#[repr(transparent)]
+pub struct Requirements<'local>(pub JObject<'local>);
+
+impl<'local> Requirements<'local> {
+ /// Create a new Java instance from the given `np_ffi_core::v1::Requirements`.
+ pub fn construct(
+ env: &mut JNIEnv<'local>,
+ requirements: np_ffi_core::v1::Requirements,
+ ) -> jni::errors::Result<Self> {
+ let requirements = requirements.requirements as jbyte;
+ pourover::call_constructor!(env, &REQUIREMENTS_CLASS, "(B)V", requirements).map(Self)
+ }
+
+ /// Attempts to convert a Java instance into a `np_ffi_core::v1::Requirements`.
+ pub fn as_core<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<np_ffi_core::v1::Requirements>> {
+ let bitmap = pourover::call_method!(env, &REQUIREMENTS_CLASS, "getValue", "()B", &self.0)?;
+ let Ok(requirements) = u8::try_from(bitmap) else {
+ return Ok(None);
+ };
+ Ok(Some(np_ffi_core::v1::Requirements { requirements }))
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1DataElement.Requirements",
+ method_name = "nativeTrySerialize"
+)]
+extern "system" fn try_serialize_requirements_de<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ this: Requirements<'local>,
+) -> JObject<'local> {
+ let Ok(maybe_requirements) = this.as_core(&mut env) else {
+ // Unrecoverable JNI error.
+ return JObject::null();
+ };
+ let Some(requirements) = maybe_requirements else {
+ // Malformed Java data structure for requirements.
+ let _ = env
+ .throw_new("java/lang/IllegalStateException", "Malformed Requirements DE in Java impl");
+ return JObject::null();
+ };
+ use np_ffi_core::serialize::v1::SerializeRequirementsBitmapResult;
+ match requirements.try_serialize() {
+ SerializeRequirementsBitmapResult::Success(generic_de) => {
+ let Ok(Some(generic_de)) = Generic::construct(&mut env, generic_de) else {
+ // Either a serialization routine yields borked stack data structures,
+ // or the JNI has hit an internal error.
+ return JObject::null();
+ };
+ generic_de.0
+ }
+ SerializeRequirementsBitmapResult::ReservedBitsSet => {
+ let _ = env.throw_new(
+ "java/lang/IllegalStateException",
+ "Reserved bits set in Requirements DE serialization",
+ );
+ JObject::null()
+ }
+ }
+}
+
+/// Reads a `byte[]` into an `Option<Vec<u8>>`, where `None` represents `null`.
+fn optional_byte_array_from_jni<'local>(
+ env: &mut JNIEnv<'local>,
+ array: impl AsRef<JByteArray<'local>>,
+) -> jni::errors::Result<Option<Vec<u8>>> {
+ if env.is_same_object(array.as_ref(), JObject::null())? {
+ Ok(None)
+ } else {
+ let bytes = env.convert_byte_array(array)?;
+ Ok(Some(bytes))
+ }
+}
+
+/// Error raised when one of the from-JNI `byte[]` conversion method
+/// gets a non-`null` result, but the length doesn't match the length
+/// of the expected target ffi-core array.
+struct InvalidArrayLength;
+
+/// Reads a `byte[]` into an ffi-core `ByteBuffer`, where `null` corresponds to empty.
+fn optionally_empty_byte_buffer_from_jni<'local, const N: usize>(
+ env: &mut JNIEnv<'local>,
+ array: impl AsRef<JByteArray<'local>>,
+) -> jni::errors::Result<Result<np_ffi_core::common::ByteBuffer<N>, InvalidArrayLength>> {
+ let maybe_array = optional_byte_array_from_jni(env, array)?;
+ match maybe_array {
+ Some(array) => {
+ if array.len() <= N {
+ let mut result = tinyvec::ArrayVec::new();
+ result.extend_from_slice(&array);
+ Ok(Ok(np_ffi_core::common::ByteBuffer::from_array_vec(result)))
+ } else {
+ Ok(Err(InvalidArrayLength))
+ }
+ }
+ None => Ok(Ok(np_ffi_core::common::ByteBuffer::default())),
+ }
+}
+
+fn optional_fixed_size_array_from_jni<'local, const N: usize>(
+ env: &mut JNIEnv<'local>,
+ array: impl AsRef<JByteArray<'local>>,
+) -> jni::errors::Result<Result<np_ffi_core::common::OptionalFixedSizeArray<N>, InvalidArrayLength>>
+{
+ let maybe_array = optional_byte_array_from_jni(env, array)?;
+ match maybe_array {
+ Some(array) => match TryInto::<[u8; N]>::try_into(array) {
+ Ok(array) => Ok(Ok(Some(array).into())),
+ Err(_) => Ok(Err(InvalidArrayLength)),
+ },
+ None => Ok(Ok(np_ffi_core::common::OptionalFixedSizeArray::default())),
+ }
+}
+
+fn optional_array_to_jni<'local, A: AsRef<[u8]>>(
+ env: &mut JNIEnv<'local>,
+ maybe_array: Option<A>,
+) -> jni::errors::Result<JByteArray<'local>> {
+ match maybe_array {
+ Some(array) => env.byte_array_from_slice(array.as_ref()),
+ None => Ok(JObject::null().into()),
}
}
diff --git a/nearby/presence/np_java_ffi/src/class/v1_discovery_credential.rs b/nearby/presence/np_java_ffi/src/class/v1_discovery_credential.rs
index 923446a..a1ebcf2 100644
--- a/nearby/presence/np_java_ffi/src/class/v1_discovery_credential.rs
+++ b/nearby/presence/np_java_ffi/src/class/v1_discovery_credential.rs
@@ -69,22 +69,6 @@
self.get_array(env, &EXPECTED_MIC_EXTENDED_SALT_IDENTITY_TOKEN_HMAC)
}
- /// Get the expected signature identity token hmac.
- pub fn get_expected_signature_identity_token_hmac<'env>(
- &self,
- env: &mut JNIEnv<'env>,
- ) -> jni::errors::Result<[u8; 32]> {
- static EXPECTED_SIGNATURE_IDENTITY_TOKEN_HMAC: FieldDesc =
- V1_DISCOVERY_CREDENTIAL_CLS.field("expectedSignatureIdentityTokenHmac", "[B");
- self.get_array(env, &EXPECTED_SIGNATURE_IDENTITY_TOKEN_HMAC)
- }
-
- /// Get the pub key.
- pub fn get_pub_key<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<[u8; 32]> {
- static PUB_KEY: FieldDesc = V1_DISCOVERY_CREDENTIAL_CLS.field("pubKey", "[B");
- self.get_array(env, &PUB_KEY)
- }
-
/// Convert this to the `np_ffi_core` representation.
pub fn get_as_core<'env>(
&self,
@@ -94,8 +78,6 @@
self.get_key_seed(env)?,
self.get_expected_mic_short_salt_identity_token_hmac(env)?,
self.get_expected_mic_extended_salt_identity_token_hmac(env)?,
- self.get_expected_signature_identity_token_hmac(env)?,
- self.get_pub_key(env)?,
))
}
}
diff --git a/nearby/presence/np_java_ffi/src/class/verification_mode.rs b/nearby/presence/np_java_ffi/src/class/verification_mode.rs
index f217207..f87820e 100644
--- a/nearby/presence/np_java_ffi/src/class/verification_mode.rs
+++ b/nearby/presence/np_java_ffi/src/class/verification_mode.rs
@@ -25,8 +25,6 @@
pub enum VerificationMode {
/// Verification is done using the Mic scheme.
Mic,
- /// Verification is done using the Signature scheme.
- Signature,
}
impl VerificationMode {
@@ -38,8 +36,6 @@
) -> jni::errors::Result<Option<Self>> {
if value == Self::mic(env)? {
Ok(Some(Self::Mic))
- } else if value == Self::signature(env)? {
- Ok(Some(Self::Signature))
} else {
Ok(None)
}
@@ -49,17 +45,9 @@
pub fn to_java<'local>(&self, env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
match self {
Self::Mic => Self::mic(env),
- Self::Signature => Self::signature(env),
}
}
- /// Fetch the `SIGNATURE` constant
- fn signature<'local>(env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
- static SIGNATURE: StaticFieldDesc = VERIFICATION_MODE_CLASS.static_field("SIGNATURE", "I");
- static VALUE: RwLock<Option<jint>> = RwLock::new(None);
- Self::lookup_static_value(env, &SIGNATURE, &VALUE)
- }
-
/// Fetch the `MIC` constant
fn mic<'local>(env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
static MIC: StaticFieldDesc = VERIFICATION_MODE_CLASS.static_field("MIC", "I");
@@ -104,7 +92,6 @@
fn from(mode: V1VerificationMode) -> Self {
match mode {
V1VerificationMode::Mic => Self::Mic,
- V1VerificationMode::Signature => Self::Signature,
}
}
}
@@ -113,7 +100,6 @@
fn from(mode: VerificationMode) -> Self {
match mode {
VerificationMode::Mic => Self::Mic,
- VerificationMode::Signature => Self::Signature,
}
}
}
diff --git a/nearby/presence/np_java_ffi/src/lib.rs b/nearby/presence/np_java_ffi/src/lib.rs
index bd03c81..99ebc14 100644
--- a/nearby/presence/np_java_ffi/src/lib.rs
+++ b/nearby/presence/np_java_ffi/src/lib.rs
@@ -21,3 +21,34 @@
pub mod class;
#[cfg(feature = "testing")]
pub mod testing;
+
+/// Helper to generate wrapper types for exception classes with no-arg constructors
+macro_rules! exception_wrapper {
+ ($(#[$docs:meta])* $name:ident, $cls:literal) => {
+ $(#[$docs])*
+ #[repr(transparent)]
+ pub struct $name<'local>(pub JObject<'local>);
+
+ impl<'local> $name<'local> {
+ /// Create a new instance.
+ pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
+ static CLS: ClassDesc = ClassDesc::new($cls);
+ pourover::call_constructor!(env, &CLS, "()V",).map(Self)
+ }
+
+ /// Create a new instance and throw it.
+ pub fn throw_new(env: &mut JNIEnv<'local>) -> jni::errors::Result<()> {
+ Self::construct(env)?.throw(env)
+ }
+ }
+
+ impl<'local> $name<'local> {
+ /// Throw this exception.
+ pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
+ env.throw(<&JThrowable>::from(self.0.as_ref()))
+ }
+ }
+
+ }
+}
+pub(crate) use exception_wrapper;
diff --git a/nearby/presence/np_java_ffi/src/testing/test_vectors.rs b/nearby/presence/np_java_ffi/src/testing/test_vectors.rs
index f2d3079..ad5d9ac 100644
--- a/nearby/presence/np_java_ffi/src/testing/test_vectors.rs
+++ b/nearby/presence/np_java_ffi/src/testing/test_vectors.rs
@@ -21,8 +21,8 @@
use pourover::jni_method;
use crate::class::{
- InsufficientSpaceException, InvalidHandleException, InvalidSectionKindException,
- UnclosedActiveSectionException, V1BroadcastCredential, V1BuilderHandle, V1SectionBuilder,
+ InsufficientSpaceException, InvalidHandleException, UnclosedActiveSectionException,
+ V1BroadcastCredential, V1BuilderHandle, V1SectionBuilder,
};
#[jni_method(package = "com.google.android.nearby.presence.rust", class = "TestVectors")]
@@ -77,10 +77,6 @@
let _ = InvalidHandleException::throw_new(&mut env);
return JObject::null();
}
- CreateV1SectionBuilderResult::IdentityKindMismatch => {
- let _ = InvalidSectionKindException::throw_new(&mut env);
- return JObject::null();
- }
CreateV1SectionBuilderResult::NoSpaceLeft => {
let _ = InsufficientSpaceException::throw_new(&mut env);
return JObject::null();
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/ActionsTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/ActionsTests.java
new file mode 100644
index 0000000..71b10cd
--- /dev/null
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/ActionsTests.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.nearby.presence.rust;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.Random;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ActionsTests {
+ private static final int NUM_RANDOM_TRIALS = 1000;
+
+ private final NpAdv npAdv = new NpAdv();
+
+ /**
+ * A test which repeatedly round-trips a random collection of (up to) 64 actions in the range [0,
+ * 255], from bitmap to V1 DE to bitmap.
+ */
+ @Test
+ public void roundtripRandomV1Actions() throws Exception {
+ Random random = new Random(12345);
+ for (int trial = 0; trial < NUM_RANDOM_TRIALS; trial++) {
+ V1DataElement.Actions actions = new V1DataElement.Actions(npAdv);
+
+ // Populate the actions bitmap.
+ int numActions = random.nextInt(63) + 1; // Needs to be non-zero.
+ for (int i = 0; i < numActions; i++) {
+ int actionId = random.nextInt(256);
+ actions.addActionWithId(actionId);
+ }
+
+ // Serialize to a DE.
+ V1DataElement.Generic genericDe = actions.trySerialize();
+
+ // Deserialize from the DE.
+ V1DataElement de = genericDe.tryDeserialize();
+ V1DataElement.Actions reconstructedActions = (V1DataElement.Actions) de;
+
+ // Check that the two actions-sets match on every action.
+ for (int actionId = 0; actionId <= 2047; actionId++) {
+ assertThat(actions.hasActionWithId(actionId))
+ .isEqualTo(reconstructedActions.hasActionWithId(actionId));
+ }
+ }
+ }
+
+ /**
+ * A test which verifies that a TooMuchDataException is thrown for a fully-populated actions
+ * bitmap.
+ */
+ @Test(expected = V1DataElement.TooMuchDataException.class)
+ public void actionsTooMuchDataException() throws Exception {
+ V1DataElement.Actions actions = new V1DataElement.Actions(npAdv);
+ for (int actionId = 0; actionId <= 2047; actionId++) {
+ actions.addActionWithId(actionId);
+ }
+ V1DataElement.Generic unused = actions.trySerialize();
+ }
+}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DecryptTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DecryptTests.java
index d951cbf..a970343 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DecryptTests.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DecryptTests.java
@@ -17,16 +17,20 @@
package com.google.android.nearby.presence.rust;
import static com.google.android.nearby.presence.rust.TestData.ALICE_METADATA;
+import static com.google.android.nearby.presence.rust.TestData.V0_BROADCAST_CRED;
import static com.google.android.nearby.presence.rust.TestData.V0_CRED;
import static com.google.android.nearby.presence.rust.TestData.V0_ENCRYPTED_ALICE_METADATA;
import static com.google.android.nearby.presence.rust.TestData.V0_PRIVATE;
import static com.google.android.nearby.presence.rust.TestData.V1_ALICE_METADATA;
+import static com.google.android.nearby.presence.rust.TestData.V1_BROADCAST_CRED;
import static com.google.android.nearby.presence.rust.TestData.V1_CRED;
import static com.google.android.nearby.presence.rust.TestData.V1_ENCRYPTED_ALICE_METADATA;
import static com.google.android.nearby.presence.rust.TestData.V1_PRIVATE;
import static com.google.common.truth.Truth.assertThat;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
+import com.google.android.nearby.presence.rust.credential.V0BroadcastCredential;
+import com.google.android.nearby.presence.rust.credential.V1BroadcastCredential;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -34,6 +38,8 @@
@RunWith(JUnit4.class)
public class DecryptTests {
+ private final NpAdv npAdv = new NpAdv();
+
public static final class TestMetadata implements CredentialBook.MatchedMetadata {
public byte[] plaintextMetadata;
public byte[] encryptedMetadata;
@@ -56,11 +62,12 @@
DeserializeResult<TestMetadata> parsePrivateAdv(byte[] bytes) {
try (CredentialBook<TestMetadata> book =
- CredentialBook.<TestMetadata>builder()
+ npAdv
+ .<TestMetadata>newCredentialBookBuilder()
.addDiscoveryCredential(V0_CRED, V0_METADATA)
.addDiscoveryCredential(V1_CRED, V1_METADATA)
.build()) {
- return NpAdv.deserializeAdvertisement(bytes, book);
+ return npAdv.deserializeAdvertisement(bytes, book);
}
}
@@ -96,4 +103,55 @@
assertThat(section.getDecryptedMetadata()).isEqualTo(V1_ALICE_METADATA);
}
}
+
+ @Test
+ public void deserializeAdvertisement_v0_canDecryptEncryptedMetadata() {
+ byte[] encryptedMetadata =
+ V0BroadcastCredential.encryptMetadata(npAdv, V0_BROADCAST_CRED, ALICE_METADATA);
+ TestMetadata v0Metadata = new TestMetadata(ALICE_METADATA, encryptedMetadata);
+ try (CredentialBook<TestMetadata> book =
+ npAdv
+ .<TestMetadata>newCredentialBookBuilder()
+ .addDiscoveryCredential(V0_CRED, v0Metadata)
+ .build()) {
+ DeserializeResult<TestMetadata> result = npAdv.deserializeAdvertisement(V0_PRIVATE, book);
+
+ assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
+
+ DeserializedV0Advertisement<TestMetadata> adv = result.getAsV0();
+
+ assertThat(adv).isNotNull();
+ assertThat(adv.isLegible()).isTrue();
+ assertThat(adv.getIdentity()).isEqualTo(IdentityKind.DECRYPTED);
+ assertThat(adv.getMatchedMetadata()).isSameInstanceAs(v0Metadata);
+ assertThat(adv.getDecryptedMetadata()).isEqualTo(ALICE_METADATA);
+ }
+ }
+
+ @Test
+ public void deserializeAdvertisement_v1_canDecryptEncryptedMetadata() {
+ byte[] encryptedMetadata =
+ V1BroadcastCredential.encryptMetadata(npAdv, V1_BROADCAST_CRED, ALICE_METADATA);
+ TestMetadata v1Metadata = new TestMetadata(ALICE_METADATA, encryptedMetadata);
+ try (CredentialBook<TestMetadata> book =
+ npAdv
+ .<TestMetadata>newCredentialBookBuilder()
+ .addDiscoveryCredential(V1_CRED, v1Metadata)
+ .build()) {
+ DeserializeResult<TestMetadata> result = npAdv.deserializeAdvertisement(V1_PRIVATE, book);
+
+ assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V1_ADVERTISEMENT);
+
+ DeserializedV1Advertisement<TestMetadata> adv = result.getAsV1();
+
+ assertThat(adv).isNotNull();
+ assertThat(adv.getNumLegibleSections()).isEqualTo(1);
+ assertThat(adv.getNumUndecryptableSections()).isEqualTo(0);
+
+ DeserializedV1Section<TestMetadata> section = adv.getSection(0);
+ assertThat(section.getIdentityKind()).isEqualTo(IdentityKind.DECRYPTED);
+ assertThat(section.getMatchedMetadata()).isSameInstanceAs(v1Metadata);
+ assertThat(section.getDecryptedMetadata()).isEqualTo(ALICE_METADATA);
+ }
+ }
}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DeserializeTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DeserializeTests.java
index b4370d2..fe2c942 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DeserializeTests.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DeserializeTests.java
@@ -18,10 +18,12 @@
import static com.google.android.nearby.presence.rust.TestData.V0_CRED;
import static com.google.android.nearby.presence.rust.TestData.V0_IDENTITY_TOKEN;
+import static com.google.android.nearby.presence.rust.TestData.V0_KEY_SEED;
import static com.google.android.nearby.presence.rust.TestData.V0_PRIVATE;
import static com.google.android.nearby.presence.rust.TestData.V0_PUBLIC;
import static com.google.android.nearby.presence.rust.TestData.V1_CRED;
import static com.google.android.nearby.presence.rust.TestData.V1_IDENTITY_TOKEN;
+import static com.google.android.nearby.presence.rust.TestData.V1_KEY_SEED;
import static com.google.android.nearby.presence.rust.TestData.V1_PRIVATE;
import static com.google.android.nearby.presence.rust.TestData.V1_PUBLIC;
import static com.google.common.truth.Truth.assertThat;
@@ -36,20 +38,23 @@
@RunWith(JUnit4.class)
public class DeserializeTests {
+ private final NpAdv npAdv = new NpAdv();
+
DeserializeResult<NoMetadata> parsePublicAdv(byte[] bytes) {
// Call parse with an empty CredentialBook
- try (CredentialBook<NoMetadata> book = CredentialBook.empty()) {
- return NpAdv.deserializeAdvertisement(bytes, book);
+ try (CredentialBook<NoMetadata> book = npAdv.newEmptyCredentialBook()) {
+ return npAdv.deserializeAdvertisement(bytes, book);
}
}
DeserializeResult<NoMetadata> parsePrivateAdv(byte[] bytes) {
try (CredentialBook<NoMetadata> book =
- CredentialBook.<NoMetadata>builder()
+ npAdv
+ .<NoMetadata>newCredentialBookBuilder()
.addDiscoveryCredential(V0_CRED, NoMetadata.INSTANCE)
.addDiscoveryCredential(V1_CRED, NoMetadata.INSTANCE)
.build()) {
- return NpAdv.deserializeAdvertisement(bytes, book);
+ return npAdv.deserializeAdvertisement(bytes, book);
}
}
@@ -98,6 +103,7 @@
assertThat(adv.getDataElementCount()).isEqualTo(1);
assertThat(adv.getDataElement(0)).isInstanceOf(V0DataElement.TxPower.class);
assertThat(adv.getIdentityToken()).isEqualTo(V0_IDENTITY_TOKEN);
+ assertThat(adv.getKeySeed()).isEqualTo(V0_KEY_SEED);
assertThat(adv.getSalt()).isEqualTo(new byte[] {(byte) 0x22, (byte) 0x22});
assertThat(adv.getMatchedMetadata()).isSameInstanceAs(NoMetadata.INSTANCE);
assertThat(adv.getDecryptedMetadata()).isNull();
@@ -134,11 +140,11 @@
final int numDes = 2;
byte[] advBytes;
- try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
+ try (V0AdvertisementBuilder builder = npAdv.newPublicV0Builder()) {
builder.addDataElement(
new V0DataElement.V0Actions(
- IdentityKind.PLAINTEXT, V0DataElement.V0ActionType.NEARBY_SHARE));
- builder.addDataElement(new V0DataElement.TxPower(10));
+ npAdv, IdentityKind.PLAINTEXT, V0DataElement.V0ActionType.NEARBY_SHARE));
+ builder.addDataElement(new V0DataElement.TxPower(npAdv, 10));
advBytes = builder.build();
}
@@ -200,7 +206,8 @@
assertThat(section.getIdentityKind()).isEqualTo(IdentityKind.DECRYPTED);
assertThat(section.getDataElementCount()).isEqualTo(1);
assertThat(section.getIdentityToken()).isEqualTo(V1_IDENTITY_TOKEN);
- assertThat(section.getVerificationMode()).isEqualTo(VerificationMode.SIGNATURE);
+ assertThat(section.getKeySeed()).isEqualTo(V1_KEY_SEED);
+ assertThat(section.getVerificationMode()).isEqualTo(VerificationMode.MIC);
assertThat(section.getMatchedMetadata()).isSameInstanceAs(NoMetadata.INSTANCE);
assertThat(section.getDecryptedMetadata()).isNull();
}
@@ -255,11 +262,11 @@
final int NUM_SECTIONS = 5;
byte[] advBytes;
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ try (V1AdvertisementBuilder builder = npAdv.newV1Builder()) {
for (int i = 0; i < NUM_SECTIONS; i++) {
builder
.addPublicSection()
- .addDataElement(new V1DataElement.Generic(123, new byte[] {(byte) i}))
+ .addDataElement(new V1DataElement.Generic(npAdv, 123, new byte[] {(byte) i}))
.finishSection();
}
advBytes = builder.build();
@@ -268,15 +275,23 @@
try (DeserializeResult<NoMetadata> result = parsePublicAdv(advBytes)) {
DeserializedV1Advertisement<NoMetadata> adv = result.getAsV1();
- byte i = 0;
+ boolean[] includedSectionIndices = new boolean[NUM_SECTIONS];
+
+ int i = 0;
for (DeserializedV1Section<NoMetadata> section : adv.getSections()) {
V1DataElement.Generic de = (V1DataElement.Generic) section.getDataElement(0);
assertThat(de.getType()).isEqualTo(123);
- // Validate section order
- assertThat(de.getData()).isEqualTo(new byte[] {i++});
+
+ // Mark included section indices from the original broadcast order.
+ assertThat(de.getData().length).isEqualTo(1);
+ includedSectionIndices[de.getData()[0]] = true;
+
+ i += 1;
}
// Validate section count
assertThat(i).isEqualTo(NUM_SECTIONS);
+ // Verify that all section contents were correctly included.
+ assertThat(includedSectionIndices).asList().doesNotContain(false);
}
}
@@ -285,10 +300,10 @@
final int numDes = 5;
byte[] advBytes;
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ try (V1AdvertisementBuilder builder = npAdv.newV1Builder()) {
try (V1SectionBuilder section = builder.addPublicSection()) {
for (int i = 0; i < numDes; i++) {
- section.addDataElement(new V1DataElement.Generic(123, new byte[] {(byte) i}));
+ section.addDataElement(new V1DataElement.Generic(npAdv, 100 + i, new byte[] {(byte) i}));
}
}
advBytes = builder.build();
@@ -301,7 +316,7 @@
byte i = 0;
for (V1DataElement de : section.getDataElements()) {
V1DataElement.Generic generic = (V1DataElement.Generic) de;
- assertThat(generic.getType()).isEqualTo(123);
+ assertThat(generic.getType()).isEqualTo(100 + i);
// Validate de order
assertThat(generic.getData()).isEqualTo(new byte[] {i++});
}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/OwnedHandleTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/OwnedHandleTests.java
index 978a75b..de9a42e 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/OwnedHandleTests.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/OwnedHandleTests.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import com.google.android.cooperativecleaner.CooperativeCleaner;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import org.junit.Rule;
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/SerializeTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/SerializeTests.java
index 7c4d88e..e771481 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/SerializeTests.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/SerializeTests.java
@@ -25,6 +25,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import com.google.android.nearby.presence.rust.SerializationException.DuplicateDataElementTypeCodeException;
import com.google.android.nearby.presence.rust.V0DataElement.TxPower;
import com.google.android.nearby.presence.rust.V0DataElement.V0ActionType;
import com.google.android.nearby.presence.rust.V0DataElement.V0Actions;
@@ -39,15 +40,20 @@
@RunWith(JUnit4.class)
public class SerializeTests {
- public static final TxPower TX_POWER = new TxPower(7);
- public static final TxPower INVALID_TX_POWER = new TxPower(-777);
+ private static final NpAdv npAdv = new NpAdv();
+
+ public static final TxPower TX_POWER = new TxPower(npAdv, 7);
+ public static final TxPower INVALID_TX_POWER = new TxPower(npAdv, -777);
public static final V0Actions PUBLIC_ACTIONS =
- new V0Actions(IdentityKind.PLAINTEXT, V0ActionType.NEARBY_SHARE, V0ActionType.CROSS_DEV_SDK);
+ new V0Actions(
+ npAdv, IdentityKind.PLAINTEXT, V0ActionType.NEARBY_SHARE, V0ActionType.CROSS_DEV_SDK);
public static final V0Actions PRIVATE_ACTIONS =
- new V0Actions(IdentityKind.DECRYPTED, V0ActionType.CALL_TRANSFER, V0ActionType.NEARBY_SHARE);
+ new V0Actions(
+ npAdv, IdentityKind.DECRYPTED, V0ActionType.CALL_TRANSFER, V0ActionType.NEARBY_SHARE);
- public static final Generic GENERIC_DE = new Generic(1234, new byte[] {0x01, 0x02, 0x03, 0x04});
+ public static final Generic GENERIC_DE =
+ new Generic(npAdv, 1234, new byte[] {0x01, 0x02, 0x03, 0x04});
@SuppressWarnings("MutablePublicArray")
public static final byte[] SALT = new byte[] {0x12, 0x34};
@@ -60,7 +66,7 @@
@Test
public void serializeAdvertisement_v1_canCreatePubSection() throws Exception {
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ try (V1AdvertisementBuilder builder = npAdv.newV1Builder()) {
try (V1SectionBuilder sectionBuilder = builder.addPublicSection()) {
sectionBuilder.addDataElement(GENERIC_DE);
}
@@ -73,7 +79,7 @@
@Test
public void serializeAdvertisement_v1_canCreateMicEncryptedSection() throws Exception {
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted()) {
+ try (V1AdvertisementBuilder builder = npAdv.newV1Builder()) {
try (V1SectionBuilder sectionBuilder =
builder.addEncryptedSection(V1_BROADCAST_CRED, VerificationMode.MIC)) {
sectionBuilder.addDataElement(GENERIC_DE);
@@ -87,7 +93,7 @@
@Test
public void serializeAdvertisement_v1_canCreateEmptyPublicAdvertisement() throws Exception {
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ try (V1AdvertisementBuilder builder = npAdv.newV1Builder()) {
byte[] adv = builder.build();
@@ -97,7 +103,7 @@
@Test
public void serializeAdvertisement_v1_canCreateEmptyPublicSection() throws Exception {
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ try (V1AdvertisementBuilder builder = npAdv.newV1Builder()) {
builder.addPublicSection().close();
byte[] adv = builder.build();
@@ -106,32 +112,31 @@
}
}
- @Test
- public void serializeAdvertisement_v1_canCreateSignatureEncryptedSection() throws Exception {
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted()) {
- try (V1SectionBuilder sectionBuilder =
- builder.addEncryptedSection(V1_BROADCAST_CRED, VerificationMode.SIGNATURE)) {
- sectionBuilder.addDataElement(GENERIC_DE);
+ @Test(expected = DuplicateDataElementTypeCodeException.class)
+ public void serializeAdvertisement_v1_duplicateTypeCodeException() throws Exception {
+ try (V1AdvertisementBuilder advBuilder = npAdv.newV1Builder()) {
+ try (V1SectionBuilder sectionBuilder = advBuilder.addPublicSection()) {
+ byte[] payload = new byte[2];
+ V1DataElement.Generic test = new V1DataElement.Generic(npAdv, 123, payload);
+ sectionBuilder.addDataElement(test);
+ sectionBuilder.addDataElement(test);
}
- byte[] adv = builder.build();
-
- assertThat(adv).isNotNull();
- assertThat(adv).isNotEmpty();
}
}
@Test
public void serializeAdvertisement_v1_canCreateRoundtripEncrypted() throws Exception {
try (CredentialBook<NoMetadata> book =
- CredentialBook.<NoMetadata>builder()
+ npAdv
+ .<NoMetadata>newCredentialBookBuilder()
.addDiscoveryCredential(V1_CRED, NoMetadata.INSTANCE)
.build();
- DeserializeResult<NoMetadata> original = NpAdv.deserializeAdvertisement(V1_PRIVATE, book);
- V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted()) {
+ DeserializeResult<NoMetadata> original = npAdv.deserializeAdvertisement(V1_PRIVATE, book);
+ V1AdvertisementBuilder builder = npAdv.newV1Builder()) {
for (DeserializedV1Section<NoMetadata> section : original.getAsV1().getSections()) {
try (V1SectionBuilder sectionBuilder =
- builder.addEncryptedSection(V1_BROADCAST_CRED, VerificationMode.SIGNATURE)) {
- for (V1DataElement de : section.getDataElements()) {
+ builder.addEncryptedSection(V1_BROADCAST_CRED, VerificationMode.MIC)) {
+ for (V1DataElement.Generic de : section.getDataElements()) {
sectionBuilder.addDataElement(de);
}
}
@@ -145,12 +150,12 @@
@Test
public void serializeAdvertisement_v1_canCreateRoundtripPublic() throws Exception {
- try (CredentialBook<NoMetadata> book = CredentialBook.empty();
- DeserializeResult<NoMetadata> original = NpAdv.deserializeAdvertisement(V1_PUBLIC, book);
- V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ try (CredentialBook<NoMetadata> book = npAdv.newEmptyCredentialBook();
+ DeserializeResult<NoMetadata> original = npAdv.deserializeAdvertisement(V1_PUBLIC, book);
+ V1AdvertisementBuilder builder = npAdv.newV1Builder()) {
for (DeserializedV1Section<NoMetadata> section : original.getAsV1().getSections()) {
try (V1SectionBuilder sectionBuilder = builder.addPublicSection()) {
- for (V1DataElement de : section.getDataElements()) {
+ for (V1DataElement.Generic de : section.getDataElements()) {
sectionBuilder.addDataElement(de);
}
}
@@ -162,30 +167,8 @@
}
@Test
- public void serializeAdvertisement_v1_cantCreatePubSectionInEncryptedAdv() throws Exception {
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted()) {
- assertThrows(
- SerializationException.InvalidSectionKindException.class,
- () -> {
- builder.addPublicSection().close();
- });
- }
- }
-
- @Test
- public void serializeAdvertisement_v1_cantCreateEncryptedSectionInPublicAdv() throws Exception {
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
- assertThrows(
- SerializationException.InvalidSectionKindException.class,
- () -> {
- builder.addEncryptedSection(V1_BROADCAST_CRED, VerificationMode.SIGNATURE).close();
- });
- }
- }
-
- @Test
public void serializeAdvertisement_v0_canSerialize() throws Exception {
- try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
+ try (V0AdvertisementBuilder builder = npAdv.newPublicV0Builder()) {
builder.addDataElement(TX_POWER);
builder.addDataElement(PUBLIC_ACTIONS);
byte[] adv = builder.build();
@@ -195,8 +178,7 @@
@Test
public void serializeAdvertisement_v0_canSerializePrivate() throws Exception {
- try (V0AdvertisementBuilder builder =
- V0AdvertisementBuilder.newEncrypted(V0_BROADCAST_CRED, SALT)) {
+ try (V0AdvertisementBuilder builder = npAdv.newEncryptedV0Builder(V0_BROADCAST_CRED, SALT)) {
builder.addDataElement(TX_POWER);
builder.addDataElement(PRIVATE_ACTIONS);
byte[] adv = builder.build();
@@ -206,13 +188,13 @@
@Test
public void serializeAdvertisement_v0_canRoundtrip() throws Exception {
- try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic();
- CredentialBook<NoMetadata> book = CredentialBook.empty()) {
+ try (V0AdvertisementBuilder builder = npAdv.newPublicV0Builder();
+ CredentialBook<NoMetadata> book = npAdv.newEmptyCredentialBook()) {
builder.addDataElement(TX_POWER);
builder.addDataElement(PUBLIC_ACTIONS);
byte[] advBytes = builder.build();
- DeserializeResult<NoMetadata> result = NpAdv.deserializeAdvertisement(advBytes, book);
+ DeserializeResult<NoMetadata> result = npAdv.deserializeAdvertisement(advBytes, book);
DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
assertThat(adv).isNotNull();
@@ -231,17 +213,17 @@
@Test
public void serializeAdvertisement_v0_canRoundtripPrivate() throws Exception {
- try (V0AdvertisementBuilder builder =
- V0AdvertisementBuilder.newEncrypted(V0_BROADCAST_CRED, SALT);
+ try (V0AdvertisementBuilder builder = npAdv.newEncryptedV0Builder(V0_BROADCAST_CRED, SALT);
CredentialBook<NoMetadata> book =
- CredentialBook.<NoMetadata>builder()
+ npAdv
+ .<NoMetadata>newCredentialBookBuilder()
.addDiscoveryCredential(V0_CRED, NoMetadata.INSTANCE)
.build()) {
builder.addDataElement(TX_POWER);
builder.addDataElement(PRIVATE_ACTIONS);
byte[] advBytes = builder.build();
- DeserializeResult<NoMetadata> result = NpAdv.deserializeAdvertisement(advBytes, book);
+ DeserializeResult<NoMetadata> result = npAdv.deserializeAdvertisement(advBytes, book);
DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
assertThat(adv).isNotNull();
@@ -260,14 +242,14 @@
@Test
public void serializeAdvertisement_v0_emptyIsError() throws Exception {
- try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
+ try (V0AdvertisementBuilder builder = npAdv.newPublicV0Builder()) {
assertThrows(SerializationException.UnencryptedSizeException.class, () -> builder.build());
}
}
@Test
public void serializeAdvertisement_v0_fullIsError() throws Exception {
- try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
+ try (V0AdvertisementBuilder builder = npAdv.newPublicV0Builder()) {
assertThrows(
SerializationException.InsufficientSpaceException.class,
() -> {
@@ -281,7 +263,7 @@
@Test
public void serializeAdvertisement_v0_publicAdvPrivateActionsIsError() throws Exception {
- try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
+ try (V0AdvertisementBuilder builder = npAdv.newPublicV0Builder()) {
assertThrows(
SerializationException.InvalidDataElementException.class,
() -> builder.addDataElement(PRIVATE_ACTIONS));
@@ -290,8 +272,7 @@
@Test
public void serializeAdvertisement_v0_privateAdvPublicActionsIsError() throws Exception {
- try (V0AdvertisementBuilder builder =
- V0AdvertisementBuilder.newEncrypted(V0_BROADCAST_CRED, SALT)) {
+ try (V0AdvertisementBuilder builder = npAdv.newEncryptedV0Builder(V0_BROADCAST_CRED, SALT)) {
assertThrows(
SerializationException.InvalidDataElementException.class,
() -> builder.addDataElement(PUBLIC_ACTIONS));
@@ -300,7 +281,7 @@
@Test
public void serializeAdvertisement_v0_invalidTxPowerIsError() throws Exception {
- try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
+ try (V0AdvertisementBuilder builder = npAdv.newPublicV0Builder()) {
assertThrows(
SerializationException.InvalidDataElementException.class,
() -> builder.addDataElement(INVALID_TX_POWER));
@@ -309,7 +290,7 @@
@Test
public void serializeAdvertisement_v0_handleIsConsumedByBuild() throws Exception {
- try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
+ try (V0AdvertisementBuilder builder = npAdv.newPublicV0Builder()) {
builder.addDataElement(TX_POWER);
byte[] unused = builder.build();
assertThrows(Handle.InvalidHandleException.class, () -> builder.addDataElement(TX_POWER));
@@ -320,7 +301,7 @@
@Test
@Ignore("b/311225033: Duplicate data element spec change not implemented")
public void serializeAdvertisement_v0_deNotAddedTwice() throws Exception {
- try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
+ try (V0AdvertisementBuilder builder = npAdv.newPublicV0Builder()) {
builder.addDataElement(TX_POWER);
assertThrows(Exception.class, () -> builder.addDataElement(TX_POWER));
}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java
index 7408de6..04f9461 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java
@@ -21,7 +21,7 @@
import com.google.android.nearby.presence.rust.credential.V1BroadcastCredential;
import com.google.android.nearby.presence.rust.credential.V1DiscoveryCredential;
-@SuppressWarnings("MutablePublicArray")
+@SuppressWarnings({"MutablePublicArray", "ConstantCaseForConstants"})
public class TestData {
private TestData() {}
@@ -219,234 +219,125 @@
new V0BroadcastCredential(V0_KEY_SEED, V0_IDENTITY_TOKEN);
public static final byte[] V1_KEY_SEED = {
- (byte) 0x3B,
- (byte) 0x4C,
- (byte) 0x0E,
- (byte) 0x39,
- (byte) 0x95,
- (byte) 0x92,
- (byte) 0x47,
- (byte) 0xC8,
- (byte) 0x85,
- (byte) 0xCC,
- (byte) 0x87,
- (byte) 0x3B,
- (byte) 0x2E,
- (byte) 0x70,
- (byte) 0x75,
- (byte) 0x87,
- (byte) 0xB5,
- (byte) 0x85,
- (byte) 0x9C,
- (byte) 0x35,
- (byte) 0x27,
- (byte) 0xD8,
- (byte) 0x22,
- (byte) 0xFD,
- (byte) 0x32,
- (byte) 0x0C,
- (byte) 0x57,
- (byte) 0xB3,
- (byte) 0x40,
- (byte) 0xE4,
- (byte) 0x3E,
- (byte) 0xAE
- };
-
- public static final byte[] V1_IDENTITY_TOKEN = {
- (byte) 0x95,
- (byte) 0x71,
- (byte) 0x75,
- (byte) 0x38,
- (byte) 0x49,
- (byte) 0xEF,
- (byte) 0x1F,
- (byte) 0xA7,
- (byte) 0x10,
- (byte) 0x58,
- (byte) 0xDD,
- (byte) 0x2C,
- (byte) 0xD5,
- (byte) 0x7E,
- (byte) 0xE6,
- (byte) 0x14
- };
-
- public static final byte[] V1_PRIVATE_KEY = {
- (byte) 0x21,
- (byte) 0xD4,
- (byte) 0x08,
- (byte) 0x74,
- (byte) 0x0F,
- (byte) 0xD5,
- (byte) 0x14,
- (byte) 0x29,
- (byte) 0xBF,
- (byte) 0x52,
- (byte) 0x3B,
- (byte) 0x10,
- (byte) 0x1B,
- (byte) 0x23,
- (byte) 0x23,
- (byte) 0x80,
- (byte) 0xCF,
- (byte) 0xA6,
- (byte) 0x86,
- (byte) 0x80,
- (byte) 0x8A,
- (byte) 0x34,
- (byte) 0xAC,
- (byte) 0x06,
- (byte) 0xC5,
- (byte) 0x06,
- (byte) 0x43,
- (byte) 0x28,
- (byte) 0x50,
- (byte) 0xD5,
- (byte) 0x9C,
- (byte) 0x89
- };
-
- public static final byte[] V1_PUB_KEY = {
- (byte) 0xC0,
- (byte) 0x87,
- (byte) 0x25,
- (byte) 0xE9,
- (byte) 0x0F,
- (byte) 0x5A,
- (byte) 0xD7,
- (byte) 0xA4,
- (byte) 0xD8,
- (byte) 0x7E,
- (byte) 0x9E,
- (byte) 0x76,
- (byte) 0xDF,
- (byte) 0x10,
- (byte) 0x44,
- (byte) 0xE9,
- (byte) 0x78,
- (byte) 0xF2,
- (byte) 0x80,
- (byte) 0x0F,
- (byte) 0x2B,
- (byte) 0x43,
- (byte) 0x03,
- (byte) 0xCA,
- (byte) 0x64,
- (byte) 0x0A,
- (byte) 0x7A,
- (byte) 0xA1,
- (byte) 0xE4,
- (byte) 0x43,
- (byte) 0x63,
- (byte) 0xAF
- };
-
- public static final byte[] V1_MIC_SHORT_HMAC = {
- (byte) 0x90,
- (byte) 0x8D,
- (byte) 0x6D,
- (byte) 0x40,
- (byte) 0x76,
- (byte) 0x0E,
- (byte) 0x13,
- (byte) 0xC8,
- (byte) 0x3C,
- (byte) 0x76,
- (byte) 0xD2,
- (byte) 0xE4,
- (byte) 0xED,
- (byte) 0x8F,
- (byte) 0xBD,
- (byte) 0x83,
- (byte) 0xED,
- (byte) 0xEC,
- (byte) 0xFD,
- (byte) 0xCE,
- (byte) 0x0A,
- (byte) 0x90,
- (byte) 0x85,
- (byte) 0x47,
- (byte) 0x5D,
- (byte) 0xAA,
- (byte) 0x3F,
- (byte) 0xAE,
- (byte) 0x6C,
- (byte) 0x90,
- (byte) 0x43,
- (byte) 0x15
- };
-
- public static final byte[] V1_MIC_LONG_HMAC = {
- (byte) 0x57,
- (byte) 0x99,
- (byte) 0x54,
- (byte) 0x4E,
- (byte) 0xB2,
- (byte) 0xB0,
- (byte) 0xA1,
- (byte) 0x99,
- (byte) 0x2F,
- (byte) 0x0D,
- (byte) 0x13,
- (byte) 0x25,
- (byte) 0xAE,
- (byte) 0xE0,
- (byte) 0x8F,
- (byte) 0xB3,
- (byte) 0xE2,
- (byte) 0x8F,
- (byte) 0xD9,
- (byte) 0x56,
- (byte) 0x8B,
- (byte) 0x70,
- (byte) 0xDE,
- (byte) 0x28,
- (byte) 0x82,
- (byte) 0x60,
- (byte) 0xE7,
- (byte) 0x71,
- (byte) 0xA4,
- (byte) 0x57,
- (byte) 0xCE,
- (byte) 0x9E
- };
-
- public static final byte[] V1_SIG_HMAC = {
- (byte) 0x37,
- (byte) 0x53,
- (byte) 0x78,
- (byte) 0x59,
- (byte) 0x3F,
- (byte) 0x8A,
- (byte) 0x06,
- (byte) 0x5B,
+ (byte) 0x18,
+ (byte) 0x01,
(byte) 0xE5,
- (byte) 0x03,
- (byte) 0x05,
+ (byte) 0xF8,
+ (byte) 0x2A,
(byte) 0xE7,
- (byte) 0xC1,
+ (byte) 0x6D,
+ (byte) 0xB1,
+ (byte) 0xD5,
+ (byte) 0x9C,
(byte) 0xD0,
- (byte) 0x29,
- (byte) 0x3D,
- (byte) 0x2C,
- (byte) 0x3D,
- (byte) 0xDA,
- (byte) 0x43,
- (byte) 0x8D,
- (byte) 0x0A,
- (byte) 0x29,
- (byte) 0x23,
- (byte) 0x58,
- (byte) 0xAC,
- (byte) 0x98,
- (byte) 0x10,
- (byte) 0x1A,
- (byte) 0x0A,
+ (byte) 0x07,
+ (byte) 0xB5,
+ (byte) 0x3C,
+ (byte) 0xF6,
+ (byte) 0xF9,
(byte) 0xA6,
- (byte) 0xB2
+ (byte) 0x08,
+ (byte) 0x96,
+ (byte) 0x14,
+ (byte) 0xBB,
+ (byte) 0xB2,
+ (byte) 0x68,
+ (byte) 0x08,
+ (byte) 0x8A,
+ (byte) 0x50,
+ (byte) 0x47,
+ (byte) 0x64,
+ (byte) 0x58,
+ (byte) 0xF6,
+ (byte) 0xBA,
+ (byte) 0xBA
};
-
+ public static final byte[] V1_IDENTITY_TOKEN = {
+ (byte) 0x5B,
+ (byte) 0x58,
+ (byte) 0x7F,
+ (byte) 0xB7,
+ (byte) 0x6F,
+ (byte) 0x38,
+ (byte) 0x5A,
+ (byte) 0xC4,
+ (byte) 0xBA,
+ (byte) 0xC0,
+ (byte) 0xB1,
+ (byte) 0xC8,
+ (byte) 0x2A,
+ (byte) 0x62,
+ (byte) 0x62,
+ (byte) 0x04
+ };
+ public static final byte[] V1_MIC_SHORT_HMAC = {
+ (byte) 0x92,
+ (byte) 0x1B,
+ (byte) 0xE0,
+ (byte) 0x6E,
+ (byte) 0xC7,
+ (byte) 0xFE,
+ (byte) 0x07,
+ (byte) 0xBA,
+ (byte) 0x9D,
+ (byte) 0xC4,
+ (byte) 0xC4,
+ (byte) 0x41,
+ (byte) 0x22,
+ (byte) 0x19,
+ (byte) 0xBB,
+ (byte) 0xEF,
+ (byte) 0x15,
+ (byte) 0x2A,
+ (byte) 0x3B,
+ (byte) 0x71,
+ (byte) 0x88,
+ (byte) 0x34,
+ (byte) 0x43,
+ (byte) 0xB4,
+ (byte) 0xAC,
+ (byte) 0x09,
+ (byte) 0x2D,
+ (byte) 0x72,
+ (byte) 0x20,
+ (byte) 0xFE,
+ (byte) 0xCD,
+ (byte) 0x40
+ };
+ public static final byte[] V1_MIC_LONG_HMAC = {
+ (byte) 0xC6,
+ (byte) 0xA0,
+ (byte) 0x2F,
+ (byte) 0x46,
+ (byte) 0x4D,
+ (byte) 0xD9,
+ (byte) 0xA3,
+ (byte) 0x24,
+ (byte) 0xC6,
+ (byte) 0xD1,
+ (byte) 0xAD,
+ (byte) 0xD7,
+ (byte) 0x62,
+ (byte) 0xBB,
+ (byte) 0x41,
+ (byte) 0x97,
+ (byte) 0xCA,
+ (byte) 0x3F,
+ (byte) 0xC5,
+ (byte) 0xB0,
+ (byte) 0xDF,
+ (byte) 0x5E,
+ (byte) 0xB3,
+ (byte) 0x56,
+ (byte) 0xB3,
+ (byte) 0x12,
+ (byte) 0x7A,
+ (byte) 0x20,
+ (byte) 0x80,
+ (byte) 0x65,
+ (byte) 0x96,
+ (byte) 0x3D
+ };
public static final byte[] V1_ALICE_METADATA = {
(byte) 0x7B,
(byte) 0x22,
@@ -543,228 +434,177 @@
(byte) 0x22,
(byte) 0x7D
};
-
public static final byte[] V1_ENCRYPTED_ALICE_METADATA = {
- (byte) 0xFD,
- (byte) 0xEC,
- (byte) 0x0B,
- (byte) 0xE8,
- (byte) 0x79,
- (byte) 0x1C,
- (byte) 0x10,
- (byte) 0x6D,
- (byte) 0x33,
- (byte) 0x11,
- (byte) 0xC7,
- (byte) 0xF3,
- (byte) 0x9B,
- (byte) 0x90,
- (byte) 0x8A,
- (byte) 0x5A,
- (byte) 0x74,
- (byte) 0x7F,
- (byte) 0x9C,
- (byte) 0x3C,
- (byte) 0xC9,
- (byte) 0xBC,
- (byte) 0x90,
- (byte) 0xC5,
- (byte) 0x88,
- (byte) 0xE0,
+ (byte) 0xD5,
(byte) 0xED,
- (byte) 0x28,
- (byte) 0xDC,
- (byte) 0x25,
- (byte) 0xE5,
- (byte) 0xD8,
- (byte) 0x9C,
- (byte) 0x28,
- (byte) 0x0F,
- (byte) 0x23,
- (byte) 0x9D,
- (byte) 0x21,
- (byte) 0xC7,
- (byte) 0xFF,
- (byte) 0xE1,
- (byte) 0x60,
- (byte) 0x62,
- (byte) 0x5B,
- (byte) 0xBC,
- (byte) 0x22,
- (byte) 0xB4,
- (byte) 0x3A,
- (byte) 0x02,
- (byte) 0x9D,
- (byte) 0x69,
- (byte) 0x53,
- (byte) 0x2A,
- (byte) 0x62,
- (byte) 0xB0,
- (byte) 0x82,
- (byte) 0xC7,
- (byte) 0x79,
- (byte) 0x9E,
- (byte) 0x30,
- (byte) 0x76,
- (byte) 0xB0,
- (byte) 0x8A,
- (byte) 0xC3,
- (byte) 0xCD,
- (byte) 0x11,
- (byte) 0xEA,
- (byte) 0x45,
- (byte) 0x4A,
- (byte) 0x3C,
- (byte) 0x25,
- (byte) 0x45,
- (byte) 0xD9,
- (byte) 0x44,
- (byte) 0x5B,
- (byte) 0xE3,
- (byte) 0xA0,
- (byte) 0x3E,
- (byte) 0x04,
- (byte) 0x79,
- (byte) 0x6B,
- (byte) 0xC3,
- (byte) 0x62,
- (byte) 0xA4,
- (byte) 0xA1,
- (byte) 0xF4,
- (byte) 0x76,
+ (byte) 0x80,
+ (byte) 0xA3,
+ (byte) 0x0E,
(byte) 0x58,
- (byte) 0x42,
- (byte) 0xDC,
- (byte) 0x37,
- (byte) 0x93,
- (byte) 0x81,
- (byte) 0x60,
- (byte) 0x8E,
- (byte) 0x00,
- (byte) 0x6D,
- (byte) 0xE0,
- (byte) 0x22,
- (byte) 0x82,
- (byte) 0x98,
- (byte) 0x40,
- (byte) 0x59,
- (byte) 0x41,
+ (byte) 0x3B,
+ (byte) 0x56,
+ (byte) 0xF8,
+ (byte) 0x6E,
+ (byte) 0x0E,
+ (byte) 0x86,
+ (byte) 0x03,
+ (byte) 0x4C,
+ (byte) 0xAF,
+ (byte) 0x27,
+ (byte) 0x6B,
+ (byte) 0x3A,
+ (byte) 0x61,
+ (byte) 0xEF,
+ (byte) 0x5D,
+ (byte) 0x3F,
+ (byte) 0x43,
+ (byte) 0xAE,
+ (byte) 0x08,
+ (byte) 0x25,
+ (byte) 0xDF,
+ (byte) 0x8B,
+ (byte) 0xAB,
+ (byte) 0xCE,
+ (byte) 0x90,
+ (byte) 0xDD,
+ (byte) 0x30,
+ (byte) 0x07,
(byte) 0xF9,
+ (byte) 0xB9,
+ (byte) 0xE6,
+ (byte) 0x05,
+ (byte) 0x2B,
+ (byte) 0x31,
+ (byte) 0x9A,
+ (byte) 0x9B,
+ (byte) 0x6E,
+ (byte) 0x82,
+ (byte) 0x1A,
+ (byte) 0xA2,
+ (byte) 0x72,
+ (byte) 0xA5,
+ (byte) 0x0A,
+ (byte) 0x5B,
+ (byte) 0x08,
+ (byte) 0x64,
+ (byte) 0xE8,
+ (byte) 0x91,
+ (byte) 0x9C,
+ (byte) 0xC9,
+ (byte) 0x18,
+ (byte) 0xA3,
+ (byte) 0x4C,
+ (byte) 0xE2,
+ (byte) 0x61,
+ (byte) 0x26,
+ (byte) 0x98,
+ (byte) 0xED,
+ (byte) 0xCC,
+ (byte) 0x98,
+ (byte) 0x03,
+ (byte) 0x35,
+ (byte) 0x1E,
+ (byte) 0xC9,
+ (byte) 0xE2,
(byte) 0x88,
- (byte) 0xB8,
- (byte) 0xFB,
+ (byte) 0xB3,
+ (byte) 0xF7,
+ (byte) 0x9C,
+ (byte) 0xD6,
+ (byte) 0xAF,
+ (byte) 0xF7,
+ (byte) 0x6F,
+ (byte) 0x7A,
+ (byte) 0x97,
+ (byte) 0xB1,
+ (byte) 0x90,
(byte) 0x9E,
- (byte) 0xED
+ (byte) 0xCB,
+ (byte) 0x20,
+ (byte) 0x49,
+ (byte) 0x1C,
+ (byte) 0x98,
+ (byte) 0x7B,
+ (byte) 0x8E,
+ (byte) 0xD7,
+ (byte) 0xCA,
+ (byte) 0x94,
+ (byte) 0x3C,
+ (byte) 0xB6,
+ (byte) 0xEA,
+ (byte) 0x4D,
+ (byte) 0x0E,
+ (byte) 0xAC,
+ (byte) 0xF5,
+ (byte) 0x15,
+ (byte) 0xC8,
+ (byte) 0x78,
+ (byte) 0x41,
+ (byte) 0x50,
+ (byte) 0x44,
+ (byte) 0xCA,
+ (byte) 0x8F,
+ (byte) 0x10
};
-
public static final byte[] V1_PRIVATE = {
(byte) 0x20,
- (byte) 0x03,
- (byte) 0x6C,
- (byte) 0x94,
- (byte) 0x77,
- (byte) 0x9A,
- (byte) 0xF9,
- (byte) 0x89,
- (byte) 0x83,
- (byte) 0x9F,
- (byte) 0x41,
- (byte) 0x98,
- (byte) 0x33,
- (byte) 0x68,
- (byte) 0x24,
- (byte) 0x9A,
- (byte) 0xCF,
- (byte) 0xAC,
- (byte) 0x8E,
- (byte) 0xC3,
- (byte) 0x46,
(byte) 0x02,
- (byte) 0x30,
- (byte) 0x89,
- (byte) 0x68,
- (byte) 0x99,
- (byte) 0xA2,
- (byte) 0xF5,
- (byte) 0x32,
- (byte) 0x36,
- (byte) 0x06,
- (byte) 0x18,
+ (byte) 0x9D,
+ (byte) 0x93,
+ (byte) 0x5A,
+ (byte) 0x7A,
+ (byte) 0x16,
+ (byte) 0xF7,
+ (byte) 0xF8,
+ (byte) 0xC9,
+ (byte) 0xE4,
+ (byte) 0x83,
+ (byte) 0x50,
(byte) 0x0D,
- (byte) 0xF6,
- (byte) 0x42,
- (byte) 0x52,
- (byte) 0xA1,
- (byte) 0x59,
- (byte) 0x46,
- (byte) 0xC8,
- (byte) 0xD1,
- (byte) 0x27,
(byte) 0x74,
- (byte) 0xFF,
- (byte) 0xBF,
- (byte) 0x53,
- (byte) 0xFE,
- (byte) 0x51,
- (byte) 0xCB,
- (byte) 0x39,
- (byte) 0x6D,
- (byte) 0x28,
- (byte) 0x3C,
- (byte) 0x7E,
- (byte) 0xD9,
- (byte) 0x6F,
- (byte) 0xB0,
- (byte) 0x70,
- (byte) 0x82,
- (byte) 0x26,
- (byte) 0x51,
- (byte) 0x11,
- (byte) 0xF2,
- (byte) 0x90,
- (byte) 0xC0,
- (byte) 0xBE,
- (byte) 0x34,
- (byte) 0x96,
- (byte) 0x57,
- (byte) 0x1F,
- (byte) 0x4F,
- (byte) 0xC0,
- (byte) 0x87,
+ (byte) 0xC5,
+ (byte) 0xFC,
+ (byte) 0x2B,
+ (byte) 0x09,
+ (byte) 0xE2,
+ (byte) 0x35,
+ (byte) 0x1B,
(byte) 0xF0,
- (byte) 0xA8,
- (byte) 0x0D,
+ (byte) 0xDE,
+ (byte) 0xFB,
+ (byte) 0x49,
+ (byte) 0xF6,
(byte) 0xD8,
- (byte) 0xE3,
- (byte) 0xC7,
- (byte) 0x8C,
- (byte) 0xAA,
- (byte) 0x7E,
- (byte) 0x41,
- (byte) 0x14,
- (byte) 0x58,
- (byte) 0xF4,
- (byte) 0xD3,
- (byte) 0x8E,
- (byte) 0x8E,
- (byte) 0xA0,
- (byte) 0x57,
- (byte) 0xF4,
- (byte) 0x63,
- (byte) 0x43,
+ (byte) 0x7C,
+ (byte) 0x6F,
+ (byte) 0x27,
+ (byte) 0x78,
+ (byte) 0xE5,
(byte) 0x74,
- (byte) 0x08,
- (byte) 0xC7,
+ (byte) 0x12,
+ (byte) 0x3C,
+ (byte) 0xC4,
+ (byte) 0x0D,
+ (byte) 0x79,
+ (byte) 0x86,
+ (byte) 0x62,
+ (byte) 0x05,
+ (byte) 0x1C,
+ (byte) 0x5A,
+ (byte) 0x13,
(byte) 0xB8,
- (byte) 0x51,
- (byte) 0x66,
- (byte) 0xB9
+ (byte) 0xDD,
+ (byte) 0xCE,
+ (byte) 0x43,
+ (byte) 0xDD,
+ (byte) 0x9B,
+ (byte) 0x97,
+ (byte) 0xC7
};
public static final V1DiscoveryCredential V1_CRED =
- new V1DiscoveryCredential(
- V1_KEY_SEED, V1_MIC_SHORT_HMAC, V1_MIC_LONG_HMAC, V1_SIG_HMAC, V1_PUB_KEY);
+ new V1DiscoveryCredential(V1_KEY_SEED, V1_MIC_SHORT_HMAC, V1_MIC_LONG_HMAC);
public static final V1BroadcastCredential V1_BROADCAST_CRED =
- new V1BroadcastCredential(V1_KEY_SEED, V1_IDENTITY_TOKEN, V1_PRIVATE_KEY);
+ new V1BroadcastCredential(V1_KEY_SEED, V1_IDENTITY_TOKEN);
}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestVectors.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestVectors.java
index c496023..afcab7d 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestVectors.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestVectors.java
@@ -25,13 +25,11 @@
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import java.io.BufferedReader;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Paths;
-import java.security.SecureRandom;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,6 +38,8 @@
@RunWith(JUnit4.class)
public class TestVectors {
+ private final NpAdv npAdv = new NpAdv();
+
static final class DataElement {
byte[] contents;
int deType;
@@ -72,8 +72,7 @@
}
@Override
- public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
+ public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
String hex = json.getAsString();
int byteLen = hex.length() / 2;
byte[] out = new byte[byteLen];
@@ -114,15 +113,12 @@
assertThat(testVectors).isNotNull();
for (TestVector tv : testVectors) {
- byte[] privateKey = new byte[32];
- SecureRandom.getInstanceStrong().nextBytes(privateKey);
- V1BroadcastCredential credential =
- new V1BroadcastCredential(tv.keySeed, tv.identityToken, privateKey);
- try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted();
+ V1BroadcastCredential credential = new V1BroadcastCredential(tv.keySeed, tv.identityToken);
+ try (V1AdvertisementBuilder builder = npAdv.newV1Builder();
V1SectionBuilder section =
nativeAddSaltedSection(builder.builder, credential, tv.sectionSalt)) {
for (DataElement de : tv.dataElements) {
- section.addDataElement(new V1DataElement.Generic(de.deType, de.contents));
+ section.addDataElement(new V1DataElement.Generic(npAdv, de.deType, de.contents));
}
section.finishSection();
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/V1DataElementTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/V1DataElementTests.java
new file mode 100644
index 0000000..3f1587e
--- /dev/null
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/V1DataElementTests.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.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.
+ */
+
+package com.google.android.nearby.presence.rust;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.Random;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class V1DataElementTests {
+ private static final int NUM_RANDOM_TRIALS = 1000;
+
+ private final NpAdv npAdv = new NpAdv();
+
+ private static final int TX_POWER_DE_TYPE = 5;
+ private static final int CAST_ID_DE_TYPE = 17;
+ private static final int DEDUP_HINT_DE_TYPE = 18;
+ private static final int CONTEXT_SYNC_SEQ_NUM_DE_TYPE = 19;
+ private static final int CONNECTIVITY_INFO_DE_TYPE = 20;
+ private static final int DEVICE_TYPE_DE_TYPE = 22;
+ private static final int MEDIA_DEDUPLICATION_ID_DE_TYPE = 23;
+
+ /**
+ * Checks that round-tripping a given DE through serialization and deserialization yields the same
+ * data element.
+ */
+ private static void checkRoundtrip(V1DataElement dataElement) throws Exception {
+ V1DataElement.Generic generic = dataElement.trySerialize();
+ V1DataElement roundtripped = generic.tryDeserialize();
+
+ assertThat(dataElement).isEqualTo(roundtripped);
+
+ V1DataElement.Generic reconstructedGeneric = roundtripped.trySerialize();
+ assertThat(generic).isEqualTo(reconstructedGeneric);
+ }
+
+ /**
+ * A test which verifies that round-tripping Tx Powers through serialization and deserialization
+ * is the identity.
+ */
+ @Test
+ public void roundtripTxPower() throws Exception {
+ for (int i = -100; i <= 20; i++) {
+ checkRoundtrip(new V1DataElement.TxPower(npAdv, i));
+ }
+ }
+
+ /**
+ * A test which verifies that round-tripping Cast Ids through serialization and deserialization is
+ * the identity.
+ */
+ @Test
+ public void roundtripCastId() throws Exception {
+ Random random = new Random(12345);
+ byte[] id = new byte[32];
+
+ for (int i = 0; i < NUM_RANDOM_TRIALS; i++) {
+ random.nextBytes(id);
+ checkRoundtrip(new V1DataElement.CastId(npAdv, id));
+ }
+ }
+
+ /**
+ * A test which verifies that round-tripping dedup hints through serialization and deserialization
+ * is the identity.
+ */
+ @Test
+ public void roundtripDedupHint() throws Exception {
+ Random random = new Random(12345);
+ byte[] hint = new byte[8];
+
+ for (int i = 0; i < NUM_RANDOM_TRIALS; i++) {
+ random.nextBytes(hint);
+ checkRoundtrip(new V1DataElement.DedupHint(npAdv, hint));
+ }
+ }
+
+ /**
+ * A test which verifies that round-tripping context sync sequence numbers through serialization
+ * and deserialization is the identity.
+ */
+ @Test
+ public void roundtripContextSyncSeqNum() throws Exception {
+ for (int i = 0; i <= 15; i++) {
+ checkRoundtrip(new V1DataElement.ContextSyncSeqNum(npAdv, i));
+ }
+ }
+
+ /**
+ * A test which verifies that round-tripping device types through serialization and
+ * deserialization is the identity.
+ */
+ @Test
+ public void roundtripDeviceType() throws Exception {
+ for (int i = 0; i <= 255; i++) {
+ checkRoundtrip(new V1DataElement.DeviceType(npAdv, i));
+ }
+ }
+
+ /**
+ * A test which verifies that round-tripping connectivity info through serialization and
+ * deserialization is the identity.
+ */
+ @Test
+ public void roundtripDeviceInfo() throws Exception {
+ Random random = new Random(12345);
+ for (int i = 0; i < NUM_RANDOM_TRIALS; i++) {
+ V1DataElement.ConnectivityInfo de = new V1DataElement.ConnectivityInfo(npAdv);
+ if (random.nextBoolean()) {
+ // Try to set BLE connectivity info.
+ V1DataElement.BleConnectivityInfo bleInfo = new V1DataElement.BleConnectivityInfo();
+ if (random.nextBoolean()) {
+ byte[] macAddress = new byte[6];
+ random.nextBytes(macAddress);
+ V1DataElement.BleConnectivityInfo unused = bleInfo.setMacAddress(macAddress);
+ }
+ if (random.nextBoolean()) {
+ // If we set a GATT service identifier, ensure that it's not
+ // an empty array, since that will round-trip to `null` and
+ // hence the round-trip comparison would fail.
+ byte[] gattServiceIdentifier = new byte[random.nextInt(17) + 1];
+ random.nextBytes(gattServiceIdentifier);
+ V1DataElement.BleConnectivityInfo unused =
+ bleInfo.setGattServiceIdentifier(gattServiceIdentifier);
+ }
+ if (random.nextBoolean()) {
+ byte[] psm = new byte[2];
+ random.nextBytes(psm);
+ V1DataElement.BleConnectivityInfo unused = bleInfo.setPsm(psm);
+ }
+ if (random.nextBoolean()) {
+ byte[] deviceToken = new byte[2];
+ random.nextBytes(deviceToken);
+ V1DataElement.BleConnectivityInfo unused = bleInfo.setDeviceToken(deviceToken);
+ }
+ // Ensure that the info is non-empty before we set it.
+ if (!bleInfo.equals(new V1DataElement.BleConnectivityInfo())) {
+ V1DataElement unused = de.setBleInfo(bleInfo);
+ }
+ }
+ if (random.nextBoolean()) {
+ // Try to set Wifi-LAN connectivity info.
+ V1DataElement.WifiLanConnectivityInfo wifiLanInfo =
+ new V1DataElement.WifiLanConnectivityInfo();
+ if (random.nextBoolean()) {
+ byte[] ipAddress;
+ if (random.nextBoolean()) {
+ ipAddress = new byte[4];
+ } else {
+ ipAddress = new byte[16];
+ }
+ random.nextBytes(ipAddress);
+ V1DataElement.WifiLanConnectivityInfo unused = wifiLanInfo.setIpAddress(ipAddress);
+ }
+ if (random.nextBoolean()) {
+ byte[] port = new byte[2];
+ random.nextBytes(port);
+ V1DataElement.WifiLanConnectivityInfo unused = wifiLanInfo.setPort(port);
+ }
+ if (random.nextBoolean()) {
+ byte[] bssid = new byte[6];
+ random.nextBytes(bssid);
+ V1DataElement.WifiLanConnectivityInfo unused = wifiLanInfo.setBssid(bssid);
+ }
+ // Ensure that the info is non-empty before we set it.
+ if (!wifiLanInfo.equals(new V1DataElement.WifiLanConnectivityInfo())) {
+ V1DataElement unused = de.setWifiLanInfo(wifiLanInfo);
+ }
+ }
+ // Empty info won't serialize, so skip if it's empty.
+ if (de.equals(new V1DataElement.ConnectivityInfo(npAdv))) {
+ continue;
+ }
+ checkRoundtrip(de);
+ }
+ }
+
+ /**
+ * A test which verifies that round-tripping Media Deduplication Ids through serialization and
+ * deserialization is the identity.
+ */
+ @Test
+ public void roundtripMediaDeduplicationId() throws Exception {
+ Random random = new Random(12345);
+ byte[] id = new byte[20];
+
+ for (int i = 0; i < NUM_RANDOM_TRIALS; i++) {
+ random.nextBytes(id);
+ checkRoundtrip(new V1DataElement.MediaDeduplicationId(npAdv, id));
+ }
+ }
+
+ /**
+ * A test which verifies that round-tripping Capabilities through serialization and
+ * deserialization is the identity.
+ */
+ @Test
+ public void roundtripCapabilities() throws Exception {
+ int maxNumSupportedCapabilities = 4;
+ V1DataElement.Capabilities capabilities = new V1DataElement.Capabilities(npAdv);
+ for (int i = 0; i < maxNumSupportedCapabilities; i++) {
+ capabilities.addCapability(i);
+ checkRoundtrip(capabilities);
+ }
+ }
+
+ /**
+ * A test which verifies that round-tripping Requirements through serialization and
+ * deserialization is the identity.
+ */
+ @Test
+ public void roundtripRequirements() throws Exception {
+ int maxNumSupportedCapabilities = 4;
+ V1DataElement.Requirements requirements = new V1DataElement.Requirements(npAdv);
+ for (int i = 0; i < maxNumSupportedCapabilities; i++) {
+ requirements.addCapability(i);
+ checkRoundtrip(requirements);
+ }
+ }
+
+ /**
+ * A test which verifies that attempting to construct a generic DE with the forbidden type-code
+ * 0xFFFFFFFF yields an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void genericDataElementForbiddenTypeCodeIllegalArgumentException() throws Exception {
+ byte[] payload = new byte[2];
+ V1DataElement unused = new V1DataElement.Generic(npAdv, 0xFFFFFFFF, payload);
+ }
+
+ /**
+ * A test which verifies that attempting to construct a generic DE with too long of a length
+ * yields an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void genericDataElementTooLongIllegalArgumentException() throws Exception {
+ byte[] payload = new byte[128];
+ V1DataElement unused = new V1DataElement.Generic(npAdv, 0, payload);
+ }
+
+ /**
+ * A test which verifies that attempting to construct an out-of-bounds Tx Power yields an
+ * exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void txPowerIllegalArgumentException() throws Exception {
+ new V1DataElement.TxPower(npAdv, 120);
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize an out-of-bounds Tx Power yields an
+ * exception.
+ */
+ @Test(expected = V1DataElement.TxPowerOutOfBoundsException.class)
+ public void txPowerOutOfBoundsException() throws Exception {
+ byte[] payload = {120};
+ V1DataElement.Generic generic = new V1DataElement.Generic(npAdv, TX_POWER_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize a wrong-length Tx Power yields an
+ * exception.
+ */
+ @Test(expected = V1DataElement.TxPowerWrongLengthException.class)
+ public void txPowerWrongLengthException() throws Exception {
+ byte[] payload = {0, 0};
+ V1DataElement.Generic generic = new V1DataElement.Generic(npAdv, TX_POWER_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /**
+ * A test which verifies that attempting to construct a wrong-length Cast Id yields an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void castIdIllegalArgumentException() throws Exception {
+ byte[] payload = new byte[33];
+ V1DataElement unused = new V1DataElement.CastId(npAdv, payload);
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize a wrong-length Cast Id yields an
+ * exception.
+ */
+ @Test(expected = V1DataElement.CastIdWrongLengthException.class)
+ public void castIdWrongLengthException() throws Exception {
+ byte[] payload = new byte[33];
+ V1DataElement.Generic generic = new V1DataElement.Generic(npAdv, CAST_ID_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /**
+ * A test which verifies that attempting to construct a wrong-length dedup hint yields an
+ * exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void dedupHintIllegalArgumentException() throws Exception {
+ byte[] payload = new byte[9];
+ V1DataElement unused = new V1DataElement.DedupHint(npAdv, payload);
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize a wrong-length dedup hint yields an
+ * exception.
+ */
+ @Test(expected = V1DataElement.DedupHintWrongLengthException.class)
+ public void dedupHintWrongLengthException() throws Exception {
+ byte[] payload = new byte[9];
+ V1DataElement.Generic generic = new V1DataElement.Generic(npAdv, DEDUP_HINT_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /**
+ * A test which verifies that attempting to construct an out-of-bounds context sync sequence
+ * number yields an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void contextSyncSeqNumIllegalArgumentException() throws Exception {
+ V1DataElement unused = new V1DataElement.ContextSyncSeqNum(npAdv, 16);
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize an out-of-bounds context sync sequence
+ * number yields an exception.
+ */
+ @Test(expected = V1DataElement.ContextSyncSeqNumOutOfBoundsException.class)
+ public void contextSyncSeqNumOutOfBoundsException() throws Exception {
+ byte[] payload = {16};
+ V1DataElement.Generic generic =
+ new V1DataElement.Generic(npAdv, CONTEXT_SYNC_SEQ_NUM_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize a wrong-length context sync sequence
+ * number yields an exception.
+ */
+ @Test(expected = V1DataElement.ContextSyncSeqNumWrongLengthException.class)
+ public void contextSyncSeqNumWrongLengthException() throws Exception {
+ byte[] payload = {0, 0};
+ V1DataElement.Generic generic =
+ new V1DataElement.Generic(npAdv, CONTEXT_SYNC_SEQ_NUM_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /**
+ * A test which verifies that attempting to construct an out-of-bounds device type yields an
+ * exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void deviceTypeIllegalArgumentException() throws Exception {
+ V1DataElement unused = new V1DataElement.DeviceType(npAdv, 256);
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize a wrong-length device type yields an
+ * exception.
+ */
+ @Test(expected = V1DataElement.DeviceTypeWrongLengthException.class)
+ public void deviceTypeWrongLengthException() throws Exception {
+ byte[] payload = {0, 0};
+ V1DataElement.Generic generic = new V1DataElement.Generic(npAdv, DEVICE_TYPE_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /**
+ * A test which verifies that attempting to serialize empty connectivity info yields an exception.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void emptyConnectivityInfoIllegalStateException() throws Exception {
+ V1DataElement.ConnectivityInfo de = new V1DataElement.ConnectivityInfo(npAdv);
+ V1DataElement.Generic unused = de.trySerialize();
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize empty connectivity info yields an
+ * exception.
+ */
+ @Test(expected = V1DataElement.ConnectivityInfoMalformedException.class)
+ public void emptyConnectivityInfoMalformedComponentsException() throws Exception {
+ byte[] payload = {};
+ V1DataElement.Generic generic =
+ new V1DataElement.Generic(npAdv, CONNECTIVITY_INFO_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize invalid BLE connectivity info yields an
+ * exception.
+ */
+ @Test(expected = V1DataElement.ConnectivityInfoComponentMalformedException.class)
+ public void invalidBleInfoMalformedComponentException() throws Exception {
+ // BLE component is specified, but the field mask is invalid (states that fields, even ones
+ // which
+ // aren't supposed to exist are present, but actually nothing follows.)
+ byte[] payload = {(byte) 0x00, (byte) 0xFF};
+ V1DataElement.Generic generic =
+ new V1DataElement.Generic(npAdv, CONNECTIVITY_INFO_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /** A test which verifies that attempting to assign a wrong-length BLE Mac yields an exception */
+ @Test(expected = IllegalArgumentException.class)
+ public void bleMacIllegalArgumentException() throws Exception {
+ new V1DataElement.BleConnectivityInfo().setMacAddress(new byte[1]);
+ }
+
+ /**
+ * A test which verifies that attempting to assign a wrong-length Gatt service identifier yields
+ * an exception
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void bleGattServiceIdentifierIllegalArgumentException() throws Exception {
+ new V1DataElement.BleConnectivityInfo().setGattServiceIdentifier(new byte[18]);
+ }
+
+ /** A test which verifies that attempting to assign a wrong-length PSM yields an exception */
+ @Test(expected = IllegalArgumentException.class)
+ public void blePsmIllegalArgumentException() throws Exception {
+ new V1DataElement.BleConnectivityInfo().setPsm(new byte[3]);
+ }
+
+ /**
+ * A test which verifies that attempting to assign a wrong-length device token yields an exception
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void bleDeviceTokenIllegalArgumentException() throws Exception {
+ new V1DataElement.BleConnectivityInfo().setDeviceToken(new byte[4]);
+ }
+
+ /**
+ * A test which verifies that attempting to assign a wrong-length IP address yields an exception
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void wifiLanIpAddressIllegalArgumentException() throws Exception {
+ new V1DataElement.WifiLanConnectivityInfo().setIpAddress(new byte[5]);
+ }
+
+ /** A test which verifies that attempting to assign a wrong-length port yields an exception */
+ @Test(expected = IllegalArgumentException.class)
+ public void wifiLanPortIllegalArgumentException() throws Exception {
+ new V1DataElement.WifiLanConnectivityInfo().setPort(new byte[3]);
+ }
+
+ /** A test which verifies that attempting to assign a wrong-length BSSID yields an exception */
+ @Test(expected = IllegalArgumentException.class)
+ public void wifiLanBSSIDIllegalArgumentException() throws Exception {
+ new V1DataElement.WifiLanConnectivityInfo().setBssid(new byte[7]);
+ }
+
+ /**
+ * A test which verifies that attempting to construct a wrong-length Media Deduplication Id yields
+ * an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void mediaDeduplicationIdIllegalArgumentException() throws Exception {
+ byte[] payload = new byte[21];
+ V1DataElement unused = new V1DataElement.MediaDeduplicationId(npAdv, payload);
+ }
+
+ /**
+ * A test which verifies that attempting to deserialize a wrong-length media deduplication Id
+ * yields an exception.
+ */
+ @Test(expected = V1DataElement.MediaDeduplicationIdWrongLengthException.class)
+ public void mediaDeduplicationIdWrongLengthException() throws Exception {
+ byte[] payload = new byte[33];
+ V1DataElement.Generic generic =
+ new V1DataElement.Generic(npAdv, MEDIA_DEDUPLICATION_ID_DE_TYPE, payload);
+ V1DataElement unused = generic.tryDeserialize();
+ }
+
+ /**
+ * A test which verifies that attempting to construct a Capabilities DE with reserved bits throws
+ * a CapabilitiesMalformedException.
+ */
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void capabilitiesMalformedException() throws Exception {
+ V1DataElement.Capabilities capabilities = new V1DataElement.Capabilities(npAdv);
+ for (int cap = 0; cap <= 7; cap++) {
+ capabilities.addCapability(cap);
+ }
+ V1DataElement.Generic unused = capabilities.trySerialize();
+ }
+
+ /**
+ * A test which verifies that attempting to construct a Requirements DE with reserved bits throws
+ * a RequirementsMalformedException.
+ */
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void requirementsMalformedException() throws Exception {
+ V1DataElement.Requirements requirements = new V1DataElement.Requirements(npAdv);
+ for (int cap = 0; cap <= 7; cap++) {
+ requirements.addCapability(cap);
+ }
+ V1DataElement.Generic unused = requirements.trySerialize();
+ }
+}
diff --git a/nearby/presence/np_java_ffi/used_by_native.pgcfg b/nearby/presence/np_java_ffi/used_by_native.pgcfg
new file mode 100644
index 0000000..9ef1215
--- /dev/null
+++ b/nearby/presence/np_java_ffi/used_by_native.pgcfg
@@ -0,0 +1,57 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.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.
+
+
+# This prevents the names of native methods from being obfuscated and prevents
+# UnsatisfiedLinkErrors.
+-keepclasseswithmembernames,includedescriptorclasses class * {
+ native <methods>;
+}
+
+
+# This file allows configuration of proguard via annotations. Apply them to
+# the elements of your program not only to ensure correct proguard
+# functionality, but to document non-obvious entry points to your code to make
+# it survive refactorings.
+
+# Annotations are implemented as attributes, so we have to explicitly keep them.
+# Catch all which encompasses attributes like RuntimeVisibleParameterAnnotations
+# and RuntimeVisibleTypeAnnotations
+-keepattributes RuntimeVisible*Annotation*
+
+# JNI is an entry point that's hard to keep track of, so there's
+# an annotation to mark fields and methods used by native code.
+
+# Keep the annotations that proguard needs to process.
+-keep class com.google.android.nearby.presence.rust.UsedBy*
+
+# Just because native code accesses members of a class, does not mean that the
+# class itself needs to be annotated - only annotate classes that are
+# referenced themselves in native code.
+-keep @com.google.android.nearby.presence.rust.UsedBy* class * {
+ <init>();
+}
+-keepclassmembers class * {
+ @com.google.android.nearby.presence.rust.UsedBy* *;
+}
+
+-dontwarn java.**
+-dontwarn androidx.**
+-dontwarn com.google.android.cooperativecleaner.**
+-dontwarn com.google.errorprone.**
+-dontwarn org.checkerframework.**
+
+# This exception only exists since Proguard will complain about a missing
+# `int ordinal()` method otherwise, even though this always exists for all Java enums.
+-dontwarn com.google.android.nearby.presence.rust.V1DataElement$ByteValuePrintFormat
diff --git a/nearby/presence/sink/src/lib.rs b/nearby/presence/sink/src/lib.rs
index fe6e5b6..3c5d598 100644
--- a/nearby/presence/sink/src/lib.rs
+++ b/nearby/presence/sink/src/lib.rs
@@ -43,7 +43,29 @@
/// Returns `Some` if all data was successfully written to the [`Sink`],
/// but if doing so failed at any point, returns `None`. If this method
/// fails, the contents of the [`Sink`] should be considered to be invalid.
- fn write_payload<S: Sink<Self::DataType> + ?Sized>(self, sink: &mut S) -> Option<()>;
+ fn write_payload<S: Sink<Self::DataType>>(self, sink: &mut S) -> Option<()>;
+}
+
+impl<T> Sink<T> for tinyvec::SliceVec<'_, T>
+where
+ T: Clone,
+{
+ fn try_extend_from_slice(&mut self, items: &[T]) -> Option<()> {
+ if items.len() > (self.capacity() - self.len()) {
+ return None;
+ }
+ // won't panic: just checked the length
+ self.extend_from_slice(items);
+ Some(())
+ }
+ fn try_push(&mut self, item: T) -> Option<()> {
+ if self.len() == self.capacity() {
+ None
+ } else {
+ self.push(item);
+ Some(())
+ }
+ }
}
impl<T, A> Sink<T> for tinyvec::ArrayVec<A>
diff --git a/nearby/presence/test_helper/src/lib.rs b/nearby/presence/test_helper/src/lib.rs
index 41446fd..42097a6 100644
--- a/nearby/presence/test_helper/src/lib.rs
+++ b/nearby/presence/test_helper/src/lib.rs
@@ -64,7 +64,7 @@
hex::decode(str).unwrap()
}
-/// Format data as hex bytes for the convenience of test data in FFI tests.
+/// Format data as comma separated hex bytes for the convenience of test data in FFI tests.
///
/// # Examples
///
diff --git a/nearby/presence/xts_aes/fuzz/Cargo.toml b/nearby/presence/xts_aes/fuzz/Cargo.toml
index a231176..7f3de20 100644
--- a/nearby/presence/xts_aes/fuzz/Cargo.toml
+++ b/nearby/presence/xts_aes/fuzz/Cargo.toml
@@ -21,7 +21,7 @@
libfuzzer-sys.workspace = true
[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)', 'cfg(rust_analyzer)'] }
[[bin]]
name = "xts_roundtrip"
diff --git a/nearby/presence/xts_aes/src/lib.rs b/nearby/presence/xts_aes/src/lib.rs
index e8a80a1..331ee27 100644
--- a/nearby/presence/xts_aes/src/lib.rs
+++ b/nearby/presence/xts_aes/src/lib.rs
@@ -586,7 +586,7 @@
enc_cipher: &'a A::EncryptCipher,
}
-impl<'a, A: Aes> XtsEncrypterTweaked<'a, A> {
+impl<A: Aes> XtsEncrypterTweaked<'_, A> {
fn advance_to_next_block_num(&mut self) {
self.tweak_state.advance_to_next_block()
}
@@ -609,7 +609,7 @@
dec_cipher: &'a A::DecryptCipher,
}
-impl<'a, A: Aes> XtsDecrypterTweaked<'a, A> {
+impl<A: Aes> XtsDecrypterTweaked<'_, A> {
fn advance_to_next_block_num(&mut self) {
self.tweak_state.advance_to_next_block()
}
diff --git a/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs b/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs
index 10b8c7d..b931960 100644
--- a/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs
+++ b/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::unwrap_used)]
+#![allow(clippy::unwrap_used, missing_docs)]
use aes::{cipher, cipher::KeyInit as _};
use crypto_provider::aes::*;
diff --git a/nearby/presence/xts_aes/tests/tests.rs b/nearby/presence/xts_aes/tests/tests.rs
index 0452f06..ac6bc68 100644
--- a/nearby/presence/xts_aes/tests/tests.rs
+++ b/nearby/presence/xts_aes/tests/tests.rs
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#![allow(missing_docs)]
+
use crypto_provider::aes::{Aes, BLOCK_SIZE};
use crypto_provider::CryptoProvider;
use crypto_provider_default::CryptoProviderImpl;
diff --git a/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs b/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs
index ede231d..751c0c6 100644
--- a/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs
+++ b/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)]
+#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, missing_docs)]
use crypto_provider::CryptoProvider;
use crypto_provider_default::CryptoProviderImpl;
diff --git a/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs b/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs
index 6661e96..9d806ad 100644
--- a/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs
+++ b/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
+#![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic, missing_docs)]
use anyhow::anyhow;
use crypto_provider::CryptoProvider;
diff --git a/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs b/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs
index ab967dc..cd663bc 100644
--- a/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs
+++ b/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![allow(clippy::unwrap_used)]
+#![allow(clippy::unwrap_used, missing_docs)]
use crypto_provider::aes::*;
use crypto_provider::CryptoProvider;
diff --git a/nearby/proguard.flags b/nearby/proguard.flags
new file mode 100644
index 0000000..7e4c162
--- /dev/null
+++ b/nearby/proguard.flags
@@ -0,0 +1,17 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+-keepclassmembers class com.google.security.cryptauth.lib.securegcm.ukey2.AlertException {
+ <init>(...);
+}
diff --git a/remoteauth/Cargo.lock b/remoteauth/Cargo.lock
index ab31216..1166237 100644
--- a/remoteauth/Cargo.lock
+++ b/remoteauth/Cargo.lock
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
[[package]]
name = "aho-corasick"
@@ -28,9 +28,9 @@
[[package]]
name = "anstream"
-version = "0.6.14"
+version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -43,55 +43,56 @@
[[package]]
name = "anstyle"
-version = "1.0.7"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
-version = "0.2.4"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.1.0"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.3"
+version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
+ "once_cell",
"windows-sys",
]
[[package]]
name = "anyhow"
-version = "1.0.86"
+version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]]
name = "autocfg"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bstr"
-version = "1.9.1"
+version = "1.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
dependencies = [
"memchr",
"serde",
@@ -110,15 +111,18 @@
[[package]]
name = "bumpalo"
-version = "3.16.0"
+version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "cc"
-version = "1.0.99"
+version = "1.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
+checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
+dependencies = [
+ "shlex",
+]
[[package]]
name = "cfg-if"
@@ -128,23 +132,23 @@
[[package]]
name = "chrono"
-version = "0.4.38"
+version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
- "windows-targets",
+ "windows-link",
]
[[package]]
name = "clap"
-version = "4.5.13"
+version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
+checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
dependencies = [
"clap_builder",
"clap_derive",
@@ -152,9 +156,9 @@
[[package]]
name = "clap_builder"
-version = "4.5.13"
+version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
+checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
dependencies = [
"anstream",
"anstyle",
@@ -164,9 +168,9 @@
[[package]]
name = "clap_derive"
-version = "4.5.13"
+version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
+checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
dependencies = [
"heck",
"proc-macro2",
@@ -176,9 +180,9 @@
[[package]]
name = "clap_lex"
-version = "0.7.1"
+version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "cmd_runner"
@@ -199,15 +203,15 @@
[[package]]
name = "colorchoice"
-version = "1.0.1"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crossbeam"
@@ -224,18 +228,18 @@
[[package]]
name = "crossbeam-channel"
-version = "0.5.13"
+version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
-version = "0.8.5"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
@@ -252,18 +256,18 @@
[[package]]
name = "crossbeam-queue"
-version = "0.3.11"
+version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.20"
+version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "ctap_protocol"
@@ -273,23 +277,33 @@
]
[[package]]
-name = "env_logger"
-version = "0.10.2"
+name = "env_filter"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
dependencies = [
- "humantime",
- "is-terminal",
"log",
"regex",
- "termcolor",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "jiff",
+ "log",
]
[[package]]
name = "file-header"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5568149106e77ae33bc3a2c3ef3839cbe63ffa4a8dd4a81612a6f9dfdbc2e9f"
+checksum = "f572687d306c51b578ca07b5b2b10ad334a486cb11673d0f88437f22d68eed06"
dependencies = [
"crossbeam",
"lazy_static",
@@ -300,9 +314,9 @@
[[package]]
name = "globset"
-version = "0.4.14"
+version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
+checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
dependencies = [
"aho-corasick",
"bstr",
@@ -318,22 +332,10 @@
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
-name = "hermit-abi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
-
-[[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-
-[[package]]
name = "iana-time-zone"
-version = "0.1.60"
+version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -353,54 +355,68 @@
]
[[package]]
-name = "is-terminal"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
-dependencies = [
- "hermit-abi",
- "libc",
- "windows-sys",
-]
-
-[[package]]
name = "is_terminal_polyfill"
-version = "1.70.0"
+version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
-version = "1.0.11"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "jiff"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e"
+dependencies = [
+ "jiff-static",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
[[package]]
name = "js-sys"
-version = "0.3.69"
+version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
+ "once_cell",
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.155"
+version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "license"
-version = "3.4.0+3.24.0"
+version = "3.5.0+3.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7da1e0d845faf299a9fe5f201a918a0dc0d5fc22c7b9580a6a23fed3a912b37"
+checksum = "ef560a8a3b3ac5fef31a9cfb8e4464cd1caa36c6bf1c1896b5d5a237e6486ee1"
dependencies = [
"reword",
"serde",
@@ -409,9 +425,9 @@
[[package]]
name = "log"
-version = "0.4.21"
+version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]]
name = "memchr"
@@ -430,9 +446,9 @@
[[package]]
name = "once_cell"
-version = "1.19.0"
+version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad"
[[package]]
name = "owo-colors"
@@ -448,28 +464,43 @@
]
[[package]]
-name = "proc-macro2"
-version = "1.0.86"
+name = "portable-atomic"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.36"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
-version = "1.10.5"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
@@ -479,9 +510,9 @@
[[package]]
name = "regex-automata"
-version = "0.4.7"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
@@ -490,9 +521,9 @@
[[package]]
name = "regex-syntax"
-version = "0.8.4"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "remote_auth_protocol"
@@ -511,10 +542,16 @@
]
[[package]]
-name = "ryu"
-version = "1.0.18"
+name = "rustversion"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
@@ -527,18 +564,18 @@
[[package]]
name = "serde"
-version = "1.0.203"
+version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.203"
+version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
@@ -547,11 +584,12 @@
[[package]]
name = "serde_json"
-version = "1.0.118"
+version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
+ "memchr",
"ryu",
"serde",
]
@@ -563,6 +601,12 @@
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -570,9 +614,9 @@
[[package]]
name = "syn"
-version = "2.0.67"
+version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90"
+checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
@@ -580,28 +624,19 @@
]
[[package]]
-name = "termcolor"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
name = "thiserror"
-version = "1.0.61"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.61"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
@@ -610,15 +645,15 @@
[[package]]
name = "unicode-ident"
-version = "1.0.12"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-segmentation"
-version = "1.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "utf8parse"
@@ -638,23 +673,24 @@
[[package]]
name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
+ "once_cell",
+ "rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
- "once_cell",
"proc-macro2",
"quote",
"syn",
@@ -663,9 +699,9 @@
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -673,9 +709,9 @@
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
@@ -686,15 +722,18 @@
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
[[package]]
name = "winapi-util"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys",
]
@@ -709,19 +748,25 @@
]
[[package]]
-name = "windows-sys"
-version = "0.52.0"
+name = "windows-link"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@@ -735,63 +780,63 @@
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "xshell"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437"
+checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d"
dependencies = [
"xshell-macros",
]
[[package]]
name = "xshell-macros"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852"
+checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547"
diff --git a/remoteauth/build_scripts/Cargo.toml b/remoteauth/build_scripts/Cargo.toml
index 604a617..9937404 100644
--- a/remoteauth/build_scripts/Cargo.toml
+++ b/remoteauth/build_scripts/Cargo.toml
@@ -9,5 +9,9 @@
anyhow.workspace = true
clap = { version = "4.5.13", features = ["derive"] }
cmd_runner = { path = "../../common/cmd_runner" }
-env_logger = "0.10.0"
+env_logger = "0.11.7"
log = "0.4.17"
+
+[[bin]]
+name = "build_scripts"
+doc = false
diff --git a/remoteauth/deny.toml b/remoteauth/deny.toml
index 7f0eda7..6b847fa 100644
--- a/remoteauth/deny.toml
+++ b/remoteauth/deny.toml
@@ -57,7 +57,8 @@
"ISC",
"Unicode-DFS-2016",
"OpenSSL",
- "Unlicense"
+ "Unlicense",
+ "Unicode-3.0",
]
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the
@@ -168,4 +169,4 @@
# if not specified. If it is specified but empty, no registries are allowed.
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
# List of URLs for allowed Git repositories
-allow-git = []
\ No newline at end of file
+allow-git = []
diff --git a/remoteauth/remote_auth_protocol/src/remote_auth_service.rs b/remoteauth/remote_auth_protocol/src/remote_auth_service.rs
index 5da33c7..d109323 100644
--- a/remoteauth/remote_auth_protocol/src/remote_auth_service.rs
+++ b/remoteauth/remote_auth_protocol/src/remote_auth_service.rs
@@ -64,7 +64,7 @@
}
}
-impl<'a, T: DeviceDiscoveryListener + PartialEq> Default for RemoteAuthService<'a, T> {
+impl<T: DeviceDiscoveryListener + PartialEq> Default for RemoteAuthService<'_, T> {
fn default() -> Self {
Self::new()
}
diff --git a/remoteauth/rust-toolchain.toml b/remoteauth/rust-toolchain.toml
new file mode 100644
index 0000000..0193dee
--- /dev/null
+++ b/remoteauth/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "1.83.0"