kernel/utilities/
binary_write.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 write interface and supporting structs.
6//!
7//! This simple trait provides a synchronous interface for printing an arbitrary
8//! binary slice. This mirrors the `core::fmt::Write` interface but doesn't
9//! expect a `&str`.
10
11/// Interface for writing an arbitrary buffer.
12pub trait BinaryWrite {
13    /// Write the `buffer` to some underlying print mechanism.
14    ///
15    /// Returns `Ok(usize)` on success with the number of bytes from `buffer`
16    /// that were written. Returns `Err(())` on any error.
17    fn write_buffer(&mut self, buffer: &[u8]) -> Result<usize, ()>;
18}
19
20/// Wrapper to convert a binary buffer writer to provide a `core::fmt::Write`
21/// interface with offset tracking. This allows a synchronous writer to use
22/// an underlying asynchronous write implementation.
23///
24/// This struct allows a synchronous writer to use the `core::fmt::Write`
25/// interface when there is a limited size buffer underneath. This struct tracks
26/// where in the overall write has actually been written to the underlying
27/// `BinaryWrite` implementation.
28///
29/// The expected usage of this tool looks like:
30///
31/// ```ignore
32/// let wrapper = WriteToBinaryOffsetWrapper::new(binary_writer);
33///
34/// // Set the byte index of the long, synchronous write where we should
35/// // actually start passing to the binary writer.
36/// wrapper.set_offset(offset);
37///
38/// // Do the long, synchronous write.
39/// let _ = wrapper.write_fmt(format_args!(...));
40///
41/// if wrapper.bytes_remaining() {
42///     // Some of the write did not finish (likely that means the binary
43///     // writer's buffer filled up).
44///     let next_offset = wrapper.get_index();
45///
46///     // Now wait for the binary write to finish, and start this process
47///     // over but from the new offset.
48/// } else {
49///     // Nothing left to print, we're done!
50/// }
51/// ```
52pub struct WriteToBinaryOffsetWrapper<'a> {
53    /// Binary writer implementation that is asynchronous and has a fixed sized
54    /// buffer.
55    binary_writer: &'a mut dyn BinaryWrite,
56    /// Where to start in the long synchronous write.
57    offset: usize,
58    /// Keep track of where in the long synchronous write we are currently
59    /// displaying.
60    index: usize,
61    /// Track if write() is called, and the `binary_writer` did not print
62    /// everything we passed to it. In that case, there are more bytes to write
63    /// on the next iteration.
64    bytes_remaining: bool,
65}
66
67impl<'a> WriteToBinaryOffsetWrapper<'a> {
68    pub fn new(binary_writer: &'a mut dyn BinaryWrite) -> Self {
69        Self {
70            binary_writer,
71            index: 0,
72            offset: 0,
73            bytes_remaining: false,
74        }
75    }
76
77    /// Set the byte to start printing from on this iteration. Call this before
78    /// calling `Write`.
79    pub fn set_offset(&mut self, offset: usize) {
80        self.offset = offset;
81    }
82
83    /// After printing, get the index we left off on to use as the offset for
84    /// the next iteration.
85    pub fn get_index(&self) -> usize {
86        self.index
87    }
88
89    /// After printing, check if there is more to print that the binary_writer
90    /// did not print.
91    pub fn bytes_remaining(&self) -> bool {
92        self.bytes_remaining
93    }
94}
95
96impl core::fmt::Write for WriteToBinaryOffsetWrapper<'_> {
97    fn write_str(&mut self, s: &str) -> core::fmt::Result {
98        let string_len = s.len();
99        if self.index + string_len < self.offset {
100            // We are still waiting for `self.offset` bytes to be send before we
101            // actually start printing.
102            self.index += string_len;
103            Ok(())
104        } else {
105            // We need to be printing at least some of this.
106            let start = if self.offset <= self.index {
107                // We're past our offset, so we can display this entire str.
108                0
109            } else {
110                // We want to start in the middle.
111                self.offset - self.index
112            };
113
114            // Calculate the number of bytes we are going to pass to the
115            // binary_writer.
116            let to_send = string_len - start;
117
118            // Actually do the write. This will return how many bytes it was
119            // able to print.
120            let ret = self
121                .binary_writer
122                .write_buffer(&(s).as_bytes()[start..string_len]);
123
124            match ret {
125                Ok(bytes_sent) => {
126                    // Update our index based on how much was sent and how much
127                    // (if any) we skipped over.
128                    self.index += bytes_sent + start;
129
130                    // Check if less was sent than we asked. This signals that
131                    // we will have more work to do on the next iteration.
132                    if to_send > bytes_sent {
133                        self.bytes_remaining = true;
134                    }
135
136                    Ok(())
137                }
138                Err(()) => Err(core::fmt::Error),
139            }
140        }
141    }
142}
143
144/// Provide a `BinaryWrite` interface on top of a synchronous `core::fmt::Write`
145/// interface.
146///
147/// Note, this MUST only be used to reverse the output of
148/// `WriteToBinaryOffsetWrapper`. That is, this assume that the binary strings
149/// are valid UTF-8, which will be the case if the binary buffer comes from some
150/// `core::fmt::Write` operation originally.
151pub(crate) struct BinaryToWriteWrapper<'a> {
152    writer: &'a mut dyn core::fmt::Write,
153}
154
155impl<'a> BinaryToWriteWrapper<'a> {
156    pub(crate) fn new(writer: &'a mut dyn core::fmt::Write) -> Self {
157        Self { writer }
158    }
159}
160
161impl BinaryWrite for BinaryToWriteWrapper<'_> {
162    fn write_buffer(&mut self, buffer: &[u8]) -> Result<usize, ()> {
163        // Convert the binary string to UTF-8 so we can print it as a string. If
164        // this is not actually a UTF-8 string, then return Err(()).
165        let s = core::str::from_utf8(buffer).or(Err(()))?;
166        let _ = self.writer.write_str(s);
167        Ok(buffer.len())
168    }
169}