summaryrefslogtreecommitdiff
path: root/src/devices/serial/fifo.rs
blob: dbf46599d12360a0b2eb297d6805dd692cc422b0 (plain)
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
// Copyright (c) 2026 taitep
// SPDX-License-Identifier: BSD-2-Clause
//
// This file is part of TRVE (https://gitea.taitep.se/taitep/trve)
// See LICENSE file in the project root for full license text.

use std::io::{self, Read, Write};

pub struct UartFifo<const CAP: usize> {
    buf: [u8; CAP],
    head: usize,
    tail: usize,
    len: usize,
}

impl<const CAP: usize> UartFifo<CAP> {
    pub fn pop_single_byte(&mut self) -> Option<u8> {
        if self.is_empty() {
            return None;
        }

        let value = self.buf[self.tail];
        self.advance_read(1);
        Some(value)
    }

    pub fn push_single_byte(&mut self, value: u8) -> bool {
        if self.is_full() {
            return false;
        }

        self.buf[self.head] = value;
        self.advance_write(1);
        true
    }

    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    pub fn is_full(&self) -> bool {
        self.len == CAP
    }

    fn write_slice(&mut self) -> &mut [u8] {
        if self.is_full() {
            return &mut [];
        }

        if self.head >= self.tail {
            &mut self.buf[self.head..]
        } else {
            &mut self.buf[self.head..self.tail]
        }
    }

    fn advance_write(&mut self, n: usize) {
        debug_assert!(n <= CAP - self.len);
        self.head = (self.head + n) % CAP;
        self.len += n;
    }

    fn read_slice(&self) -> &[u8] {
        if self.is_empty() {
            return &[];
        }

        if self.tail < self.head {
            &self.buf[self.tail..self.head]
        } else {
            &self.buf[self.tail..]
        }
    }

    fn advance_read(&mut self, n: usize) {
        debug_assert!(n <= self.len);
        self.tail = (self.tail + n) % CAP;
        self.len -= n;
    }

    pub fn read_from<R: Read>(&mut self, reader: &mut R) -> io::Result<usize> {
        let slice = self.write_slice();
        if slice.is_empty() {
            return Ok(0);
        }

        let n = reader.read(slice)?;
        self.advance_write(n);
        Ok(n)
    }

    pub fn write_to<W: Write>(&mut self, writer: &mut W) -> io::Result<usize> {
        let slice = self.read_slice();
        if slice.is_empty() {
            return Ok(0);
        }

        let n = writer.write(slice)?;
        self.advance_read(n);
        Ok(n)
    }
}

impl<const SIZE: usize> Default for UartFifo<SIZE> {
    fn default() -> Self {
        UartFifo {
            buf: [0; SIZE],
            head: 0,
            tail: 0,
            len: 0,
        }
    }
}