xref: /cloud-hypervisor/serial_buffer/src/lib.rs (revision 80b2c98a68d4c68f372f849e8d26f7cae5867000)
1 // Copyright © 2021 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 
6 use std::collections::VecDeque;
7 use std::io::Write;
8 use std::sync::atomic::{AtomicBool, Ordering};
9 use std::sync::Arc;
10 
11 const MAX_BUFFER_SIZE: usize = 1 << 20;
12 
13 // Circular buffer implementation for serial output.
14 // Read from head; push to tail
15 pub struct SerialBuffer {
16     buffer: VecDeque<u8>,
17     out: Box<dyn Write + Send>,
18     write_out: Arc<AtomicBool>,
19 }
20 
21 impl SerialBuffer {
22     pub fn new(out: Box<dyn Write + Send>, write_out: Arc<AtomicBool>) -> Self {
23         Self {
24             buffer: VecDeque::new(),
25             out,
26             write_out,
27         }
28     }
29 
30     fn fill_buffer(&mut self, buf: &[u8]) {
31         if buf.len() >= MAX_BUFFER_SIZE {
32             let offset = buf.len() - MAX_BUFFER_SIZE;
33             self.buffer = VecDeque::from(buf[offset..].to_vec());
34             return;
35         }
36 
37         let num_allowed_bytes = MAX_BUFFER_SIZE - buf.len();
38         if self.buffer.len() > num_allowed_bytes {
39             let num_bytes_to_remove = self.buffer.len() - num_allowed_bytes;
40             self.buffer.drain(..num_bytes_to_remove);
41         }
42 
43         self.buffer.extend(buf);
44     }
45 }
46 
47 impl Write for SerialBuffer {
48     fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
49         // Simply fill the buffer if we're not allowed to write to the out
50         // device.
51         if !self.write_out.load(Ordering::Acquire) {
52             self.fill_buffer(buf);
53             return Ok(buf.len());
54         }
55 
56         // In case we're allowed to write to the out device, we flush the
57         // content of the buffer.
58         self.flush()?;
59 
60         // If after flushing the buffer, it's still not empty, that means
61         // only a subset of the bytes was written and we should fill the buffer
62         // with what's coming from the serial.
63         if !self.buffer.is_empty() {
64             self.fill_buffer(buf);
65             return Ok(buf.len());
66         }
67 
68         // We reach this point if we're allowed to write to the out device
69         // and we know there's nothing left in the buffer.
70         let mut offset = 0;
71         loop {
72             match self.out.write(&buf[offset..]) {
73                 Ok(written_bytes) => {
74                     if written_bytes < buf.len() - offset {
75                         offset += written_bytes;
76                         continue;
77                     }
78                 }
79                 Err(e) => {
80                     if !matches!(e.kind(), std::io::ErrorKind::WouldBlock) {
81                         return Err(e);
82                     }
83                     self.fill_buffer(&buf[offset..]);
84                 }
85             }
86             break;
87         }
88 
89         // Make sure we flush anything that might have been written to the
90         // out device.
91         self.out.flush()?;
92 
93         Ok(buf.len())
94     }
95 
96     // This function flushes the content of the buffer to the out device if
97     // it is allowed to, otherwise this is a no-op.
98     fn flush(&mut self) -> Result<(), std::io::Error> {
99         if !self.write_out.load(Ordering::Acquire) {
100             return Ok(());
101         }
102 
103         while let Some(byte) = self.buffer.pop_front() {
104             if self.out.write_all(&[byte]).is_err() {
105                 self.buffer.push_front(byte);
106                 break;
107             }
108         }
109         self.out.flush()
110     }
111 }
112