1 /* sane - Scanner Access Now Easy.
2 
3    Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
4 
5    This file is part of the SANE package.
6 
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 */
20 
21 #ifndef BACKEND_GENESYS_LINE_BUFFER_H
22 #define BACKEND_GENESYS_LINE_BUFFER_H
23 
24 #include "error.h"
25 
26 #include <algorithm>
27 #include <cstdint>
28 #include <cstddef>
29 #include <vector>
30 
31 namespace genesys {
32 
33 class RowBuffer
34 {
35 public:
RowBuffer(std::size_t line_bytes)36     RowBuffer(std::size_t line_bytes) : row_bytes_{line_bytes} {}
37     RowBuffer(const RowBuffer&) = default;
38     RowBuffer& operator=(const RowBuffer&) = default;
39     ~RowBuffer() = default;
40 
get_row_ptr(std::size_t y) const41     const std::uint8_t* get_row_ptr(std::size_t y) const
42     {
43         if (y >= height()) {
44             throw SaneException("y %zu is out of range", y);
45         }
46         return data_.data() + row_bytes_ * get_row_index(y);
47     }
48 
get_row_ptr(std::size_t y)49     std::uint8_t* get_row_ptr(std::size_t y)
50     {
51         if (y >= height()) {
52             throw SaneException("y %zu is out of range", y);
53         }
54         return data_.data() + row_bytes_ * get_row_index(y);
55     }
56 
get_front_row_ptr() const57     const std::uint8_t* get_front_row_ptr() const { return get_row_ptr(0); }
get_front_row_ptr()58     std::uint8_t* get_front_row_ptr() { return get_row_ptr(0); }
get_back_row_ptr() const59     const std::uint8_t* get_back_row_ptr() const { return get_row_ptr(height() - 1); }
get_back_row_ptr()60     std::uint8_t* get_back_row_ptr() { return get_row_ptr(height() - 1); }
61 
empty() const62     bool empty() const { return is_linear_ && first_ == last_; }
63 
full()64     bool full()
65     {
66         if (is_linear_) {
67             return last_ == buffer_end_;
68         }
69         return first_ == last_;
70     }
71 
is_linear() const72     bool is_linear() const { return is_linear_; }
73 
linearize()74     void linearize()
75     {
76         if (!is_linear_) {
77             std::rotate(data_.begin(), data_.begin() + row_bytes_ * first_, data_.end());
78             last_ = height();
79             first_ = 0;
80             is_linear_ = true;
81         }
82     }
83 
pop_front()84     void pop_front()
85     {
86         if (empty()) {
87             throw SaneException("Trying to pop out of empty() line buffer");
88         }
89 
90         first_++;
91         if (first_ == last_) {
92             first_ = 0;
93             last_ = 0;
94             is_linear_ = true;
95         } else  if (first_ == buffer_end_) {
96             first_ = 0;
97             is_linear_ = true;
98         }
99     }
100 
push_front()101     void push_front()
102     {
103         if (height() + 1 >= height_capacity()) {
104             ensure_capacity(std::max<std::size_t>(1, height() * 2));
105         }
106 
107         if (first_ == 0) {
108             is_linear_ = false;
109             first_ = buffer_end_;
110         }
111         first_--;
112     }
113 
pop_back()114     void pop_back()
115     {
116         if (empty()) {
117             throw SaneException("Trying to pop out of empty() line buffer");
118         }
119         if (last_ == 0) {
120             last_ = buffer_end_;
121             is_linear_ = true;
122         }
123         last_--;
124         if (first_ == last_) {
125             first_ = 0;
126             last_ = 0;
127             is_linear_ = true;
128         }
129     }
130 
push_back()131     void push_back()
132     {
133         if (height() + 1 >= height_capacity()) {
134             ensure_capacity(std::max<std::size_t>(1, height() * 2));
135         }
136 
137         if (last_ == buffer_end_) {
138             is_linear_ = false;
139             last_ = 0;
140         }
141         last_++;
142     }
143 
row_bytes() const144     std::size_t row_bytes() const { return row_bytes_; }
145 
height() const146     std::size_t height() const
147     {
148         if (!is_linear_) {
149             return last_ + buffer_end_ - first_;
150         }
151         return last_ - first_;
152     }
153 
height_capacity() const154     std::size_t height_capacity() const { return buffer_end_; }
155 
clear()156     void clear()
157     {
158         first_ = 0;
159         last_ = 0;
160     }
161 
162 private:
get_row_index(std::size_t index) const163     std::size_t get_row_index(std::size_t index) const
164     {
165         if (index >= buffer_end_ - first_) {
166             return index - (buffer_end_ - first_);
167         }
168         return index + first_;
169     }
170 
ensure_capacity(std::size_t capacity)171     void ensure_capacity(std::size_t capacity)
172     {
173         if (capacity < height_capacity())
174             return;
175         linearize();
176         data_.resize(capacity * row_bytes_);
177         buffer_end_ = capacity;
178     }
179 
180 private:
181     std::size_t row_bytes_ = 0;
182     std::size_t first_ = 0;
183     std::size_t last_ = 0;
184     std::size_t buffer_end_ = 0;
185     bool is_linear_ = true;
186     std::vector<std::uint8_t> data_;
187 };
188 
189 } // namespace genesys
190 
191 #endif // BACKEND_GENESYS_LINE_BUFFER_H
192