Skip to main content

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}