Skip to main content

capsules_system/
write_to_binary.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Basic binary writer for synchronous writes of binary buffers.
6//!
7//! This mirrors the `core::fmt::Write` interface but doesn't expect a `&str`.
8
9use kernel::utilities::binary_write::BinaryWrite;
10
11/// Wrapper to convert a binary buffer writer to provide a `core::fmt::Write`
12/// interface with offset tracking. This allows a synchronous writer to use
13/// an underlying asynchronous write implementation.
14///
15/// This struct allows a synchronous writer to use the `core::fmt::Write`
16/// interface when there is a limited size buffer underneath. This struct tracks
17/// where in the overall write has actually been written to the underlying
18/// `BinaryWrite` implementation.
19///
20/// The expected usage of this tool looks like:
21///
22/// ```ignore
23/// let wrapper = WriteToBinaryOffsetWrapper::new(binary_writer);
24///
25/// // Set the byte index of the long, synchronous write where we should
26/// // actually start passing to the binary writer.
27/// wrapper.set_offset(offset);
28///
29/// // Do the long, synchronous write.
30/// let _ = wrapper.write_fmt(format_args!(...));
31///
32/// if wrapper.bytes_remaining() {
33///     // Some of the write did not finish (likely that means the binary
34///     // writer's buffer filled up).
35///     let next_offset = wrapper.get_index();
36///
37///     // Now wait for the binary write to finish, and start this process
38///     // over but from the new offset.
39/// } else {
40///     // Nothing left to print, we're done!
41/// }
42/// ```
43pub struct WriteToBinaryOffsetWrapper<'a> {
44    /// Binary writer implementation that is asynchronous and has a fixed sized
45    /// buffer.
46    binary_writer: &'a mut dyn BinaryWrite,
47    /// Where to start in the long synchronous write.
48    offset: usize,
49    /// Keep track of where in the long synchronous write we are currently
50    /// displaying.
51    index: usize,
52    /// Track if write() is called, and the `binary_writer` did not print
53    /// everything we passed to it. In that case, there are more bytes to write
54    /// on the next iteration.
55    bytes_remaining: bool,
56}
57
58impl<'a> WriteToBinaryOffsetWrapper<'a> {
59    pub fn new(binary_writer: &'a mut dyn BinaryWrite) -> Self {
60        Self {
61            binary_writer,
62            index: 0,
63            offset: 0,
64            bytes_remaining: false,
65        }
66    }
67
68    /// Set the byte to start printing from on this iteration. Call this before
69    /// calling `Write`.
70    pub fn set_offset(&mut self, offset: usize) {
71        self.offset = offset;
72    }
73
74    /// After printing, get the index we left off on to use as the offset for
75    /// the next iteration.
76    pub fn get_index(&self) -> usize {
77        self.index
78    }
79
80    /// After printing, check if there is more to print that the binary_writer
81    /// did not print.
82    pub fn bytes_remaining(&self) -> bool {
83        self.bytes_remaining
84    }
85}
86
87impl core::fmt::Write for WriteToBinaryOffsetWrapper<'_> {
88    fn write_str(&mut self, s: &str) -> core::fmt::Result {
89        let string_len = s.len();
90        if self.index + string_len < self.offset {
91            // We are still waiting for `self.offset` bytes to be send before we
92            // actually start printing.
93            self.index += string_len;
94            Ok(())
95        } else {
96            // We need to be printing at least some of this.
97            let start = if self.offset <= self.index {
98                // We're past our offset, so we can display this entire str.
99                0
100            } else {
101                // We want to start in the middle.
102                self.offset.saturating_sub(self.index)
103            };
104
105            // Calculate the number of bytes we are going to pass to the
106            // binary_writer.
107            let to_send = string_len - start;
108
109            // Actually do the write. This will return how many bytes it was
110            // able to print.
111            let ret = self
112                .binary_writer
113                .write_buffer(&(s).as_bytes()[start..string_len]);
114
115            match ret {
116                Ok(bytes_sent) => {
117                    // Update our index based on how much was sent and how much
118                    // (if any) we skipped over.
119                    self.index += bytes_sent + start;
120
121                    // Check if less was sent than we asked. This signals that
122                    // we will have more work to do on the next iteration.
123                    if to_send > bytes_sent {
124                        self.bytes_remaining = true;
125                    }
126
127                    Ok(())
128                }
129                Err(()) => Err(core::fmt::Error),
130            }
131        }
132    }
133}