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}