foundation_ur/ur/
encoder.rs

1// SPDX-FileCopyrightText: © 2023 Foundation Devices, Inc. <hello@foundationdevices.com>
2// SPDX-FileCopyrightText: © 2020 Dominik Spicher <dominikspicher@gmail.com>
3// SPDX-License-Identifier: MIT
4
5//! Encoder.
6
7use crate::{fountain, ur::UR};
8
9use core::str;
10
11/// An encoder.
12#[cfg(feature = "alloc")]
13pub type Encoder<'a, 'b> = BaseEncoder<'a, 'b, fountain::encoder::Alloc>;
14
15#[cfg(feature = "alloc")]
16impl<'a, 'b> Encoder<'a, 'b> {
17    /// Construct a new [`Encoder`].
18    pub const fn new() -> Self {
19        Self {
20            fountain: fountain::encoder::Encoder::new(),
21            ur_type: None,
22        }
23    }
24}
25
26/// An static encoder.
27///
28/// Does not allocate memory.
29pub type HeaplessEncoder<'a, 'b, const MAX_FRAGMENT_LEN: usize, const MAX_SEQUENCE_COUNT: usize> =
30    BaseEncoder<'a, 'b, fountain::encoder::Heapless<MAX_FRAGMENT_LEN, MAX_SEQUENCE_COUNT>>;
31
32impl<'a, 'b, const MAX_FRAGMENT_LEN: usize, const MAX_SEQUENCE_COUNT: usize>
33    HeaplessEncoder<'a, 'b, MAX_FRAGMENT_LEN, MAX_SEQUENCE_COUNT>
34{
35    /// Construct a new [`HeaplessEncoder`].
36    pub const fn new() -> Self {
37        Self {
38            fountain: fountain::encoder::HeaplessEncoder::new(),
39            ur_type: None,
40        }
41    }
42}
43
44/// A uniform resource encoder with an underlying fountain encoding.
45///
46/// # Examples
47///
48/// See the [`crate`] documentation for an example.
49pub struct BaseEncoder<'a, 'b, T: fountain::encoder::Types> {
50    ur_type: Option<&'a str>,
51    fountain: fountain::encoder::BaseEncoder<'b, T>,
52}
53
54impl<'a, 'b, T: fountain::encoder::Types> BaseEncoder<'a, 'b, T> {
55    /// Creates a new encoder for the given message payload.
56    ///
57    /// The emitted fountain parts will respect the maximum fragment length
58    /// argument.
59    ///
60    /// # Examples
61    ///
62    /// See the [`crate`] documentation for an example.
63    ///
64    /// # Panics
65    ///
66    /// This function panics if `ur_type` or `message` is empty, or if
67    /// `max_fragment_length` is zero.
68    pub fn start(&mut self, ur_type: &'a str, message: &'b [u8], max_fragment_length: usize) {
69        self.ur_type = Some(ur_type);
70        self.fountain.start(message, max_fragment_length);
71    }
72
73    /// Returns the current count of already emitted parts.
74    ///
75    /// # Examples
76    ///
77    /// ```
78    /// # use foundation_ur::HeaplessEncoder;
79    /// # let mut encoder: HeaplessEncoder<8, 8> = HeaplessEncoder::new();
80    /// encoder.start("bytes", "data".as_bytes(), 5);
81    ///
82    /// assert_eq!(encoder.current_sequence(), 0);
83    /// encoder.next_part();
84    /// assert_eq!(encoder.current_sequence(), 1);
85    /// ```
86    #[inline]
87    pub fn current_sequence(&self) -> u32 {
88        self.fountain.current_sequence()
89    }
90
91    /// Returns the number of segments the original message has been split up into.
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// # use foundation_ur::HeaplessEncoder;
97    /// # let mut encoder: HeaplessEncoder<8, 8> = HeaplessEncoder::new();
98    /// encoder.start("bytes", "data".as_bytes(), 3);
99    /// assert_eq!(encoder.sequence_count(), 2);
100    /// ```
101    #[inline]
102    pub fn sequence_count(&self) -> u32 {
103        self.fountain.sequence_count()
104    }
105
106    /// Returns the URI corresponding to next fountain part.
107    ///
108    /// # Examples
109    ///
110    /// See the [`crate`] documentation for an example.
111    pub fn next_part(&mut self) -> UR {
112        UR::MultiPartDeserialized {
113            ur_type: self.ur_type.expect("encoder is not initialized"),
114            fragment: self.fountain.next_part(),
115        }
116    }
117}
118
119#[cfg(test)]
120#[cfg(feature = "alloc")]
121pub mod tests {
122    use super::*;
123    use crate::ur::tests::make_message_ur;
124
125    #[test]
126    fn test_ur_encoder() {
127        const TEST_VECTORS: &[&str] = &[
128            "ur:bytes/1-9/lpadascfadaxcywenbpljkhdcahkadaemejtswhhylkepmykhhtsytsnoyoyaxaedsuttydmmhhpktpmsrjtdkgslpgh",
129            "ur:bytes/2-9/lpaoascfadaxcywenbpljkhdcagwdpfnsboxgwlbaawzuefywkdplrsrjynbvygabwjldapfcsgmghhkhstlrdcxaefz",
130            "ur:bytes/3-9/lpaxascfadaxcywenbpljkhdcahelbknlkuejnbadmssfhfrdpsbiegecpasvssovlgeykssjykklronvsjksopdzmol",
131            "ur:bytes/4-9/lpaaascfadaxcywenbpljkhdcasotkhemthydawydtaxneurlkosgwcekonertkbrlwmplssjtammdplolsbrdzcrtas",
132            "ur:bytes/5-9/lpahascfadaxcywenbpljkhdcatbbdfmssrkzmcwnezelennjpfzbgmuktrhtejscktelgfpdlrkfyfwdajldejokbwf",
133            "ur:bytes/6-9/lpamascfadaxcywenbpljkhdcackjlhkhybssklbwefectpfnbbectrljectpavyrolkzczcpkmwidmwoxkilghdsowp",
134            "ur:bytes/7-9/lpatascfadaxcywenbpljkhdcavszmwnjkwtclrtvaynhpahrtoxmwvwatmedibkaegdosftvandiodagdhthtrlnnhy",
135            "ur:bytes/8-9/lpayascfadaxcywenbpljkhdcadmsponkkbbhgsoltjntegepmttmoonftnbuoiyrehfrtsabzsttorodklubbuyaetk",
136            "ur:bytes/9-9/lpasascfadaxcywenbpljkhdcajskecpmdckihdyhphfotjojtfmlnwmadspaxrkytbztpbauotbgtgtaeaevtgavtny",
137            "ur:bytes/10-9/lpbkascfadaxcywenbpljkhdcahkadaemejtswhhylkepmykhhtsytsnoyoyaxaedsuttydmmhhpktpmsrjtwdkiplzs",
138            "ur:bytes/11-9/lpbdascfadaxcywenbpljkhdcahelbknlkuejnbadmssfhfrdpsbiegecpasvssovlgeykssjykklronvsjkvetiiapk",
139            "ur:bytes/12-9/lpbnascfadaxcywenbpljkhdcarllaluzmdmgstospeyiefmwejlwtpedamktksrvlcygmzemovovllarodtmtbnptrs",
140            "ur:bytes/13-9/lpbtascfadaxcywenbpljkhdcamtkgtpknghchchyketwsvwgwfdhpgmgtylctotzopdrpayoschcmhplffziachrfgd",
141            "ur:bytes/14-9/lpbaascfadaxcywenbpljkhdcapazewnvonnvdnsbyleynwtnsjkjndeoldydkbkdslgjkbbkortbelomueekgvstegt",
142            "ur:bytes/15-9/lpbsascfadaxcywenbpljkhdcaynmhpddpzmversbdqdfyrehnqzlugmjzmnmtwmrouohtstgsbsahpawkditkckynwt",
143            "ur:bytes/16-9/lpbeascfadaxcywenbpljkhdcawygekobamwtlihsnpalnsghenskkiynthdzotsimtojetprsttmukirlrsbtamjtpd",
144            "ur:bytes/17-9/lpbyascfadaxcywenbpljkhdcamklgftaxykpewyrtqzhydntpnytyisincxmhtbceaykolduortotiaiaiafhiaoyce",
145            "ur:bytes/18-9/lpbgascfadaxcywenbpljkhdcahkadaemejtswhhylkepmykhhtsytsnoyoyaxaedsuttydmmhhpktpmsrjtntwkbkwy",
146            "ur:bytes/19-9/lpbwascfadaxcywenbpljkhdcadekicpaajootjzpsdrbalpeywllbdsnbinaerkurspbncxgslgftvtsrjtksplcpeo",
147            "ur:bytes/20-9/lpbbascfadaxcywenbpljkhdcayapmrleeleaxpasfrtrdkncffwjyjzgyetdmlewtkpktgllepfrltataztksmhkbot",
148        ];
149
150        let ur = make_message_ur(256, "Wolf");
151
152        fn test<'a, T: fountain::encoder::Types>(
153            encoder: &mut BaseEncoder<'static, 'a, T>,
154            ur: &'a [u8],
155        ) {
156            encoder.start("bytes", &ur, 30);
157            assert_eq!(encoder.sequence_count(), 9);
158            for (index, &part) in TEST_VECTORS.iter().enumerate() {
159                assert_eq!(encoder.current_sequence(), index.try_into().unwrap());
160                assert_eq!(encoder.next_part().to_string(), part);
161            }
162        }
163
164        let mut heapless_encoder: HeaplessEncoder<'_, '_, 30, 16> = HeaplessEncoder::new();
165        let mut encoder = Encoder::new();
166
167        test(&mut heapless_encoder, &ur);
168        test(&mut encoder, &ur);
169    }
170}