Skip to main content

pamoja_lorawan/packages/
fragment.rs

1//! Fragmented data block transport, TS004-2.0.0.
2//!
3//! A firmware image does not fit in a LoRaWAN frame, so it goes across in pieces. The server
4//! cuts the block into `nb_frag` fragments of `frag_size` bytes and sends them one per
5//! downlink, then keeps sending: every fragment after the first `nb_frag` is a coded one,
6//! the exclusive-or of a pseudo-random half of the originals. A device that missed some of
7//! the originals can solve for them from any `nb_frag` independent fragments it did hear, so
8//! a session survives losses without anyone asking for a retransmission.
9//!
10//! - [`parity_line`] and [`prbs23`] build the matrix rows those coded fragments come from.
11//! - [`Fragmenter`] cuts a block up and produces any fragment of the session, coded or not.
12//! - [`Defragmenter`] puts one back together from whatever arrives, in storage the caller
13//!   provides: it never allocates, and it tells you how much it needs.
14//! - [`FragCommand`] carries the session setup, status, delete and acknowledgment commands
15//!   on port [`PORT`], and [`data_block_int_key`] with [`BlockMic`] check the reassembled
16//!   block against the code the setup carried.
17//!
18//! The coded fragments a device must agree with its server about are seeded from the index
19//! among the coded fragments, not the index within the whole session. That is what Semtech's
20//! LoRa Basics Modem and ChirpStack's fragmentation server both do, and the two have to
21//! agree for any of this to work. The MATLAB in the specification's appendix reads as though
22//! the whole-session index were the seed.
23//!
24//! # Examples
25//!
26//! A block goes across in five fragments, two are lost, and two coded fragments rebuild it:
27//!
28//! ```
29//! use pamoja_lorawan::packages::fragment::{Defragmenter, Fragmenter};
30//!
31//! let block = b"the quick brown fox jumps!";
32//! let sender = Fragmenter::new(block, 6)?;
33//! assert_eq!((sender.nb_frag(), sender.padding()), (5, 4));
34//!
35//! let mut store = [0u8; 30];
36//! let mut matrix = [0u8; 64];
37//! let mut receiver = Defragmenter::new(sender.nb_frag(), 6, &mut store, &mut matrix)?;
38//!
39//! // Fragments 3 and 4 never arrive; the coded ones that follow stand in for them.
40//! let mut piece = [0u8; 6];
41//! for n in [1, 2, 5, 6, 8] {
42//!     sender.fragment(n, &mut piece)?;
43//!     receiver.fragment(n, &piece)?;
44//! }
45//! assert!(receiver.done());
46//! assert_eq!(&receiver.block()[..block.len()], block);
47//! # Ok::<(), Box<dyn std::error::Error>>(())
48//! ```
49
50use super::{payload, write, PackageVersion};
51use crate::crypto::Cipher;
52use crate::{Direction, LorawanError};
53
54/// The port the fragmented data block transport package is spoken on, section 3.
55pub const PORT: u8 = 201;
56
57/// The identifier of this package, section 3.
58pub const PACKAGE: u8 = 3;
59
60/// The version of this package implemented here, section 3.
61pub const VERSION: u8 = 2;
62
63/// How many fragmentation sessions a device may hold at once, section 3.3.
64pub const SESSIONS: usize = 4;
65
66/// The most fragments one session carries, which the fourteen-bit index bounds.
67pub const MAX_FRAGMENTS: u16 = (1 << 14) - 1;
68
69/// The longest command of this package other than a data fragment, in bytes.
70pub const MAX_COMMAND: usize = 17;
71
72const CID_PACKAGE_VERSION: u8 = 0x00;
73const CID_SESSION_STATUS: u8 = 0x01;
74const CID_SESSION_SETUP: u8 = 0x02;
75const CID_SESSION_DELETE: u8 = 0x03;
76const CID_BLOCK_RECEIVED: u8 = 0x04;
77const CID_DATA_FRAGMENT: u8 = 0x08;
78
79/// Steps the 23-bit pseudo-random sequence the parity matrix is drawn from, appendix A.1.
80///
81/// # Arguments
82///
83/// * `x` - the current value.
84///
85/// # Returns
86///
87/// The next one.
88///
89/// # Examples
90///
91/// ```
92/// use pamoja_lorawan::packages::fragment::prbs23;
93///
94/// assert_eq!(prbs23(1), 1 << 22, "a one shifts out and feeds back");
95/// ```
96#[must_use]
97pub const fn prbs23(x: u32) -> u32 {
98    let b0 = x & 1;
99    let b1 = (x >> 5) & 1;
100    (x >> 1) + ((b0 ^ b1) << 22)
101}
102
103/// Builds one row of the parity matrix: which uncoded fragments a coded one is made of.
104///
105/// # Arguments
106///
107/// * `coded` - which coded fragment this is, counting from one. A session's fragment `n`
108///   past its `nb_frag` uncoded ones is coded fragment `n`.
109/// * `nb_frag` - how many uncoded fragments the block was cut into.
110/// * `line` - a bit for every uncoded fragment, cleared and then filled in, little end
111///   first. It must hold at least `nb_frag` bits.
112///
113/// # Returns
114///
115/// How many fragments the coded one is the exclusive-or of, which is half of `nb_frag`
116/// rounded down.
117///
118/// # Examples
119///
120/// ```
121/// use pamoja_lorawan::packages::fragment::parity_line;
122///
123/// let mut line = [0u8; 1];
124/// assert_eq!(parity_line(1, 6, &mut line), 3);
125/// assert_eq!(line[0] & 0b0011_1111, 0b0001_0011, "fragments 1, 2 and 5");
126/// ```
127pub fn parity_line(coded: u16, nb_frag: u16, line: &mut [u8]) -> usize {
128    let m = u32::from(nb_frag);
129    let bytes = (nb_frag as usize).div_ceil(8);
130    for byte in line.iter_mut().take(bytes) {
131        *byte = 0;
132    }
133    // A power of two makes the generator fall into patterns, so it draws from one more
134    // than the count and throws away anything out of range, appendix A.1.
135    let span = m + u32::from(m.is_power_of_two());
136    let mut x = 1 + 1001 * u32::from(coded);
137    let wanted = (nb_frag / 2) as usize;
138    let mut set = 0;
139    while set < wanted {
140        let mut r = 1 << 16;
141        while r >= m {
142            x = prbs23(x);
143            r = x % span;
144        }
145        let at = r as usize;
146        if line[at / 8] & (1 << (at % 8)) == 0 {
147            line[at / 8] |= 1 << (at % 8);
148            set += 1;
149        }
150    }
151    set
152}
153
154/// Whether a bit is set in a little-end-first bitset.
155const fn bit(bits: &[u8], at: usize) -> bool {
156    bits[at / 8] & (1 << (at % 8)) != 0
157}
158
159/// Sets or clears a bit in a little-end-first bitset.
160const fn set_bit(bits: &mut [u8], at: usize, on: bool) {
161    if on {
162        bits[at / 8] |= 1 << (at % 8);
163    } else {
164        bits[at / 8] &= !(1 << (at % 8));
165    }
166}
167
168/// Cuts a data block into the fragments of a session, and builds the coded ones.
169///
170/// The block is left where it is: nothing is copied until a fragment is asked for.
171#[derive(Clone, Copy, Debug)]
172pub struct Fragmenter<'a> {
173    block: &'a [u8],
174    frag_size: u8,
175    nb_frag: u16,
176    padding: u8,
177}
178
179impl<'a> Fragmenter<'a> {
180    /// Prepares a block for a session.
181    ///
182    /// # Arguments
183    ///
184    /// * `block` - the data to send.
185    /// * `frag_size` - how many bytes each fragment carries.
186    ///
187    /// # Returns
188    ///
189    /// The fragmenter, which says how many fragments the block takes and how much padding
190    /// the last one needs.
191    ///
192    /// # Errors
193    ///
194    /// [`LorawanError::MalformedFrame`] for a fragment size of zero or a block that needs
195    /// more than [`MAX_FRAGMENTS`] fragments.
196    pub fn new(block: &'a [u8], frag_size: u8) -> Result<Fragmenter<'a>, LorawanError> {
197        if frag_size == 0 {
198            return Err(LorawanError::MalformedFrame);
199        }
200        let size = usize::from(frag_size);
201        let nb_frag = block.len().div_ceil(size);
202        if nb_frag == 0 || nb_frag > MAX_FRAGMENTS as usize {
203            return Err(LorawanError::MalformedFrame);
204        }
205        Ok(Fragmenter {
206            block,
207            frag_size,
208            nb_frag: nb_frag as u16,
209            padding: (nb_frag * size - block.len()) as u8,
210        })
211    }
212
213    /// How many uncoded fragments the block was cut into.
214    ///
215    /// # Returns
216    ///
217    /// The count, which the session setup carries as `NbFrag`.
218    #[must_use]
219    pub const fn nb_frag(&self) -> u16 {
220        self.nb_frag
221    }
222
223    /// How many bytes of padding the last fragment carries.
224    ///
225    /// # Returns
226    ///
227    /// The count, which the session setup carries and the receiver strips.
228    #[must_use]
229    pub const fn padding(&self) -> u8 {
230        self.padding
231    }
232
233    /// How many bytes each fragment carries.
234    ///
235    /// # Returns
236    ///
237    /// The size.
238    #[must_use]
239    pub const fn frag_size(&self) -> u8 {
240        self.frag_size
241    }
242
243    /// Builds one fragment of the session.
244    ///
245    /// # Arguments
246    ///
247    /// * `n` - which fragment, counting from one. Up to `nb_frag` it is a piece of the
248    ///   block; past that it is a coded fragment, the exclusive-or of a pseudo-random half
249    ///   of the pieces.
250    /// * `out` - where to write it, at least `frag_size` bytes.
251    ///
252    /// # Returns
253    ///
254    /// How many bytes were written, which is always `frag_size`.
255    ///
256    /// # Errors
257    ///
258    /// [`LorawanError::MalformedFrame`] for a fragment number of zero, and
259    /// [`LorawanError::PayloadTooLong`] when the buffer is too small.
260    pub fn fragment(&self, n: u16, out: &mut [u8]) -> Result<usize, LorawanError> {
261        let size = usize::from(self.frag_size);
262        let room = out.get_mut(..size).ok_or(LorawanError::PayloadTooLong)?;
263        if n == 0 {
264            return Err(LorawanError::MalformedFrame);
265        }
266        room.fill(0);
267        if n <= self.nb_frag {
268            self.piece(usize::from(n - 1), room);
269            return Ok(size);
270        }
271        let mut line = [0u8; (MAX_FRAGMENTS as usize).div_ceil(8)];
272        parity_line(n - self.nb_frag, self.nb_frag, &mut line);
273        for index in 0..usize::from(self.nb_frag) {
274            if bit(&line, index) {
275                let mut piece = [0u8; 255];
276                self.piece(index, &mut piece[..size]);
277                for (out, byte) in room.iter_mut().zip(&piece[..size]) {
278                    *out ^= byte;
279                }
280            }
281        }
282        Ok(size)
283    }
284
285    /// Copies one uncoded fragment out, padding the last one with zeros.
286    fn piece(&self, index: usize, out: &mut [u8]) {
287        let size = usize::from(self.frag_size);
288        let at = index * size;
289        let end = (at + size).min(self.block.len());
290        out.fill(0);
291        if at < self.block.len() {
292            out[..end - at].copy_from_slice(&self.block[at..end]);
293        }
294    }
295}
296
297/// Why a fragmentation session could not be put back together.
298#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
299pub enum FragError {
300    /// The session parameters do not describe a session this build can run.
301    Session,
302    /// The caller's storage is too small for the session it was given.
303    Storage,
304    /// More uncoded fragments were lost than the caller left room to solve for.
305    Memory,
306    /// A fragment's number is outside the session.
307    Fragment,
308}
309
310impl core::fmt::Display for FragError {
311    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
312        match self {
313            FragError::Session => f.write_str("the fragmentation session is not one this runs"),
314            FragError::Storage => f.write_str("the storage is too small for the session"),
315            FragError::Memory => {
316                f.write_str("more fragments were lost than there is room to solve for")
317            }
318            FragError::Fragment => f.write_str("the fragment is not part of the session"),
319        }
320    }
321}
322
323impl core::error::Error for FragError {}
324
325/// How far a fragmentation session has got, small enough to write down.
326///
327/// A device that loses power partway through a firmware download keeps its block storage and
328/// its working storage in flash; this is the handful of numbers it needs beside them to carry
329/// on where it left off, rather than asking for the whole image again.
330#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
331pub struct Progress {
332    /// How many fragments arrived, coded, uncoded and repeated.
333    pub received: u16,
334    /// How many uncoded fragments were lost.
335    pub lost: u16,
336    /// How many rows of the system are filled in.
337    pub rows: u16,
338    /// The highest uncoded fragment number seen.
339    pub highest: u16,
340    /// Whether the block is whole.
341    pub done: bool,
342}
343
344/// Puts a data block back together from the fragments that arrive, TS004-2.0.0 appendix A.2.
345///
346/// The uncoded fragments go straight into the caller's block storage. A coded fragment is
347/// reduced against everything already known and, if it still says something new, kept as one
348/// row of a triangular system over the fragments that were lost. Once there are as many rows
349/// as there are losses, the system is solved back into the block.
350///
351/// Nothing is allocated. The block storage is `nb_frag * frag_size` bytes, and the matrix
352/// storage bounds how many losses can be solved for: [`Defragmenter::matrix_len`] says how
353/// much room a given number of losses takes.
354pub struct Defragmenter<'a> {
355    nb_frag: u16,
356    frag_size: u8,
357    block: &'a mut [u8],
358    matrix: &'a mut [u8],
359    max_lost: u16,
360    lost: u16,
361    rows: u16,
362    received: u16,
363    highest: u16,
364    done: bool,
365}
366
367impl<'a> Defragmenter<'a> {
368    /// How many bytes of matrix storage a session needs.
369    ///
370    /// # Arguments
371    ///
372    /// * `nb_frag` - how many uncoded fragments the block was cut into.
373    /// * `max_lost` - the most uncoded fragments the session should survive losing.
374    ///
375    /// # Returns
376    ///
377    /// The byte count: a row for each loss, as wide as the losses; the map from row to
378    /// fragment; and a bit for each uncoded fragment saying whether it is in hand.
379    ///
380    /// # Examples
381    ///
382    /// ```
383    /// use pamoja_lorawan::packages::fragment::Defragmenter;
384    ///
385    /// assert_eq!(Defragmenter::matrix_len(100, 8), 8 + 2 * 8 + 13);
386    /// ```
387    #[must_use]
388    pub const fn matrix_len(nb_frag: u16, max_lost: u16) -> usize {
389        let lost = max_lost as usize;
390        lost * lost.div_ceil(8) + 2 * lost + (nb_frag as usize).div_ceil(8)
391    }
392
393    /// Starts a session.
394    ///
395    /// # Arguments
396    ///
397    /// * `nb_frag` - how many uncoded fragments the block was cut into.
398    /// * `frag_size` - how many bytes each fragment carries.
399    /// * `block` - where the block is put back together, `nb_frag * frag_size` bytes.
400    /// * `matrix` - the working storage, whose size decides how many losses can be solved
401    ///   for; [`Defragmenter::matrix_len`] sizes it.
402    ///
403    /// # Returns
404    ///
405    /// The session, with nothing received.
406    ///
407    /// # Errors
408    ///
409    /// [`FragError::Session`] for a session this build does not run, and
410    /// [`FragError::Storage`] when the block storage is too small.
411    pub fn new(
412        nb_frag: u16,
413        frag_size: u8,
414        block: &'a mut [u8],
415        matrix: &'a mut [u8],
416    ) -> Result<Defragmenter<'a>, FragError> {
417        Defragmenter::opened(nb_frag, frag_size, block, matrix, true)
418    }
419
420    /// Opens a session, clearing the storage or taking it as it is.
421    fn opened(
422        nb_frag: u16,
423        frag_size: u8,
424        block: &'a mut [u8],
425        matrix: &'a mut [u8],
426        clear: bool,
427    ) -> Result<Defragmenter<'a>, FragError> {
428        if nb_frag == 0 || nb_frag > MAX_FRAGMENTS || frag_size == 0 {
429            return Err(FragError::Session);
430        }
431        let needed = usize::from(nb_frag) * usize::from(frag_size);
432        if block.len() < needed {
433            return Err(FragError::Storage);
434        }
435        // The largest number of losses this much matrix storage can carry.
436        let mut max_lost = 0u16;
437        while max_lost < nb_frag && Defragmenter::matrix_len(nb_frag, max_lost + 1) <= matrix.len()
438        {
439            max_lost += 1;
440        }
441        if Defragmenter::matrix_len(nb_frag, max_lost) > matrix.len() {
442            return Err(FragError::Storage);
443        }
444        if clear {
445            matrix.fill(0);
446            block[..needed].fill(0);
447        }
448        Ok(Defragmenter {
449            nb_frag,
450            frag_size,
451            block,
452            matrix,
453            max_lost,
454            lost: 0,
455            rows: 0,
456            received: 0,
457            highest: 0,
458            done: false,
459        })
460    }
461
462    /// Starts a session again where one left off.
463    ///
464    /// The storage must be the same two buffers the earlier session held, with whatever they
465    /// had in them, and the progress must be the one it reported. Nothing is cleared.
466    ///
467    /// # Arguments
468    ///
469    /// * `nb_frag` - how many uncoded fragments the block was cut into.
470    /// * `frag_size` - how many bytes each fragment carries.
471    /// * `block` - the block storage, as the earlier session left it.
472    /// * `matrix` - the working storage, as the earlier session left it.
473    /// * `progress` - what [`Defragmenter::progress`] reported.
474    ///
475    /// # Returns
476    ///
477    /// The session, carrying on.
478    ///
479    /// # Errors
480    ///
481    /// [`FragError::Session`] for a session this build does not run, and
482    /// [`FragError::Storage`] when the storage is too small.
483    ///
484    /// # Examples
485    ///
486    /// ```
487    /// use pamoja_lorawan::packages::fragment::{Defragmenter, Fragmenter};
488    ///
489    /// let block = b"a block that outlives the power";
490    /// let sender = Fragmenter::new(block, 8)?;
491    /// let mut store = [0u8; 32];
492    /// let mut matrix = [0u8; 64];
493    ///
494    /// let mut piece = [0u8; 8];
495    /// let mut session = Defragmenter::new(sender.nb_frag(), 8, &mut store, &mut matrix)?;
496    /// sender.fragment(1, &mut piece)?;
497    /// session.fragment(1, &piece)?;
498    /// let progress = session.progress();
499    ///
500    /// // The power goes. The two buffers were in flash; this is all that was beside them.
501    /// let mut session =
502    ///     Defragmenter::resumed(sender.nb_frag(), 8, &mut store, &mut matrix, progress)?;
503    /// for n in 2..=sender.nb_frag() {
504    ///     sender.fragment(n, &mut piece)?;
505    ///     session.fragment(n, &piece)?;
506    /// }
507    /// assert!(session.done());
508    /// assert_eq!(&session.block()[..block.len()], block);
509    /// # Ok::<(), Box<dyn std::error::Error>>(())
510    /// ```
511    pub fn resumed(
512        nb_frag: u16,
513        frag_size: u8,
514        block: &'a mut [u8],
515        matrix: &'a mut [u8],
516        progress: Progress,
517    ) -> Result<Defragmenter<'a>, FragError> {
518        let mut session = Defragmenter::opened(nb_frag, frag_size, block, matrix, false)?;
519        session.received = progress.received;
520        session.lost = progress.lost;
521        session.rows = progress.rows;
522        session.highest = progress.highest;
523        session.done = progress.done;
524        Ok(session)
525    }
526
527    /// How far the session has got, to be kept beside its storage.
528    ///
529    /// # Returns
530    ///
531    /// The numbers [`Defragmenter::resumed`] takes.
532    #[must_use]
533    pub const fn progress(&self) -> Progress {
534        Progress {
535            received: self.received,
536            lost: self.lost,
537            rows: self.rows,
538            highest: self.highest,
539            done: self.done,
540        }
541    }
542
543    /// The block, as far as it has been put back together.
544    ///
545    /// # Returns
546    ///
547    /// The bytes, which are the whole block once [`Defragmenter::done`] is true, padding
548    /// and all. The padding the session setup named is at the end.
549    #[must_use]
550    pub fn block(&self) -> &[u8] {
551        let needed = usize::from(self.nb_frag) * usize::from(self.frag_size);
552        &self.block[..needed]
553    }
554
555    /// Whether the whole block is there.
556    ///
557    /// # Returns
558    ///
559    /// `true` once every uncoded fragment has arrived or been solved for.
560    #[must_use]
561    pub const fn done(&self) -> bool {
562        self.done
563    }
564
565    /// How many fragments arrived, coded and uncoded, including repeats.
566    ///
567    /// # Returns
568    ///
569    /// The count, which a status answer reports as `NbFragReceived`.
570    #[must_use]
571    pub const fn received(&self) -> u16 {
572        self.received
573    }
574
575    /// How many uncoded fragments are still missing.
576    ///
577    /// # Returns
578    ///
579    /// The count, which is also the least number of further independent fragments needed.
580    /// A status answer reports it as `MissingFrag`, capped at 255.
581    #[must_use]
582    pub const fn missing(&self) -> u16 {
583        if self.done {
584            0
585        } else {
586            self.lost - self.rows + (self.nb_frag - self.highest)
587        }
588    }
589
590    /// How many losses this session can still solve for.
591    ///
592    /// # Returns
593    ///
594    /// The count the caller's matrix storage allows.
595    #[must_use]
596    pub const fn max_lost(&self) -> u16 {
597        self.max_lost
598    }
599
600    /// Takes one fragment of the session.
601    ///
602    /// # Arguments
603    ///
604    /// * `n` - which fragment, counting from one.
605    /// * `data` - its bytes, `frag_size` of them.
606    ///
607    /// # Returns
608    ///
609    /// Whether the block is now complete.
610    ///
611    /// # Errors
612    ///
613    /// [`FragError::Fragment`] for a fragment outside the session or of the wrong size, and
614    /// [`FragError::Memory`] when more uncoded fragments were lost than there is room to
615    /// solve for.
616    pub fn fragment(&mut self, n: u16, data: &[u8]) -> Result<bool, FragError> {
617        let size = usize::from(self.frag_size);
618        if n == 0 || data.len() < size {
619            return Err(FragError::Fragment);
620        }
621        self.received = self.received.saturating_add(1);
622        if self.done {
623            return Ok(true);
624        }
625        if n <= self.nb_frag {
626            self.uncoded(n, &data[..size]);
627            return Ok(self.done);
628        }
629        self.coded(n - self.nb_frag, &data[..size])
630    }
631
632    /// Stores an uncoded fragment and notes what was lost before it.
633    fn uncoded(&mut self, n: u16, data: &[u8]) {
634        let size = usize::from(self.frag_size);
635        let index = usize::from(n - 1);
636        if !self.arrived(index) {
637            self.block[index * size..index * size + size].copy_from_slice(data);
638            self.mark(index);
639        }
640        if n > self.highest {
641            // Everything between the last one seen and this one was lost.
642            for missing in self.highest..n - 1 {
643                if !self.arrived(usize::from(missing)) {
644                    self.note_lost(missing);
645                }
646            }
647            self.highest = n;
648        }
649        if self.highest == self.nb_frag && self.lost == 0 {
650            self.done = true;
651        }
652    }
653
654    /// Takes a coded fragment into the system, solving the block when it completes it.
655    fn coded(&mut self, coded: u16, data: &[u8]) -> Result<bool, FragError> {
656        // Every uncoded fragment has been sent by now, so anything not seen is lost.
657        for missing in self.highest..self.nb_frag {
658            if !self.arrived(usize::from(missing)) {
659                self.note_lost(missing);
660            }
661        }
662        self.highest = self.nb_frag;
663        if self.lost == 0 {
664            self.done = true;
665            return Ok(true);
666        }
667        if self.lost > self.max_lost {
668            return Err(FragError::Memory);
669        }
670
671        let size = usize::from(self.frag_size);
672        let mut line = [0u8; (MAX_FRAGMENTS as usize).div_ceil(8)];
673        parity_line(coded, self.nb_frag, &mut line);
674
675        // Reduce against the fragments already in hand, leaving a combination of the lost
676        // ones only, and fold their data into the coded fragment as we go.
677        let mut piece = [0u8; 255];
678        piece[..size].copy_from_slice(data);
679        let mut row = [0u8; ((MAX_FRAGMENTS as usize) / 8) + 1];
680        let lost = usize::from(self.lost);
681        for at in 0..lost {
682            let fragment = usize::from(self.lost_at(at));
683            if bit(&line, fragment) {
684                set_bit(&mut row, at, true);
685            }
686        }
687        for index in 0..usize::from(self.nb_frag) {
688            if bit(&line, index) && self.arrived(index) {
689                for (out, byte) in piece[..size]
690                    .iter_mut()
691                    .zip(&self.block[index * size..index * size + size])
692                {
693                    *out ^= byte;
694                }
695            }
696        }
697
698        // Reduce against the rows already stored, which are in echelon form.
699        for at in 0..lost {
700            if !bit(&row, at) || !self.row_used(at) {
701                continue;
702            }
703            self.xor_row(at, &mut row);
704            let slot = usize::from(self.lost_at(at));
705            for (out, byte) in piece[..size]
706                .iter_mut()
707                .zip(&self.block[slot * size..slot * size + size])
708            {
709                *out ^= byte;
710            }
711        }
712
713        // What is left either says nothing new, or becomes a new row.
714        let Some(first) = (0..lost).find(|at| bit(&row, *at)) else {
715            return Ok(false);
716        };
717        self.store_row(first, &row);
718        let slot = usize::from(self.lost_at(first));
719        self.block[slot * size..slot * size + size].copy_from_slice(&piece[..size]);
720        self.rows += 1;
721        if self.rows == self.lost {
722            self.solve();
723            self.done = true;
724        }
725        Ok(self.done)
726    }
727
728    /// Runs the triangular system back into the block, appendix A.2 step 5.
729    fn solve(&mut self) {
730        let size = usize::from(self.frag_size);
731        let lost = usize::from(self.lost);
732        for at in (0..lost).rev() {
733            let slot = usize::from(self.lost_at(at));
734            for other in at + 1..lost {
735                if self.row_bit(at, other) {
736                    let source = usize::from(self.lost_at(other));
737                    for byte in 0..size {
738                        self.block[slot * size + byte] ^= self.block[source * size + byte];
739                    }
740                }
741            }
742            self.mark(slot);
743        }
744    }
745
746    /// The layout of the working storage: the row bits, then the lost list, then the
747    /// bitset of which uncoded fragments arrived.
748    const fn row_bytes(&self) -> usize {
749        (self.max_lost as usize).div_ceil(8)
750    }
751
752    /// Where the list of lost fragments starts.
753    const fn lost_at_offset(&self) -> usize {
754        self.max_lost as usize * self.row_bytes()
755    }
756
757    /// Where the bitset of arrived fragments starts.
758    const fn arrived_offset(&self) -> usize {
759        self.lost_at_offset() + 2 * self.max_lost as usize
760    }
761
762    /// Whether an uncoded fragment has arrived or been solved for.
763    fn arrived(&self, index: usize) -> bool {
764        let at = self.arrived_offset() + index / 8;
765        self.matrix[at] & (1 << (index % 8)) != 0
766    }
767
768    /// Notes that an uncoded fragment is in hand.
769    fn mark(&mut self, index: usize) {
770        let at = self.arrived_offset() + index / 8;
771        self.matrix[at] |= 1 << (index % 8);
772    }
773
774    /// Notes that an uncoded fragment was lost, giving it a row of the system.
775    fn note_lost(&mut self, fragment: u16) {
776        if self.lost >= self.max_lost {
777            self.lost += 1;
778            return;
779        }
780        let base = self.lost_at_offset() + 2 * usize::from(self.lost);
781        self.matrix[base] = (fragment & 0xFF) as u8;
782        self.matrix[base + 1] = (fragment >> 8) as u8;
783        self.lost += 1;
784    }
785
786    /// Which fragment a row of the system stands for.
787    fn lost_at(&self, at: usize) -> u16 {
788        let base = self.lost_at_offset() + 2 * at;
789        u16::from(self.matrix[base]) | (u16::from(self.matrix[base + 1]) << 8)
790    }
791
792    /// Whether a row of the system has been filled in.
793    fn row_used(&self, at: usize) -> bool {
794        self.row_bit(at, at)
795    }
796
797    /// One bit of a stored row.
798    fn row_bit(&self, row: usize, column: usize) -> bool {
799        let base = row * self.row_bytes();
800        self.matrix[base + column / 8] & (1 << (column % 8)) != 0
801    }
802
803    /// Exclusive-ors a stored row into a working one.
804    fn xor_row(&self, row: usize, into: &mut [u8]) {
805        let base = row * self.row_bytes();
806        let stored = &self.matrix[base..base + self.row_bytes()];
807        for (out, byte) in into.iter_mut().zip(stored) {
808            *out ^= byte;
809        }
810    }
811
812    /// Writes a working row into the system.
813    fn store_row(&mut self, row: usize, from: &[u8]) {
814        let width = self.row_bytes();
815        let base = row * width;
816        self.matrix[base..base + width].copy_from_slice(&from[..width]);
817    }
818}
819
820/// Derives the key that signs a data block, section 3.3.
821///
822/// # Arguments
823///
824/// * `root_key` - the device's `GenAppKey` on LoRaWAN 1.0.x, or its `AppKey` on 1.1.
825///
826/// # Returns
827///
828/// The `DataBlockIntKey`, which only ever signs data blocks.
829///
830/// # Examples
831///
832/// ```
833/// use pamoja_lorawan::packages::fragment::data_block_int_key;
834///
835/// let key = data_block_int_key(&[0x2B; 16]);
836/// assert_ne!(key, [0x2B; 16], "it is a key of its own");
837/// ```
838#[must_use]
839pub fn data_block_int_key(root_key: &[u8; 16]) -> [u8; 16] {
840    let mut block = [0u8; 16];
841    block[0] = 0x30;
842    Cipher::new(root_key).encrypt_block(&block)
843}
844
845/// The integrity code over a whole data block, section 3.3.
846///
847/// The block is signed a piece at a time, so a device checks an image it never holds twice.
848pub struct BlockMic<'a> {
849    stream: crate::crypto::CmacStream<'a>,
850}
851
852/// The cipher a block code is taken under, which the code borrows.
853pub struct BlockMicKey {
854    cipher: Cipher,
855}
856
857impl BlockMicKey {
858    /// Prepares the key a block's code is taken under.
859    ///
860    /// # Arguments
861    ///
862    /// * `data_block_int_key` - the key [`data_block_int_key`] derived.
863    ///
864    /// # Returns
865    ///
866    /// The prepared key.
867    #[must_use]
868    pub fn new(data_block_int_key: &[u8; 16]) -> BlockMicKey {
869        BlockMicKey {
870            cipher: Cipher::new(data_block_int_key),
871        }
872    }
873
874    /// Starts a code over one session's block.
875    ///
876    /// # Arguments
877    ///
878    /// * `session_cnt` - the session counter the setup carried.
879    /// * `frag_index` - which of the device's sessions this is.
880    /// * `descriptor` - the four bytes the setup described the block with.
881    /// * `block_len` - the length of the block in bytes, without its padding.
882    ///
883    /// # Returns
884    ///
885    /// The code, waiting for the block itself.
886    #[must_use]
887    pub fn start(
888        &self,
889        session_cnt: u16,
890        frag_index: u8,
891        descriptor: [u8; 4],
892        block_len: u32,
893    ) -> BlockMic<'_> {
894        let mut b0 = [0u8; 16];
895        b0[0] = 0x49;
896        b0[1..3].copy_from_slice(&session_cnt.to_le_bytes());
897        b0[3] = frag_index;
898        b0[4..8].copy_from_slice(&descriptor);
899        b0[12..16].copy_from_slice(&block_len.to_le_bytes());
900        let mut stream = self.cipher.cmac_stream();
901        stream.update(&b0);
902        BlockMic { stream }
903    }
904}
905
906impl BlockMic<'_> {
907    /// Adds a piece of the block.
908    ///
909    /// # Arguments
910    ///
911    /// * `data` - the next bytes of the block, in order, without any padding.
912    pub fn update(&mut self, data: &[u8]) {
913        self.stream.update(data);
914    }
915
916    /// Finishes the code.
917    ///
918    /// # Returns
919    ///
920    /// The four bytes a session setup carries.
921    #[must_use]
922    pub fn finish(self) -> [u8; 4] {
923        let full = self.stream.finish();
924        [full[0], full[1], full[2], full[3]]
925    }
926}
927
928/// What a device makes of a session setup, table 14.
929#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
930pub struct SetupStatus {
931    /// The device does not run the fragmentation algorithm the setup named.
932    pub unsupported_algorithm: bool,
933    /// The device has not the memory for a block that size.
934    pub not_enough_memory: bool,
935    /// The session index is not one the device keeps.
936    pub unsupported_index: bool,
937    /// The descriptor is not one the device accepts.
938    pub wrong_descriptor: bool,
939    /// The session counter repeats one already used for this index.
940    pub session_replay: bool,
941    /// Which session this answer is about.
942    pub frag_index: u8,
943}
944
945impl SetupStatus {
946    /// Whether the setup was accepted.
947    ///
948    /// # Returns
949    ///
950    /// `true` when no error bit is set.
951    #[must_use]
952    pub const fn accepted(&self) -> bool {
953        !self.unsupported_algorithm
954            && !self.not_enough_memory
955            && !self.unsupported_index
956            && !self.wrong_descriptor
957            && !self.session_replay
958    }
959
960    /// Reads the coded status.
961    const fn from_bits(bits: u8) -> SetupStatus {
962        SetupStatus {
963            unsupported_algorithm: bits & 0x01 != 0,
964            not_enough_memory: bits & 0x02 != 0,
965            unsupported_index: bits & 0x04 != 0,
966            wrong_descriptor: bits & 0x08 != 0,
967            session_replay: bits & 0x10 != 0,
968            frag_index: bits >> 6,
969        }
970    }
971
972    /// Writes the coded status.
973    const fn bits(&self) -> u8 {
974        (self.unsupported_algorithm as u8)
975            | ((self.not_enough_memory as u8) << 1)
976            | ((self.unsupported_index as u8) << 2)
977            | ((self.wrong_descriptor as u8) << 3)
978            | ((self.session_replay as u8) << 4)
979            | ((self.frag_index & 0x03) << 6)
980    }
981}
982
983/// One command of the fragmented data block transport package.
984#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
985pub enum FragCommand<'a> {
986    /// The server asking which version of this package the device implements.
987    PackageVersionReq,
988    /// The device's answer, section 3.1.
989    PackageVersionAns(PackageVersion),
990    /// The server asking how a session is going, section 3.2.
991    FragSessionStatusReq {
992        /// Which session.
993        frag_index: u8,
994        /// Whether every device answers, or only those still missing fragments.
995        all_participants: bool,
996    },
997    /// The device's answer, section 3.2.
998    FragSessionStatusAns {
999        /// Which session.
1000        frag_index: u8,
1001        /// How many fragments arrived, coded, uncoded and repeated.
1002        received: u16,
1003        /// How many uncoded fragments are still missing, capped at 255.
1004        missing: u8,
1005        /// Whether the block's integrity code did not check out.
1006        mic_error: bool,
1007        /// Whether the session ran out of memory to defragment with.
1008        memory_error: bool,
1009        /// Whether the session does not exist on this device.
1010        no_session: bool,
1011    },
1012    /// The server setting a session up, section 3.3.
1013    FragSessionSetupReq {
1014        /// Which session, 0 to 3.
1015        frag_index: u8,
1016        /// Which multicast groups may feed it, a bit for each.
1017        mc_group_bit_mask: u8,
1018        /// How many uncoded fragments the block was cut into.
1019        nb_frag: u16,
1020        /// How many bytes each fragment carries.
1021        frag_size: u8,
1022        /// Whether the device reports the block once it has it.
1023        ack_reception: bool,
1024        /// Which fragmentation algorithm to run.
1025        frag_algo: u8,
1026        /// The coded spread of the delay before a device answers.
1027        block_ack_delay: u8,
1028        /// How many bytes of padding the last fragment carries.
1029        padding: u8,
1030        /// The four bytes the server describes the block with.
1031        descriptor: [u8; 4],
1032        /// The session counter, which must rise for each new block.
1033        session_cnt: u16,
1034        /// The code over the block the device checks once it has it all.
1035        mic: [u8; 4],
1036    },
1037    /// The device's answer, section 3.3.
1038    FragSessionSetupAns(SetupStatus),
1039    /// The server deleting a session, section 3.4.
1040    FragSessionDeleteReq {
1041        /// Which session.
1042        frag_index: u8,
1043    },
1044    /// The device's answer, section 3.4.
1045    FragSessionDeleteAns {
1046        /// Which session.
1047        frag_index: u8,
1048        /// Whether the device had no such session.
1049        no_session: bool,
1050    },
1051    /// The device reporting a block it has completely received, section 3.5.
1052    FragDataBlockReceivedReq {
1053        /// Which session.
1054        frag_index: u8,
1055        /// Whether the block's integrity code did not check out.
1056        mic_error: bool,
1057    },
1058    /// The server's acknowledgment, section 3.5.
1059    FragDataBlockReceivedAns {
1060        /// Which session.
1061        frag_index: u8,
1062    },
1063    /// One fragment of a block, section 3.6.
1064    DataFragment {
1065        /// Which session.
1066        frag_index: u8,
1067        /// Which fragment, counting from one.
1068        n: u16,
1069        /// Its bytes.
1070        data: &'a [u8],
1071    },
1072}
1073
1074impl<'a> FragCommand<'a> {
1075    /// The identifier this command travels under.
1076    ///
1077    /// # Returns
1078    ///
1079    /// The identifier, 0x00 to 0x04 or 0x08 for a data fragment.
1080    #[must_use]
1081    pub const fn cid(&self) -> u8 {
1082        match self {
1083            FragCommand::PackageVersionReq | FragCommand::PackageVersionAns(_) => {
1084                CID_PACKAGE_VERSION
1085            }
1086            FragCommand::FragSessionStatusReq { .. } | FragCommand::FragSessionStatusAns { .. } => {
1087                CID_SESSION_STATUS
1088            }
1089            FragCommand::FragSessionSetupReq { .. } | FragCommand::FragSessionSetupAns(_) => {
1090                CID_SESSION_SETUP
1091            }
1092            FragCommand::FragSessionDeleteReq { .. } | FragCommand::FragSessionDeleteAns { .. } => {
1093                CID_SESSION_DELETE
1094            }
1095            FragCommand::FragDataBlockReceivedReq { .. }
1096            | FragCommand::FragDataBlockReceivedAns { .. } => CID_BLOCK_RECEIVED,
1097            FragCommand::DataFragment { .. } => CID_DATA_FRAGMENT,
1098        }
1099    }
1100
1101    /// Which way this command travels.
1102    ///
1103    /// # Returns
1104    ///
1105    /// [`Direction::Uplink`] for what a device sends, [`Direction::Downlink`] for what a
1106    /// server sends.
1107    #[must_use]
1108    pub const fn direction(&self) -> Direction {
1109        match self {
1110            FragCommand::PackageVersionAns(_)
1111            | FragCommand::FragSessionStatusAns { .. }
1112            | FragCommand::FragSessionSetupAns(_)
1113            | FragCommand::FragSessionDeleteAns { .. }
1114            | FragCommand::FragDataBlockReceivedReq { .. } => Direction::Uplink,
1115            _ => Direction::Downlink,
1116        }
1117    }
1118
1119    /// Reads one command from the front of a message.
1120    ///
1121    /// A data fragment takes the whole message, as section 3 asks, so it reads to the end.
1122    ///
1123    /// # Arguments
1124    ///
1125    /// * `direction` - which way the frame carrying it traveled.
1126    /// * `bytes` - the message, from this command's identifier on.
1127    ///
1128    /// # Returns
1129    ///
1130    /// The command and how many bytes it took.
1131    ///
1132    /// # Errors
1133    ///
1134    /// [`LorawanError::MalformedFrame`] when the message ends inside the command, and
1135    /// [`LorawanError::UnknownCommand`] for an identifier this package does not define.
1136    pub fn parse(
1137        direction: Direction,
1138        bytes: &'a [u8],
1139    ) -> Result<(FragCommand<'a>, usize), LorawanError> {
1140        let cid = *bytes.first().ok_or(LorawanError::MalformedFrame)?;
1141        let up = matches!(direction, Direction::Uplink);
1142        match (cid, up) {
1143            (CID_PACKAGE_VERSION, false) => Ok((FragCommand::PackageVersionReq, 1)),
1144            (CID_PACKAGE_VERSION, true) => {
1145                let (fields, taken) = payload(bytes, 2)?;
1146                Ok((
1147                    FragCommand::PackageVersionAns(PackageVersion {
1148                        package: fields[0],
1149                        version: fields[1],
1150                    }),
1151                    taken,
1152                ))
1153            }
1154            (CID_SESSION_STATUS, false) => {
1155                let (fields, taken) = payload(bytes, 1)?;
1156                Ok((
1157                    FragCommand::FragSessionStatusReq {
1158                        frag_index: (fields[0] >> 1) & 0x03,
1159                        all_participants: fields[0] & 0x01 != 0,
1160                    },
1161                    taken,
1162                ))
1163            }
1164            (CID_SESSION_STATUS, true) => {
1165                let (fields, taken) = payload(bytes, 4)?;
1166                let received = u16::from_le_bytes([fields[1], fields[2]]);
1167                Ok((
1168                    FragCommand::FragSessionStatusAns {
1169                        frag_index: (received >> 14) as u8,
1170                        received: received & 0x3FFF,
1171                        missing: fields[3],
1172                        mic_error: fields[0] & 0x02 != 0,
1173                        memory_error: fields[0] & 0x01 != 0,
1174                        no_session: fields[0] & 0x04 != 0,
1175                    },
1176                    taken,
1177                ))
1178            }
1179            (CID_SESSION_SETUP, false) => {
1180                let (fields, taken) = payload(bytes, 16)?;
1181                Ok((
1182                    FragCommand::FragSessionSetupReq {
1183                        frag_index: (fields[0] >> 4) & 0x03,
1184                        mc_group_bit_mask: fields[0] & 0x0F,
1185                        nb_frag: u16::from_le_bytes([fields[1], fields[2]]),
1186                        frag_size: fields[3],
1187                        ack_reception: fields[4] & 0x40 != 0,
1188                        frag_algo: (fields[4] >> 3) & 0x07,
1189                        block_ack_delay: fields[4] & 0x07,
1190                        padding: fields[5],
1191                        descriptor: [fields[6], fields[7], fields[8], fields[9]],
1192                        session_cnt: u16::from_le_bytes([fields[10], fields[11]]),
1193                        mic: [fields[12], fields[13], fields[14], fields[15]],
1194                    },
1195                    taken,
1196                ))
1197            }
1198            (CID_SESSION_SETUP, true) => {
1199                let (fields, taken) = payload(bytes, 1)?;
1200                Ok((
1201                    FragCommand::FragSessionSetupAns(SetupStatus::from_bits(fields[0])),
1202                    taken,
1203                ))
1204            }
1205            (CID_SESSION_DELETE, false) => {
1206                let (fields, taken) = payload(bytes, 1)?;
1207                Ok((
1208                    FragCommand::FragSessionDeleteReq {
1209                        frag_index: fields[0] & 0x03,
1210                    },
1211                    taken,
1212                ))
1213            }
1214            (CID_SESSION_DELETE, true) => {
1215                let (fields, taken) = payload(bytes, 1)?;
1216                Ok((
1217                    FragCommand::FragSessionDeleteAns {
1218                        frag_index: fields[0] & 0x03,
1219                        no_session: fields[0] & 0x04 != 0,
1220                    },
1221                    taken,
1222                ))
1223            }
1224            (CID_BLOCK_RECEIVED, true) => {
1225                let (fields, taken) = payload(bytes, 1)?;
1226                Ok((
1227                    FragCommand::FragDataBlockReceivedReq {
1228                        frag_index: fields[0] & 0x03,
1229                        mic_error: fields[0] & 0x04 != 0,
1230                    },
1231                    taken,
1232                ))
1233            }
1234            (CID_BLOCK_RECEIVED, false) => {
1235                let (fields, taken) = payload(bytes, 1)?;
1236                Ok((
1237                    FragCommand::FragDataBlockReceivedAns {
1238                        frag_index: fields[0] & 0x03,
1239                    },
1240                    taken,
1241                ))
1242            }
1243            (CID_DATA_FRAGMENT, false) => {
1244                let fields = bytes.get(1..3).ok_or(LorawanError::MalformedFrame)?;
1245                let index = u16::from_le_bytes([fields[0], fields[1]]);
1246                Ok((
1247                    FragCommand::DataFragment {
1248                        frag_index: (index >> 14) as u8,
1249                        n: index & 0x3FFF,
1250                        data: &bytes[3..],
1251                    },
1252                    bytes.len(),
1253                ))
1254            }
1255            _ => Err(LorawanError::UnknownCommand(cid)),
1256        }
1257    }
1258
1259    /// Writes this command out.
1260    ///
1261    /// A session setup writes its integrity code, which the caller takes over the block
1262    /// with [`BlockMicKey`].
1263    ///
1264    /// # Arguments
1265    ///
1266    /// * `out` - where to write it.
1267    ///
1268    /// # Returns
1269    ///
1270    /// How many bytes were written.
1271    ///
1272    /// # Errors
1273    ///
1274    /// [`LorawanError::PayloadTooLong`] when the buffer is too small.
1275    pub fn encode(&self, out: &mut [u8]) -> Result<usize, LorawanError> {
1276        match self {
1277            FragCommand::PackageVersionReq => write(out, CID_PACKAGE_VERSION, &[]),
1278            FragCommand::PackageVersionAns(version) => write(
1279                out,
1280                CID_PACKAGE_VERSION,
1281                &[version.package, version.version],
1282            ),
1283            FragCommand::FragSessionStatusReq {
1284                frag_index,
1285                all_participants,
1286            } => write(
1287                out,
1288                CID_SESSION_STATUS,
1289                &[((frag_index & 0x03) << 1) | u8::from(*all_participants)],
1290            ),
1291            FragCommand::FragSessionStatusAns {
1292                frag_index,
1293                received,
1294                missing,
1295                mic_error,
1296                memory_error,
1297                no_session,
1298            } => {
1299                let coded = (received & 0x3FFF) | (u16::from(*frag_index) << 14);
1300                let status = u8::from(*memory_error)
1301                    | (u8::from(*mic_error) << 1)
1302                    | (u8::from(*no_session) << 2);
1303                let bytes = coded.to_le_bytes();
1304                write(
1305                    out,
1306                    CID_SESSION_STATUS,
1307                    &[status, bytes[0], bytes[1], *missing],
1308                )
1309            }
1310            FragCommand::FragSessionSetupReq {
1311                frag_index,
1312                mc_group_bit_mask,
1313                nb_frag,
1314                frag_size,
1315                ack_reception,
1316                frag_algo,
1317                block_ack_delay,
1318                padding,
1319                descriptor,
1320                session_cnt,
1321                mic,
1322            } => {
1323                let nb = nb_frag.to_le_bytes();
1324                let count = session_cnt.to_le_bytes();
1325                let control = (u8::from(*ack_reception) << 6)
1326                    | ((frag_algo & 0x07) << 3)
1327                    | (block_ack_delay & 0x07);
1328                write(
1329                    out,
1330                    CID_SESSION_SETUP,
1331                    &[
1332                        ((frag_index & 0x03) << 4) | (mc_group_bit_mask & 0x0F),
1333                        nb[0],
1334                        nb[1],
1335                        *frag_size,
1336                        control,
1337                        *padding,
1338                        descriptor[0],
1339                        descriptor[1],
1340                        descriptor[2],
1341                        descriptor[3],
1342                        count[0],
1343                        count[1],
1344                        mic[0],
1345                        mic[1],
1346                        mic[2],
1347                        mic[3],
1348                    ],
1349                )
1350            }
1351            FragCommand::FragSessionSetupAns(status) => {
1352                write(out, CID_SESSION_SETUP, &[status.bits()])
1353            }
1354            FragCommand::FragSessionDeleteReq { frag_index } => {
1355                write(out, CID_SESSION_DELETE, &[frag_index & 0x03])
1356            }
1357            FragCommand::FragSessionDeleteAns {
1358                frag_index,
1359                no_session,
1360            } => write(
1361                out,
1362                CID_SESSION_DELETE,
1363                &[(frag_index & 0x03) | (u8::from(*no_session) << 2)],
1364            ),
1365            FragCommand::FragDataBlockReceivedReq {
1366                frag_index,
1367                mic_error,
1368            } => write(
1369                out,
1370                CID_BLOCK_RECEIVED,
1371                &[(frag_index & 0x03) | (u8::from(*mic_error) << 2)],
1372            ),
1373            FragCommand::FragDataBlockReceivedAns { frag_index } => {
1374                write(out, CID_BLOCK_RECEIVED, &[frag_index & 0x03])
1375            }
1376            FragCommand::DataFragment {
1377                frag_index,
1378                n,
1379                data,
1380            } => {
1381                let coded = (n & 0x3FFF) | (u16::from(*frag_index) << 14);
1382                let bytes = coded.to_le_bytes();
1383                let len = data.len() + 3;
1384                let room = out.get_mut(..len).ok_or(LorawanError::PayloadTooLong)?;
1385                room[0] = CID_DATA_FRAGMENT;
1386                room[1] = bytes[0];
1387                room[2] = bytes[1];
1388                room[3..].copy_from_slice(data);
1389                Ok(len)
1390            }
1391        }
1392    }
1393}