diff --git a/Cargo.lock b/Cargo.lock index 85c83b1..68048f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "1.0.0" @@ -61,6 +67,57 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clap" version = "4.6.1" @@ -107,6 +164,79 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[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-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "env_filter" version = "1.0.1" @@ -130,18 +260,85 @@ dependencies = [ "log", ] +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + [[package]] name = "jiff" version = "0.2.24" @@ -166,6 +363,24 @@ dependencies = [ "syn", ] +[[package]] +name = "js-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + [[package]] name = "log" version = "0.4.29" @@ -178,12 +393,67 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + [[package]] name = "once_cell_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -217,6 +487,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.12.3" @@ -251,10 +541,36 @@ name = "roto" version = "0.1.0" dependencies = [ "clap", + "criterion", "env_logger", "log", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -275,6 +591,25 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "strsim" version = "0.11.1" @@ -292,6 +627,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "unicode-ident" version = "1.0.24" @@ -304,6 +649,80 @@ 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 = "wasm-bindgen" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + [[package]] name = "windows-link" version = "0.2.1" @@ -318,3 +737,29 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index adb3dac..97bd00c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,10 @@ edition = "2024" clap = { version = "4", features = ["derive"] } log = "0.4" env_logger = "0.11" + +[dev-dependencies] +criterion = { version = "0.5", features = ["html_reports"] } + +[[bench]] +name = "hackers_bench" +harness = false diff --git a/README.md b/README.md index 8e761fe..bc69b69 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,14 @@ returning a slice of the bytes written. `CodeGeneratorResponse` to stdout. `protoc` then writes those `.rs` files to disk. The generated files are included directly in the crate that uses the protobuffers. +Sample usage: + +``` +protoc -Iproto/ proto/hackers.proto --plugin=./target/debug/protoc-gen-roto --roto_out=src/ +``` + +This will generate a file, src/hackers.rs. + ## Generated code For each protobuf message roto generates two types: @@ -117,6 +125,81 @@ using `FieldIterator` and records the byte offset of each field's tag. Subsequen call `ProtoAccessor::get_value_at(offset)` — no re-scanning. For repeated fields, the start and end offsets of the field range are recorded to bound iteration efficiently. +## Benchmarks + +Two benchmark suites share the same binary data files and the same four +measurement groups: + +| Group | What is timed | +| --------------- | ------------------------------------------------------- | +| `shallow_parse` | Become ready to read any field (one scan / full decode) | +| `deep_parse` | Walk the full tree: Campaign → Operations → Hackers | +| `field_access` | Read individual fields on an already-parsed message | +| `iterate` | Count top-level and nested repeated fields | + +### 1 — Generate the shared data files (do this once) + +Data files are written to `data/bench/`. + +```sh +cargo run --release --bin gen_bench_data -- --preset tiny +cargo run --release --bin gen_bench_data -- --preset small +cargo run --release --bin gen_bench_data -- --preset medium +cargo run --release --bin gen_bench_data -- --preset large +``` + +For even larger inputs use `--preset huge` (~500 MB) or set the knobs +directly: + +```sh +# ~50 MB: 500 operations × 100 KB stolen_data each +cargo run --release --bin gen_bench_data -- --ops 500 --stolen-kb 100 --output data/bench/50mb.pb +``` + +### 2 — Rust benchmark (criterion) + +```sh +cargo bench --bench hackers_bench +``` + +HTML reports are written to `target/criterion/`. Run a single group: + +```sh +cargo bench --bench hackers_bench -- shallow_parse +``` + +### 3 — C / upb benchmark + +Requires protobuf ≥ 21 with `protoc-gen-upb` (ships with modern `protoc`). + +```sh +cd upb_test +make # compiles hackers_bench from the pre-generated upb files +./hackers_bench +``` + +To regenerate the upb C files from `proto/hackers.proto`: + +```sh +cd upb_test && make regen +``` + +### Interpreting the comparison + +The two libraries have fundamentally different models: + +- **roto `shallow_parse`** does one linear scan recording byte offsets — no + allocation, no field decoding. Subsequent field reads decode on demand at + the stored offset. +- **upb `Campaign_parse`** fully decodes the entire message tree into + arena-allocated structs upfront. Subsequent field reads are direct struct + member lookups (~1 ns). + +The result: roto's parse is faster and allocation-free; upb's field access +after parsing is faster. For workloads that read every field the costs +invert; for workloads that read a handful of fields from large messages roto +wins. + ## Literature https://protobuf.dev/programming-guides/encoding/ diff --git a/benches/hackers_bench.rs b/benches/hackers_bench.rs new file mode 100644 index 0000000..34fe054 --- /dev/null +++ b/benches/hackers_bench.rs @@ -0,0 +1,215 @@ +//! Benchmark suite for roto — themed after the 1995 film *Hackers*. +//! +//! Proto schema: `proto/hackers.proto` +//! Generated types: `src/hackers.rs` (via `protoc-gen-roto`) +//! +//! # Setup +//! +//! Generate the data files once before running benchmarks: +//! +//! ```sh +//! cargo run --release --bin gen_bench_data -- --preset tiny +//! cargo run --release --bin gen_bench_data -- --preset small +//! cargo run --release --bin gen_bench_data -- --preset medium +//! cargo run --release --bin gen_bench_data -- --preset large +//! ``` +//! +//! Then run: +//! +//! ```sh +//! cargo bench --bench hackers_bench +//! ``` +//! +//! Benchmark groups: +//! - `shallow_parse` — `Campaign::new(data)`, one scan of the whole blob +//! - `deep_parse` — Campaign → Operations → Hackers (a `::new()` per level) +//! - `field_access` — individual field reads on pre-parsed messages (O(1)) +//! - `iterate` — counting repeated fields at different nesting depths + +use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; +use roto::hackers::{Campaign, Hacker, Operation, Worm}; +use std::hint::black_box; + +// ============================================================================= +// Data loading +// ============================================================================= + +/// Load a pre-generated data file from `data/bench/.pb`. +/// Returns `None` (and prints a hint) if the file does not exist. +fn load(name: &str) -> Option> { + let path = format!("data/bench/{name}.pb"); + match std::fs::read(&path) { + Ok(data) => Some(data), + Err(_) => { + eprintln!( + "[skip] {path} not found — \ + run `cargo run --release --bin gen_bench_data -- --preset {name}` first" + ); + None + } + } +} + +// ============================================================================= +// Benchmarks +// ============================================================================= + +/// `Campaign::new()` — one linear scan to record field offsets, no allocation. +/// Throughput reported in MB/s so different sizes are directly comparable. +fn bench_shallow_parse(c: &mut Criterion) { + let cases = [ + ("tiny", load("tiny")), + ("small", load("small")), + ("medium", load("medium")), + ("large", load("large")), + ]; + + let mut group = c.benchmark_group("shallow_parse"); + for (label, maybe_data) in &cases { + let Some(data) = maybe_data else { continue }; + group.throughput(Throughput::Bytes(data.len() as u64)); + group.bench_with_input(BenchmarkId::new("Campaign::new", label), data, |b, data| { + b.iter(|| Campaign::new(black_box(data)).unwrap()) + }); + } + group.finish(); +} + +/// Walk every level of the tree: Campaign → Operations → Hackers. +/// Each `::new()` is an additional linear scan of that sub-message's bytes. +fn bench_deep_parse(c: &mut Criterion) { + let cases = [ + ("tiny", load("tiny")), + ("small", load("small")), + ("medium", load("medium")), + ]; + + let mut group = c.benchmark_group("deep_parse"); + for (label, maybe_data) in &cases { + let Some(data) = maybe_data else { continue }; + group.throughput(Throughput::Bytes(data.len() as u64)); + group.bench_with_input( + BenchmarkId::new("Campaign+Ops+Hackers", label), + data, + |b, data| { + b.iter(|| { + let campaign = Campaign::new(data).unwrap(); + let mut hacker_count = 0usize; + for op_res in campaign.operations() { + let (op_bytes, _) = op_res.unwrap(); + let op = Operation::new(op_bytes).unwrap(); + for crew_res in op.crew() { + let (hacker_bytes, _) = crew_res.unwrap(); + let hacker = Hacker::new(hacker_bytes).unwrap(); + let _ = black_box(hacker.handle().unwrap()); + hacker_count += 1; + } + } + black_box(hacker_count) + }) + }, + ); + } + group.finish(); +} + +/// O(1) field accesses on pre-parsed messages. +/// Measures only the decode step at a known offset — not the scan. +fn bench_field_access(c: &mut Criterion) { + let Some(data) = load("small") else { return }; + + let campaign = Campaign::new(&data).unwrap(); + let (op_bytes, _) = campaign.operations().next().unwrap().unwrap(); + let op = Operation::new(op_bytes).unwrap(); + let (hacker_bytes, _) = op.crew().next().unwrap().unwrap(); + let hacker = Hacker::new(hacker_bytes).unwrap(); + let worm = Worm::new(op.worm().unwrap()).unwrap(); + + let mut group = c.benchmark_group("field_access"); + + group.bench_function("campaign::name", |b| { + b.iter(|| black_box(campaign.name().unwrap())) + }); + group.bench_function("campaign::total_bytes_stolen", |b| { + b.iter(|| black_box(campaign.total_bytes_stolen().unwrap())) + }); + group.bench_function("operation::codename", |b| { + b.iter(|| black_box(op.codename().unwrap())) + }); + group.bench_function("operation::timestamp", |b| { + b.iter(|| black_box(op.timestamp().unwrap())) + }); + group.bench_function("operation::successful", |b| { + b.iter(|| black_box(op.successful().unwrap())) + }); + group.bench_function("hacker::handle", |b| { + b.iter(|| black_box(hacker.handle().unwrap())) + }); + group.bench_function("hacker::skill_level (f32)", |b| { + b.iter(|| black_box(hacker.skill_level().unwrap())) + }); + group.bench_function("hacker::is_elite (bool)", |b| { + b.iter(|| black_box(hacker.is_elite().unwrap())) + }); + group.bench_function("worm::polymorphic (bool)", |b| { + b.iter(|| black_box(worm.polymorphic().unwrap())) + }); + group.bench_function("worm::payload (bytes)", |b| { + b.iter(|| black_box(worm.payload().unwrap())) + }); + + group.finish(); +} + +/// Iterate repeated fields at different depths. +fn bench_iterate(c: &mut Criterion) { + let cases = [ + ("tiny", load("tiny")), + ("small", load("small")), + ("medium", load("medium")), + ]; + + let mut group = c.benchmark_group("iterate"); + for (label, maybe_data) in &cases { + let Some(data) = maybe_data else { continue }; + + // Top-level repeated field — walk Operation blobs, no inner parse. + group.bench_with_input( + BenchmarkId::new("count_operations", label), + data, + |b, data| { + b.iter(|| { + let campaign = Campaign::new(data).unwrap(); + black_box(campaign.operations().count()) + }) + }, + ); + + // Nested repeated field — parse each Operation to reach its crew. + group.bench_with_input( + BenchmarkId::new("count_all_crew", label), + data, + |b, data| { + b.iter(|| { + let campaign = Campaign::new(data).unwrap(); + let mut n = 0usize; + for op_res in campaign.operations() { + let (op_bytes, _) = op_res.unwrap(); + n += Operation::new(op_bytes).unwrap().crew().count(); + } + black_box(n) + }) + }, + ); + } + group.finish(); +} + +criterion_group!( + benches, + bench_shallow_parse, + bench_deep_parse, + bench_field_access, + bench_iterate +); +criterion_main!(benches); diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..f2e59b9 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1 @@ +bench/ diff --git a/proto/hackers.proto b/proto/hackers.proto new file mode 100644 index 0000000..f456b9c --- /dev/null +++ b/proto/hackers.proto @@ -0,0 +1,56 @@ +syntax = "proto3"; + +message Tool { + string name = 1; + string version = 2; + bytes payload = 3; + bool is_active = 4; + int32 exploit_count = 5; +} + +message Connection { + string host = 1; + int32 port = 2; + bool encrypted = 3; + int64 bandwidth_bps = 4; + bytes session_key = 5; +} + +message Hacker { + string handle = 1; + string real_name = 2; + int32 age = 3; + float skill_level = 4; // Fixed32 + bool is_elite = 5; + int64 crew_id = 6; + repeated string exploits = 7; + repeated Tool tools = 8; + Connection active_connection = 9; +} + +message Worm { + string name = 1; + int32 variant = 2; + int64 size_bytes = 3; + bytes payload = 4; + bool polymorphic = 5; + repeated string targets = 6; +} + +message Operation { + string codename = 1; + string target_corp = 2; + int64 timestamp = 3; + bool successful = 4; + bytes stolen_data = 5; + repeated Hacker crew = 6; + Worm worm = 7; + repeated string log_entries = 8; + int32 severity = 9; +} + +message Campaign { + string name = 1; + repeated Operation operations = 2; + int64 total_bytes_stolen = 3; +} diff --git a/src/bin/gen_bench_data.rs b/src/bin/gen_bench_data.rs new file mode 100644 index 0000000..ad0d558 --- /dev/null +++ b/src/bin/gen_bench_data.rs @@ -0,0 +1,477 @@ +//! Generates Hackers-themed benchmark proto binaries using the roto builder API. +//! +//! Run this once to create the data files that `hackers_bench` loads: +//! +//! ```sh +//! cargo run --release --bin gen_bench_data -- --preset tiny +//! cargo run --release --bin gen_bench_data -- --preset small +//! cargo run --release --bin gen_bench_data -- --preset medium +//! cargo run --release --bin gen_bench_data -- --preset large +//! cargo run --release --bin gen_bench_data -- --preset huge +//! +//! # Custom: ~50 MB — 500 ops × 100 KB stolen_data each +//! cargo run --release --bin gen_bench_data -- \ +//! --ops 500 --stolen-kb 100 --output data/bench/50mb.pb +//! ``` +//! +//! Files land in `data/bench/` by default. + +use clap::Parser; +use roto::hackers::{ + CampaignBuilder, ConnectionBuilder, HackerBuilder, OperationBuilder, ToolBuilder, WormBuilder, +}; +use std::io::{self, Write}; +use std::path::Path; + +// ============================================================================= +// CLI +// ============================================================================= + +#[derive(Parser)] +#[command( + name = "gen_bench_data", + about = "Generate Hackers-themed proto binaries for benchmarks" +)] +struct Args { + /// Output file. Defaults to data/bench/.pb when --preset is used. + #[arg(short, long)] + output: Option, + + /// Named size preset: tiny | small | medium | large | huge + #[arg(short, long)] + preset: Option, + + /// Number of Operation messages. + #[arg(long, default_value_t = 100)] + ops: usize, + + /// Kilobytes of random stolen_data padding per Operation. + #[arg(long, default_value_t = 0)] + stolen_kb: usize, + + /// Hacker crew members per Operation. + #[arg(long, default_value_t = 3)] + crew: usize, + + /// RNG seed. + #[arg(long, default_value_t = 42)] + seed: u64, +} + +// ============================================================================= +// Minimal xorshift64 RNG +// ============================================================================= + +struct Rng(u64); + +impl Rng { + fn new(seed: u64) -> Self { + Self(if seed == 0 { 0xdeadbeef_cafebabe } else { seed }) + } + fn next(&mut self) -> u64 { + self.0 ^= self.0 << 13; + self.0 ^= self.0 >> 7; + self.0 ^= self.0 << 17; + self.0 + } + fn below(&mut self, n: usize) -> usize { + (self.next() as usize) % n + } + fn range(&mut self, lo: u64, hi: u64) -> u64 { + lo + self.next() % (hi - lo) + } + fn bool(&mut self) -> bool { + self.next() & 1 == 0 + } + fn pick<'a, T>(&mut self, s: &'a [T]) -> &'a T { + &s[self.below(s.len())] + } + fn bytes(&mut self, n: usize) -> Vec { + (0..n).map(|_| self.next() as u8).collect() + } +} + +// ============================================================================= +// Flavour text +// ============================================================================= + +const HANDLES: &[&str] = &[ + "Zero Cool", + "Acid Burn", + "Phantom Phreak", + "Cereal Killer", + "Lord Nikon", + "The Plague", + "Crash Override", +]; +const REAL_NAMES: &[&str] = &[ + "Dade Murphy", + "Kate Libby", + "Richard Gill", + "Emmanuel Goldstein", + "Paul Cook", + "Eugene Belford", +]; +const EXPLOITS: &[&str] = &[ + "buffer overflow", + "stack smash", + "heap spray", + "race condition", + "SQL injection", + "CSRF", + "XSS", + "RCE", + "privesc", + "kernel panic", +]; +const TOOL_NAMES: &[&str] = &[ + "nmap", + "metasploit", + "netcat", + "tcpdump", + "Wireshark", + "sqlmap", + "Burp Suite", + "hashcat", + "john", +]; +const CORPS: &[&str] = &[ + "ELLINGSON MINERAL", + "Cyberdelia", + "The Gibson", + "Prism BBS", + "Elite BBS", + "CRT Systems", +]; +const LOG_LINES: &[&str] = &[ + "Hack the planet!", + "Mess with the best, die like the rest.", + "I'm in.", + "They're tracing us.", + "It's a Unix system! I know this!", + "You are elite.", + "Garbage file accessed.", +]; +const WORM_TARGETS: &[&str] = &[ + "ELLINGSON MINERAL", + "Cyberdelia", + "The Gibson", + "Prism", + "CRT BBS", +]; +const OP_NAMES: &[&str] = &[ + "OPERATION HACK THE PLANET", + "GIBSON BREACH", + "ELLINGSON STING", + "WORM UNLEASHED", + "PHANTOM ACCESS", + "DA VINCI", +]; + +// ============================================================================= +// Message builders using the generated roto::hackers API +// ============================================================================= + +fn gen_tool(rng: &mut Rng) -> Vec { + let payload_n = rng.range(8, 64) as usize; + let payload = rng.bytes(payload_n); + let version = format!("{}.{}", rng.range(1, 9), rng.range(0, 99)); + let mut buf = vec![0u8; 512]; + ToolBuilder::builder(&mut buf) + .name(*rng.pick(TOOL_NAMES)) + .unwrap() + .version(&version) + .unwrap() + .payload(&payload) + .unwrap() + .is_active(rng.bool() as u64) + .unwrap() + .exploit_count(rng.range(0, 50) as i32) + .unwrap() + .finish() + .unwrap() + .to_vec() +} + +fn gen_connection(rng: &mut Rng) -> Vec { + let host = format!("192.168.{}.{}", rng.range(1, 254), rng.range(1, 254)); + let session_key = rng.bytes(32); + let mut buf = vec![0u8; 256]; + ConnectionBuilder::builder(&mut buf) + .host(&host) + .unwrap() + .port(rng.range(1024, 65535) as i32) + .unwrap() + .encrypted(rng.bool() as u64) + .unwrap() + .bandwidth_bps(rng.range(1200, 1_000_000_000)) + .unwrap() + .session_key(&session_key) + .unwrap() + .finish() + .unwrap() + .to_vec() +} + +fn gen_hacker(rng: &mut Rng) -> Vec { + let tools: Vec> = (0..rng.range(1, 4)).map(|_| gen_tool(rng)).collect(); + let connection = gen_connection(rng); + // Float (skill_level) is written as raw bytes by the generated builder + let skill_bits = (rng.range(10, 100) as f32 / 10.0).to_bits().to_le_bytes(); + let handle = *rng.pick(HANDLES); + let real_name = *rng.pick(REAL_NAMES); + let n_exploits = rng.range(2, 5) as usize; + let exploits: Vec<&str> = (0..n_exploits).map(|_| *rng.pick(EXPLOITS)).collect(); + let crew_id = rng.next(); + + let mut buf = vec![0u8; 8 * 1024]; + let mut b = HackerBuilder::builder(&mut buf) + .handle(handle) + .unwrap() + .real_name(real_name) + .unwrap() + .age(rng.range(16, 35) as i32) + .unwrap() + .skill_level(&skill_bits) + .unwrap() + .is_elite(rng.bool() as u64) + .unwrap() + .crew_id(crew_id) + .unwrap(); + for e in &exploits { + b = b.exploits(e).unwrap(); + } + for t in &tools { + b = b.tools(t).unwrap(); + } + b.active_connection(&connection) + .unwrap() + .finish() + .unwrap() + .to_vec() +} + +fn gen_worm(rng: &mut Rng) -> Vec { + let payload = rng.bytes(64); + let name = format!("da_vinci.{}", rng.range(1, 99)); + let n_targets = rng.range(1, 4) as usize; + let targets: Vec<&str> = (0..n_targets).map(|_| *rng.pick(WORM_TARGETS)).collect(); + + let mut buf = vec![0u8; 1024]; + let mut b = WormBuilder::builder(&mut buf) + .name(&name) + .unwrap() + .variant(rng.range(1, 5) as i32) + .unwrap() + .size_bytes(rng.range(1024, 10_000_000)) + .unwrap() + .payload(&payload) + .unwrap() + .polymorphic(rng.bool() as u64) + .unwrap(); + for t in &targets { + b = b.targets(t).unwrap(); + } + b.finish().unwrap().to_vec() +} + +fn gen_operation(rng: &mut Rng, crew_count: usize, stolen_bytes: usize) -> Vec { + let crew: Vec> = (0..crew_count).map(|_| gen_hacker(rng)).collect(); + let worm = gen_worm(rng); + let stolen = if stolen_bytes > 0 { + rng.bytes(stolen_bytes) + } else { + vec![] + }; + let codename = *rng.pick(OP_NAMES); + let corp = *rng.pick(CORPS); + let ts = 810_000_000u64 + rng.range(0, 10_000_000); + let n_logs = rng.range(2, 6) as usize; + let logs: Vec<&str> = (0..n_logs).map(|_| *rng.pick(LOG_LINES)).collect(); + + // Operation buffer: crew + worm + stolen_data + small overhead + let crew_size: usize = crew.iter().map(|h| h.len() + 5).sum(); + let buf_size = crew_size + worm.len() + stolen_bytes + 1024; + let mut buf = vec![0u8; buf_size]; + + let mut b = OperationBuilder::builder(&mut buf) + .codename(codename) + .unwrap() + .target_corp(corp) + .unwrap() + .timestamp(ts) + .unwrap() + .successful(rng.bool() as u64) + .unwrap(); + if stolen_bytes > 0 { + b = b.stolen_data(&stolen).unwrap(); + } + for h in &crew { + b = b.crew(h).unwrap(); + } + b = b.worm(&worm).unwrap(); + for l in &logs { + b = b.log_entries(l).unwrap(); + } + b.severity(rng.range(1, 10) as i32) + .unwrap() + .finish() + .unwrap() + .to_vec() +} + +fn gen_campaign( + rng: &mut Rng, + op_count: usize, + crew_per_op: usize, + stolen_bytes: usize, +) -> Vec { + let ops: Vec> = (0..op_count) + .map(|i| { + if i > 0 && i % 100 == 0 { + eprintln!( + " {i}/{op_count} operations ({:.1} MB in ops so far)…", + i * (stolen_bytes + 10_000) / 1_000_000 + ); + } + gen_operation(rng, crew_per_op, stolen_bytes) + }) + .collect(); + + // Pre-compute campaign buffer size: for each op the wire encoding is + // tag(1B) + varint_length(1-5B) + op_bytes + let ops_wire_size: usize = ops + .iter() + .map(|o| 1 + varint_len(o.len() as u64) + o.len()) + .sum(); + let mut buf = vec![0u8; ops_wire_size + 64]; + + let mut b = CampaignBuilder::builder(&mut buf) + .name("HACK THE PLANET CAMPAIGN") + .unwrap(); + for op in &ops { + b = b.operations(op).unwrap(); + } + b.total_bytes_stolen((stolen_bytes * op_count) as u64) + .unwrap() + .finish() + .unwrap() + .to_vec() +} + +/// Number of bytes needed to encode `v` as a varint. +fn varint_len(mut v: u64) -> usize { + let mut n = 1usize; + while v >= 128 { + v >>= 7; + n += 1; + } + n +} + +// ============================================================================= +// Preset table +// ============================================================================= + +struct Preset { + ops: usize, + crew: usize, + stolen_kb: usize, +} + +fn resolve(name: &str) -> Option { + match name { + // ops crew stolen_kb approx size + "tiny" => Some(Preset { + ops: 1, + crew: 1, + stolen_kb: 0, + }), // ~400 B + "small" => Some(Preset { + ops: 20, + crew: 3, + stolen_kb: 0, + }), // ~25 KB + "medium" => Some(Preset { + ops: 2_000, + crew: 3, + stolen_kb: 0, + }), // ~2 MB + "large" => Some(Preset { + ops: 200, + crew: 3, + stolen_kb: 500, + }), // ~100 MB + "huge" => Some(Preset { + ops: 1_000, + crew: 3, + stolen_kb: 500, + }), // ~500 MB + _ => None, + } +} + +// ============================================================================= +// main +// ============================================================================= + +fn main() { + let mut args = Args::parse(); + + if let Some(ref name) = args.preset.clone() { + match resolve(name) { + Some(p) => { + args.ops = p.ops; + args.crew = p.crew; + args.stolen_kb = p.stolen_kb; + } + None => { + eprintln!("Unknown preset '{name}'. Valid: tiny, small, medium, large, huge"); + std::process::exit(1); + } + } + } + + let stolen_bytes = args.stolen_kb * 1024; + let approx_mb = args.ops * (700 + args.crew * 300 + stolen_bytes) / 1_000_000; + eprintln!( + "Generating: {} ops × {} crew, {} KB stolen_data each → ~{} MB", + args.ops, args.crew, args.stolen_kb, approx_mb + ); + + let mut rng = Rng::new(args.seed); + let data = gen_campaign(&mut rng, args.ops, args.crew, stolen_bytes); + + eprintln!( + "Generated {} bytes ({:.2} MB)", + data.len(), + data.len() as f64 / 1_000_000.0 + ); + + // Default output: data/bench/.pb, or stdout if no output and no preset + let out_path = args + .output + .clone() + .or_else(|| args.preset.as_ref().map(|p| format!("data/bench/{}.pb", p))); + + match out_path { + Some(ref path) => { + if let Some(parent) = Path::new(path).parent() { + if !parent.as_os_str().is_empty() { + std::fs::create_dir_all(parent).unwrap_or_else(|e| eprintln!("Warning: {e}")); + } + } + std::fs::write(path, &data).unwrap_or_else(|e| { + eprintln!("Error writing {path}: {e}"); + std::process::exit(1); + }); + eprintln!("Saved to {path}"); + } + None => { + io::stdout().write_all(&data).unwrap_or_else(|e| { + eprintln!("Error: {e}"); + std::process::exit(1); + }); + } + } +} diff --git a/src/generator.rs b/src/generator.rs index 968a205..b76e0b2 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -53,7 +53,7 @@ fn map_type_to_rust_accessor(field_type: i32, label: i32) -> (String, String) { ), // TYPE_DOUBLE 2 => ( "f32".to_string(), - "f32::from_le_bytes(bytes.try_into().map_err(|_| crate::RotoError::WireFormatViolation)?)".to_string(), + "Ok(f32::from_le_bytes(bytes.try_into().map_err(|_| crate::RotoError::WireFormatViolation)?))".to_string(), ), // TYPE_FLOAT 3 | 5 | 15 | 17 => ( "i32".to_string(), diff --git a/src/hackers.rs b/src/hackers.rs new file mode 100644 index 0000000..f71e43f --- /dev/null +++ b/src/hackers.rs @@ -0,0 +1,801 @@ +// @generated by protoc-gen-roto — do not edit + +use crate::{ProtoAccessor, ProtoBuilder, Result, RotoError, read_varint, RepeatedFieldIterator}; +use std::str; + + +pub struct Tool<'a> { + accessor: crate::ProtoAccessor<'a>, + name_offset: Option, + version_offset: Option, + payload_offset: Option, + is_active_offset: Option, + exploit_count_offset: Option, +} + +impl<'a> Tool<'a> { + pub fn new(data: &'a [u8]) -> crate::Result { + let accessor = crate::ProtoAccessor::new(data)?; + let mut name_offset = None; + let mut version_offset = None; + let mut payload_offset = None; + let mut is_active_offset = None; + let mut exploit_count_offset = None; + for item in accessor.fields() { + let (offset, tag, _) = item?; + if tag.field_number == 1 { name_offset = Some(offset); } + if tag.field_number == 2 { version_offset = Some(offset); } + if tag.field_number == 3 { payload_offset = Some(offset); } + if tag.field_number == 4 { is_active_offset = Some(offset); } + if tag.field_number == 5 { exploit_count_offset = Some(offset); } + } + + Ok(Self { + accessor, +name_offset, +version_offset, +payload_offset, +is_active_offset, +exploit_count_offset, + }) + } + + pub fn name(&self) -> crate::Result<&'a str> { + let offset = self.name_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn version(&self) -> crate::Result<&'a str> { + let offset = self.version_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn payload(&self) -> crate::Result<&'a [u8]> { + let offset = self.payload_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + Ok(bytes) + } + + pub fn is_active(&self) -> crate::Result { + let offset = self.is_active_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn exploit_count(&self) -> crate::Result { + let offset = self.exploit_count_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + +} + +pub struct ToolBuilder<'b> { + builder: crate::ProtoBuilder<'b>, +} + +impl<'b> ToolBuilder<'b> { + pub fn builder(buf: &mut [u8]) -> ToolBuilder<'_> { + ToolBuilder { + builder: crate::ProtoBuilder::new(buf), + } + } + + pub fn name(mut self, value: &str) -> crate::Result { + self.builder.write_string(1, value)?; + Ok(self) + } + + pub fn version(mut self, value: &str) -> crate::Result { + self.builder.write_string(2, value)?; + Ok(self) + } + + pub fn payload(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(3, value)?; + Ok(self) + } + + pub fn is_active(mut self, value: u64) -> crate::Result { + self.builder.write_varint(4, value)?; + Ok(self) + } + + pub fn exploit_count(mut self, value: i32) -> crate::Result { + self.builder.write_int32(5, value)?; + Ok(self) + } + + pub fn finish(self) -> crate::Result<&'b mut [u8]> { + self.builder.finish() + } +} + +pub struct Connection<'a> { + accessor: crate::ProtoAccessor<'a>, + host_offset: Option, + port_offset: Option, + encrypted_offset: Option, + bandwidth_bps_offset: Option, + session_key_offset: Option, +} + +impl<'a> Connection<'a> { + pub fn new(data: &'a [u8]) -> crate::Result { + let accessor = crate::ProtoAccessor::new(data)?; + let mut host_offset = None; + let mut port_offset = None; + let mut encrypted_offset = None; + let mut bandwidth_bps_offset = None; + let mut session_key_offset = None; + for item in accessor.fields() { + let (offset, tag, _) = item?; + if tag.field_number == 1 { host_offset = Some(offset); } + if tag.field_number == 2 { port_offset = Some(offset); } + if tag.field_number == 3 { encrypted_offset = Some(offset); } + if tag.field_number == 4 { bandwidth_bps_offset = Some(offset); } + if tag.field_number == 5 { session_key_offset = Some(offset); } + } + + Ok(Self { + accessor, +host_offset, +port_offset, +encrypted_offset, +bandwidth_bps_offset, +session_key_offset, + }) + } + + pub fn host(&self) -> crate::Result<&'a str> { + let offset = self.host_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn port(&self) -> crate::Result { + let offset = self.port_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn encrypted(&self) -> crate::Result { + let offset = self.encrypted_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn bandwidth_bps(&self) -> crate::Result { + let offset = self.bandwidth_bps_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn session_key(&self) -> crate::Result<&'a [u8]> { + let offset = self.session_key_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + Ok(bytes) + } + +} + +pub struct ConnectionBuilder<'b> { + builder: crate::ProtoBuilder<'b>, +} + +impl<'b> ConnectionBuilder<'b> { + pub fn builder(buf: &mut [u8]) -> ConnectionBuilder<'_> { + ConnectionBuilder { + builder: crate::ProtoBuilder::new(buf), + } + } + + pub fn host(mut self, value: &str) -> crate::Result { + self.builder.write_string(1, value)?; + Ok(self) + } + + pub fn port(mut self, value: i32) -> crate::Result { + self.builder.write_int32(2, value)?; + Ok(self) + } + + pub fn encrypted(mut self, value: u64) -> crate::Result { + self.builder.write_varint(3, value)?; + Ok(self) + } + + pub fn bandwidth_bps(mut self, value: u64) -> crate::Result { + self.builder.write_varint(4, value)?; + Ok(self) + } + + pub fn session_key(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(5, value)?; + Ok(self) + } + + pub fn finish(self) -> crate::Result<&'b mut [u8]> { + self.builder.finish() + } +} + +pub struct Hacker<'a> { + accessor: crate::ProtoAccessor<'a>, + handle_offset: Option, + real_name_offset: Option, + age_offset: Option, + skill_level_offset: Option, + is_elite_offset: Option, + crew_id_offset: Option, + exploits_start: Option, + exploits_end: Option, + tools_start: Option, + tools_end: Option, + active_connection_offset: Option, +} + +impl<'a> Hacker<'a> { + pub fn new(data: &'a [u8]) -> crate::Result { + let accessor = crate::ProtoAccessor::new(data)?; + let mut handle_offset = None; + let mut real_name_offset = None; + let mut age_offset = None; + let mut skill_level_offset = None; + let mut is_elite_offset = None; + let mut crew_id_offset = None; + let mut exploits_start = None; + let mut exploits_end = None; + let mut tools_start = None; + let mut tools_end = None; + let mut active_connection_offset = None; + for item in accessor.fields() { + let (offset, tag, _) = item?; + if tag.field_number == 1 { handle_offset = Some(offset); } + if tag.field_number == 2 { real_name_offset = Some(offset); } + if tag.field_number == 3 { age_offset = Some(offset); } + if tag.field_number == 4 { skill_level_offset = Some(offset); } + if tag.field_number == 5 { is_elite_offset = Some(offset); } + if tag.field_number == 6 { crew_id_offset = Some(offset); } + if tag.field_number == 7 { + if exploits_start.is_none() { exploits_start = Some(offset); } + exploits_end = Some(offset); + } + if tag.field_number == 8 { + if tools_start.is_none() { tools_start = Some(offset); } + tools_end = Some(offset); + } + if tag.field_number == 9 { active_connection_offset = Some(offset); } + } + + Ok(Self { + accessor, +handle_offset, +real_name_offset, +age_offset, +skill_level_offset, +is_elite_offset, +crew_id_offset, +exploits_start, exploits_end, +tools_start, tools_end, +active_connection_offset, + }) + } + + pub fn handle(&self) -> crate::Result<&'a str> { + let offset = self.handle_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn real_name(&self) -> crate::Result<&'a str> { + let offset = self.real_name_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn age(&self) -> crate::Result { + let offset = self.age_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn skill_level(&self) -> crate::Result { + let offset = self.skill_level_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + Ok(f32::from_le_bytes(bytes.try_into().map_err(|_| crate::RotoError::WireFormatViolation)?)) + } + + pub fn is_elite(&self) -> crate::Result { + let offset = self.is_elite_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn crew_id(&self) -> crate::Result { + let offset = self.crew_id_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn exploits(&self) -> crate::RepeatedFieldIterator<'a> { + match (self.exploits_start, self.exploits_end) { + (Some(start), Some(end)) => self.accessor.iter_repeated_range(7, start, end), + _ => self.accessor.iter_repeated(7), + } + } + + pub fn tools(&self) -> crate::RepeatedFieldIterator<'a> { + match (self.tools_start, self.tools_end) { + (Some(start), Some(end)) => self.accessor.iter_repeated_range(8, start, end), + _ => self.accessor.iter_repeated(8), + } + } + + pub fn active_connection(&self) -> crate::Result<&'a [u8]> { + let offset = self.active_connection_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + Ok(bytes) + } + +} + +pub struct HackerBuilder<'b> { + builder: crate::ProtoBuilder<'b>, +} + +impl<'b> HackerBuilder<'b> { + pub fn builder(buf: &mut [u8]) -> HackerBuilder<'_> { + HackerBuilder { + builder: crate::ProtoBuilder::new(buf), + } + } + + pub fn handle(mut self, value: &str) -> crate::Result { + self.builder.write_string(1, value)?; + Ok(self) + } + + pub fn real_name(mut self, value: &str) -> crate::Result { + self.builder.write_string(2, value)?; + Ok(self) + } + + pub fn age(mut self, value: i32) -> crate::Result { + self.builder.write_int32(3, value)?; + Ok(self) + } + + pub fn skill_level(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(4, value)?; + Ok(self) + } + + pub fn is_elite(mut self, value: u64) -> crate::Result { + self.builder.write_varint(5, value)?; + Ok(self) + } + + pub fn crew_id(mut self, value: u64) -> crate::Result { + self.builder.write_varint(6, value)?; + Ok(self) + } + + pub fn exploits(mut self, value: &str) -> crate::Result { + self.builder.write_string(7, value)?; + Ok(self) + } + + pub fn tools(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(8, value)?; + Ok(self) + } + + pub fn active_connection(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(9, value)?; + Ok(self) + } + + pub fn finish(self) -> crate::Result<&'b mut [u8]> { + self.builder.finish() + } +} + +pub struct Worm<'a> { + accessor: crate::ProtoAccessor<'a>, + name_offset: Option, + variant_offset: Option, + size_bytes_offset: Option, + payload_offset: Option, + polymorphic_offset: Option, + targets_start: Option, + targets_end: Option, +} + +impl<'a> Worm<'a> { + pub fn new(data: &'a [u8]) -> crate::Result { + let accessor = crate::ProtoAccessor::new(data)?; + let mut name_offset = None; + let mut variant_offset = None; + let mut size_bytes_offset = None; + let mut payload_offset = None; + let mut polymorphic_offset = None; + let mut targets_start = None; + let mut targets_end = None; + for item in accessor.fields() { + let (offset, tag, _) = item?; + if tag.field_number == 1 { name_offset = Some(offset); } + if tag.field_number == 2 { variant_offset = Some(offset); } + if tag.field_number == 3 { size_bytes_offset = Some(offset); } + if tag.field_number == 4 { payload_offset = Some(offset); } + if tag.field_number == 5 { polymorphic_offset = Some(offset); } + if tag.field_number == 6 { + if targets_start.is_none() { targets_start = Some(offset); } + targets_end = Some(offset); + } + } + + Ok(Self { + accessor, +name_offset, +variant_offset, +size_bytes_offset, +payload_offset, +polymorphic_offset, +targets_start, targets_end, + }) + } + + pub fn name(&self) -> crate::Result<&'a str> { + let offset = self.name_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn variant(&self) -> crate::Result { + let offset = self.variant_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn size_bytes(&self) -> crate::Result { + let offset = self.size_bytes_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn payload(&self) -> crate::Result<&'a [u8]> { + let offset = self.payload_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + Ok(bytes) + } + + pub fn polymorphic(&self) -> crate::Result { + let offset = self.polymorphic_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn targets(&self) -> crate::RepeatedFieldIterator<'a> { + match (self.targets_start, self.targets_end) { + (Some(start), Some(end)) => self.accessor.iter_repeated_range(6, start, end), + _ => self.accessor.iter_repeated(6), + } + } + +} + +pub struct WormBuilder<'b> { + builder: crate::ProtoBuilder<'b>, +} + +impl<'b> WormBuilder<'b> { + pub fn builder(buf: &mut [u8]) -> WormBuilder<'_> { + WormBuilder { + builder: crate::ProtoBuilder::new(buf), + } + } + + pub fn name(mut self, value: &str) -> crate::Result { + self.builder.write_string(1, value)?; + Ok(self) + } + + pub fn variant(mut self, value: i32) -> crate::Result { + self.builder.write_int32(2, value)?; + Ok(self) + } + + pub fn size_bytes(mut self, value: u64) -> crate::Result { + self.builder.write_varint(3, value)?; + Ok(self) + } + + pub fn payload(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(4, value)?; + Ok(self) + } + + pub fn polymorphic(mut self, value: u64) -> crate::Result { + self.builder.write_varint(5, value)?; + Ok(self) + } + + pub fn targets(mut self, value: &str) -> crate::Result { + self.builder.write_string(6, value)?; + Ok(self) + } + + pub fn finish(self) -> crate::Result<&'b mut [u8]> { + self.builder.finish() + } +} + +pub struct Operation<'a> { + accessor: crate::ProtoAccessor<'a>, + codename_offset: Option, + target_corp_offset: Option, + timestamp_offset: Option, + successful_offset: Option, + stolen_data_offset: Option, + crew_start: Option, + crew_end: Option, + worm_offset: Option, + log_entries_start: Option, + log_entries_end: Option, + severity_offset: Option, +} + +impl<'a> Operation<'a> { + pub fn new(data: &'a [u8]) -> crate::Result { + let accessor = crate::ProtoAccessor::new(data)?; + let mut codename_offset = None; + let mut target_corp_offset = None; + let mut timestamp_offset = None; + let mut successful_offset = None; + let mut stolen_data_offset = None; + let mut crew_start = None; + let mut crew_end = None; + let mut worm_offset = None; + let mut log_entries_start = None; + let mut log_entries_end = None; + let mut severity_offset = None; + for item in accessor.fields() { + let (offset, tag, _) = item?; + if tag.field_number == 1 { codename_offset = Some(offset); } + if tag.field_number == 2 { target_corp_offset = Some(offset); } + if tag.field_number == 3 { timestamp_offset = Some(offset); } + if tag.field_number == 4 { successful_offset = Some(offset); } + if tag.field_number == 5 { stolen_data_offset = Some(offset); } + if tag.field_number == 6 { + if crew_start.is_none() { crew_start = Some(offset); } + crew_end = Some(offset); + } + if tag.field_number == 7 { worm_offset = Some(offset); } + if tag.field_number == 8 { + if log_entries_start.is_none() { log_entries_start = Some(offset); } + log_entries_end = Some(offset); + } + if tag.field_number == 9 { severity_offset = Some(offset); } + } + + Ok(Self { + accessor, +codename_offset, +target_corp_offset, +timestamp_offset, +successful_offset, +stolen_data_offset, +crew_start, crew_end, +worm_offset, +log_entries_start, log_entries_end, +severity_offset, + }) + } + + pub fn codename(&self) -> crate::Result<&'a str> { + let offset = self.codename_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn target_corp(&self) -> crate::Result<&'a str> { + let offset = self.target_corp_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn timestamp(&self) -> crate::Result { + let offset = self.timestamp_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn successful(&self) -> crate::Result { + let offset = self.successful_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v != 0).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn stolen_data(&self) -> crate::Result<&'a [u8]> { + let offset = self.stolen_data_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + Ok(bytes) + } + + pub fn crew(&self) -> crate::RepeatedFieldIterator<'a> { + match (self.crew_start, self.crew_end) { + (Some(start), Some(end)) => self.accessor.iter_repeated_range(6, start, end), + _ => self.accessor.iter_repeated(6), + } + } + + pub fn worm(&self) -> crate::Result<&'a [u8]> { + let offset = self.worm_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + Ok(bytes) + } + + pub fn log_entries(&self) -> crate::RepeatedFieldIterator<'a> { + match (self.log_entries_start, self.log_entries_end) { + (Some(start), Some(end)) => self.accessor.iter_repeated_range(8, start, end), + _ => self.accessor.iter_repeated(8), + } + } + + pub fn severity(&self) -> crate::Result { + let offset = self.severity_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + +} + +pub struct OperationBuilder<'b> { + builder: crate::ProtoBuilder<'b>, +} + +impl<'b> OperationBuilder<'b> { + pub fn builder(buf: &mut [u8]) -> OperationBuilder<'_> { + OperationBuilder { + builder: crate::ProtoBuilder::new(buf), + } + } + + pub fn codename(mut self, value: &str) -> crate::Result { + self.builder.write_string(1, value)?; + Ok(self) + } + + pub fn target_corp(mut self, value: &str) -> crate::Result { + self.builder.write_string(2, value)?; + Ok(self) + } + + pub fn timestamp(mut self, value: u64) -> crate::Result { + self.builder.write_varint(3, value)?; + Ok(self) + } + + pub fn successful(mut self, value: u64) -> crate::Result { + self.builder.write_varint(4, value)?; + Ok(self) + } + + pub fn stolen_data(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(5, value)?; + Ok(self) + } + + pub fn crew(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(6, value)?; + Ok(self) + } + + pub fn worm(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(7, value)?; + Ok(self) + } + + pub fn log_entries(mut self, value: &str) -> crate::Result { + self.builder.write_string(8, value)?; + Ok(self) + } + + pub fn severity(mut self, value: i32) -> crate::Result { + self.builder.write_int32(9, value)?; + Ok(self) + } + + pub fn finish(self) -> crate::Result<&'b mut [u8]> { + self.builder.finish() + } +} + +pub struct Campaign<'a> { + accessor: crate::ProtoAccessor<'a>, + name_offset: Option, + operations_start: Option, + operations_end: Option, + total_bytes_stolen_offset: Option, +} + +impl<'a> Campaign<'a> { + pub fn new(data: &'a [u8]) -> crate::Result { + let accessor = crate::ProtoAccessor::new(data)?; + let mut name_offset = None; + let mut operations_start = None; + let mut operations_end = None; + let mut total_bytes_stolen_offset = None; + for item in accessor.fields() { + let (offset, tag, _) = item?; + if tag.field_number == 1 { name_offset = Some(offset); } + if tag.field_number == 2 { + if operations_start.is_none() { operations_start = Some(offset); } + operations_end = Some(offset); + } + if tag.field_number == 3 { total_bytes_stolen_offset = Some(offset); } + } + + Ok(Self { + accessor, +name_offset, +operations_start, operations_end, +total_bytes_stolen_offset, + }) + } + + pub fn name(&self) -> crate::Result<&'a str> { + let offset = self.name_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + str::from_utf8(bytes).map_err(|_| crate::RotoError::WireFormatViolation) + } + + pub fn operations(&self) -> crate::RepeatedFieldIterator<'a> { + match (self.operations_start, self.operations_end) { + (Some(start), Some(end)) => self.accessor.iter_repeated_range(2, start, end), + _ => self.accessor.iter_repeated(2), + } + } + + pub fn total_bytes_stolen(&self) -> crate::Result { + let offset = self.total_bytes_stolen_offset.ok_or(crate::RotoError::FieldNotFound)?; + let (bytes, _) = self.accessor.get_value_at(offset)?; + crate::read_varint(bytes).map(|(v, _)| v as i32).map_err(|_| crate::RotoError::WireFormatViolation) + } + +} + +pub struct CampaignBuilder<'b> { + builder: crate::ProtoBuilder<'b>, +} + +impl<'b> CampaignBuilder<'b> { + pub fn builder(buf: &mut [u8]) -> CampaignBuilder<'_> { + CampaignBuilder { + builder: crate::ProtoBuilder::new(buf), + } + } + + pub fn name(mut self, value: &str) -> crate::Result { + self.builder.write_string(1, value)?; + Ok(self) + } + + pub fn operations(mut self, value: &[u8]) -> crate::Result { + self.builder.write_bytes(2, value)?; + Ok(self) + } + + pub fn total_bytes_stolen(mut self, value: u64) -> crate::Result { + self.builder.write_varint(3, value)?; + Ok(self) + } + + pub fn finish(self) -> crate::Result<&'b mut [u8]> { + self.builder.finish() + } +} + diff --git a/src/lib.rs b/src/lib.rs index ba9c2d2..92e1fff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod generator; pub mod google; +pub mod hackers; // Uncomment this to check if the code compiles // #[path = "../proto/google/protobuf/descriptor.rs"] // pub mod descriptor; @@ -220,11 +221,19 @@ impl<'a> ProtoAccessor<'a> { } _ => (cursor_after_tag, value_len), }; - Ok((&self.data[value_offset..value_offset + actual_value_len], tag.wire_type)) + Ok(( + &self.data[value_offset..value_offset + actual_value_len], + tag.wire_type, + )) } /// Returns an iterator that scans a specific range of the buffer for all occurrences of the specified field. - pub fn iter_repeated_range(&self, field_number: u32, start: usize, end: usize) -> RepeatedFieldIterator<'a> { + pub fn iter_repeated_range( + &self, + field_number: u32, + start: usize, + end: usize, + ) -> RepeatedFieldIterator<'a> { RepeatedFieldIterator::new_range(self.data, field_number, start, end) } } @@ -280,7 +289,11 @@ impl<'a> Iterator for FieldIterator<'a> { self.cursor = cursor_after_tag + value_len; - Some(Ok((self.cursor - tag_len - value_len, tag, &self.data[value_offset..value_offset + actual_value_len]))) + Some(Ok(( + self.cursor - tag_len - value_len, + tag, + &self.data[value_offset..value_offset + actual_value_len], + ))) } } @@ -293,10 +306,7 @@ pub struct RepeatedFieldIterator<'a> { impl<'a> RepeatedFieldIterator<'a> { pub fn new(data: &'a [u8], field_number: u32) -> Self { Self { - iterator: FieldIterator { - data, - cursor: 0, - }, + iterator: FieldIterator { data, cursor: 0 }, field_number, end_offset: None, } @@ -491,7 +501,8 @@ mod tests { } assert_eq!(i32_vals, vec![1, 2, 3, 4, 5]); - let repeated_strings: Vec<_> = acc.iter_repeated(18) + let repeated_strings: Vec<_> = acc + .iter_repeated(18) .map(|r| { let (val, _) = r.expect("Failed to decode repeated string"); std::str::from_utf8(val).expect("Invalid utf8") @@ -499,7 +510,8 @@ mod tests { .collect(); assert_eq!(repeated_strings, vec!["one", "two", "three"]); - let repeated_nested: Vec<_> = acc.iter_repeated(19) + let repeated_nested: Vec<_> = acc + .iter_repeated(19) .map(|r| { let (val, _) = r.expect("Failed to decode repeated nested"); let nested_acc = ProtoAccessor::new(val).unwrap(); @@ -519,7 +531,8 @@ mod tests { assert_eq!(id, 200); // Validate that fields appear in the expected relative order - let field_numbers: Vec = acc.fields() + let field_numbers: Vec = acc + .fields() .map(|r| r.expect("Failed to decode field").1.field_number) .collect(); @@ -528,7 +541,12 @@ mod tests { let mut found_count = 0; for &f in &field_numbers { if essential_fields.contains(&f) { - assert!(f >= last_field, "Fields appeared out of order: {} came after {}", f, last_field); + assert!( + f >= last_field, + "Fields appeared out of order: {} came after {}", + f, + last_field + ); last_field = f; found_count += 1; } diff --git a/upb_test/.gitignore b/upb_test/.gitignore new file mode 100644 index 0000000..fad439d --- /dev/null +++ b/upb_test/.gitignore @@ -0,0 +1,2 @@ +test +hackers_bench diff --git a/upb_test/Makefile b/upb_test/Makefile new file mode 100644 index 0000000..0beb77c --- /dev/null +++ b/upb_test/Makefile @@ -0,0 +1,22 @@ +CC = cc +CFLAGS = -O2 -std=c11 -D_POSIX_C_SOURCE=199309L -I. -I/usr/include -Wall -Wextra +LDFLAGS = -lupb -lutf8_range + +SRCS = hackers_bench.c hackers.upb.c hackers.upb_minitable.c +TARGET = hackers_bench + +.PHONY: all clean regen + +all: $(TARGET) + +$(TARGET): $(SRCS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +# Re-generate upb files from proto/hackers.proto +regen: + protoc -I ../proto ../proto/hackers.proto \ + --upb_out=. \ + --upb_minitable_out=. + +clean: + rm -f $(TARGET) diff --git a/upb_test/hackers.upb.c b/upb_test/hackers.upb.c new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/upb_test/hackers.upb.c @@ -0,0 +1 @@ + diff --git a/upb_test/hackers.upb.h b/upb_test/hackers.upb.h new file mode 100644 index 0000000..50db7eb --- /dev/null +++ b/upb_test/hackers.upb.h @@ -0,0 +1,1172 @@ +/* This file was generated by upb_generator from the input file: + * + * hackers.proto + * + * Do not edit -- your changes will be discarded when the file is + * regenerated. + * NO CHECKED-IN PROTOBUF GENCODE */ + +#ifndef HACKERS_PROTO_UPB_H__UPB_H_ +#define HACKERS_PROTO_UPB_H__UPB_H_ + +#include "upb/generated_code_support.h" + +#include "hackers.upb_minitable.h" + + +// Must be last. +#include "upb/port/def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Tool { upb_Message UPB_PRIVATE(base); } Tool; +typedef struct Connection { upb_Message UPB_PRIVATE(base); } Connection; +typedef struct Hacker { upb_Message UPB_PRIVATE(base); } Hacker; +typedef struct Worm { upb_Message UPB_PRIVATE(base); } Worm; +typedef struct Operation { upb_Message UPB_PRIVATE(base); } Operation; +typedef struct Campaign { upb_Message UPB_PRIVATE(base); } Campaign; + + + +/* Tool */ + +UPB_INLINE Tool* Tool_new(upb_Arena* arena) { + return (Tool*)_upb_Message_New(&Tool_msg_init, arena); +} +UPB_INLINE Tool* Tool_parse(const char* buf, size_t size, upb_Arena* arena) { + Tool* ret = Tool_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Tool_msg_init, NULL, 0, arena) != + kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE Tool* Tool_parse_ex(const char* buf, size_t size, + const upb_ExtensionRegistry* extreg, + int options, upb_Arena* arena) { + Tool* ret = Tool_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Tool_msg_init, extreg, options, + arena) != kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE char* Tool_serialize(const Tool* msg, upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Tool_msg_init, 0, arena, &ptr, len); + return ptr; +} +UPB_INLINE char* Tool_serialize_ex(const Tool* msg, int options, + upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Tool_msg_init, options, arena, &ptr, len); + return ptr; +} +UPB_INLINE void Tool_clear_name(Tool* msg) { + const upb_MiniTableField field = {1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Tool_name(const Tool* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Tool_clear_version(Tool* msg) { + const upb_MiniTableField field = {2, UPB_SIZE(24, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Tool_version(const Tool* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {2, UPB_SIZE(24, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Tool_clear_payload(Tool* msg) { + const upb_MiniTableField field = {3, UPB_SIZE(32, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Tool_payload(const Tool* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {3, UPB_SIZE(32, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Tool_clear_is_active(Tool* msg) { + const upb_MiniTableField field = {4, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE bool Tool_is_active(const Tool* msg) { + bool default_val = false; + bool ret; + const upb_MiniTableField field = {4, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Tool_clear_exploit_count(Tool* msg) { + const upb_MiniTableField field = {5, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int32_t Tool_exploit_count(const Tool* msg) { + int32_t default_val = (int32_t)0; + int32_t ret; + const upb_MiniTableField field = {5, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} + +UPB_INLINE void Tool_set_name(Tool *msg, upb_StringView value) { + const upb_MiniTableField field = {1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Tool_set_version(Tool *msg, upb_StringView value) { + const upb_MiniTableField field = {2, UPB_SIZE(24, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Tool_set_payload(Tool *msg, upb_StringView value) { + const upb_MiniTableField field = {3, UPB_SIZE(32, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Tool_set_is_active(Tool *msg, bool value) { + const upb_MiniTableField field = {4, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Tool_set_exploit_count(Tool *msg, int32_t value) { + const upb_MiniTableField field = {5, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} + +/* Connection */ + +UPB_INLINE Connection* Connection_new(upb_Arena* arena) { + return (Connection*)_upb_Message_New(&Connection_msg_init, arena); +} +UPB_INLINE Connection* Connection_parse(const char* buf, size_t size, upb_Arena* arena) { + Connection* ret = Connection_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Connection_msg_init, NULL, 0, arena) != + kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE Connection* Connection_parse_ex(const char* buf, size_t size, + const upb_ExtensionRegistry* extreg, + int options, upb_Arena* arena) { + Connection* ret = Connection_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Connection_msg_init, extreg, options, + arena) != kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE char* Connection_serialize(const Connection* msg, upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Connection_msg_init, 0, arena, &ptr, len); + return ptr; +} +UPB_INLINE char* Connection_serialize_ex(const Connection* msg, int options, + upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Connection_msg_init, options, arena, &ptr, len); + return ptr; +} +UPB_INLINE void Connection_clear_host(Connection* msg) { + const upb_MiniTableField field = {1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Connection_host(const Connection* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Connection_clear_port(Connection* msg) { + const upb_MiniTableField field = {2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int32_t Connection_port(const Connection* msg) { + int32_t default_val = (int32_t)0; + int32_t ret; + const upb_MiniTableField field = {2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Connection_clear_encrypted(Connection* msg) { + const upb_MiniTableField field = {3, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE bool Connection_encrypted(const Connection* msg) { + bool default_val = false; + bool ret; + const upb_MiniTableField field = {3, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Connection_clear_bandwidth_bps(Connection* msg) { + const upb_MiniTableField field = {4, UPB_SIZE(32, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int64_t Connection_bandwidth_bps(const Connection* msg) { + int64_t default_val = (int64_t)0ll; + int64_t ret; + const upb_MiniTableField field = {4, UPB_SIZE(32, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Connection_clear_session_key(Connection* msg) { + const upb_MiniTableField field = {5, UPB_SIZE(24, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Connection_session_key(const Connection* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {5, UPB_SIZE(24, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} + +UPB_INLINE void Connection_set_host(Connection *msg, upb_StringView value) { + const upb_MiniTableField field = {1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Connection_set_port(Connection *msg, int32_t value) { + const upb_MiniTableField field = {2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Connection_set_encrypted(Connection *msg, bool value) { + const upb_MiniTableField field = {3, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Connection_set_bandwidth_bps(Connection *msg, int64_t value) { + const upb_MiniTableField field = {4, UPB_SIZE(32, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Connection_set_session_key(Connection *msg, upb_StringView value) { + const upb_MiniTableField field = {5, UPB_SIZE(24, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} + +/* Hacker */ + +UPB_INLINE Hacker* Hacker_new(upb_Arena* arena) { + return (Hacker*)_upb_Message_New(&Hacker_msg_init, arena); +} +UPB_INLINE Hacker* Hacker_parse(const char* buf, size_t size, upb_Arena* arena) { + Hacker* ret = Hacker_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Hacker_msg_init, NULL, 0, arena) != + kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE Hacker* Hacker_parse_ex(const char* buf, size_t size, + const upb_ExtensionRegistry* extreg, + int options, upb_Arena* arena) { + Hacker* ret = Hacker_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Hacker_msg_init, extreg, options, + arena) != kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE char* Hacker_serialize(const Hacker* msg, upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Hacker_msg_init, 0, arena, &ptr, len); + return ptr; +} +UPB_INLINE char* Hacker_serialize_ex(const Hacker* msg, int options, + upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Hacker_msg_init, options, arena, &ptr, len); + return ptr; +} +UPB_INLINE void Hacker_clear_handle(Hacker* msg) { + const upb_MiniTableField field = {1, UPB_SIZE(32, 24), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Hacker_handle(const Hacker* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {1, UPB_SIZE(32, 24), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Hacker_clear_real_name(Hacker* msg) { + const upb_MiniTableField field = {2, 40, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Hacker_real_name(const Hacker* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {2, 40, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Hacker_clear_age(Hacker* msg) { + const upb_MiniTableField field = {3, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int32_t Hacker_age(const Hacker* msg) { + int32_t default_val = (int32_t)0; + int32_t ret; + const upb_MiniTableField field = {3, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Hacker_clear_skill_level(Hacker* msg) { + const upb_MiniTableField field = {4, 16, 0, kUpb_NoSub, 2, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE float Hacker_skill_level(const Hacker* msg) { + float default_val = 0; + float ret; + const upb_MiniTableField field = {4, 16, 0, kUpb_NoSub, 2, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Hacker_clear_is_elite(Hacker* msg) { + const upb_MiniTableField field = {5, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE bool Hacker_is_elite(const Hacker* msg) { + bool default_val = false; + bool ret; + const upb_MiniTableField field = {5, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Hacker_clear_crew_id(Hacker* msg) { + const upb_MiniTableField field = {6, UPB_SIZE(48, 56), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int64_t Hacker_crew_id(const Hacker* msg) { + int64_t default_val = (int64_t)0ll; + int64_t ret; + const upb_MiniTableField field = {6, UPB_SIZE(48, 56), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Hacker_clear_exploits(Hacker* msg) { + const upb_MiniTableField field = {7, UPB_SIZE(20, 64), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView const* Hacker_exploits(const Hacker* msg, size_t* size) { + const upb_MiniTableField field = {7, UPB_SIZE(20, 64), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (upb_StringView const*)upb_Array_DataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE const upb_Array* _Hacker_exploits_upb_array(const Hacker* msg, size_t* size) { + const upb_MiniTableField field = {7, UPB_SIZE(20, 64), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE upb_Array* _Hacker_exploits_mutable_upb_array(Hacker* msg, size_t* size, upb_Arena* arena) { + const upb_MiniTableField field = {7, UPB_SIZE(20, 64), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Array* arr = upb_Message_GetOrCreateMutableArray(UPB_UPCAST(msg), + &field, arena); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE void Hacker_clear_tools(Hacker* msg) { + const upb_MiniTableField field = {8, UPB_SIZE(24, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE const Tool* const* Hacker_tools(const Hacker* msg, size_t* size) { + const upb_MiniTableField field = {8, UPB_SIZE(24, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Tool_msg_init); + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (const Tool* const*)upb_Array_DataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE const upb_Array* _Hacker_tools_upb_array(const Hacker* msg, size_t* size) { + const upb_MiniTableField field = {8, UPB_SIZE(24, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Tool_msg_init); + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE upb_Array* _Hacker_tools_mutable_upb_array(Hacker* msg, size_t* size, upb_Arena* arena) { + const upb_MiniTableField field = {8, UPB_SIZE(24, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Tool_msg_init); + upb_Array* arr = upb_Message_GetOrCreateMutableArray(UPB_UPCAST(msg), + &field, arena); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE void Hacker_clear_active_connection(Hacker* msg) { + const upb_MiniTableField field = {9, UPB_SIZE(28, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE const Connection* Hacker_active_connection(const Hacker* msg) { + const Connection* default_val = NULL; + const Connection* ret; + const upb_MiniTableField field = {9, UPB_SIZE(28, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Connection_msg_init); + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE bool Hacker_has_active_connection(const Hacker* msg) { + const upb_MiniTableField field = {9, UPB_SIZE(28, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + return upb_Message_HasBaseField(UPB_UPCAST(msg), &field); +} + +UPB_INLINE void Hacker_set_handle(Hacker *msg, upb_StringView value) { + const upb_MiniTableField field = {1, UPB_SIZE(32, 24), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Hacker_set_real_name(Hacker *msg, upb_StringView value) { + const upb_MiniTableField field = {2, 40, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Hacker_set_age(Hacker *msg, int32_t value) { + const upb_MiniTableField field = {3, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Hacker_set_skill_level(Hacker *msg, float value) { + const upb_MiniTableField field = {4, 16, 0, kUpb_NoSub, 2, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Hacker_set_is_elite(Hacker *msg, bool value) { + const upb_MiniTableField field = {5, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Hacker_set_crew_id(Hacker *msg, int64_t value) { + const upb_MiniTableField field = {6, UPB_SIZE(48, 56), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE upb_StringView* Hacker_mutable_exploits(Hacker* msg, size_t* size) { + upb_MiniTableField field = {7, UPB_SIZE(20, 64), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Array* arr = upb_Message_GetMutableArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (upb_StringView*)upb_Array_MutableDataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE upb_StringView* Hacker_resize_exploits(Hacker* msg, size_t size, upb_Arena* arena) { + upb_MiniTableField field = {7, UPB_SIZE(20, 64), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + return (upb_StringView*)upb_Message_ResizeArrayUninitialized(UPB_UPCAST(msg), + &field, size, arena); +} +UPB_INLINE bool Hacker_add_exploits(Hacker* msg, upb_StringView val, upb_Arena* arena) { + upb_MiniTableField field = {7, UPB_SIZE(20, 64), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Array* arr = upb_Message_GetOrCreateMutableArray( + UPB_UPCAST(msg), &field, arena); + if (!arr || !UPB_PRIVATE(_upb_Array_ResizeUninitialized)( + arr, arr->UPB_PRIVATE(size) + 1, arena)) { + return false; + } + UPB_PRIVATE(_upb_Array_Set) + (arr, arr->UPB_PRIVATE(size) - 1, &val, sizeof(val)); + return true; +} +UPB_INLINE Tool** Hacker_mutable_tools(Hacker* msg, size_t* size) { + upb_MiniTableField field = {8, UPB_SIZE(24, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Tool_msg_init); + upb_Array* arr = upb_Message_GetMutableArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (Tool**)upb_Array_MutableDataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE Tool** Hacker_resize_tools(Hacker* msg, size_t size, upb_Arena* arena) { + upb_MiniTableField field = {8, UPB_SIZE(24, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + return (Tool**)upb_Message_ResizeArrayUninitialized(UPB_UPCAST(msg), + &field, size, arena); +} +UPB_INLINE struct Tool* Hacker_add_tools(Hacker* msg, upb_Arena* arena) { + upb_MiniTableField field = {8, UPB_SIZE(24, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Tool_msg_init); + upb_Array* arr = upb_Message_GetOrCreateMutableArray( + UPB_UPCAST(msg), &field, arena); + if (!arr || !UPB_PRIVATE(_upb_Array_ResizeUninitialized)( + arr, arr->UPB_PRIVATE(size) + 1, arena)) { + return NULL; + } + struct Tool* sub = (struct Tool*)_upb_Message_New(&Tool_msg_init, arena); + if (!arr || !sub) return NULL; + UPB_PRIVATE(_upb_Array_Set) + (arr, arr->UPB_PRIVATE(size) - 1, &sub, sizeof(sub)); + return sub; +} +UPB_INLINE void Hacker_set_active_connection(Hacker *msg, Connection* value) { + const upb_MiniTableField field = {9, UPB_SIZE(28, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Connection_msg_init); + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE struct Connection* Hacker_mutable_active_connection(Hacker* msg, upb_Arena* arena) { + struct Connection* sub = (struct Connection*)Hacker_active_connection(msg); + if (sub == NULL) { + sub = (struct Connection*)_upb_Message_New(&Connection_msg_init, arena); + if (sub) Hacker_set_active_connection(msg, sub); + } + return sub; +} + +/* Worm */ + +UPB_INLINE Worm* Worm_new(upb_Arena* arena) { + return (Worm*)_upb_Message_New(&Worm_msg_init, arena); +} +UPB_INLINE Worm* Worm_parse(const char* buf, size_t size, upb_Arena* arena) { + Worm* ret = Worm_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Worm_msg_init, NULL, 0, arena) != + kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE Worm* Worm_parse_ex(const char* buf, size_t size, + const upb_ExtensionRegistry* extreg, + int options, upb_Arena* arena) { + Worm* ret = Worm_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Worm_msg_init, extreg, options, + arena) != kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE char* Worm_serialize(const Worm* msg, upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Worm_msg_init, 0, arena, &ptr, len); + return ptr; +} +UPB_INLINE char* Worm_serialize_ex(const Worm* msg, int options, + upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Worm_msg_init, options, arena, &ptr, len); + return ptr; +} +UPB_INLINE void Worm_clear_name(Worm* msg) { + const upb_MiniTableField field = {1, UPB_SIZE(20, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Worm_name(const Worm* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {1, UPB_SIZE(20, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Worm_clear_variant(Worm* msg) { + const upb_MiniTableField field = {2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int32_t Worm_variant(const Worm* msg) { + int32_t default_val = (int32_t)0; + int32_t ret; + const upb_MiniTableField field = {2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Worm_clear_size_bytes(Worm* msg) { + const upb_MiniTableField field = {3, UPB_SIZE(40, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int64_t Worm_size_bytes(const Worm* msg) { + int64_t default_val = (int64_t)0ll; + int64_t ret; + const upb_MiniTableField field = {3, UPB_SIZE(40, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Worm_clear_payload(Worm* msg) { + const upb_MiniTableField field = {4, UPB_SIZE(28, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Worm_payload(const Worm* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {4, UPB_SIZE(28, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Worm_clear_polymorphic(Worm* msg) { + const upb_MiniTableField field = {5, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE bool Worm_polymorphic(const Worm* msg) { + bool default_val = false; + bool ret; + const upb_MiniTableField field = {5, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Worm_clear_targets(Worm* msg) { + const upb_MiniTableField field = {6, UPB_SIZE(16, 56), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView const* Worm_targets(const Worm* msg, size_t* size) { + const upb_MiniTableField field = {6, UPB_SIZE(16, 56), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (upb_StringView const*)upb_Array_DataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE const upb_Array* _Worm_targets_upb_array(const Worm* msg, size_t* size) { + const upb_MiniTableField field = {6, UPB_SIZE(16, 56), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE upb_Array* _Worm_targets_mutable_upb_array(Worm* msg, size_t* size, upb_Arena* arena) { + const upb_MiniTableField field = {6, UPB_SIZE(16, 56), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Array* arr = upb_Message_GetOrCreateMutableArray(UPB_UPCAST(msg), + &field, arena); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} + +UPB_INLINE void Worm_set_name(Worm *msg, upb_StringView value) { + const upb_MiniTableField field = {1, UPB_SIZE(20, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Worm_set_variant(Worm *msg, int32_t value) { + const upb_MiniTableField field = {2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Worm_set_size_bytes(Worm *msg, int64_t value) { + const upb_MiniTableField field = {3, UPB_SIZE(40, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Worm_set_payload(Worm *msg, upb_StringView value) { + const upb_MiniTableField field = {4, UPB_SIZE(28, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Worm_set_polymorphic(Worm *msg, bool value) { + const upb_MiniTableField field = {5, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE upb_StringView* Worm_mutable_targets(Worm* msg, size_t* size) { + upb_MiniTableField field = {6, UPB_SIZE(16, 56), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Array* arr = upb_Message_GetMutableArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (upb_StringView*)upb_Array_MutableDataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE upb_StringView* Worm_resize_targets(Worm* msg, size_t size, upb_Arena* arena) { + upb_MiniTableField field = {6, UPB_SIZE(16, 56), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + return (upb_StringView*)upb_Message_ResizeArrayUninitialized(UPB_UPCAST(msg), + &field, size, arena); +} +UPB_INLINE bool Worm_add_targets(Worm* msg, upb_StringView val, upb_Arena* arena) { + upb_MiniTableField field = {6, UPB_SIZE(16, 56), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Array* arr = upb_Message_GetOrCreateMutableArray( + UPB_UPCAST(msg), &field, arena); + if (!arr || !UPB_PRIVATE(_upb_Array_ResizeUninitialized)( + arr, arr->UPB_PRIVATE(size) + 1, arena)) { + return false; + } + UPB_PRIVATE(_upb_Array_Set) + (arr, arr->UPB_PRIVATE(size) - 1, &val, sizeof(val)); + return true; +} + +/* Operation */ + +UPB_INLINE Operation* Operation_new(upb_Arena* arena) { + return (Operation*)_upb_Message_New(&Operation_msg_init, arena); +} +UPB_INLINE Operation* Operation_parse(const char* buf, size_t size, upb_Arena* arena) { + Operation* ret = Operation_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Operation_msg_init, NULL, 0, arena) != + kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE Operation* Operation_parse_ex(const char* buf, size_t size, + const upb_ExtensionRegistry* extreg, + int options, upb_Arena* arena) { + Operation* ret = Operation_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Operation_msg_init, extreg, options, + arena) != kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE char* Operation_serialize(const Operation* msg, upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Operation_msg_init, 0, arena, &ptr, len); + return ptr; +} +UPB_INLINE char* Operation_serialize_ex(const Operation* msg, int options, + upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Operation_msg_init, options, arena, &ptr, len); + return ptr; +} +UPB_INLINE void Operation_clear_codename(Operation* msg) { + const upb_MiniTableField field = {1, UPB_SIZE(28, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Operation_codename(const Operation* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {1, UPB_SIZE(28, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Operation_clear_target_corp(Operation* msg) { + const upb_MiniTableField field = {2, UPB_SIZE(36, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Operation_target_corp(const Operation* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {2, UPB_SIZE(36, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Operation_clear_timestamp(Operation* msg) { + const upb_MiniTableField field = {3, UPB_SIZE(56, 64), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int64_t Operation_timestamp(const Operation* msg) { + int64_t default_val = (int64_t)0ll; + int64_t ret; + const upb_MiniTableField field = {3, UPB_SIZE(56, 64), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Operation_clear_successful(Operation* msg) { + const upb_MiniTableField field = {4, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE bool Operation_successful(const Operation* msg) { + bool default_val = false; + bool ret; + const upb_MiniTableField field = {4, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Operation_clear_stolen_data(Operation* msg) { + const upb_MiniTableField field = {5, UPB_SIZE(44, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Operation_stolen_data(const Operation* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {5, UPB_SIZE(44, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Operation_clear_crew(Operation* msg) { + const upb_MiniTableField field = {6, UPB_SIZE(12, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE const Hacker* const* Operation_crew(const Operation* msg, size_t* size) { + const upb_MiniTableField field = {6, UPB_SIZE(12, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Hacker_msg_init); + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (const Hacker* const*)upb_Array_DataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE const upb_Array* _Operation_crew_upb_array(const Operation* msg, size_t* size) { + const upb_MiniTableField field = {6, UPB_SIZE(12, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Hacker_msg_init); + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE upb_Array* _Operation_crew_mutable_upb_array(Operation* msg, size_t* size, upb_Arena* arena) { + const upb_MiniTableField field = {6, UPB_SIZE(12, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Hacker_msg_init); + upb_Array* arr = upb_Message_GetOrCreateMutableArray(UPB_UPCAST(msg), + &field, arena); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE void Operation_clear_worm(Operation* msg) { + const upb_MiniTableField field = {7, UPB_SIZE(16, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE const Worm* Operation_worm(const Operation* msg) { + const Worm* default_val = NULL; + const Worm* ret; + const upb_MiniTableField field = {7, UPB_SIZE(16, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Worm_msg_init); + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE bool Operation_has_worm(const Operation* msg) { + const upb_MiniTableField field = {7, UPB_SIZE(16, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + return upb_Message_HasBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE void Operation_clear_log_entries(Operation* msg) { + const upb_MiniTableField field = {8, UPB_SIZE(20, 88), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView const* Operation_log_entries(const Operation* msg, size_t* size) { + const upb_MiniTableField field = {8, UPB_SIZE(20, 88), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (upb_StringView const*)upb_Array_DataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE const upb_Array* _Operation_log_entries_upb_array(const Operation* msg, size_t* size) { + const upb_MiniTableField field = {8, UPB_SIZE(20, 88), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE upb_Array* _Operation_log_entries_mutable_upb_array(Operation* msg, size_t* size, upb_Arena* arena) { + const upb_MiniTableField field = {8, UPB_SIZE(20, 88), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Array* arr = upb_Message_GetOrCreateMutableArray(UPB_UPCAST(msg), + &field, arena); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE void Operation_clear_severity(Operation* msg) { + const upb_MiniTableField field = {9, UPB_SIZE(24, 12), 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int32_t Operation_severity(const Operation* msg) { + int32_t default_val = (int32_t)0; + int32_t ret; + const upb_MiniTableField field = {9, UPB_SIZE(24, 12), 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} + +UPB_INLINE void Operation_set_codename(Operation *msg, upb_StringView value) { + const upb_MiniTableField field = {1, UPB_SIZE(28, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Operation_set_target_corp(Operation *msg, upb_StringView value) { + const upb_MiniTableField field = {2, UPB_SIZE(36, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Operation_set_timestamp(Operation *msg, int64_t value) { + const upb_MiniTableField field = {3, UPB_SIZE(56, 64), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Operation_set_successful(Operation *msg, bool value) { + const upb_MiniTableField field = {4, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE void Operation_set_stolen_data(Operation *msg, upb_StringView value) { + const upb_MiniTableField field = {5, UPB_SIZE(44, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE Hacker** Operation_mutable_crew(Operation* msg, size_t* size) { + upb_MiniTableField field = {6, UPB_SIZE(12, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Hacker_msg_init); + upb_Array* arr = upb_Message_GetMutableArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (Hacker**)upb_Array_MutableDataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE Hacker** Operation_resize_crew(Operation* msg, size_t size, upb_Arena* arena) { + upb_MiniTableField field = {6, UPB_SIZE(12, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + return (Hacker**)upb_Message_ResizeArrayUninitialized(UPB_UPCAST(msg), + &field, size, arena); +} +UPB_INLINE struct Hacker* Operation_add_crew(Operation* msg, upb_Arena* arena) { + upb_MiniTableField field = {6, UPB_SIZE(12, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Hacker_msg_init); + upb_Array* arr = upb_Message_GetOrCreateMutableArray( + UPB_UPCAST(msg), &field, arena); + if (!arr || !UPB_PRIVATE(_upb_Array_ResizeUninitialized)( + arr, arr->UPB_PRIVATE(size) + 1, arena)) { + return NULL; + } + struct Hacker* sub = (struct Hacker*)_upb_Message_New(&Hacker_msg_init, arena); + if (!arr || !sub) return NULL; + UPB_PRIVATE(_upb_Array_Set) + (arr, arr->UPB_PRIVATE(size) - 1, &sub, sizeof(sub)); + return sub; +} +UPB_INLINE void Operation_set_worm(Operation *msg, Worm* value) { + const upb_MiniTableField field = {7, UPB_SIZE(16, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Worm_msg_init); + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE struct Worm* Operation_mutable_worm(Operation* msg, upb_Arena* arena) { + struct Worm* sub = (struct Worm*)Operation_worm(msg); + if (sub == NULL) { + sub = (struct Worm*)_upb_Message_New(&Worm_msg_init, arena); + if (sub) Operation_set_worm(msg, sub); + } + return sub; +} +UPB_INLINE upb_StringView* Operation_mutable_log_entries(Operation* msg, size_t* size) { + upb_MiniTableField field = {8, UPB_SIZE(20, 88), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Array* arr = upb_Message_GetMutableArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (upb_StringView*)upb_Array_MutableDataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE upb_StringView* Operation_resize_log_entries(Operation* msg, size_t size, upb_Arena* arena) { + upb_MiniTableField field = {8, UPB_SIZE(20, 88), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + return (upb_StringView*)upb_Message_ResizeArrayUninitialized(UPB_UPCAST(msg), + &field, size, arena); +} +UPB_INLINE bool Operation_add_log_entries(Operation* msg, upb_StringView val, upb_Arena* arena) { + upb_MiniTableField field = {8, UPB_SIZE(20, 88), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Array* arr = upb_Message_GetOrCreateMutableArray( + UPB_UPCAST(msg), &field, arena); + if (!arr || !UPB_PRIVATE(_upb_Array_ResizeUninitialized)( + arr, arr->UPB_PRIVATE(size) + 1, arena)) { + return false; + } + UPB_PRIVATE(_upb_Array_Set) + (arr, arr->UPB_PRIVATE(size) - 1, &val, sizeof(val)); + return true; +} +UPB_INLINE void Operation_set_severity(Operation *msg, int32_t value) { + const upb_MiniTableField field = {9, UPB_SIZE(24, 12), 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} + +/* Campaign */ + +UPB_INLINE Campaign* Campaign_new(upb_Arena* arena) { + return (Campaign*)_upb_Message_New(&Campaign_msg_init, arena); +} +UPB_INLINE Campaign* Campaign_parse(const char* buf, size_t size, upb_Arena* arena) { + Campaign* ret = Campaign_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Campaign_msg_init, NULL, 0, arena) != + kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE Campaign* Campaign_parse_ex(const char* buf, size_t size, + const upb_ExtensionRegistry* extreg, + int options, upb_Arena* arena) { + Campaign* ret = Campaign_new(arena); + if (!ret) return NULL; + if (upb_Decode(buf, size, UPB_UPCAST(ret), &Campaign_msg_init, extreg, options, + arena) != kUpb_DecodeStatus_Ok) { + return NULL; + } + return ret; +} +UPB_INLINE char* Campaign_serialize(const Campaign* msg, upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Campaign_msg_init, 0, arena, &ptr, len); + return ptr; +} +UPB_INLINE char* Campaign_serialize_ex(const Campaign* msg, int options, + upb_Arena* arena, size_t* len) { + char* ptr; + (void)upb_Encode(UPB_UPCAST(msg), &Campaign_msg_init, options, arena, &ptr, len); + return ptr; +} +UPB_INLINE void Campaign_clear_name(Campaign* msg) { + const upb_MiniTableField field = {1, UPB_SIZE(12, 8), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE upb_StringView Campaign_name(const Campaign* msg) { + upb_StringView default_val = upb_StringView_FromString(""); + upb_StringView ret; + const upb_MiniTableField field = {1, UPB_SIZE(12, 8), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} +UPB_INLINE void Campaign_clear_operations(Campaign* msg) { + const upb_MiniTableField field = {2, UPB_SIZE(8, 24), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE const Operation* const* Campaign_operations(const Campaign* msg, size_t* size) { + const upb_MiniTableField field = {2, UPB_SIZE(8, 24), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Operation_msg_init); + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (const Operation* const*)upb_Array_DataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE const upb_Array* _Campaign_operations_upb_array(const Campaign* msg, size_t* size) { + const upb_MiniTableField field = {2, UPB_SIZE(8, 24), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Operation_msg_init); + const upb_Array* arr = upb_Message_GetArray(UPB_UPCAST(msg), &field); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE upb_Array* _Campaign_operations_mutable_upb_array(Campaign* msg, size_t* size, upb_Arena* arena) { + const upb_MiniTableField field = {2, UPB_SIZE(8, 24), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Operation_msg_init); + upb_Array* arr = upb_Message_GetOrCreateMutableArray(UPB_UPCAST(msg), + &field, arena); + if (size) { + *size = arr ? arr->UPB_PRIVATE(size) : 0; + } + return arr; +} +UPB_INLINE void Campaign_clear_total_bytes_stolen(Campaign* msg) { + const upb_MiniTableField field = {3, UPB_SIZE(24, 32), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_ClearBaseField(UPB_UPCAST(msg), &field); +} +UPB_INLINE int64_t Campaign_total_bytes_stolen(const Campaign* msg) { + int64_t default_val = (int64_t)0ll; + int64_t ret; + const upb_MiniTableField field = {3, UPB_SIZE(24, 32), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + _upb_Message_GetNonExtensionField(UPB_UPCAST(msg), &field, + &default_val, &ret); + return ret; +} + +UPB_INLINE void Campaign_set_name(Campaign *msg, upb_StringView value) { + const upb_MiniTableField field = {1, UPB_SIZE(12, 8), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} +UPB_INLINE Operation** Campaign_mutable_operations(Campaign* msg, size_t* size) { + upb_MiniTableField field = {2, UPB_SIZE(8, 24), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Operation_msg_init); + upb_Array* arr = upb_Message_GetMutableArray(UPB_UPCAST(msg), &field); + if (arr) { + if (size) *size = arr->UPB_PRIVATE(size); + return (Operation**)upb_Array_MutableDataPtr(arr); + } else { + if (size) *size = 0; + return NULL; + } +} +UPB_INLINE Operation** Campaign_resize_operations(Campaign* msg, size_t size, upb_Arena* arena) { + upb_MiniTableField field = {2, UPB_SIZE(8, 24), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + return (Operation**)upb_Message_ResizeArrayUninitialized(UPB_UPCAST(msg), + &field, size, arena); +} +UPB_INLINE struct Operation* Campaign_add_operations(Campaign* msg, upb_Arena* arena) { + upb_MiniTableField field = {2, UPB_SIZE(8, 24), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}; + UPB_PRIVATE(_upb_MiniTable_StrongReference)(&Operation_msg_init); + upb_Array* arr = upb_Message_GetOrCreateMutableArray( + UPB_UPCAST(msg), &field, arena); + if (!arr || !UPB_PRIVATE(_upb_Array_ResizeUninitialized)( + arr, arr->UPB_PRIVATE(size) + 1, arena)) { + return NULL; + } + struct Operation* sub = (struct Operation*)_upb_Message_New(&Operation_msg_init, arena); + if (!arr || !sub) return NULL; + UPB_PRIVATE(_upb_Array_Set) + (arr, arr->UPB_PRIVATE(size) - 1, &sub, sizeof(sub)); + return sub; +} +UPB_INLINE void Campaign_set_total_bytes_stolen(Campaign *msg, int64_t value) { + const upb_MiniTableField field = {3, UPB_SIZE(24, 32), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}; + upb_Message_SetBaseField((upb_Message *)msg, &field, &value); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port/undef.inc" + +#endif /* HACKERS_PROTO_UPB_H__UPB_H_ */ diff --git a/upb_test/hackers.upb_minitable.c b/upb_test/hackers.upb_minitable.c new file mode 100644 index 0000000..ad6cfd7 --- /dev/null +++ b/upb_test/hackers.upb_minitable.c @@ -0,0 +1,175 @@ +/* This file was generated by upb_generator from the input file: + * + * hackers.proto + * + * Do not edit -- your changes will be discarded when the file is + * regenerated. + * NO CHECKED-IN PROTOBUF GENCODE */ + +#include +#include "upb/generated_code_support.h" +#include "hackers.upb_minitable.h" + +// Must be last. +#include "upb/port/def.inc" + +extern const struct upb_MiniTable UPB_PRIVATE(_kUpb_MiniTable_StaticallyTreeShaken); +static const upb_MiniTableField Tool__fields[5] = { + {1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {2, UPB_SIZE(24, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {3, UPB_SIZE(32, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {4, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}, + {5, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}, +}; + +const upb_MiniTable Tool_msg_init = { + NULL, + &Tool__fields[0], + UPB_SIZE(40, 64), 5, kUpb_ExtMode_NonExtendable, 5, UPB_FASTTABLE_MASK(255), 0, +#ifdef UPB_TRACING_ENABLED + "Tool", +#endif +}; + +const upb_MiniTable* Tool_msg_init_ptr = &Tool_msg_init; +static const upb_MiniTableField Connection__fields[5] = { + {1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}, + {3, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}, + {4, UPB_SIZE(32, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}, + {5, UPB_SIZE(24, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, +}; + +const upb_MiniTable Connection_msg_init = { + NULL, + &Connection__fields[0], + UPB_SIZE(40, 56), 5, kUpb_ExtMode_NonExtendable, 5, UPB_FASTTABLE_MASK(255), 0, +#ifdef UPB_TRACING_ENABLED + "Connection", +#endif +}; + +const upb_MiniTable* Connection_msg_init_ptr = &Connection_msg_init; +static const upb_MiniTableSubInternal Hacker__submsgs[2] = { + {.UPB_PRIVATE(submsg) = &Tool_msg_init_ptr}, + {.UPB_PRIVATE(submsg) = &Connection_msg_init_ptr}, +}; + +static const upb_MiniTableField Hacker__fields[9] = { + {1, UPB_SIZE(32, 24), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {2, 40, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {3, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}, + {4, 16, 0, kUpb_NoSub, 2, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}, + {5, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}, + {6, UPB_SIZE(48, 56), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}, + {7, UPB_SIZE(20, 64), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}, + {8, UPB_SIZE(24, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}, + {9, UPB_SIZE(28, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}, +}; + +const upb_MiniTable Hacker_msg_init = { + &Hacker__submsgs[0], + &Hacker__fields[0], + UPB_SIZE(56, 88), 9, kUpb_ExtMode_NonExtendable, 9, UPB_FASTTABLE_MASK(56), 0, +#ifdef UPB_TRACING_ENABLED + "Hacker", +#endif + UPB_FASTTABLE_INIT({ + {0x0000000000000000, &_upb_FastDecoder_DecodeGeneric}, + {0x0000000000000000, &_upb_FastDecoder_DecodeGeneric}, + {0x0000000000000000, &_upb_FastDecoder_DecodeGeneric}, + {0x0000000000000000, &_upb_FastDecoder_DecodeGeneric}, + {0x001000003f000025, &upb_DecodeFast_Fixed32_Scalar_Tag1Byte}, + {0x0000000000000000, &_upb_FastDecoder_DecodeGeneric}, + {0x0000000000000000, &_upb_FastDecoder_DecodeGeneric}, + {0x0000000000000000, &_upb_FastDecoder_DecodeGeneric}, + }) +}; + +const upb_MiniTable* Hacker_msg_init_ptr = &Hacker_msg_init; +static const upb_MiniTableField Worm__fields[6] = { + {1, UPB_SIZE(20, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}, + {3, UPB_SIZE(40, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}, + {4, UPB_SIZE(28, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {5, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}, + {6, UPB_SIZE(16, 56), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}, +}; + +const upb_MiniTable Worm_msg_init = { + NULL, + &Worm__fields[0], + UPB_SIZE(48, 64), 6, kUpb_ExtMode_NonExtendable, 6, UPB_FASTTABLE_MASK(255), 0, +#ifdef UPB_TRACING_ENABLED + "Worm", +#endif +}; + +const upb_MiniTable* Worm_msg_init_ptr = &Worm_msg_init; +static const upb_MiniTableSubInternal Operation__submsgs[2] = { + {.UPB_PRIVATE(submsg) = &Hacker_msg_init_ptr}, + {.UPB_PRIVATE(submsg) = &Worm_msg_init_ptr}, +}; + +static const upb_MiniTableField Operation__fields[9] = { + {1, UPB_SIZE(28, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {2, UPB_SIZE(36, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {3, UPB_SIZE(56, 64), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}, + {4, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)}, + {5, UPB_SIZE(44, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {6, UPB_SIZE(12, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}, + {7, UPB_SIZE(16, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}, + {8, UPB_SIZE(20, 88), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}, + {9, UPB_SIZE(24, 12), 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)}, +}; + +const upb_MiniTable Operation_msg_init = { + &Operation__submsgs[0], + &Operation__fields[0], + UPB_SIZE(64, 96), 9, kUpb_ExtMode_NonExtendable, 9, UPB_FASTTABLE_MASK(255), 0, +#ifdef UPB_TRACING_ENABLED + "Operation", +#endif +}; + +const upb_MiniTable* Operation_msg_init_ptr = &Operation_msg_init; +static const upb_MiniTableSubInternal Campaign__submsgs[1] = { + {.UPB_PRIVATE(submsg) = &Operation_msg_init_ptr}, +}; + +static const upb_MiniTableField Campaign__fields[3] = { + {1, UPB_SIZE(12, 8), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)}, + {2, UPB_SIZE(8, 24), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)}, + {3, UPB_SIZE(24, 32), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)}, +}; + +const upb_MiniTable Campaign_msg_init = { + &Campaign__submsgs[0], + &Campaign__fields[0], + UPB_SIZE(32, 40), 3, kUpb_ExtMode_NonExtendable, 3, UPB_FASTTABLE_MASK(255), 0, +#ifdef UPB_TRACING_ENABLED + "Campaign", +#endif +}; + +const upb_MiniTable* Campaign_msg_init_ptr = &Campaign_msg_init; +static const upb_MiniTable *messages_layout[6] = { + &Tool_msg_init, + &Connection_msg_init, + &Hacker_msg_init, + &Worm_msg_init, + &Operation_msg_init, + &Campaign_msg_init, +}; + +const upb_MiniTableFile hackers_proto_upb_file_layout = { + messages_layout, + NULL, + NULL, + 6, + 0, + 0, +}; + +#include "upb/port/undef.inc" + diff --git a/upb_test/hackers.upb_minitable.h b/upb_test/hackers.upb_minitable.h new file mode 100644 index 0000000..54a9b36 --- /dev/null +++ b/upb_test/hackers.upb_minitable.h @@ -0,0 +1,42 @@ +/* This file was generated by upb_generator from the input file: + * + * hackers.proto + * + * Do not edit -- your changes will be discarded when the file is + * regenerated. + * NO CHECKED-IN PROTOBUF GENCODE */ + +#ifndef HACKERS_PROTO_UPB_H__UPB_MINITABLE_H_ +#define HACKERS_PROTO_UPB_H__UPB_MINITABLE_H_ + +#include "upb/generated_code_support.h" + +// Must be last. +#include "upb/port/def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const upb_MiniTable Tool_msg_init; +extern const upb_MiniTable* Tool_msg_init_ptr; +extern const upb_MiniTable Connection_msg_init; +extern const upb_MiniTable* Connection_msg_init_ptr; +extern const upb_MiniTable Hacker_msg_init; +extern const upb_MiniTable* Hacker_msg_init_ptr; +extern const upb_MiniTable Worm_msg_init; +extern const upb_MiniTable* Worm_msg_init_ptr; +extern const upb_MiniTable Operation_msg_init; +extern const upb_MiniTable* Operation_msg_init_ptr; +extern const upb_MiniTable Campaign_msg_init; +extern const upb_MiniTable* Campaign_msg_init_ptr; + +extern const upb_MiniTableFile hackers_proto_upb_file_layout; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port/undef.inc" + +#endif /* HACKERS_PROTO_UPB_H__UPB_MINITABLE_H_ */ diff --git a/upb_test/hackers_bench.c b/upb_test/hackers_bench.c new file mode 100644 index 0000000..2bf7347 --- /dev/null +++ b/upb_test/hackers_bench.c @@ -0,0 +1,380 @@ +/* + * hackers_bench.c — C/upb benchmark mirroring benches/hackers_bench.rs + * + * Proto: proto/hackers.proto + * Generated files: hackers.upb.h / .c, hackers.upb_minitable.h / .c + * + * Build: make + * Run: ./hackers_bench + * + * Data files are read from ../data/bench/.pb — the same files + * produced by `cargo run --release --bin gen_bench_data -- --preset `. + * + * The four benchmark groups match the Rust/criterion groups exactly: + * + * shallow_parse — Campaign_parse() + Arena_Free() per iteration. + * upb fully decodes the message; roto merely scans + * for field offsets. This is the most important + * comparison: total cost to "be ready to read". + * + * deep_parse — parse + walk Campaign → Operations → every Hacker, + * touching each Hacker's handle field. + * + * field_access — message pre-parsed once outside the loop; each + * micro-benchmark times a single field read. + * upb: direct struct lookup. roto: decode at offset. + * + * iterate — count_operations: parse + count top-level repeated. + * count_all_crew: parse + count nested repeated. + */ + +#include +#include +#include +#include +#include +#include + +#include "hackers.upb.h" +#include "hackers.upb_minitable.h" + +/* ========================================================================== + * Timing + * ========================================================================== */ + +static uint64_t now_ns(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec; +} + +/* ========================================================================== + * Black-box sink — prevents the compiler from optimising away benchmark work. + * We write the result of every meaningful computation here. + * ========================================================================== */ + +static volatile uintptr_t g_sink; + +/* ========================================================================== + * File I/O + * ========================================================================== */ + +typedef struct { + uint8_t *data; + size_t len; + char path[256]; +} BenchData; + +static bool load_bench_data(BenchData *out, const char *name) { + snprintf(out->path, sizeof(out->path), "../data/bench/%s.pb", name); + FILE *f = fopen(out->path, "rb"); + if (!f) { + printf("[skip] %s not found — " + "run `cargo run --release --bin gen_bench_data -- --preset %s` first\n", + out->path, name); + out->data = NULL; + out->len = 0; + return false; + } + fseek(f, 0, SEEK_END); + out->len = (size_t)ftell(f); + rewind(f); + out->data = malloc(out->len); + if (!out->data) { fclose(f); return false; } + fread(out->data, 1, out->len, f); + fclose(f); + return true; +} + +static void free_bench_data(BenchData *d) { + free(d->data); + d->data = NULL; +} + +/* ========================================================================== + * Benchmark runner + * + * Finds a batch size such that one batch takes ≥1 ms, then runs batches + * until at least BENCH_MIN_SECS of wall time has elapsed. Reports the + * mean ns/iter and, if bytes > 0, the MB/s throughput. + * ========================================================================== */ + +#define BENCH_MIN_SECS 0.5 + +typedef void (*bench_fn)(void *state); + +static void run_bench(bench_fn fn, void *state, size_t bytes, const char *label) { + /* warmup */ + for (int i = 0; i < 5; i++) fn(state); + + /* calibrate: find batch size so one batch ≥ 1 ms */ + uint64_t batch = 1; + while (batch < 10000000ULL) { + uint64_t t0 = now_ns(); + for (uint64_t i = 0; i < batch; i++) fn(state); + if (now_ns() - t0 >= 1000000ULL) break; /* 1 ms */ + batch *= 4; + } + + /* measure */ + uint64_t target_ns = (uint64_t)(BENCH_MIN_SECS * 1e9); + uint64_t total_ns = 0; + uint64_t total_its = 0; + while (total_ns < target_ns) { + uint64_t t0 = now_ns(); + for (uint64_t i = 0; i < batch; i++) fn(state); + total_ns += now_ns() - t0; + total_its += batch; + } + + double ns_per_iter = (double)total_ns / (double)total_its; + if (bytes > 0) { + double mb_per_sec = (double)bytes / ns_per_iter * 1000.0; + printf(" %-46s %9.2f ns/iter %8.2f MB/s\n", + label, ns_per_iter, mb_per_sec); + } else { + printf(" %-46s %9.2f ns/iter\n", label, ns_per_iter); + } +} + +/* ========================================================================== + * shallow_parse — Campaign_parse() + upb_Arena_Free() per iteration + * + * Measures the full cost of becoming "ready to access any field", matching + * the Rust `Campaign::new()` benchmark. upb fully decodes; roto only scans. + * ========================================================================== */ + +static void fn_shallow_parse(void *state) { + BenchData *d = state; + upb_Arena *arena = upb_Arena_New(); + Campaign *c = Campaign_parse((const char *)d->data, d->len, arena); + g_sink = (uintptr_t)c; + upb_Arena_Free(arena); +} + +static void bench_shallow_parse(void) { + const char *sizes[] = {"tiny", "small", "medium", "large", NULL}; + printf("\n=== shallow_parse ===\n"); + for (int i = 0; sizes[i]; i++) { + BenchData d; + if (!load_bench_data(&d, sizes[i])) continue; + char label[80]; + snprintf(label, sizeof(label), "Campaign_parse/%s [%zu B]", sizes[i], d.len); + run_bench(fn_shallow_parse, &d, d.len, label); + free_bench_data(&d); + } +} + +/* ========================================================================== + * deep_parse — parse + walk Campaign → Operations → Hackers + * + * After Campaign_parse(), upb has already decoded everything. The "deep" + * walk is pointer-chasing through the decoded tree. In roto each level + * calls ::new(), paying another linear scan over that sub-message's bytes. + * ========================================================================== */ + +static void fn_deep_parse(void *state) { + BenchData *d = state; + upb_Arena *arena = upb_Arena_New(); + Campaign *c = Campaign_parse((const char *)d->data, d->len, arena); + + size_t n_ops; + const Operation * const *ops = Campaign_operations(c, &n_ops); + size_t hacker_count = 0; + for (size_t i = 0; i < n_ops; i++) { + size_t n_crew; + const Hacker * const *crew = Operation_crew(ops[i], &n_crew); + for (size_t j = 0; j < n_crew; j++) { + upb_StringView handle = Hacker_handle(crew[j]); + g_sink = (uintptr_t)handle.data; + hacker_count++; + } + } + g_sink = hacker_count; + upb_Arena_Free(arena); +} + +static void bench_deep_parse(void) { + const char *sizes[] = {"tiny", "small", "medium", NULL}; + printf("\n=== deep_parse ===\n"); + for (int i = 0; sizes[i]; i++) { + BenchData d; + if (!load_bench_data(&d, sizes[i])) continue; + char label[80]; + snprintf(label, sizeof(label), "Campaign+Ops+Hackers/%s [%zu B]", sizes[i], d.len); + run_bench(fn_deep_parse, &d, d.len, label); + free_bench_data(&d); + } +} + +/* ========================================================================== + * field_access — individual field reads on a pre-parsed message + * + * Parse once outside the loop; each micro-benchmark measures the accessor + * call itself. upb: a struct-field read with a MiniTable lookup. + * roto: decode the value at a pre-recorded byte offset. + * ========================================================================== */ + +typedef struct { + upb_Arena *arena; + Campaign *campaign; + Operation *op; + Hacker *hacker; + Worm *worm; +} FieldState; + +static void fn_field_campaign_name(void *s) { + upb_StringView v = Campaign_name(((FieldState *)s)->campaign); + g_sink = (uintptr_t)v.data; +} +static void fn_field_total_bytes_stolen(void *s) { + g_sink = (uintptr_t)(uint64_t)Campaign_total_bytes_stolen(((FieldState *)s)->campaign); +} +static void fn_field_op_codename(void *s) { + upb_StringView v = Operation_codename(((FieldState *)s)->op); + g_sink = (uintptr_t)v.data; +} +static void fn_field_op_timestamp(void *s) { + g_sink = (uintptr_t)(uint64_t)Operation_timestamp(((FieldState *)s)->op); +} +static void fn_field_op_successful(void *s) { + g_sink = (uintptr_t)Operation_successful(((FieldState *)s)->op); +} +static void fn_field_hacker_handle(void *s) { + upb_StringView v = Hacker_handle(((FieldState *)s)->hacker); + g_sink = (uintptr_t)v.data; +} +static void fn_field_hacker_skill_level(void *s) { + /* store float bits to avoid FPU → int conversion costs */ + float f = Hacker_skill_level(((FieldState *)s)->hacker); + uint32_t bits; memcpy(&bits, &f, 4); + g_sink = bits; +} +static void fn_field_hacker_is_elite(void *s) { + g_sink = (uintptr_t)Hacker_is_elite(((FieldState *)s)->hacker); +} +static void fn_field_worm_polymorphic(void *s) { + g_sink = (uintptr_t)Worm_polymorphic(((FieldState *)s)->worm); +} +static void fn_field_worm_payload(void *s) { + upb_StringView v = Worm_payload(((FieldState *)s)->worm); + g_sink = (uintptr_t)v.data; +} + +static void bench_field_access(void) { + BenchData d; + if (!load_bench_data(&d, "small")) return; + + upb_Arena *arena = upb_Arena_New(); + Campaign *campaign = Campaign_parse((const char *)d.data, d.len, arena); + if (!campaign) { fprintf(stderr, "parse failed\n"); return; } + + size_t n_ops; + const Operation * const *ops = Campaign_operations(campaign, &n_ops); + if (n_ops == 0) { fprintf(stderr, "no operations\n"); return; } + Operation *op = (Operation *)ops[0]; /* cast away const for state */ + + size_t n_crew; + const Hacker * const *crew = Operation_crew(op, &n_crew); + if (n_crew == 0) { fprintf(stderr, "no crew\n"); return; } + Hacker *hacker = (Hacker *)crew[0]; + + const Worm *worm = Operation_worm(op); + if (!worm) { fprintf(stderr, "no worm\n"); return; } + + FieldState state = { + .arena = arena, + .campaign = campaign, + .op = op, + .hacker = hacker, + .worm = (Worm *)worm, + }; + + printf("\n=== field_access ===\n"); + run_bench(fn_field_campaign_name, &state, 0, "campaign::name"); + run_bench(fn_field_total_bytes_stolen, &state, 0, "campaign::total_bytes_stolen"); + run_bench(fn_field_op_codename, &state, 0, "operation::codename"); + run_bench(fn_field_op_timestamp, &state, 0, "operation::timestamp"); + run_bench(fn_field_op_successful, &state, 0, "operation::successful"); + run_bench(fn_field_hacker_handle, &state, 0, "hacker::handle"); + run_bench(fn_field_hacker_skill_level, &state, 0, "hacker::skill_level (f32)"); + run_bench(fn_field_hacker_is_elite, &state, 0, "hacker::is_elite (bool)"); + run_bench(fn_field_worm_polymorphic, &state, 0, "worm::polymorphic (bool)"); + run_bench(fn_field_worm_payload, &state, 0, "worm::payload (bytes)"); + + upb_Arena_Free(arena); + free_bench_data(&d); +} + +/* ========================================================================== + * iterate — count repeated fields at different depths + * + * count_operations: after parsing, Campaign_operations() returns pointer+count + * in O(1) — upb already decoded the array. + * roto's Campaign::new() scan IS the counting work. + * + * count_all_crew: parse + walk ops + sum crew sizes. + * ========================================================================== */ + +static void fn_count_operations(void *state) { + BenchData *d = state; + upb_Arena *arena = upb_Arena_New(); + Campaign *c = Campaign_parse((const char *)d->data, d->len, arena); + size_t n; + Campaign_operations(c, &n); + g_sink = n; + upb_Arena_Free(arena); +} + +static void fn_count_all_crew(void *state) { + BenchData *d = state; + upb_Arena *arena = upb_Arena_New(); + Campaign *c = Campaign_parse((const char *)d->data, d->len, arena); + size_t n_ops; + const Operation * const *ops = Campaign_operations(c, &n_ops); + size_t total = 0; + for (size_t i = 0; i < n_ops; i++) { + size_t n_crew; + Operation_crew(ops[i], &n_crew); + total += n_crew; + } + g_sink = total; + upb_Arena_Free(arena); +} + +static void bench_iterate(void) { + const char *sizes[] = {"tiny", "small", "medium", NULL}; + printf("\n=== iterate ===\n"); + for (int i = 0; sizes[i]; i++) { + BenchData d; + if (!load_bench_data(&d, sizes[i])) continue; + + char label[80]; + + snprintf(label, sizeof(label), "count_operations/%s [%zu B]", sizes[i], d.len); + run_bench(fn_count_operations, &d, d.len, label); + + snprintf(label, sizeof(label), "count_all_crew/%s [%zu B]", sizes[i], d.len); + run_bench(fn_count_all_crew, &d, d.len, label); + + free_bench_data(&d); + } +} + +/* ========================================================================== + * main + * ========================================================================== */ + +int main(void) { + printf("hackers_bench (upb / protobuf %s)\n", "33.1"); + printf("Data files: ../data/bench/.pb\n"); + printf("Run `cargo run --release --bin gen_bench_data -- --preset ` to generate.\n"); + + bench_shallow_parse(); + bench_deep_parse(); + bench_field_access(); + bench_iterate(); + + printf("\n"); + return 0; +} diff --git a/upb_test/test.c b/upb_test/test.c new file mode 100644 index 0000000..abbfc0d --- /dev/null +++ b/upb_test/test.c @@ -0,0 +1,13 @@ +#include +#include +#include "hackers.upb.h" +#include "hackers.upb_minitable.h" + +int main(void) { + upb_Arena *arena = upb_Arena_New(); + Campaign *c = Campaign_new(arena); + (void)c; + printf("name: %.*s\n", (int)Campaign_name(c).size, Campaign_name(c).data); + upb_Arena_Free(arena); + return 0; +}