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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Basic binary write interface and supporting structs.
//!
//! This simple trait provides a synchronous interface for printing an arbitrary
//! binary slice. This mirrors the `core::fmt::Write` interface but doesn't
//! expect a `&str`.

/// Interface for writing an arbitrary buffer.
pub trait BinaryWrite {
    /// Write the `buffer` to some underlying print mechanism.
    ///
    /// Returns `Ok(usize)` on success with the number of bytes from `buffer`
    /// that were written. Returns `Err(())` on any error.
    fn write_buffer(&mut self, buffer: &[u8]) -> Result<usize, ()>;
}

/// Wrapper to convert a binary buffer writer to provide a `core::fmt::Write`
/// interface with offset tracking. This allows a synchronous writer to use
/// an underlying asynchronous write implementation.
///
/// This struct allows a synchronous writer to use the `core::fmt::Write`
/// interface when there is a limited size buffer underneath. This struct tracks
/// where in the overall write has actually been written to the underlying
/// `BinaryWrite` implementation.
///
/// The expected usage of this tool looks like:
///
/// ```ignore
/// let wrapper = WriteToBinaryOffsetWrapper::new(binary_writer);
///
/// // Set the byte index of the long, synchronous write where we should
/// // actually start passing to the binary writer.
/// wrapper.set_offset(offset);
///
/// // Do the long, synchronous write.
/// let _ = wrapper.write_fmt(format_args!(...));
///
/// if wrapper.bytes_remaining() {
///     // Some of the write did not finish (likely that means the binary
///     // writer's buffer filled up).
///     let next_offset = wrapper.get_index();
///
///     // Now wait for the binary write to finish, and start this process
///     // over but from the new offset.
/// } else {
///     // Nothing left to print, we're done!
/// }
/// ```
pub(crate) struct WriteToBinaryOffsetWrapper<'a> {
    /// Binary writer implementation that is asynchronous and has a fixed sized
    /// buffer.
    binary_writer: &'a mut dyn BinaryWrite,
    /// Where to start in the long synchronous write.
    offset: usize,
    /// Keep track of where in the long synchronous write we are currently
    /// displaying.
    index: usize,
    /// Track if write() is called, and the `binary_writer` did not print
    /// everything we passed to it. In that case, there are more bytes to write
    /// on the next iteration.
    bytes_remaining: bool,
}

impl<'a> WriteToBinaryOffsetWrapper<'a> {
    pub(crate) fn new(binary_writer: &'a mut dyn BinaryWrite) -> WriteToBinaryOffsetWrapper {
        WriteToBinaryOffsetWrapper {
            binary_writer,
            index: 0,
            offset: 0,
            bytes_remaining: false,
        }
    }

    /// Set the byte to start printing from on this iteration. Call this before
    /// calling `Write`.
    pub(crate) fn set_offset(&mut self, offset: usize) {
        self.offset = offset;
    }

    /// After printing, get the index we left off on to use as the offset for
    /// the next iteration.
    pub(crate) fn get_index(&self) -> usize {
        self.index
    }

    /// After printing, check if there is more to print that the binary_writer
    /// did not print.
    pub(crate) fn bytes_remaining(&self) -> bool {
        self.bytes_remaining
    }
}

impl<'a> core::fmt::Write for WriteToBinaryOffsetWrapper<'a> {
    fn write_str(&mut self, s: &str) -> core::fmt::Result {
        let string_len = s.len();
        if self.index + string_len < self.offset {
            // We are still waiting for `self.offset` bytes to be send before we
            // actually start printing.
            self.index += string_len;
            Ok(())
        } else {
            // We need to be printing at least some of this.
            let start = if self.offset <= self.index {
                // We're past our offset, so we can display this entire str.
                0
            } else {
                // We want to start in the middle.
                self.offset - self.index
            };

            // Calculate the number of bytes we are going to pass to the
            // binary_writer.
            let to_send = string_len - start;

            // Actually do the write. This will return how many bytes it was
            // able to print.
            let ret = self
                .binary_writer
                .write_buffer(&(s).as_bytes()[start..string_len]);

            match ret {
                Ok(bytes_sent) => {
                    // Update our index based on how much was sent and how much
                    // (if any) we skipped over.
                    self.index += bytes_sent + start;

                    // Check if less was sent than we asked. This signals that
                    // we will have more work to do on the next iteration.
                    if to_send > bytes_sent {
                        self.bytes_remaining = true;
                    }

                    Ok(())
                }
                Err(()) => Err(core::fmt::Error),
            }
        }
    }
}

/// Provide a `BinaryWrite` interface on top of a synchronous `core::fmt::Write`
/// interface.
///
/// Note, this MUST only be used to reverse the output of
/// `WriteToBinaryOffsetWrapper`. That is, this assume that the binary strings
/// are valid UTF-8, which will be the case if the binary buffer comes from some
/// `core::fmt::Write` operation originally.
pub(crate) struct BinaryToWriteWrapper<'a> {
    writer: &'a mut dyn core::fmt::Write,
}

impl<'a> BinaryToWriteWrapper<'a> {
    pub(crate) fn new(writer: &'a mut dyn core::fmt::Write) -> BinaryToWriteWrapper {
        BinaryToWriteWrapper { writer }
    }
}

impl<'a> BinaryWrite for BinaryToWriteWrapper<'a> {
    fn write_buffer(&mut self, buffer: &[u8]) -> Result<usize, ()> {
        // Convert the binary string to UTF-8 so we can print it as a string. If
        // this is not actually a UTF-8 string, then return Err(()).
        let s = core::str::from_utf8(buffer).or(Err(()))?;
        let _ = self.writer.write_str(s);
        Ok(buffer.len())
    }
}