1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! The guest and host ABI for iris self-decoding datasets.
//!
//! The ABI is the only surface in iris that can ossify, so it is shaped like a wire protocol: length prefixed records, negotiated capabilities, and a defined way for either side to refuse politely.
//!
//! This crate is `no_std` and has no dependencies at all, and both of those are checked by CI. It
//! ends up inside every decoder anyone writes, so anything it pulls in, everyone pays for.
//!
//! # How a conversation goes
//!
//! The host sends a [`Hello`] saying which ABI version it speaks and what it can do. The decoder
//! answers with a [`HelloAck`] saying what it needs. [`negotiate`] compares the two and either
//! produces an [`Agreement`] or a [`Refusal`] that names the capability that was missing. After
//! that the host sends [`ScanRequest`] records and the rows come back as [`Batch`] records. When the
//! decoder needs bytes it has not been given it asks for them, as a [`RangeRequest`] record where
//! there is a channel to put one on and as a call answered by a [`RangeStatus`] where the decoder is
//! stopped inside the request waiting for it.
//!
//! ```
//! use iris_abi::{Capability, CapabilitySet, Hello, HelloAck, negotiate};
//!
//! let host = Hello {
//! abi_major: iris_abi::ABI_MAJOR,
//! abi_minor: iris_abi::ABI_MINOR,
//! window_bytes: 64 << 20,
//! max_batch_rows: 8192,
//! offered: CapabilitySet::new()
//! .with(Capability::REQUIRE_RANGE)
//! .with(Capability::SLIDING_WINDOW),
//! source_bytes: 4 << 30,
//! };
//! let decoder = HelloAck {
//! abi_major: iris_abi::ABI_MAJOR,
//! abi_minor: iris_abi::ABI_MINOR,
//! required: CapabilitySet::new().with(Capability::REQUIRE_RANGE),
//! optional: CapabilitySet::new().with(Capability::PROJECTION),
//! decoder_id: "example",
//! };
//!
//! let agreed = negotiate(&host, &decoder).expect("the host offers what the decoder needs");
//! assert!(agreed.has(Capability::REQUIRE_RANGE));
//! // The host offers sliding windows but this decoder never asked for them, so it is not on.
//! assert!(!agreed.has(Capability::SLIDING_WINDOW));
//! // The decoder would like projection pushdown but this host does not do it, and that is fine
//! // because it was optional.
//! assert!(!agreed.has(Capability::PROJECTION));
//! ```
//!
//! # What is allowed to change
//!
//! Adding a field to the end of a record is allowed and does not bump that record's version, because
//! a reader that does not know about the field steps over it. Adding a record is allowed, because a
//! reader that does not know a tag steps over the whole record. Adding a capability is allowed,
//! because a side that does not offer it says so and the other side decides what to do.
//!
//! Removing a field, reordering fields, changing what a field means, or changing what a capability
//! bit means are all breaking, and all of them are supposed to be loud rather than silent. The
//! tests in `tests/forward_compat.rs` hold the compatible half of that line.
// Nothing in here parses untrusted bytes with a pointer. If a future change needs to, it needs a
// conversation first, because this crate is the one piece of iris that runs inside every decoder
// anybody writes.
pub use ;
pub use ;
pub use ;
pub use ;
pub use RangeStatus;
pub use ;
pub use ;
/// The major ABI version this build speaks.
///
/// Zero means the record layouts are still allowed to move. When this goes to one it means the
/// layouts in [`message`] are frozen, and freezing them is a milestone with a written compatibility
/// note behind it rather than something that happens because a refactor felt finished.
pub const ABI_MAJOR: u16 = 0;
/// The minor ABI version this build speaks.
///
/// This goes up when a field or a record or a capability is added. Two sides at different minor
/// versions can always talk to each other, and they settle on the lower of the two.
pub const ABI_MINOR: u16 = 3;