blob_stream/
in_logic.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/nimble-rust/nimble
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use crate::err::BlobError;
6use crate::in_stream::BlobStreamIn;
7use crate::protocol::{AckChunkData, SetChunkData};
8use crate::ChunkIndex;
9
10#[derive(Debug, Copy, Clone, Eq, PartialEq)]
11pub struct Info {
12    pub total_octet_size: usize,
13    pub chunk_octet_size: usize,
14    pub chunk_count: usize,
15    pub chunk_count_received: usize,
16    pub waiting_for_chunk_index: ChunkIndex,
17}
18
19/// `Logic` handles the logic for receiving and processing chunks of data
20/// in a streaming context. It manages the internal state and interactions
21/// between the sender and receiver commands.
22#[derive(Debug)]
23pub struct Logic {
24    in_stream: BlobStreamIn,
25}
26
27impl Logic {
28    /// Creates a new `Logic` instance with the specified `octet_count` and `chunk_size`.
29    ///
30    /// # Arguments
31    ///
32    /// * `octet_count` - The total number of octets (bytes) expected in the stream.
33    /// * `chunk_size` - The size of each chunk in the stream.
34    ///
35    /// # Returns
36    ///
37    /// A new `Logic` instance.
38    ///
39    /// # Example
40    ///
41    /// ```
42    /// use blob_stream::in_logic::Logic;
43    /// let in_logic = Logic::new(1024, 64);
44    /// ```
45    #[must_use]
46    pub fn new(octet_count: usize, chunk_size: usize) -> Self {
47        Self {
48            in_stream: BlobStreamIn::new(octet_count, chunk_size),
49        }
50    }
51
52    #[must_use]
53    pub fn info(&self) -> Info {
54        Info {
55            total_octet_size: self.in_stream.octet_count,
56            chunk_octet_size: self.in_stream.fixed_chunk_size,
57            chunk_count: self.in_stream.bit_array.bit_count(),
58            chunk_count_received: self.in_stream.bit_array.count_set_bits(),
59            waiting_for_chunk_index: self
60                .in_stream
61                .bit_array
62                .first_unset_bit()
63                .unwrap_or_else(|| self.in_stream.bit_array.bit_count()),
64        }
65    }
66
67    /// Processes a `SenderToReceiverCommands` command, applying it to the internal stream.
68    ///
69    /// Currently, this function only handles the `SetChunk` command, which updates the
70    /// stream with a new chunk of data.
71    ///
72    /// # Arguments
73    ///
74    /// * `command` - The command sent by the sender, containing the chunk data.
75    ///
76    /// # Errors
77    ///
78    /// Returns an [`io::Error`] if the chunk cannot be set due to an I/O error.
79    ///
80    /// # Example
81    ///
82    /// ```
83    /// use blob_stream::in_logic::Logic;
84    /// use blob_stream::protocol::{SetChunkData};
85    ///
86    /// let mut in_logic = Logic::new(1024, 5);
87    /// let chunk_data = SetChunkData {
88    ///   chunk_index: 1,
89    ///   payload: [0x8f, 0x23, 0x98, 0xfa, 0x99].into(),
90    /// };
91    /// in_logic.receive(&chunk_data).unwrap();
92    /// ```
93    #[allow(clippy::cast_possible_truncation)]
94    pub fn receive(&mut self, chunk_data: &SetChunkData) -> Result<(), BlobError> {
95        self.in_stream
96            .set_chunk(chunk_data.chunk_index as ChunkIndex, &chunk_data.payload)
97    }
98
99    pub fn send(&mut self) -> AckChunkData {
100        let waiting_for_chunk_index = self
101            .in_stream
102            .bit_array
103            .first_unset_bit()
104            .unwrap_or_else(|| self.in_stream.bit_array.bit_count());
105
106        let receive_mask = self
107            .in_stream
108            .bit_array
109            .atom_from_index(waiting_for_chunk_index + 1);
110
111        AckChunkData {
112            waiting_for_chunk_index: waiting_for_chunk_index as u32,
113            receive_mask_after_last: receive_mask,
114        }
115    }
116
117    /// Retrieves the full blob data if all chunks have been received.
118    ///
119    /// # Returns
120    ///
121    /// An `Some(&[u8])` containing the full blob data if all chunks have been received,
122    /// or `None` if the blob is incomplete.
123    ///
124    /// # Example
125    ///
126    /// ```
127    /// use blob_stream::in_logic::Logic;
128    /// let mut in_logic = Logic::new(1024, 64);
129    /// if let Some(blob) = in_logic.blob() {
130    ///     // Use the blob data
131    /// }
132    /// ```
133    #[must_use]
134    pub fn blob(&self) -> Option<&[u8]> {
135        self.in_stream.blob()
136    }
137
138    #[must_use]
139    pub fn is_complete(&self) -> bool {
140        self.in_stream.is_complete()
141    }
142}