orderable_bytes/lib.rs
1#![deny(missing_docs)]
2// Library code does not panic: it returns an error or carries the invariant
3// in a type. Tests may. Clippy has no lint for `assert!`;
4// `scripts/no-runtime-asserts.py` covers that in CI.
5#![cfg_attr(
6 not(test),
7 deny(
8 clippy::panic,
9 clippy::unwrap_used,
10 clippy::expect_used,
11 clippy::unreachable,
12 clippy::todo,
13 clippy::unimplemented,
14 clippy::panic_in_result_fn
15 )
16)]
17//! Canonical, order-preserving byte encodings for plaintext types.
18//!
19//! Each supported type implements [`OrderableBytes`], which maps a
20//! value to bytes whose byte-wise lexicographic order agrees with the
21//! type's natural total order, and whose byte equality agrees with the
22//! type's value equality. The resulting bytes are scheme-agnostic —
23//! they're intended for any comparison-as-bytes scheme that wants to
24//! preserve plaintext order on ciphertexts (e.g. `ore-rs` BlockORE, an
25//! OPE construction, an ordered hash).
26//!
27//! Every type also implements exactly one of two subtraits, which say
28//! how its bytes may be handled:
29//!
30//! - [`FixedOrderableBytes`]: every value encodes to the same length,
31//! [`ENCODED_LEN`](FixedOrderableBytes::ENCODED_LEN), available as an
32//! owned array. Numbers, `bool`, `char`, byte arrays `[u8; N]`,
33//! `Decimal` and the `chrono` types.
34//! - [`VariableOrderableBytes`]: the length varies with the value, and
35//! the bytes are borrowed from it. Strings and byte strings (see
36//! [`variable`]).
37//!
38//! A reference `&T` implements the same traits as `T`.
39//!
40//! | Need | Bound | Call |
41//! |---|---|---|
42//! | Pad, store or return fixed-length bytes | `T: FixedOrderableBytes` | [`to_fixed_orderable_bytes`](FixedOrderableBytes::to_fixed_orderable_bytes) (owned) |
43//! | Bytes of either kind, used immediately | `T: OrderableBytes` | [`to_orderable_bytes`](OrderableBytes::to_orderable_bytes) (may borrow) |
44//! | Strings or byte strings specifically | `T: VariableOrderableBytes` | [`to_orderable_bytes`](OrderableBytes::to_orderable_bytes) (borrowed) |
45//!
46//! Padding to a block size is only safe for [`FixedOrderableBytes`];
47//! bound on [`OrderableBytes`] only when you handle both kinds correctly.
48//!
49//! The traits are sealed: only this crate implements them, so every
50//! encoding is pinned by its golden vectors, and the rules above hold
51//! for every type that implements them.
52//!
53//! Encoders are gated behind per-type feature flags so callers only pay
54//! for the dependencies they actually use.
55
56/// Implements [`OrderableBytes`] and [`FixedOrderableBytes`] for a type
57/// whose encoding is always `$len` bytes, from one body that builds the
58/// array. Writing both impls from one length keeps
59/// [`FixedOrderableBytes::ENCODED_LEN`], [`FixedOrderableBytes::Array`]
60/// and [`OrderableBytes::Bytes`] in agreement.
61macro_rules! impl_fixed_orderable_bytes {
62 ($(#[$attr:meta])* $ty:ty, $len:literal, |$value:ident| $body:block) => {
63 impl $crate::private::Sealed for $ty {}
64
65 impl $crate::OrderableBytes for $ty {
66 type Bytes<'a> = [u8; $len];
67
68 fn to_orderable_bytes(&self) -> [u8; $len] {
69 $crate::FixedOrderableBytes::to_fixed_orderable_bytes(self)
70 }
71 }
72
73 $(#[$attr])*
74 impl $crate::FixedOrderableBytes for $ty {
75 const ENCODED_LEN: usize = $len;
76
77 type Array = [u8; $len];
78
79 fn to_fixed_orderable_bytes(&self) -> [u8; $len] {
80 let $value = self;
81 $body
82 }
83 }
84 };
85}
86
87#[cfg(feature = "chrono")]
88pub mod chrono;
89#[cfg(feature = "decimal")]
90pub mod decimal;
91pub mod primitive;
92pub mod variable;
93
94#[cfg(test)]
95#[macro_use]
96extern crate quickcheck;
97
98// Runs the README's Rust examples as doctests, so the README cannot drift
99// from the API. The examples use no optional feature, so they run under
100// a plain `cargo test` as well as with `--all-features`.
101#[cfg(doctest)]
102#[doc = include_str!("../README.md")]
103struct ReadmeDoctests;
104
105mod private {
106 /// Restricts the traits below to this crate's types. See the crate
107 /// docs for why they are sealed.
108 pub trait Sealed {}
109}
110
111/// Maps a value to its canonical, order-preserving byte encoding.
112///
113/// Implementors guarantee, for any `a` and `b` of the implementing type:
114///
115/// - **Equality:** byte equality of the outputs agrees with the type's
116/// value equality (`a.to_orderable_bytes() == b.to_orderable_bytes()`
117/// iff `a == b`).
118/// - **Order:** byte-wise lexicographic comparison of the outputs agrees
119/// with the type's natural total order
120/// (`a.to_orderable_bytes() <= b.to_orderable_bytes()` iff `a <= b`).
121///
122/// Every implementor also implements exactly one of
123/// [`FixedOrderableBytes`] or [`VariableOrderableBytes`]. Code that
124/// needs a fixed length, such as code that pads to a block size, must
125/// bound on [`FixedOrderableBytes`], not on this trait.
126///
127/// The trait is sealed, so types outside this crate can't implement it:
128///
129/// ```compile_fail,E0277
130/// use orderable_bytes::OrderableBytes;
131///
132/// struct Mine(u32);
133///
134/// impl OrderableBytes for Mine {
135/// type Bytes<'a> = [u8; 4];
136/// fn to_orderable_bytes(&self) -> [u8; 4] {
137/// self.0.to_be_bytes()
138/// }
139/// }
140/// ```
141///
142/// In 0.1 this trait was called `ToOrderableBytes` and was fixed-length
143/// only. It was renamed so that 0.1 code bounded on it fails to compile
144/// rather than silently accepting variable-length types: migrate such
145/// bounds to [`FixedOrderableBytes`].
146pub trait OrderableBytes: private::Sealed {
147 /// The bytes returned by
148 /// [`to_orderable_bytes`](Self::to_orderable_bytes): a
149 /// `[u8; ENCODED_LEN]` array for fixed-length types, or a slice
150 /// borrowed from `self` for variable-length ones, so encoding a
151 /// string makes no copy of it.
152 ///
153 /// Because it may borrow from `self`, it can't outlive the value.
154 /// To keep fixed-length bytes, use
155 /// [`FixedOrderableBytes::to_fixed_orderable_bytes`].
156 type Bytes<'a>: AsRef<[u8]>
157 where
158 Self: 'a;
159
160 /// Build the canonical, order-preserving byte encoding of `self`.
161 fn to_orderable_bytes(&self) -> Self::Bytes<'_>;
162}
163
164/// An [`OrderableBytes`] encoding whose length is the same for every
165/// value of the type.
166///
167/// [`to_fixed_orderable_bytes`](Self::to_fixed_orderable_bytes) returns
168/// the same bytes as
169/// [`to_orderable_bytes`](OrderableBytes::to_orderable_bytes), as an
170/// owned [`Array`](Self::Array) of exactly
171/// [`ENCODED_LEN`](Self::ENCODED_LEN) bytes that doesn't borrow from the
172/// value. Generic code can store or return it:
173///
174/// ```
175/// use orderable_bytes::FixedOrderableBytes;
176///
177/// fn term<T: FixedOrderableBytes>(value: T) -> T::Array {
178/// value.to_fixed_orderable_bytes()
179/// }
180///
181/// assert_eq!(term(7u32), [0, 0, 0, 7]);
182/// ```
183///
184/// Fixed-length encodings may be zero-extended to a larger block, since
185/// every value is extended the same way. Bounding on this trait is what
186/// keeps variable-length types out of such code:
187///
188/// ```
189/// use orderable_bytes::FixedOrderableBytes;
190///
191/// fn pad_to_16<T: FixedOrderableBytes>(value: &T) -> [u8; 16] {
192/// let mut block = [0u8; 16];
193/// block[..T::ENCODED_LEN].copy_from_slice(value.to_fixed_orderable_bytes().as_ref());
194/// block
195/// }
196///
197/// assert!(pad_to_16(&1u32) < pad_to_16(&2u32));
198/// ```
199///
200/// ```compile_fail,E0277
201/// # use orderable_bytes::FixedOrderableBytes;
202/// # fn pad_to_16<T: FixedOrderableBytes>(value: &T) -> [u8; 16] {
203/// # let mut block = [0u8; 16];
204/// # block[..T::ENCODED_LEN].copy_from_slice(value.to_fixed_orderable_bytes().as_ref());
205/// # block
206/// # }
207/// // Strings are variable-length, so padding them is rejected.
208/// pad_to_16(&String::from("a"));
209/// ```
210pub trait FixedOrderableBytes: OrderableBytes {
211 /// Length, in bytes, of the canonical encoding.
212 const ENCODED_LEN: usize;
213
214 /// The owned encoding: always `[u8; ENCODED_LEN]`.
215 type Array: AsRef<[u8]> + Copy;
216
217 /// Build the canonical, order-preserving byte encoding of `self` as
218 /// an owned array. The bytes are the same as
219 /// [`to_orderable_bytes`](OrderableBytes::to_orderable_bytes) returns.
220 fn to_fixed_orderable_bytes(&self) -> Self::Array;
221}
222
223/// An [`OrderableBytes`] encoding whose length varies with the value.
224///
225/// A value sorts before any longer value it is a prefix of. The bytes do
226/// not mark where they end, so they must never be padded or
227/// concatenated: see [`variable`] for why, and for the implementations.
228pub trait VariableOrderableBytes: OrderableBytes {}
229
230impl<T: OrderableBytes + ?Sized> private::Sealed for &T {}
231
232/// A reference encodes exactly as the value it points to.
233impl<T: OrderableBytes + ?Sized> OrderableBytes for &T {
234 type Bytes<'a>
235 = T::Bytes<'a>
236 where
237 Self: 'a;
238
239 fn to_orderable_bytes(&self) -> T::Bytes<'_> {
240 (**self).to_orderable_bytes()
241 }
242}
243
244impl<T: FixedOrderableBytes + ?Sized> FixedOrderableBytes for &T {
245 const ENCODED_LEN: usize = T::ENCODED_LEN;
246
247 type Array = T::Array;
248
249 fn to_fixed_orderable_bytes(&self) -> T::Array {
250 (**self).to_fixed_orderable_bytes()
251 }
252}
253
254impl<T: VariableOrderableBytes + ?Sized> VariableOrderableBytes for &T {}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn a_reference_encodes_as_its_value() {
262 fn any<T: OrderableBytes>(value: T) -> Vec<u8> {
263 value.to_orderable_bytes().as_ref().to_vec()
264 }
265 fn fixed<T: FixedOrderableBytes>(value: T) -> T::Array {
266 value.to_fixed_orderable_bytes()
267 }
268 fn is_variable<T: VariableOrderableBytes>(_: T) {}
269
270 // The references are the point here, so bind them to names
271 // rather than borrowing inline.
272 let n = 7u32;
273 let s = String::from("abc");
274 let (n_ref, s_ref, str_ref_ref): (&u32, &String, &&str) = (&n, &s, &"abc");
275
276 assert_eq!(any("abc"), b"abc");
277 assert_eq!(any(str_ref_ref), b"abc");
278 assert_eq!(any(n_ref), [0, 0, 0, 7]);
279 assert_eq!(fixed(n_ref), [0, 0, 0, 7]);
280 assert_eq!(<&u32 as FixedOrderableBytes>::ENCODED_LEN, 4);
281 is_variable("abc");
282 is_variable(s_ref);
283 }
284}