tigerbeetle 0.0.34 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/ext/tb_client/extconf.rb +13 -13
- data/ext/tb_client/tigerbeetle/LICENSE +177 -0
- data/ext/tb_client/tigerbeetle/build.zig +2327 -0
- data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
- data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
- data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
- data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1084 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
- data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
- data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
- data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
- data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
- data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
- data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
- data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
- data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
- data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
- data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
- data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
- data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
- data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
- data/ext/tb_client/tigerbeetle/src/direction.zig +11 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
- data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
- data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
- data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
- data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
- data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
- data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
- data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
- data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
- data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
- data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
- data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
- data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
- data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
- data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
- data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
- data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
- data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
- data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
- data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
- data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
- data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
- data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +362 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +1036 -0
- data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
- data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
- data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
- data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
- data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +105 -0
- data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
- data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
- data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
- data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +557 -0
- data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
- data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
- data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
- data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
- data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
- data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
- data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
- data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
- data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
- data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
- data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
- data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
- data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
- data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
- data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
- data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
- data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
- data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
- data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
- data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
- data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
- data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
- data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
- data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
- data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
- data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
- data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
- data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
- data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
- data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
- data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
- data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
- data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
- data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
- data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
- data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
- data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
- data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
- data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
- data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
- data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
- data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
- data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
- data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
- data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
- data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
- data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
- data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
- data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
- data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
- data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
- data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
- data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
- data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
- data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
- data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
- data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
- data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
- data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
- data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
- data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
- data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
- data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
- data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
- data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
- data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
- data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
- data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
- data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
- data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
- data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
- data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
- data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
- data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
- data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
- data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
- data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
- data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
- data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
- data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
- data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
- data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
- data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2910 -0
- data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
- data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
- data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
- data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
- data/lib/tb_client/shared_lib.rb +12 -5
- data/lib/tigerbeetle/client.rb +1 -1
- data/lib/tigerbeetle/platforms.rb +9 -0
- data/lib/tigerbeetle/version.rb +2 -2
- data/tigerbeetle.gemspec +22 -5
- metadata +242 -3
- data/ext/tb_client/pkg.tar.gz +0 -0
|
@@ -0,0 +1,1283 @@
|
|
|
1
|
+
///! Implements the AMQP (Advanced Message Queuing Protocol) 0.9.1 wire protocol.
|
|
2
|
+
///! https://www.amqp.org/sites/amqp.org/files/amqp0-9-1.zip
|
|
3
|
+
///!
|
|
4
|
+
///! The `Frame` is the basic unit of the AMQP protocol. Its minimum size is
|
|
5
|
+
///! 8 bytes, and the maximum size can be negotiated between the client and server.
|
|
6
|
+
///!
|
|
7
|
+
///! # Frame layout:
|
|
8
|
+
///! ┌────────┬──────────┬────────┐ ┌───────────────┐ ┌───────┐
|
|
9
|
+
///! │ type │ channel │ size │ │ payload │ │ 0xCE │
|
|
10
|
+
///! │ u8 │ u16 │ u32 │ │ variable size │ │ u8 │
|
|
11
|
+
///! └────────┴──────────┴────────┘ └───────────────┘ └───────┘
|
|
12
|
+
///! There are four frame types: "method", "header", "body", and "heartbeat".
|
|
13
|
+
///! Each frame type (except by heartbeat) has different types of payloads.
|
|
14
|
+
///!
|
|
15
|
+
///! # Method payload:
|
|
16
|
+
///! ┌──────────┬───────────┬─────────────────┐
|
|
17
|
+
///! │ class_id │ method_id │ arguments │
|
|
18
|
+
///! │ u16 │ u16 │ variable size │
|
|
19
|
+
///! └──────────┴───────────┴─────────────────┘
|
|
20
|
+
///! The `spec.zig` file contains declarations for all methods defined by the
|
|
21
|
+
///! specification and their expected arguments.
|
|
22
|
+
///!
|
|
23
|
+
///! # Header payload:
|
|
24
|
+
///! ┌──────────┬────────┬────────────┬────────────────┬─────────────────┐
|
|
25
|
+
///! │ class_id │ weight │ body_size │ property_flags │ properties │
|
|
26
|
+
///! │ u16 │ u16 │ u64 │ u16 │ variable size │
|
|
27
|
+
///! └──────────┴────────┴────────────┴────────────────┴─────────────────┘
|
|
28
|
+
///! Certain "method" frames are followed by a "header" frame. For example, in the `basic-publish`
|
|
29
|
+
///! method, the content header contains metadata about the message being published. The frame with
|
|
30
|
+
///! `type == header` always follows its corresponding `type == method` frame.
|
|
31
|
+
///! See `BasicProperties` for parsing the `property_flags` and `properties`.
|
|
32
|
+
///!
|
|
33
|
+
///! # Body payload:
|
|
34
|
+
///! ┌───────────────┐
|
|
35
|
+
///! │ content │
|
|
36
|
+
///! │ variable size │
|
|
37
|
+
///! └───────────────┘
|
|
38
|
+
///! The body frame contains the application-specific content of the message.
|
|
39
|
+
///! The body can be split across multiple frames if `body_size` exceeds the frame size, however we
|
|
40
|
+
///! only support single-frame bodies.
|
|
41
|
+
///!
|
|
42
|
+
///! # Endianness:
|
|
43
|
+
///! Integers are encoded in network byte order (big endian).
|
|
44
|
+
///!
|
|
45
|
+
const std = @import("std");
|
|
46
|
+
const stdx = @import("stdx");
|
|
47
|
+
const assert = std.debug.assert;
|
|
48
|
+
const maybe = stdx.maybe;
|
|
49
|
+
const KiB = stdx.KiB;
|
|
50
|
+
|
|
51
|
+
const spec = @import("spec.zig");
|
|
52
|
+
|
|
53
|
+
pub const frame_min_size = spec.FRAME_MIN_SIZE;
|
|
54
|
+
pub const tcp_port_default = 5672;
|
|
55
|
+
|
|
56
|
+
/// The major, minor, and revision numbers can take any value from 0 to 99 for official
|
|
57
|
+
/// specifications.
|
|
58
|
+
/// Major, minor, and revision numbers of 100 and above are reserved for internal testing
|
|
59
|
+
/// and development purposes.
|
|
60
|
+
pub const version = .{
|
|
61
|
+
.major = 0,
|
|
62
|
+
.minor = 9,
|
|
63
|
+
.revision = 1,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/// The protocol header consists of the upper case letters "AMQP"
|
|
67
|
+
/// followed by the constant 0 and the AMQP version number.
|
|
68
|
+
pub const protocol_header: *const [8]u8 = "AMQP" ++ [_]u8{
|
|
69
|
+
0,
|
|
70
|
+
version.major,
|
|
71
|
+
version.minor,
|
|
72
|
+
version.revision,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
pub const DeliveryMode = enum(u8) {
|
|
76
|
+
transient = 1,
|
|
77
|
+
persistent = 2,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
pub const FrameEnd = enum(u8) {
|
|
81
|
+
value = spec.FRAME_END,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
pub const FrameType = enum(u8) {
|
|
85
|
+
method = spec.FRAME_METHOD,
|
|
86
|
+
header = spec.FRAME_HEADER,
|
|
87
|
+
body = spec.FRAME_BODY,
|
|
88
|
+
heartbeat = spec.FRAME_HEARTBEAT,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
pub const MethodHeader = packed struct(u32) {
|
|
92
|
+
class: u16,
|
|
93
|
+
method: u16,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
pub const Channel = enum(u16) {
|
|
97
|
+
/// The channel number is 0 for all frames which are global to the connection.
|
|
98
|
+
global = 0,
|
|
99
|
+
/// Id of the current channel.
|
|
100
|
+
/// Supporting multiple channels is unnecessary, as messages are submitted in batches
|
|
101
|
+
/// through io_uring without concurrency.
|
|
102
|
+
current = 1,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
pub const ErrorCodes = enum(u16) {
|
|
106
|
+
/// The client attempted to transfer content larger than the server could accept
|
|
107
|
+
/// at the present time. The client may retry at a later time.
|
|
108
|
+
ContentTooLarge = spec.SOFT_ERROR_CONTENT_TOO_LARGE,
|
|
109
|
+
/// Returned when RabbitMQ sends back with 'basic.return' when a
|
|
110
|
+
/// 'mandatory' message cannot be delivered to any queue.
|
|
111
|
+
NoRoute = spec.SOFT_ERROR_NO_ROUTE,
|
|
112
|
+
/// When the exchange cannot deliver to a consumer when the immediate flag is
|
|
113
|
+
/// set. As a result of pending data on the queue or the absence of any
|
|
114
|
+
/// consumers of the queue.
|
|
115
|
+
NoConsumers = spec.SOFT_ERROR_NO_CONSUMERS,
|
|
116
|
+
/// The client attempted to work with a server entity to which it has no
|
|
117
|
+
/// access due to security settings.
|
|
118
|
+
AccessRefused = spec.SOFT_ERROR_ACCESS_REFUSED,
|
|
119
|
+
/// The client attempted to work with a server entity that does not exist.
|
|
120
|
+
NotFound = spec.SOFT_ERROR_NOT_FOUND,
|
|
121
|
+
/// The client attempted to work with a server entity to which it has no
|
|
122
|
+
/// access because another client is working with it.
|
|
123
|
+
ResourceLocked = spec.SOFT_ERROR_RESOURCE_LOCKED,
|
|
124
|
+
/// The client requested a method that was not allowed because some precondition
|
|
125
|
+
/// failed.
|
|
126
|
+
PreconditionFailed = spec.SOFT_ERROR_PRECONDITION_FAILED,
|
|
127
|
+
/// An operator intervened to close the connection for some reason. The client
|
|
128
|
+
/// may retry at some later date.
|
|
129
|
+
ConnectionForced = spec.HARD_ERROR_CONNECTION_FORCED,
|
|
130
|
+
/// The client tried to work with an unknown virtual host.
|
|
131
|
+
InvalidPath = spec.HARD_ERROR_INVALID_PATH,
|
|
132
|
+
/// The sender sent a malformed frame that the recipient could not decode.
|
|
133
|
+
/// This strongly implies a programming error in the sending peer.
|
|
134
|
+
FrameError = spec.HARD_ERROR_FRAME_ERROR,
|
|
135
|
+
/// The sender sent a frame that contained illegal values for one or more
|
|
136
|
+
/// fields. This strongly implies a programming error in the sending peer.
|
|
137
|
+
SyntaxError = spec.HARD_ERROR_SYNTAX_ERROR,
|
|
138
|
+
/// The client sent an invalid sequence of frames, attempting to perform an
|
|
139
|
+
/// operation that was considered invalid by the server. This usually implies
|
|
140
|
+
/// a programming error in the client.
|
|
141
|
+
CommandInvalid = spec.HARD_ERROR_COMMAND_INVALID,
|
|
142
|
+
/// The client attempted to work with a channel that had not been correctly
|
|
143
|
+
/// opened. This most likely indicates a fault in the client layer.
|
|
144
|
+
ChannelError = spec.HARD_ERROR_CHANNEL_ERROR,
|
|
145
|
+
/// The peer sent a frame that was not expected, usually in the context of
|
|
146
|
+
/// a content header and body. This strongly indicates a fault in the peer's
|
|
147
|
+
/// content processing.
|
|
148
|
+
UnexpectedFrame = spec.HARD_ERROR_UNEXPECTED_FRAME,
|
|
149
|
+
/// The server could not complete the method because it lacked sufficient
|
|
150
|
+
/// resources. This may be due to the client creating too many of some type
|
|
151
|
+
/// of entity.
|
|
152
|
+
ResourceError = spec.HARD_ERROR_RESOURCE_ERROR,
|
|
153
|
+
/// The client tried to work with some entity in a manner that is prohibited
|
|
154
|
+
/// by the server, due to security settings or by some other criteria.
|
|
155
|
+
NotAllowed = spec.HARD_ERROR_NOT_ALLOWED,
|
|
156
|
+
/// The client tried to use functionality that is not implemented in the
|
|
157
|
+
/// server.
|
|
158
|
+
NotImplemented = spec.HARD_ERROR_NOT_IMPLEMENTED,
|
|
159
|
+
/// The server could not complete the method because of an internal error.
|
|
160
|
+
/// The server may require intervention by an operator in order to resume
|
|
161
|
+
/// normal operations.
|
|
162
|
+
InternalError = spec.HARD_ERROR_INTERNAL_ERROR,
|
|
163
|
+
|
|
164
|
+
_,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
pub const FieldValueTag = enum(u8) {
|
|
168
|
+
boolean = 't',
|
|
169
|
+
uint8 = 'B',
|
|
170
|
+
int8 = 'b',
|
|
171
|
+
uint16 = 'u',
|
|
172
|
+
int16 = 's',
|
|
173
|
+
uint32 = 'i',
|
|
174
|
+
int32 = 'I',
|
|
175
|
+
// Both `l` and `L` are decoded as signed integers by RabbitMQ:
|
|
176
|
+
// https://www.rabbitmq.com/amqp-0-9-1-errata#section_3
|
|
177
|
+
// https://github.com/rabbitmq/rabbitmq-server/issues/1093#issuecomment-276351183
|
|
178
|
+
int64 = 'l',
|
|
179
|
+
string = 'S',
|
|
180
|
+
timestamp = 'T',
|
|
181
|
+
field_table = 'F',
|
|
182
|
+
void = 'V',
|
|
183
|
+
|
|
184
|
+
// We don't send or expect to receive these types from the AMQP server.
|
|
185
|
+
// Only user-defined tables would use them.
|
|
186
|
+
not_implemented_uint64 = 'L',
|
|
187
|
+
not_implemented_field_array = 'A',
|
|
188
|
+
not_implemented_float = 'f',
|
|
189
|
+
not_implemented_double = 'd',
|
|
190
|
+
not_implemented_decimal = 'D',
|
|
191
|
+
not_implemented_byte_array = 'x',
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
pub const Decoder = struct {
|
|
195
|
+
pub const Error = error{
|
|
196
|
+
BufferExhausted,
|
|
197
|
+
Unexpected,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
pub const FrameHeader = extern struct {
|
|
201
|
+
type: FrameType,
|
|
202
|
+
channel: Channel,
|
|
203
|
+
size: u32,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
pub const Header = struct {
|
|
207
|
+
class: u16,
|
|
208
|
+
weight: u16,
|
|
209
|
+
body_size: u64,
|
|
210
|
+
property_flags: u16,
|
|
211
|
+
properties: []const u8,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
pub const BasicProperties = BasicPropertiesType(.decode);
|
|
215
|
+
|
|
216
|
+
/// `FieldValue` represents a `tag` + `value` pair as specified by the AMQP spec.
|
|
217
|
+
pub const FieldValue = FieldValueType(.decode);
|
|
218
|
+
|
|
219
|
+
/// Allows iteration over the contents of an AMQP table read from the receive buffer.
|
|
220
|
+
pub const Table = struct {
|
|
221
|
+
pub const Iterator = struct {
|
|
222
|
+
decoder: Decoder,
|
|
223
|
+
|
|
224
|
+
pub fn reset(self: *Iterator) void {
|
|
225
|
+
self.decoder.reset();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
pub fn next(self: *Iterator) Decoder.Error!?struct {
|
|
229
|
+
key: []const u8,
|
|
230
|
+
value: FieldValue,
|
|
231
|
+
} {
|
|
232
|
+
if (self.decoder.empty()) return null;
|
|
233
|
+
return .{
|
|
234
|
+
.key = try self.decoder.read_short_string(),
|
|
235
|
+
.value = try self.decoder.read_field(),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
length: u32,
|
|
241
|
+
pointer: [*]const u8,
|
|
242
|
+
|
|
243
|
+
pub fn init(value: []const u8) Table {
|
|
244
|
+
assert(value.len <= std.math.maxInt(u32));
|
|
245
|
+
return .{
|
|
246
|
+
.length = @intCast(value.len),
|
|
247
|
+
.pointer = value.ptr,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
pub fn slice(self: Table) []const u8 {
|
|
252
|
+
return self.pointer[0..self.length];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
pub fn iterator(self: Table) Iterator {
|
|
256
|
+
return .{
|
|
257
|
+
.decoder = Decoder.init(self.slice()),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
buffer: []const u8,
|
|
263
|
+
/// Invariants: index <= buffer.len
|
|
264
|
+
index: usize,
|
|
265
|
+
|
|
266
|
+
pub fn init(buffer: []const u8) Decoder {
|
|
267
|
+
return .{
|
|
268
|
+
.buffer = buffer,
|
|
269
|
+
.index = 0,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
pub fn empty(self: *const Decoder) bool {
|
|
274
|
+
return self.index == self.buffer.len;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
pub fn read_int(self: *Decoder, comptime T: type) Error!T {
|
|
278
|
+
comptime assert(@typeInfo(T) == .int);
|
|
279
|
+
comptime assert(@typeInfo(T).int.signedness == .unsigned);
|
|
280
|
+
comptime assert(@sizeOf(T) == 1 or @sizeOf(T) == 2 or @sizeOf(T) == 4 or @sizeOf(T) == 8);
|
|
281
|
+
if (self.index + @sizeOf(T) > self.buffer.len) return error.BufferExhausted;
|
|
282
|
+
defer {
|
|
283
|
+
self.index += @sizeOf(T);
|
|
284
|
+
assert(self.index <= self.buffer.len);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return std.mem.readInt(T, self.buffer[self.index..][0..@sizeOf(T)], .big);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
pub fn read_enum(self: *Decoder, comptime Enum: type) Error!Enum {
|
|
291
|
+
comptime assert(@typeInfo(Enum) == .@"enum");
|
|
292
|
+
const Int = std.meta.Tag(Enum);
|
|
293
|
+
const value = try self.read_int(Int);
|
|
294
|
+
return std.meta.intToEnum(
|
|
295
|
+
Enum,
|
|
296
|
+
value,
|
|
297
|
+
) catch |err| switch (err) {
|
|
298
|
+
error.InvalidEnumTag => return error.Unexpected,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
pub fn read_bool(self: *Decoder) Error!bool {
|
|
303
|
+
const value = try self.read_int(u8);
|
|
304
|
+
return value != 0;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
pub fn read_short_string(self: *Decoder) Error![]const u8 {
|
|
308
|
+
const length: u8 = try self.read_int(u8);
|
|
309
|
+
return try self.read_bytes(length);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
pub fn read_long_string(self: *Decoder) Error![]const u8 {
|
|
313
|
+
const length: u32 = try self.read_int(u32);
|
|
314
|
+
return try self.read_bytes(length);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
pub fn read_table(self: *Decoder) Error!Table {
|
|
318
|
+
const length: u32 = try self.read_int(u32);
|
|
319
|
+
const bytes = try self.read_bytes(length);
|
|
320
|
+
return Table.init(bytes);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
fn read_bytes(self: *Decoder, length: u32) Error![]const u8 {
|
|
324
|
+
assert(self.index <= self.buffer.len);
|
|
325
|
+
if (self.index + length > self.buffer.len) return error.BufferExhausted;
|
|
326
|
+
defer {
|
|
327
|
+
self.index += length;
|
|
328
|
+
assert(self.index <= self.buffer.len);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return self.buffer[self.index..][0..length];
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
pub fn read_field(self: *Decoder) Error!FieldValue {
|
|
335
|
+
const tag = try self.read_enum(FieldValueTag);
|
|
336
|
+
const value: FieldValue = switch (tag) {
|
|
337
|
+
.boolean => .{ .boolean = try self.read_bool() },
|
|
338
|
+
.uint8 => .{ .uint8 = try self.read_int(u8) },
|
|
339
|
+
.int8 => .{ .int8 = @bitCast(try self.read_int(u8)) },
|
|
340
|
+
.uint16 => .{ .uint16 = try self.read_int(u16) },
|
|
341
|
+
.int16 => .{ .int16 = @bitCast(try self.read_int(u16)) },
|
|
342
|
+
.uint32 => .{ .uint32 = try self.read_int(u32) },
|
|
343
|
+
.int32 => .{ .int32 = @bitCast(try self.read_int(u32)) },
|
|
344
|
+
.int64 => .{ .int64 = @bitCast(try self.read_int(u64)) },
|
|
345
|
+
.string => .{ .string = try self.read_long_string() },
|
|
346
|
+
.timestamp => .{ .timestamp = try self.read_int(u64) },
|
|
347
|
+
.field_table => .{ .field_table = try self.read_table() },
|
|
348
|
+
.void => .void,
|
|
349
|
+
|
|
350
|
+
.not_implemented_uint64,
|
|
351
|
+
.not_implemented_field_array,
|
|
352
|
+
.not_implemented_float,
|
|
353
|
+
.not_implemented_double,
|
|
354
|
+
.not_implemented_decimal,
|
|
355
|
+
.not_implemented_byte_array,
|
|
356
|
+
=> fatal("AMQP type '{c}' not supported.", .{@intFromEnum(tag)}),
|
|
357
|
+
};
|
|
358
|
+
assert(value == tag);
|
|
359
|
+
return value;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
pub fn read_frame_header(self: *Decoder) Error!FrameHeader {
|
|
363
|
+
return .{
|
|
364
|
+
.type = try self.read_enum(FrameType),
|
|
365
|
+
.channel = try self.read_enum(Channel),
|
|
366
|
+
.size = try self.read_int(u32),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
pub fn read_frame_end(self: *Decoder) Error!void {
|
|
371
|
+
_ = try self.read_enum(FrameEnd);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
pub fn read_method_header(self: *Decoder) Error!MethodHeader {
|
|
375
|
+
return .{
|
|
376
|
+
.class = try self.read_int(u16),
|
|
377
|
+
.method = try self.read_int(u16),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
pub fn read_header(self: *Decoder, frame_size: usize) Error!Header {
|
|
382
|
+
const initial_index = self.index;
|
|
383
|
+
|
|
384
|
+
const class = try self.read_int(u16);
|
|
385
|
+
const weight = try self.read_int(u16);
|
|
386
|
+
const body_size = try self.read_int(u64);
|
|
387
|
+
const property_flags = try self.read_int(u16);
|
|
388
|
+
|
|
389
|
+
if (initial_index + frame_size > self.buffer.len) return error.BufferExhausted;
|
|
390
|
+
const properties = self.buffer[self.index .. initial_index + frame_size];
|
|
391
|
+
self.index += properties.len;
|
|
392
|
+
|
|
393
|
+
try self.read_frame_end();
|
|
394
|
+
|
|
395
|
+
return .{
|
|
396
|
+
.class = class,
|
|
397
|
+
.weight = weight,
|
|
398
|
+
.body_size = body_size,
|
|
399
|
+
.property_flags = @bitCast(property_flags),
|
|
400
|
+
.properties = properties,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
pub fn read_body(self: *Decoder, frame_size: usize) Error![]const u8 {
|
|
405
|
+
if (self.index + frame_size > self.buffer.len) return error.BufferExhausted;
|
|
406
|
+
const body = self.buffer[self.index..][0..frame_size];
|
|
407
|
+
self.index += frame_size;
|
|
408
|
+
assert(self.index <= self.buffer.len);
|
|
409
|
+
try self.read_frame_end();
|
|
410
|
+
return body;
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
pub const Encoder = struct {
|
|
415
|
+
pub const FrameHeader = struct {
|
|
416
|
+
/// Total size in bytes including the `size` field.
|
|
417
|
+
pub const size_total = @sizeOf(@FieldType(Decoder.FrameHeader, "type")) +
|
|
418
|
+
@sizeOf(@FieldType(Decoder.FrameHeader, "channel")) +
|
|
419
|
+
@sizeOf(@FieldType(Decoder.FrameHeader, "size"));
|
|
420
|
+
|
|
421
|
+
type: FrameType,
|
|
422
|
+
channel: Channel,
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
pub const Header = struct {
|
|
426
|
+
/// Total size in bytes including the `body_size` field.
|
|
427
|
+
pub const size_total = @sizeOf(@FieldType(Decoder.Header, "class")) +
|
|
428
|
+
@sizeOf(@FieldType(Decoder.Header, "weight")) +
|
|
429
|
+
@sizeOf(@FieldType(Decoder.Header, "body_size"));
|
|
430
|
+
|
|
431
|
+
class: u16,
|
|
432
|
+
weight: u16,
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
pub const BasicProperties = BasicPropertiesType(.encode);
|
|
436
|
+
|
|
437
|
+
/// `FieldValue` represents a `tag` + `value` pair as specified by the AMQP spec.
|
|
438
|
+
pub const FieldValue = FieldValueType(.encode);
|
|
439
|
+
|
|
440
|
+
/// Interface for a user-defined set of values to be encoded as an AMQP table
|
|
441
|
+
/// directly into the send buffer without copying.
|
|
442
|
+
pub const Table = struct {
|
|
443
|
+
pub const VTable = struct {
|
|
444
|
+
write: *const fn (*const anyopaque, *TableEncoder) void,
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
context: *const anyopaque,
|
|
448
|
+
vtable: *const VTable,
|
|
449
|
+
|
|
450
|
+
pub fn write(self: Table, encoder: *TableEncoder) void {
|
|
451
|
+
self.vtable.write(self.context, encoder);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
/// Interface for user-defined content to be written directly
|
|
456
|
+
/// into the send buffer without copying.
|
|
457
|
+
pub const Body = struct {
|
|
458
|
+
pub const VTable = struct {
|
|
459
|
+
write: *const fn (*const anyopaque, []u8) usize,
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
context: *const anyopaque,
|
|
463
|
+
vtable: *const VTable,
|
|
464
|
+
|
|
465
|
+
pub fn write(self: Body, buffer: []u8) usize {
|
|
466
|
+
return self.vtable.write(self.context, buffer);
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
pub const TableEncoder = struct {
|
|
471
|
+
encoder: *Encoder,
|
|
472
|
+
|
|
473
|
+
pub fn put(self: *TableEncoder, key: []const u8, value: FieldValue) void {
|
|
474
|
+
self.encoder.write_short_string(key);
|
|
475
|
+
self.encoder.write_field(value);
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
buffer: []u8,
|
|
480
|
+
index: usize,
|
|
481
|
+
|
|
482
|
+
frame_reference: ?struct {
|
|
483
|
+
index: usize,
|
|
484
|
+
frame_header: FrameHeader,
|
|
485
|
+
},
|
|
486
|
+
header_reference: ?struct {
|
|
487
|
+
index: usize,
|
|
488
|
+
header: Header,
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
pub fn init(buffer: []u8) Encoder {
|
|
492
|
+
return .{
|
|
493
|
+
.buffer = buffer,
|
|
494
|
+
.index = 0,
|
|
495
|
+
.frame_reference = null,
|
|
496
|
+
.header_reference = null,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
pub fn write_int(self: *Encoder, comptime T: type, value: T) void {
|
|
501
|
+
comptime assert(@typeInfo(T) == .int);
|
|
502
|
+
comptime assert(@sizeOf(T) == 1 or @sizeOf(T) == 2 or @sizeOf(T) == 4 or @sizeOf(T) == 8);
|
|
503
|
+
assert(self.index + @sizeOf(T) <= self.buffer.len);
|
|
504
|
+
std.mem.writeInt(T, self.buffer[self.index..][0..@sizeOf(T)], value, .big);
|
|
505
|
+
self.index += @sizeOf(T);
|
|
506
|
+
assert(self.index <= self.buffer.len);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
pub fn write_bool(self: *Encoder, value: bool) void {
|
|
510
|
+
self.write_int(u8, @intFromBool(value));
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
pub fn write_short_string(self: *Encoder, value: []const u8) void {
|
|
514
|
+
assert(value.len <= std.math.maxInt(u8));
|
|
515
|
+
self.write_int(u8, @intCast(value.len));
|
|
516
|
+
assert(self.index + value.len <= self.buffer.len);
|
|
517
|
+
stdx.copy_left(.inexact, u8, self.buffer[self.index..], value);
|
|
518
|
+
self.index += value.len;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
pub fn write_long_string(self: *Encoder, value: []const u8) void {
|
|
522
|
+
assert(value.len <= std.math.maxInt(u32));
|
|
523
|
+
self.write_int(u32, @intCast(value.len));
|
|
524
|
+
assert(self.index + value.len <= self.buffer.len);
|
|
525
|
+
stdx.copy_left(.inexact, u8, self.buffer[self.index..], value);
|
|
526
|
+
self.index += value.len;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
pub fn write_long_string_body(self: *Encoder, body: ?Body) void {
|
|
530
|
+
if (body == null) {
|
|
531
|
+
self.write_int(u32, 0); // Zero sized string.
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const start_index = self.index;
|
|
536
|
+
self.index += @sizeOf(u32);
|
|
537
|
+
assert(self.index <= self.buffer.len);
|
|
538
|
+
|
|
539
|
+
self.index += body.?.write(self.buffer[self.index..]);
|
|
540
|
+
assert(self.index <= self.buffer.len);
|
|
541
|
+
const end_index = self.index;
|
|
542
|
+
|
|
543
|
+
const size: u32 = @intCast(end_index - start_index - @sizeOf(u32));
|
|
544
|
+
self.index = start_index;
|
|
545
|
+
self.write_int(u32, size);
|
|
546
|
+
self.index = end_index;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
pub fn write_table(self: *Encoder, table: ?Table) void {
|
|
550
|
+
if (table == null) {
|
|
551
|
+
self.write_int(u32, 0); // Zero sized table.
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const start_index = self.index;
|
|
556
|
+
self.index += @sizeOf(u32);
|
|
557
|
+
assert(self.index <= self.buffer.len);
|
|
558
|
+
|
|
559
|
+
var table_encoder: TableEncoder = .{ .encoder = self };
|
|
560
|
+
table.?.write(&table_encoder);
|
|
561
|
+
const end_index = self.index;
|
|
562
|
+
|
|
563
|
+
const size: u32 = @intCast(end_index - start_index - @sizeOf(u32));
|
|
564
|
+
self.index = start_index;
|
|
565
|
+
self.write_int(u32, size);
|
|
566
|
+
self.index = end_index;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
pub fn write_field(self: *Encoder, field: FieldValue) void {
|
|
570
|
+
const tag: FieldValueTag = field;
|
|
571
|
+
self.write_int(u8, @intFromEnum(tag));
|
|
572
|
+
switch (field) {
|
|
573
|
+
.boolean => |value| self.write_bool(value),
|
|
574
|
+
.uint8 => |value| self.write_int(u8, value),
|
|
575
|
+
.int8 => |value| self.write_int(u8, @bitCast(value)),
|
|
576
|
+
.uint16 => |value| self.write_int(u16, value),
|
|
577
|
+
.int16 => |value| self.write_int(u16, @bitCast(value)),
|
|
578
|
+
.uint32 => |value| self.write_int(u32, value),
|
|
579
|
+
.int32 => |value| self.write_int(u32, @bitCast(value)),
|
|
580
|
+
.int64 => |value| self.write_int(u64, @bitCast(value)),
|
|
581
|
+
.string => |value| self.write_long_string(value),
|
|
582
|
+
.timestamp => |value| self.write_int(u64, value),
|
|
583
|
+
.field_table => |value| self.write_table(value),
|
|
584
|
+
.void => {},
|
|
585
|
+
|
|
586
|
+
.not_implemented_uint64,
|
|
587
|
+
.not_implemented_field_array,
|
|
588
|
+
.not_implemented_float,
|
|
589
|
+
.not_implemented_double,
|
|
590
|
+
.not_implemented_decimal,
|
|
591
|
+
.not_implemented_byte_array,
|
|
592
|
+
=> fatal("AMQP type '{c}' not supported.", .{@intFromEnum(tag)}),
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
pub fn write_bytes(self: *Encoder, bytes: []const u8) void {
|
|
597
|
+
assert(bytes.len > 0);
|
|
598
|
+
assert(self.index + bytes.len <= self.buffer.len);
|
|
599
|
+
stdx.copy_disjoint(.inexact, u8, self.buffer[self.index..], bytes);
|
|
600
|
+
self.index += bytes.len;
|
|
601
|
+
assert(self.index <= self.buffer.len);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
pub fn begin_frame(self: *Encoder, frame_header: FrameHeader) void {
|
|
605
|
+
assert(self.frame_reference == null);
|
|
606
|
+
assert(self.header_reference == null or frame_header.type == .body);
|
|
607
|
+
// Reserve the frame header bytes to be updated by `finish_frame()`.
|
|
608
|
+
assert(self.index + FrameHeader.size_total <= self.buffer.len);
|
|
609
|
+
const frame_header_index = self.index;
|
|
610
|
+
self.index += FrameHeader.size_total;
|
|
611
|
+
self.frame_reference = .{
|
|
612
|
+
.index = frame_header_index,
|
|
613
|
+
.frame_header = frame_header,
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
pub fn finish_frame(self: *Encoder, frame_type: FrameType) void {
|
|
618
|
+
assert(self.frame_reference != null);
|
|
619
|
+
assert(self.frame_reference.?.frame_header.type == frame_type);
|
|
620
|
+
maybe(self.header_reference == null);
|
|
621
|
+
|
|
622
|
+
const reference = self.frame_reference.?;
|
|
623
|
+
self.frame_reference = null;
|
|
624
|
+
assert(reference.index + FrameHeader.size_total <= self.index);
|
|
625
|
+
const restore_index = self.index;
|
|
626
|
+
// The frame size field in the FrameHeader must be updated.
|
|
627
|
+
// It represents the payload size, excluding the FrameHeader
|
|
628
|
+
// and the frame end byte.
|
|
629
|
+
const size: u32 = @intCast(restore_index - reference.index - FrameHeader.size_total);
|
|
630
|
+
self.index = reference.index;
|
|
631
|
+
self.write_int(u8, @intFromEnum(reference.frame_header.type));
|
|
632
|
+
self.write_int(u16, @intFromEnum(reference.frame_header.channel));
|
|
633
|
+
self.write_int(u32, size);
|
|
634
|
+
|
|
635
|
+
self.index = restore_index;
|
|
636
|
+
self.write_int(u8, spec.FRAME_END);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
pub fn begin_header(self: *Encoder, header: Header) void {
|
|
640
|
+
// Reserve the frame header bytes to be updated by `finish_header()`.
|
|
641
|
+
assert(self.frame_reference != null);
|
|
642
|
+
assert(self.frame_reference.?.frame_header.type == .header);
|
|
643
|
+
assert(self.header_reference == null);
|
|
644
|
+
const header_index = self.index;
|
|
645
|
+
self.index += Header.size_total;
|
|
646
|
+
self.header_reference = .{
|
|
647
|
+
.header = header,
|
|
648
|
+
.index = header_index,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
pub fn finish_header(self: *Encoder, body_size: u64) void {
|
|
653
|
+
assert((body_size == 0) == (self.frame_reference == null));
|
|
654
|
+
assert(body_size == 0 or self.frame_reference.?.frame_header.type == .body);
|
|
655
|
+
assert(self.header_reference != null);
|
|
656
|
+
|
|
657
|
+
const reference = self.header_reference.?;
|
|
658
|
+
self.header_reference = null;
|
|
659
|
+
assert(reference.index + Header.size_total <= self.index);
|
|
660
|
+
const restore_index = self.index;
|
|
661
|
+
self.index = reference.index;
|
|
662
|
+
self.write_int(u16, reference.header.class);
|
|
663
|
+
self.write_int(u16, reference.header.weight);
|
|
664
|
+
self.write_int(u64, body_size);
|
|
665
|
+
self.index = restore_index;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
pub fn write_method_header(self: *Encoder, method_header: MethodHeader) void {
|
|
669
|
+
assert(self.frame_reference != null);
|
|
670
|
+
assert(self.frame_reference.?.frame_header.type == .method);
|
|
671
|
+
assert(self.header_reference == null);
|
|
672
|
+
self.write_int(u16, method_header.class);
|
|
673
|
+
self.write_int(u16, method_header.method);
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
fn FieldValueType(comptime target: enum { encode, decode }) type {
|
|
678
|
+
return union(FieldValueTag) {
|
|
679
|
+
boolean: bool,
|
|
680
|
+
uint8: u8,
|
|
681
|
+
int8: i8,
|
|
682
|
+
uint16: u16,
|
|
683
|
+
int16: i16,
|
|
684
|
+
uint32: u32,
|
|
685
|
+
int32: i32,
|
|
686
|
+
int64: i64,
|
|
687
|
+
string: []const u8,
|
|
688
|
+
timestamp: u64,
|
|
689
|
+
field_table: switch (target) {
|
|
690
|
+
.encode => Encoder.Table,
|
|
691
|
+
.decode => Decoder.Table,
|
|
692
|
+
},
|
|
693
|
+
void,
|
|
694
|
+
|
|
695
|
+
not_implemented_uint64,
|
|
696
|
+
not_implemented_field_array,
|
|
697
|
+
not_implemented_float,
|
|
698
|
+
not_implemented_double,
|
|
699
|
+
not_implemented_decimal,
|
|
700
|
+
not_implemented_byte_array,
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
fn BasicPropertiesType(comptime target: enum { encode, decode }) type {
|
|
705
|
+
return struct {
|
|
706
|
+
const BasicProperties = @This();
|
|
707
|
+
|
|
708
|
+
/// MIME content type of the message payload.
|
|
709
|
+
content_type: ?[]const u8 = null,
|
|
710
|
+
/// MIME content encoding of the message payload.
|
|
711
|
+
content_encoding: ?[]const u8 = null,
|
|
712
|
+
/// Application-defined custom headers.
|
|
713
|
+
headers: ?switch (target) {
|
|
714
|
+
.encode => Encoder.Table,
|
|
715
|
+
.decode => Decoder.Table,
|
|
716
|
+
} = null,
|
|
717
|
+
/// For queues that implement persistence,
|
|
718
|
+
/// whether the message will be logged to disk and survive a broker restart.
|
|
719
|
+
delivery_mode: ?DeliveryMode = null,
|
|
720
|
+
/// Message priority, 0 to 9.
|
|
721
|
+
priority: ?u8 = null,
|
|
722
|
+
/// Application-defined correlation identifier.
|
|
723
|
+
correlation_id: ?[]const u8 = null,
|
|
724
|
+
/// Address to reply to.
|
|
725
|
+
reply_to: ?[]const u8 = null,
|
|
726
|
+
/// Message expiration specification.
|
|
727
|
+
expiration: ?[]const u8 = null,
|
|
728
|
+
/// Application-defined message identifier.
|
|
729
|
+
message_id: ?[]const u8 = null,
|
|
730
|
+
/// Message timestamp (UNIX epoch in seconds).
|
|
731
|
+
timestamp: ?u64 = null,
|
|
732
|
+
/// Application-defined message type name.
|
|
733
|
+
type: ?[]const u8 = null,
|
|
734
|
+
/// Application-defined creating user id
|
|
735
|
+
user_id: ?[]const u8 = null,
|
|
736
|
+
/// Application-defined creating application id.
|
|
737
|
+
app_id: ?[]const u8 = null,
|
|
738
|
+
cluster_id: ?[]const u8 = null,
|
|
739
|
+
|
|
740
|
+
fn property_flags(self: *const BasicProperties) u16 {
|
|
741
|
+
var bitset: stdx.BitSetType(16) = .{};
|
|
742
|
+
inline for (std.meta.fields(BasicProperties), 0..) |field, index| {
|
|
743
|
+
bitset.set_value(index, @field(self, field.name) != null);
|
|
744
|
+
}
|
|
745
|
+
return @bitReverse(bitset.bits);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
pub fn decode(flags: u16, content: []const u8) Decoder.Error!BasicProperties {
|
|
749
|
+
comptime assert(target == .decode);
|
|
750
|
+
|
|
751
|
+
var reader = Decoder.init(content);
|
|
752
|
+
var bitset: stdx.BitSetType(16) = .{ .bits = @bitReverse(flags) };
|
|
753
|
+
var properties: BasicProperties = .{};
|
|
754
|
+
inline for (std.meta.fields(BasicProperties), 0..) |field, index| {
|
|
755
|
+
if (bitset.is_set(index)) {
|
|
756
|
+
const FieldType = std.meta.Child(field.type);
|
|
757
|
+
@field(properties, field.name) = try switch (FieldType) {
|
|
758
|
+
[]const u8 => reader.read_short_string(),
|
|
759
|
+
Decoder.Table => reader.read_table(),
|
|
760
|
+
DeliveryMode => reader.read_enum(DeliveryMode),
|
|
761
|
+
u64 => reader.read_int(u64),
|
|
762
|
+
u8 => reader.read_int(u8),
|
|
763
|
+
else => comptime unreachable,
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
assert(reader.index == content.len);
|
|
768
|
+
return properties;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
pub fn encode(self: *const BasicProperties, encoder: *Encoder) void {
|
|
772
|
+
comptime assert(target == .encode);
|
|
773
|
+
|
|
774
|
+
encoder.write_int(u16, self.property_flags());
|
|
775
|
+
inline for (std.meta.fields(BasicProperties)) |field| {
|
|
776
|
+
if (@field(self, field.name)) |value| {
|
|
777
|
+
switch (@TypeOf(value)) {
|
|
778
|
+
[]const u8 => encoder.write_short_string(value),
|
|
779
|
+
Encoder.Table => encoder.write_table(value),
|
|
780
|
+
DeliveryMode => encoder.write_int(u8, @intFromEnum(value)),
|
|
781
|
+
u64 => encoder.write_int(u64, value),
|
|
782
|
+
u8 => encoder.write_int(u8, value),
|
|
783
|
+
else => unreachable,
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/// Terminates the process with non-zero exit code.
|
|
792
|
+
/// Use fatal when encountering an environmental error.
|
|
793
|
+
/// Similar to `vsr.fatal`, but not logged in the `vsr` scope.
|
|
794
|
+
pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
|
|
795
|
+
const log = std.log.scoped(.amqp);
|
|
796
|
+
log.err(format, args);
|
|
797
|
+
|
|
798
|
+
const vsr = @import("../../vsr.zig");
|
|
799
|
+
const status = vsr.FatalReason.cli.exit_status();
|
|
800
|
+
assert(status != 0);
|
|
801
|
+
std.process.exit(status);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const testing = std.testing;
|
|
805
|
+
|
|
806
|
+
test "amqp: Encoder/Decoder primitives" {
|
|
807
|
+
var buffer = try testing.allocator.alloc(u8, frame_min_size);
|
|
808
|
+
defer testing.allocator.free(buffer);
|
|
809
|
+
|
|
810
|
+
const Primitives = enum {
|
|
811
|
+
bool,
|
|
812
|
+
uint64,
|
|
813
|
+
uint32,
|
|
814
|
+
uint16,
|
|
815
|
+
uint8,
|
|
816
|
+
short_string,
|
|
817
|
+
long_string,
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
var prng = stdx.PRNG.from_seed_testing();
|
|
821
|
+
for (0..4096) |_| {
|
|
822
|
+
var encoder = Encoder.init(buffer);
|
|
823
|
+
|
|
824
|
+
switch (prng.enum_uniform(Primitives)) {
|
|
825
|
+
.bool => {
|
|
826
|
+
const value = prng.boolean();
|
|
827
|
+
encoder.write_bool(value);
|
|
828
|
+
|
|
829
|
+
var decoder = Decoder.init(buffer[0..encoder.index]);
|
|
830
|
+
try testing.expectEqual(value, try decoder.read_bool());
|
|
831
|
+
},
|
|
832
|
+
inline .uint64, .uint32, .uint16, .uint8 => |tag| {
|
|
833
|
+
const Int = switch (tag) {
|
|
834
|
+
.uint64 => u64,
|
|
835
|
+
.uint32 => u32,
|
|
836
|
+
.uint16 => u16,
|
|
837
|
+
.uint8 => u8,
|
|
838
|
+
else => comptime unreachable,
|
|
839
|
+
};
|
|
840
|
+
const value = prng.int(Int);
|
|
841
|
+
encoder.write_int(Int, value);
|
|
842
|
+
|
|
843
|
+
var decoder = Decoder.init(buffer[0..encoder.index]);
|
|
844
|
+
try testing.expectEqual(value, try decoder.read_int(Int));
|
|
845
|
+
},
|
|
846
|
+
.short_string => {
|
|
847
|
+
const size = prng.range_inclusive(u32, 0, 255);
|
|
848
|
+
const value = try testing.allocator.alloc(u8, size);
|
|
849
|
+
defer testing.allocator.free(value);
|
|
850
|
+
|
|
851
|
+
prng.fill(value);
|
|
852
|
+
encoder.write_short_string(value);
|
|
853
|
+
|
|
854
|
+
var decoder = Decoder.init(buffer[0..encoder.index]);
|
|
855
|
+
try testing.expectEqualStrings(value, try decoder.read_short_string());
|
|
856
|
+
},
|
|
857
|
+
.long_string => {
|
|
858
|
+
const size = prng.range_inclusive(u32, 256, frame_min_size - @sizeOf(u32));
|
|
859
|
+
const value = try testing.allocator.alloc(u8, size);
|
|
860
|
+
defer testing.allocator.free(value);
|
|
861
|
+
|
|
862
|
+
prng.fill(value);
|
|
863
|
+
encoder.write_long_string(value);
|
|
864
|
+
|
|
865
|
+
var decoder = Decoder.init(buffer[0..encoder.index]);
|
|
866
|
+
try testing.expectEqualStrings(value, try decoder.read_long_string());
|
|
867
|
+
},
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
test "amqp: Encoder/Decoder enums" {
|
|
873
|
+
var buffer = try testing.allocator.alloc(u8, frame_min_size);
|
|
874
|
+
defer testing.allocator.free(buffer);
|
|
875
|
+
|
|
876
|
+
const Enum = enum(u8) {
|
|
877
|
+
a = 1,
|
|
878
|
+
b = 2,
|
|
879
|
+
c = 3,
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
for (std.enums.values(Enum)) |value| {
|
|
883
|
+
var encoder: Encoder = Encoder.init(buffer);
|
|
884
|
+
encoder.write_int(u8, @intFromEnum(value));
|
|
885
|
+
|
|
886
|
+
var decoder: Decoder = Decoder.init(buffer[0..buffer.len]);
|
|
887
|
+
try testing.expectEqual(value, try decoder.read_enum(Enum));
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// Invalid enum:
|
|
891
|
+
var encoder: Encoder = Encoder.init(buffer);
|
|
892
|
+
encoder.write_int(u8, 0);
|
|
893
|
+
|
|
894
|
+
var decoder: Decoder = Decoder.init(buffer[0..buffer.len]);
|
|
895
|
+
try testing.expectError(error.Unexpected, decoder.read_enum(Enum));
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
test "amqp: BasicProperties property_flags" {
|
|
899
|
+
// Sets the field with any value, just to compute the `property_flags`.
|
|
900
|
+
const BasicProperties = BasicPropertiesType(.decode);
|
|
901
|
+
const set_flag = struct {
|
|
902
|
+
fn set_flag(set_field: std.meta.FieldEnum(BasicProperties)) u16 {
|
|
903
|
+
var properties: BasicProperties = .{};
|
|
904
|
+
switch (set_field) {
|
|
905
|
+
inline else => |field| {
|
|
906
|
+
const Field = std.meta.Child(@FieldType(BasicProperties, @tagName(field)));
|
|
907
|
+
@field(properties, @tagName(field)) = switch (Field) {
|
|
908
|
+
[]const u8 => "",
|
|
909
|
+
DeliveryMode => .persistent,
|
|
910
|
+
u8, u64 => 0,
|
|
911
|
+
Decoder.Table => Decoder.Table.init(&.{}),
|
|
912
|
+
else => comptime unreachable,
|
|
913
|
+
};
|
|
914
|
+
},
|
|
915
|
+
}
|
|
916
|
+
return properties.property_flags();
|
|
917
|
+
}
|
|
918
|
+
}.set_flag;
|
|
919
|
+
|
|
920
|
+
const empty: BasicProperties = .{};
|
|
921
|
+
try testing.expectEqual(@as(u16, 0x0000), empty.property_flags());
|
|
922
|
+
|
|
923
|
+
// The last bit corresponding to the first property (it's big endian).
|
|
924
|
+
try testing.expectEqual(@as(u16, 0x8000), set_flag(.content_type));
|
|
925
|
+
try testing.expectEqual(@as(u16, 0x4000), set_flag(.content_encoding));
|
|
926
|
+
try testing.expectEqual(@as(u16, 0x2000), set_flag(.headers));
|
|
927
|
+
try testing.expectEqual(@as(u16, 0x1000), set_flag(.delivery_mode));
|
|
928
|
+
try testing.expectEqual(@as(u16, 0x0800), set_flag(.priority));
|
|
929
|
+
try testing.expectEqual(@as(u16, 0x0400), set_flag(.correlation_id));
|
|
930
|
+
try testing.expectEqual(@as(u16, 0x0200), set_flag(.reply_to));
|
|
931
|
+
try testing.expectEqual(@as(u16, 0x0100), set_flag(.expiration));
|
|
932
|
+
try testing.expectEqual(@as(u16, 0x0080), set_flag(.message_id));
|
|
933
|
+
try testing.expectEqual(@as(u16, 0x0040), set_flag(.timestamp));
|
|
934
|
+
try testing.expectEqual(@as(u16, 0x0020), set_flag(.type));
|
|
935
|
+
try testing.expectEqual(@as(u16, 0x0010), set_flag(.user_id));
|
|
936
|
+
try testing.expectEqual(@as(u16, 0x0008), set_flag(.app_id));
|
|
937
|
+
try testing.expectEqual(@as(u16, 0x0004), set_flag(.cluster_id));
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
test "amqp: BasicProperties encode/decode" {
|
|
941
|
+
var buffer = try testing.allocator.alloc(u8, frame_min_size);
|
|
942
|
+
defer testing.allocator.free(buffer);
|
|
943
|
+
|
|
944
|
+
var prng = stdx.PRNG.from_seed_testing();
|
|
945
|
+
for (0..4096) |_| {
|
|
946
|
+
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
|
947
|
+
defer arena.deinit();
|
|
948
|
+
|
|
949
|
+
const properties = try TestingBasicProperties.random(.{
|
|
950
|
+
.arena = arena.allocator(),
|
|
951
|
+
.prng = &prng,
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
var encoder = Encoder.init(buffer);
|
|
955
|
+
properties.encode(&encoder);
|
|
956
|
+
|
|
957
|
+
// Decoding:
|
|
958
|
+
var decoder = Decoder.init(buffer[0..encoder.index]);
|
|
959
|
+
const flags = try decoder.read_int(u16);
|
|
960
|
+
const properties_decoded = try Decoder.BasicProperties.decode(
|
|
961
|
+
flags,
|
|
962
|
+
decoder.buffer[decoder.index..],
|
|
963
|
+
);
|
|
964
|
+
try testing.expect(try TestingBasicProperties.eql(
|
|
965
|
+
arena.allocator(),
|
|
966
|
+
properties,
|
|
967
|
+
properties_decoded,
|
|
968
|
+
));
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
test "amqp: Table encode/decode" {
|
|
973
|
+
// 64k ought to be enough for any random!
|
|
974
|
+
var buffer = try testing.allocator.alloc(u8, 64 * KiB);
|
|
975
|
+
defer testing.allocator.free(buffer);
|
|
976
|
+
|
|
977
|
+
var prng = stdx.PRNG.from_seed_testing();
|
|
978
|
+
for (0..4096) |_| {
|
|
979
|
+
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
|
980
|
+
defer arena.deinit();
|
|
981
|
+
|
|
982
|
+
const object = try TestingTable.random(.{
|
|
983
|
+
.arena = arena.allocator(),
|
|
984
|
+
.prng = &prng,
|
|
985
|
+
.recursive = true,
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
// Encoding the complex object:
|
|
989
|
+
var encoder = Encoder.init(buffer);
|
|
990
|
+
encoder.write_table(object.table());
|
|
991
|
+
|
|
992
|
+
// Decoding:
|
|
993
|
+
var decoder = Decoder.init(buffer[0..encoder.index]);
|
|
994
|
+
const object_decoded = try TestingTable.from_table(
|
|
995
|
+
arena.allocator(),
|
|
996
|
+
try decoder.read_table(),
|
|
997
|
+
);
|
|
998
|
+
try testing.expect(TestingTable.eql(object, object_decoded));
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
test "amqp: frame and header" {
|
|
1003
|
+
const Snap = stdx.Snap;
|
|
1004
|
+
const snap = Snap.snap_fn("src");
|
|
1005
|
+
|
|
1006
|
+
var buffer = try testing.allocator.alloc(u8, frame_min_size);
|
|
1007
|
+
defer testing.allocator.free(buffer);
|
|
1008
|
+
|
|
1009
|
+
{
|
|
1010
|
+
// Method frame.
|
|
1011
|
+
var encoder = Encoder.init(buffer);
|
|
1012
|
+
encoder.begin_frame(.{ .type = .method, .channel = .global });
|
|
1013
|
+
encoder.write_method_header(.{ .class = 1, .method = 10 });
|
|
1014
|
+
encoder.finish_frame(.method);
|
|
1015
|
+
try snap(@src(),
|
|
1016
|
+
\\01 00 00 00 00 00 04 00 01 00 0a ce
|
|
1017
|
+
).diff_hex(buffer[0..encoder.index]);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
{
|
|
1021
|
+
// Method + header.
|
|
1022
|
+
var encoder = Encoder.init(buffer);
|
|
1023
|
+
encoder.begin_frame(.{ .type = .method, .channel = .global });
|
|
1024
|
+
encoder.write_method_header(.{ .class = 10, .method = 100 });
|
|
1025
|
+
encoder.finish_frame(.method);
|
|
1026
|
+
|
|
1027
|
+
encoder.begin_frame(.{ .type = .header, .channel = .current });
|
|
1028
|
+
encoder.begin_header(.{ .class = 10, .weight = 0 });
|
|
1029
|
+
encoder.finish_frame(.header);
|
|
1030
|
+
encoder.finish_header(0);
|
|
1031
|
+
|
|
1032
|
+
try snap(@src(),
|
|
1033
|
+
\\01 00 00 00 00 00 04 00 0a 00 64 ce 02 00 01 00
|
|
1034
|
+
\\00 00 0c 00 0a 00 00 00 00 00 00 00 00 00 00 ce
|
|
1035
|
+
).diff_hex(buffer[0..encoder.index]);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
{
|
|
1039
|
+
// Method + header + body.
|
|
1040
|
+
var encoder = Encoder.init(buffer);
|
|
1041
|
+
encoder.begin_frame(.{ .type = .method, .channel = .global });
|
|
1042
|
+
encoder.write_method_header(.{ .class = 100, .method = 1000 });
|
|
1043
|
+
encoder.finish_frame(.method);
|
|
1044
|
+
|
|
1045
|
+
encoder.begin_frame(.{ .type = .header, .channel = .current });
|
|
1046
|
+
encoder.begin_header(.{ .class = 100, .weight = 0 });
|
|
1047
|
+
encoder.finish_frame(.header);
|
|
1048
|
+
|
|
1049
|
+
encoder.begin_frame(.{ .type = .body, .channel = .current });
|
|
1050
|
+
encoder.write_bytes("body");
|
|
1051
|
+
encoder.finish_header("body".len);
|
|
1052
|
+
encoder.finish_frame(.body);
|
|
1053
|
+
|
|
1054
|
+
try snap(@src(),
|
|
1055
|
+
\\01 00 00 00 00 00 04 00 64 03 e8 ce 02 00 01 00
|
|
1056
|
+
\\00 00 0c 00 64 00 00 00 00 00 00 00 00 00 04 ce
|
|
1057
|
+
\\03 00 01 00 00 00 04 62 6f 64 79 ce
|
|
1058
|
+
).diff_hex(buffer[0..encoder.index]);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
const TestingTable = struct {
|
|
1063
|
+
const Timestamp = u63;
|
|
1064
|
+
|
|
1065
|
+
boolean: ?bool = null,
|
|
1066
|
+
string: ?[]const u8 = null,
|
|
1067
|
+
int64: ?i64 = null,
|
|
1068
|
+
uint32: ?u32 = null,
|
|
1069
|
+
int32: ?i32 = null,
|
|
1070
|
+
uint16: ?u16 = null,
|
|
1071
|
+
int16: ?i16 = null,
|
|
1072
|
+
uint8: ?u8 = null,
|
|
1073
|
+
int8: ?i8 = null,
|
|
1074
|
+
field_table: ?*const TestingTable = null,
|
|
1075
|
+
timestamp: ?Timestamp = null,
|
|
1076
|
+
|
|
1077
|
+
const empty: TestingTable = .{};
|
|
1078
|
+
|
|
1079
|
+
fn table(self: *const TestingTable) Encoder.Table {
|
|
1080
|
+
const vtable: Encoder.Table.VTable = comptime .{
|
|
1081
|
+
.write = &struct {
|
|
1082
|
+
fn write(context: *const anyopaque, encoder: *Encoder.TableEncoder) void {
|
|
1083
|
+
const object: *const TestingTable = @ptrCast(@alignCast(context));
|
|
1084
|
+
inline for (std.meta.fields(TestingTable)) |field| {
|
|
1085
|
+
if (@field(object, field.name)) |value| {
|
|
1086
|
+
encoder.put(field.name, switch (std.meta.Child(field.type)) {
|
|
1087
|
+
bool => .{ .boolean = value },
|
|
1088
|
+
[]const u8 => .{ .string = value },
|
|
1089
|
+
i64 => .{ .int64 = value },
|
|
1090
|
+
u32 => .{ .uint32 = value },
|
|
1091
|
+
i32 => .{ .int32 = value },
|
|
1092
|
+
u16 => .{ .uint16 = value },
|
|
1093
|
+
i16 => .{ .int16 = value },
|
|
1094
|
+
u8 => .{ .uint8 = value },
|
|
1095
|
+
i8 => .{ .int8 = value },
|
|
1096
|
+
*const TestingTable => .{ .field_table = value.table() },
|
|
1097
|
+
Timestamp => .{ .timestamp = value },
|
|
1098
|
+
else => comptime unreachable,
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}.write,
|
|
1104
|
+
};
|
|
1105
|
+
return .{ .context = self, .vtable = &vtable };
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
fn from_table(arena: std.mem.Allocator, decoder: Decoder.Table) !*const TestingTable {
|
|
1109
|
+
var object = try arena.create(TestingTable);
|
|
1110
|
+
object.* = TestingTable.empty;
|
|
1111
|
+
|
|
1112
|
+
var iterator = decoder.iterator();
|
|
1113
|
+
while (try iterator.next()) |entry| {
|
|
1114
|
+
const FieldEnum = std.meta.FieldEnum(TestingTable);
|
|
1115
|
+
const entry_field = std.meta.stringToEnum(FieldEnum, entry.key).?;
|
|
1116
|
+
switch (entry_field) {
|
|
1117
|
+
inline else => |field| {
|
|
1118
|
+
const Field = @FieldType(TestingTable, @tagName(field));
|
|
1119
|
+
@field(object, @tagName(field)) = switch (std.meta.Child(Field)) {
|
|
1120
|
+
bool => entry.value.boolean,
|
|
1121
|
+
[]const u8 => entry.value.string,
|
|
1122
|
+
i64 => entry.value.int64,
|
|
1123
|
+
u32 => entry.value.uint32,
|
|
1124
|
+
i32 => entry.value.int32,
|
|
1125
|
+
u16 => entry.value.uint16,
|
|
1126
|
+
i16 => entry.value.int16,
|
|
1127
|
+
u8 => entry.value.uint8,
|
|
1128
|
+
i8 => entry.value.int8,
|
|
1129
|
+
*const TestingTable => try from_table(arena, entry.value.field_table),
|
|
1130
|
+
Timestamp => @intCast(entry.value.timestamp),
|
|
1131
|
+
else => comptime unreachable,
|
|
1132
|
+
};
|
|
1133
|
+
},
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
return object;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
fn eql(table1: *const TestingTable, table2: *const TestingTable) bool {
|
|
1140
|
+
inline for (std.meta.fields(TestingTable)) |field| {
|
|
1141
|
+
const both_null = @field(table1, field.name) == null and
|
|
1142
|
+
@field(table2, field.name) == null;
|
|
1143
|
+
if (!both_null) {
|
|
1144
|
+
const value1 = @field(table1, field.name) orelse return false;
|
|
1145
|
+
const value2 = @field(table2, field.name) orelse return false;
|
|
1146
|
+
|
|
1147
|
+
const equals = switch (std.meta.Child(field.type)) {
|
|
1148
|
+
bool => value1 == value2,
|
|
1149
|
+
[]const u8 => std.mem.eql(u8, value1, value2),
|
|
1150
|
+
i64, u32, i32, u16, i16, u8, i8 => value1 == value2,
|
|
1151
|
+
*const TestingTable => eql(value1, value2),
|
|
1152
|
+
Timestamp => value1 == value2,
|
|
1153
|
+
else => comptime unreachable,
|
|
1154
|
+
};
|
|
1155
|
+
if (!equals) return false;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
return true;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
fn random(options: struct {
|
|
1163
|
+
arena: std.mem.Allocator,
|
|
1164
|
+
prng: *stdx.PRNG,
|
|
1165
|
+
recursive: bool,
|
|
1166
|
+
}) !*const TestingTable {
|
|
1167
|
+
const ratio = stdx.PRNG.ratio;
|
|
1168
|
+
|
|
1169
|
+
const is_empty = options.prng.chance(ratio(5, 100));
|
|
1170
|
+
if (is_empty) return &TestingTable.empty;
|
|
1171
|
+
|
|
1172
|
+
var object = try options.arena.create(TestingTable);
|
|
1173
|
+
inline for (std.meta.fields(TestingTable)) |field| {
|
|
1174
|
+
const is_null = options.prng.chance(ratio(5, 100));
|
|
1175
|
+
if (is_null) {
|
|
1176
|
+
@field(object, field.name) = null;
|
|
1177
|
+
} else switch (std.meta.Child(field.type)) {
|
|
1178
|
+
bool => {
|
|
1179
|
+
@field(object, field.name) = options.prng.boolean();
|
|
1180
|
+
},
|
|
1181
|
+
[]const u8 => {
|
|
1182
|
+
const size = options.prng.range_inclusive(u32, 0, 255);
|
|
1183
|
+
const str = try options.arena.alloc(u8, size);
|
|
1184
|
+
options.prng.fill(str);
|
|
1185
|
+
@field(object, field.name) = str;
|
|
1186
|
+
},
|
|
1187
|
+
u32, u16, u8 => |Int| {
|
|
1188
|
+
@field(object, field.name) = options.prng.int(Int);
|
|
1189
|
+
},
|
|
1190
|
+
i64, i32, i16, i8 => |Int| {
|
|
1191
|
+
const Unsigned = std.meta.Int(.unsigned, @bitSizeOf(Int));
|
|
1192
|
+
@field(object, field.name) = @bitCast(options.prng.int(Unsigned));
|
|
1193
|
+
},
|
|
1194
|
+
*const TestingTable => {
|
|
1195
|
+
@field(object, field.name) = if (options.recursive)
|
|
1196
|
+
try random(options)
|
|
1197
|
+
else
|
|
1198
|
+
null;
|
|
1199
|
+
},
|
|
1200
|
+
Timestamp => {
|
|
1201
|
+
@field(object, field.name) = options.prng.int(Timestamp);
|
|
1202
|
+
},
|
|
1203
|
+
else => comptime unreachable,
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return object;
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
pub const TestingBasicProperties = struct {
|
|
1211
|
+
pub fn random(options: struct {
|
|
1212
|
+
arena: std.mem.Allocator,
|
|
1213
|
+
prng: *stdx.PRNG,
|
|
1214
|
+
default: Encoder.BasicProperties = .{},
|
|
1215
|
+
}) !Encoder.BasicProperties {
|
|
1216
|
+
const is_null = stdx.PRNG.ratio(5, 100);
|
|
1217
|
+
var properties: Encoder.BasicProperties = .{};
|
|
1218
|
+
inline for (std.meta.fields(Encoder.BasicProperties)) |field| {
|
|
1219
|
+
if (@field(options.default, field.name)) |default| {
|
|
1220
|
+
@field(properties, field.name) = default;
|
|
1221
|
+
} else if (options.prng.chance(is_null)) {
|
|
1222
|
+
@field(properties, field.name) = null;
|
|
1223
|
+
} else switch (std.meta.Child(field.type)) {
|
|
1224
|
+
[]const u8 => {
|
|
1225
|
+
const size = options.prng.range_inclusive(u32, 0, 255);
|
|
1226
|
+
const str = try options.arena.alloc(u8, size);
|
|
1227
|
+
options.prng.fill(str);
|
|
1228
|
+
@field(properties, field.name) = str;
|
|
1229
|
+
},
|
|
1230
|
+
u64, u8 => |Int| {
|
|
1231
|
+
@field(properties, field.name) = options.prng.int(Int);
|
|
1232
|
+
},
|
|
1233
|
+
DeliveryMode => {
|
|
1234
|
+
@field(properties, field.name) = options.prng.enum_uniform(DeliveryMode);
|
|
1235
|
+
},
|
|
1236
|
+
Encoder.Table => {
|
|
1237
|
+
const object = try TestingTable.random(.{
|
|
1238
|
+
.arena = options.arena,
|
|
1239
|
+
.prng = options.prng,
|
|
1240
|
+
.recursive = false,
|
|
1241
|
+
});
|
|
1242
|
+
@field(properties, field.name) = object.table();
|
|
1243
|
+
},
|
|
1244
|
+
else => comptime unreachable,
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
return properties;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
pub fn eql(
|
|
1251
|
+
arena: std.mem.Allocator,
|
|
1252
|
+
properties1: Encoder.BasicProperties,
|
|
1253
|
+
properties2: Decoder.BasicProperties,
|
|
1254
|
+
) !bool {
|
|
1255
|
+
inline for (std.meta.fields(Encoder.BasicProperties)) |field| {
|
|
1256
|
+
const both_null = @field(properties1, field.name) == null and
|
|
1257
|
+
@field(properties2, field.name) == null;
|
|
1258
|
+
if (!both_null) {
|
|
1259
|
+
const value1 = @field(properties1, field.name) orelse return false;
|
|
1260
|
+
const value2 = @field(properties2, field.name) orelse return false;
|
|
1261
|
+
|
|
1262
|
+
const equals = switch (std.meta.Child(field.type)) {
|
|
1263
|
+
[]const u8 => std.mem.eql(u8, value1, value2),
|
|
1264
|
+
u64, u8 => value1 == value2,
|
|
1265
|
+
DeliveryMode => value1 == value2,
|
|
1266
|
+
Encoder.Table => eql: {
|
|
1267
|
+
const encoded_object: *const TestingTable = @ptrCast(@alignCast(
|
|
1268
|
+
value1.context,
|
|
1269
|
+
));
|
|
1270
|
+
const decoded_object: *const TestingTable = try TestingTable.from_table(
|
|
1271
|
+
arena,
|
|
1272
|
+
value2,
|
|
1273
|
+
);
|
|
1274
|
+
break :eql TestingTable.eql(encoded_object, decoded_object);
|
|
1275
|
+
},
|
|
1276
|
+
else => comptime unreachable,
|
|
1277
|
+
};
|
|
1278
|
+
if (!equals) return false;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
return true;
|
|
1282
|
+
}
|
|
1283
|
+
};
|