blob: 879ab5cc30d8c5b990dc3a064552ca574b5765d9 [file] [log] [blame]
Brian Silverman55c62022018-09-03 19:13:44 -07001#ifndef MOTORS_PERIPHERAL_UART_BUFFER_H_
2#define MOTORS_PERIPHERAL_UART_BUFFER_H_
3
4#include <array>
5
6#include "third_party/GSL/include/gsl/gsl"
7
8namespace frc971 {
9namespace teensy {
10
11// Manages a circular buffer of data to send out.
12template<int kSize>
13class UartBuffer {
14 public:
15 // Returns the number of characters added.
Brian Silverman4787a6e2018-10-06 16:00:54 -070016 __attribute__((warn_unused_result)) int PushSpan(gsl::span<const char> data);
Brian Silverman55c62022018-09-03 19:13:44 -070017
Brian Silverman4f958792019-02-16 18:20:04 -080018 // max is the maximum size the returned spans should be.
19 // The data in the result is only valid until another method is called.
20 // Note that this may not return all available data when doing so would
21 // require wrapping around, but it will always return a non-empty span if any
22 // data is available.
23 gsl::span<const char> PopSpan(int max);
24
Brian Silverman55c62022018-09-03 19:13:44 -070025 bool empty() const { return size_ == 0; }
Brian Silverman4f958792019-02-16 18:20:04 -080026 bool full() const { return size_ == kSize; }
Brian Silverman55c62022018-09-03 19:13:44 -070027
28 // This may only be called when !empty().
29 char PopSingle();
Brian Silverman4f958792019-02-16 18:20:04 -080030 // This may only be called when !full().
31 void PushSingle(char c);
Brian Silverman55c62022018-09-03 19:13:44 -070032
33 static constexpr int size() { return kSize; }
34
35 private:
Brian Silverman4f958792019-02-16 18:20:04 -080036 // The index at which we will pop the next character.
Brian Silverman55c62022018-09-03 19:13:44 -070037 int start_ = 0;
38 // How many characters we currently have.
39 int size_ = 0;
40
41 ::std::array<char, kSize> data_;
42};
43
Brian Silverman4f958792019-02-16 18:20:04 -080044template <int kSize>
Brian Silverman4787a6e2018-10-06 16:00:54 -070045int UartBuffer<kSize>::PushSpan(gsl::span<const char> data) {
Brian Silverman55c62022018-09-03 19:13:44 -070046 const int end_location = (start_ + size_) % kSize;
47 const int remaining_end = ::std::min(kSize - size_, kSize - end_location);
48 const int on_end = ::std::min<int>(data.size(), remaining_end);
49 if (on_end > 0) {
50 memcpy(&data_[end_location], data.data(), on_end);
51 }
52 size_ += on_end;
53 const int not_on_end = data.size() - on_end;
54 if (not_on_end == 0) {
55 return data.size();
56 }
57
58 const int remaining_start = ::std::min(kSize - size_, start_);
59 const int on_start = ::std::min(not_on_end, remaining_start);
60 memcpy(data_.data(), &data[on_end], on_start);
61 size_ += on_start;
62 return on_end + on_start;
63}
64
Brian Silverman4f958792019-02-16 18:20:04 -080065template <int kSize>
66gsl::span<const char> UartBuffer<kSize>::PopSpan(int max) {
67 const size_t result_size = std::min(max, std::min(kSize - start_, size_));
68 const auto result = gsl::span<const char>(data_).subspan(start_, result_size);
69 start_ = (start_ + result_size) % kSize;
70 size_ -= result_size;
71 return result;
72}
73
74template <int kSize>
Brian Silverman55c62022018-09-03 19:13:44 -070075char UartBuffer<kSize>::PopSingle() {
76 const char r = data_[start_];
77 --size_;
78 start_ = (start_ + 1) % kSize;
79 return r;
80}
81
Brian Silverman4f958792019-02-16 18:20:04 -080082template <int kSize>
83void UartBuffer<kSize>::PushSingle(char c) {
84 const int end_location = (start_ + size_) % kSize;
85 data_[end_location] = c;
86 ++size_;
87}
88
Brian Silverman55c62022018-09-03 19:13:44 -070089} // namespace teensy
90} // namespace frc971
91
92#endif // MOTORS_PERIPHERAL_UART_BUFFER_H_