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