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