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