iris_format/meta.rs
1//! The records that make up the footer.
2//!
3//! Each one encodes and decodes itself against the `iris-abi` reader and writer, so the framing
4//! rules are the ones already written down in `docs/ABI.md`. In particular a record may grow a
5//! field at the end without a version bump, and a reader that does not know about the field steps
6//! over it, which is why every `decode` here reads what it knows and stops.
7
8use iris_abi::{CapabilitySet, Reader, Result as AbiResult, Writer};
9
10use crate::digest::Digest;
11use crate::layout::{DIGEST_SIZE, DecoderLocation, SchemaEncoding, SectionKind};
12
13/// What the dataset is and how big it is.
14#[derive(Clone, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
15pub struct Dataset {
16 /// How many rows there are in total.
17 ///
18 /// A `u64` because a `u32` runs out at four billion rows, which is a size people already have.
19 pub rows: u64,
20 /// A human readable name, for error messages and for `iris describe`. Not an identifier and
21 /// nothing looks it up.
22 pub name: String,
23}
24
25impl Dataset {
26 /// The record version this build writes.
27 pub const VERSION: u16 = 1;
28
29 /// Writes this record's payload.
30 ///
31 /// # Errors
32 ///
33 /// Returns whatever the writer returns, which in practice means the buffer was too small.
34 pub fn encode(&self, w: &mut Writer<'_>) -> AbiResult<()> {
35 w.u64(self.rows)?;
36 w.var_str(&self.name)
37 }
38
39 /// Reads this record's payload.
40 ///
41 /// # Errors
42 ///
43 /// Returns [`iris_abi::Error`] if the payload is truncated or the name is not UTF-8.
44 pub fn decode(p: &mut Reader<'_>) -> AbiResult<Self> {
45 Ok(Self {
46 rows: p.u64()?,
47 name: p.var_str()?.to_owned(),
48 })
49 }
50}
51
52/// The Arrow schema, carried as bytes this crate does not look inside.
53#[derive(Clone, PartialEq, Eq, Debug)]
54pub struct Schema<'a> {
55 /// How the bytes are encoded.
56 pub encoding: SchemaEncoding,
57 /// The bytes.
58 pub bytes: &'a [u8],
59}
60
61impl<'a> Schema<'a> {
62 /// The record version this build writes.
63 pub const VERSION: u16 = 1;
64
65 /// Writes this record's payload.
66 ///
67 /// # Errors
68 ///
69 /// Returns whatever the writer returns.
70 pub fn encode(&self, w: &mut Writer<'_>) -> AbiResult<()> {
71 w.u32(self.encoding.code())?;
72 w.var_bytes(self.bytes)
73 }
74
75 /// Reads this record's payload.
76 ///
77 /// # Errors
78 ///
79 /// Returns [`iris_abi::Error`] if the payload is truncated.
80 pub fn decode(p: &mut Reader<'a>) -> AbiResult<Self> {
81 Ok(Self {
82 encoding: SchemaEncoding::from_code(p.u32()?),
83 bytes: p.var_bytes()?,
84 })
85 }
86}
87
88/// Which decoder reads this dataset.
89#[derive(Clone, PartialEq, Eq, Debug)]
90pub struct DecoderRef<'a> {
91 /// The ABI major version the decoder was built against.
92 pub abi_major: u16,
93 /// The ABI minor version the decoder was built against.
94 pub abi_minor: u16,
95 /// Where the module is.
96 pub location: DecoderLocation,
97 /// The digest of the module.
98 ///
99 /// This is the identity of the decoder. A host that already trusts a native implementation of
100 /// this exact module substitutes it here and skips the sandbox, and a host that does not runs
101 /// the bytes it hashed.
102 pub digest: Digest,
103 /// What the decoder needs the host to be able to do.
104 ///
105 /// Carrying this in the container as well as in the handshake means a host can refuse a dataset
106 /// before it has loaded a single instruction of it, which is a much better error than one that
107 /// arrives halfway through a query.
108 pub required: CapabilitySet,
109 /// A human readable name, for error messages.
110 pub name: &'a str,
111}
112
113impl<'a> DecoderRef<'a> {
114 /// The record version this build writes.
115 pub const VERSION: u16 = 1;
116
117 /// Writes this record's payload.
118 ///
119 /// # Errors
120 ///
121 /// Returns whatever the writer returns.
122 pub fn encode(&self, w: &mut Writer<'_>) -> AbiResult<()> {
123 w.u16(self.abi_major)?;
124 w.u16(self.abi_minor)?;
125 let (kind, section) = match self.location {
126 DecoderLocation::Embedded { section } => (0, section),
127 DecoderLocation::External => (1, 0),
128 };
129 w.u32(kind)?;
130 w.u32(section)?;
131 w.u32(0)?;
132 w.raw(self.digest.as_bytes())?;
133 w.var_bytes(self.required.as_bytes())?;
134 w.var_str(self.name)
135 }
136
137 /// Reads this record's payload.
138 ///
139 /// An unknown location kind decodes as [`DecoderLocation::External`], because the one thing a
140 /// reader must not do with a decoder it cannot place is run something else.
141 ///
142 /// # Errors
143 ///
144 /// Returns [`iris_abi::Error`] if the payload is truncated or the name is not UTF-8.
145 pub fn decode(p: &mut Reader<'a>) -> AbiResult<Self> {
146 let abi_major = p.u16()?;
147 let abi_minor = p.u16()?;
148 let kind = p.u32()?;
149 let section = p.u32()?;
150 let _reserved = p.u32()?;
151 let raw = p.bytes(DIGEST_SIZE)?;
152 let mut digest = [0u8; DIGEST_SIZE];
153 digest.copy_from_slice(raw);
154 Ok(Self {
155 abi_major,
156 abi_minor,
157 location: if kind == 0 {
158 DecoderLocation::Embedded { section }
159 } else {
160 DecoderLocation::External
161 },
162 digest: Digest(digest),
163 required: p.capability_set()?,
164 name: p.var_str()?,
165 })
166 }
167}
168
169/// One run of bytes in the payload area.
170#[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
171pub struct Section {
172 /// What the rest of the container refers to this section by.
173 pub id: u32,
174 /// What it holds.
175 pub kind: SectionKind,
176 /// Where it starts, counted from the start of the file.
177 pub offset: u64,
178 /// How many bytes long it is.
179 pub len: u64,
180 /// The digest of those bytes.
181 pub digest: Digest,
182}
183
184impl Section {
185 /// The record version this build writes.
186 pub const VERSION: u16 = 1;
187
188 /// Where the section ends, or `None` if the arithmetic overflows.
189 ///
190 /// The `Option` is the point. These two numbers came out of a file somebody else wrote, and the
191 /// first thing a hostile one does is pick an offset and a length that add up to something
192 /// small.
193 #[must_use]
194 pub const fn end(&self) -> Option<u64> {
195 self.offset.checked_add(self.len)
196 }
197
198 /// Writes this record's payload.
199 ///
200 /// # Errors
201 ///
202 /// Returns whatever the writer returns.
203 pub fn encode(&self, w: &mut Writer<'_>) -> AbiResult<()> {
204 w.u32(self.id)?;
205 w.u32(self.kind.code())?;
206 w.u64(self.offset)?;
207 w.u64(self.len)?;
208 w.raw(self.digest.as_bytes())
209 }
210
211 /// Reads this record's payload.
212 ///
213 /// # Errors
214 ///
215 /// Returns [`iris_abi::Error`] if the payload is truncated.
216 pub fn decode(p: &mut Reader<'_>) -> AbiResult<Self> {
217 let id = p.u32()?;
218 let kind = SectionKind::from_code(p.u32()?);
219 let offset = p.u64()?;
220 let len = p.u64()?;
221 let raw = p.bytes(DIGEST_SIZE)?;
222 let mut digest = [0u8; DIGEST_SIZE];
223 digest.copy_from_slice(raw);
224 Ok(Self {
225 id,
226 kind,
227 offset,
228 len,
229 digest: Digest(digest),
230 })
231 }
232}