ssb_legacy_msg_data/
lib.rs

1//! This crate implements the ssb
2//! [legacy data format](https://spec.scuttlebutt.nz/feed/datamodel.html),
3//! i.e. the free-form data that forms the content of legacy messages.
4//!
5//! Two encodings are implemented: the
6//! [signing encoding](https://spec.scuttlebutt.nz/feed/datamodel.html#signing-encoding), and the
7//! [json transport encoding](https://spec.scuttlebutt.nz/feed/datamodel.html#json-transport-encoding).
8#![warn(missing_docs)]
9
10extern crate encode_unicode;
11extern crate indexmap;
12extern crate ryu_ecmascript;
13extern crate serde;
14extern crate strtod2;
15#[macro_use]
16extern crate serde_derive;
17extern crate base64;
18
19pub mod json;
20pub mod value;
21
22use std::cmp::Ordering;
23use std::fmt;
24
25/// A wrapper around `f64` to indicate that the float is compatible with the ssb legacy message
26/// data model, i.e. it is [neither an infinity, nor `-0.0`, nor a `NaN`](https://spec.scuttlebutt.nz/feed/datamodel.html#floats).
27///
28/// Because a `LegacyF64` is never `NaN`, it can implement `Eq` and `Ord`, which regular `f64`
29/// can not.
30///
31/// To obtain the inner value, use the `From<LegacyF64> for f64` impl.
32#[derive(Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
33pub struct LegacyF64(f64);
34
35impl LegacyF64 {
36    /// Safe conversion of an arbitrary `f64` into a `LegacyF64`.
37    ///
38    /// ```
39    /// use ssb_legacy_msg_data::LegacyF64;
40    ///
41    /// assert!(LegacyF64::from_f64(0.0).is_some());
42    /// assert!(LegacyF64::from_f64(-1.1).is_some());
43    /// assert!(LegacyF64::from_f64(-0.0).is_none());
44    /// assert!(LegacyF64::from_f64(std::f64::INFINITY).is_none());
45    /// assert!(LegacyF64::from_f64(std::f64::NEG_INFINITY).is_none());
46    /// assert!(LegacyF64::from_f64(std::f64::NAN).is_none());
47    /// ```
48    pub fn from_f64(f: f64) -> Option<LegacyF64> {
49        if LegacyF64::is_valid(f) {
50            Some(LegacyF64(f))
51        } else {
52            None
53        }
54    }
55
56    /// Wraps the given `f64` as a `LegacyF64` without checking if it is valid.
57    ///
58    /// When the `debug_assertions` feature is enabled (when compiling without optimizations),
59    /// this function panics when given an invalid `f64`.
60    ///
61    /// # Safety
62    /// You must not pass infinity, negative infinity, negative zero or a `NaN` to this
63    /// function. Any method on the resulting `LegacyF64` could panic or exhibit undefined
64    /// behavior.
65    ///
66    /// ```
67    /// use ssb_legacy_msg_data::LegacyF64;
68    ///
69    /// let fine = unsafe { LegacyF64::from_f64_unchecked(1.1) };
70    ///
71    /// // Never do this:
72    /// // let everything_is_terrible = unsafe { LegacyF64::from_f64_unchecked(-0.0) };
73    /// ```
74    pub unsafe fn from_f64_unchecked(f: f64) -> LegacyF64 {
75        debug_assert!(LegacyF64::is_valid(f));
76        LegacyF64(f)
77    }
78
79    /// Checks whether a given `f64`
80    /// [may be used](https://spec.scuttlebutt.nz/feed/datamodel.html#floats) as a `LegacyF64`.
81    pub fn is_valid(f: f64) -> bool {
82        if f == 0.0 {
83            f.is_sign_positive()
84        } else {
85            f.is_finite() && (f != 0.0)
86        }
87    }
88}
89
90impl fmt::Display for LegacyF64 {
91    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
92        self.0.fmt(f)
93    }
94}
95
96impl fmt::Debug for LegacyF64 {
97    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
98        self.0.fmt(f)
99    }
100}
101
102impl Eq for LegacyF64 {}
103
104impl Ord for LegacyF64 {
105    fn cmp(&self, other: &Self) -> Ordering {
106        self.partial_cmp(other).unwrap()
107    }
108}
109
110impl PartialOrd for LegacyF64 {
111    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
112        Some(self.cmp(other))
113    }
114}
115
116impl From<LegacyF64> for f64 {
117    fn from(f: LegacyF64) -> Self {
118        f.0
119    }
120}
121
122/// Checks whether a given `u64` is allowed for usage in ssb data (it is
123/// not larger than 2^53).
124pub fn is_u64_valid(n: u64) -> bool {
125    n < 9007199254740992
126}
127
128/// Checks whether a given `i64` is allowed for usage in ssb data (its
129/// absolute value is not larger than 2^53).
130pub fn is_i64_valid(n: i64) -> bool {
131    n < 9007199254740992 && n > -9007199254740992
132}
133
134/// An iterator that yields the
135/// [bytes](https://spec.scuttlebutt.nz/feed/datamodel.html#legacy-hash-computation) needed to compute
136/// a hash of some legacy data.
137///
138/// Created by [`to_weird_encoding`](to_weird_encoding).
139///
140/// The total number of bytes yielded by this is also the
141/// [length](https://spec.scuttlebutt.nz/feed/datamodel.html#legacy-length-computation) of the data.
142pub struct WeirdEncodingIterator<'a>(std::iter::Map<std::str::EncodeUtf16<'a>, fn(u16) -> u8>);
143
144impl<'a> Iterator for WeirdEncodingIterator<'a> {
145    type Item = u8;
146
147    fn next(&mut self) -> Option<Self::Item> {
148        self.0.next()
149    }
150}
151
152/// Create an owned representation of the
153/// [weird encoding](https://spec.scuttlebutt.nz/feed/datamodel.html#legacy-hash-computation)
154/// used for hash computation of legacy ssb messages. The number of bytes yielded by this
155/// iterator coincides with the
156/// [length](https://spec.scuttlebutt.nz/feed/datamodel.html#legacy-length-computation)
157/// of the data.
158pub fn to_weird_encoding(s: &str) -> WeirdEncodingIterator {
159    WeirdEncodingIterator(s.encode_utf16().map(|x| x as u8))
160}
161
162/// Compute the [length](https://spec.scuttlebutt.nz/feed/datamodel.html#legacy-length-computation)
163/// of some data. Note that this takes time linear in the length of the data,
164/// so you might want to use a [`WeirdEncodingIterator`](WeirdEncodingIterator)
165/// for computing hash and length in one go.
166pub fn legacy_length(s: &str) -> usize {
167    let mut len = 0;
168    for c in s.chars() {
169        if c as u32 <= 0xFFFF {
170            len += 1;
171        } else {
172            len += 2;
173        }
174    }
175    len
176}