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}