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}