1 /**
2 * Copyright (c) 2019-2020 Hartmut Brandt.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #ifndef constbuf_h_1578777513
27 #define constbuf_h_1578777513
28
29 #include <array>
30 #include <cassert>
31 #include <cstdint>
32
33 #if !defined(HAVE_EXPR_IN_ARRAY_SIZE) && (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>= 9 || (__GNUC__ == 9 && __GNUC_MINOR__ >= 1))))
34 #define HAVE_EXPR_IN_ARRAY_SIZE 1
35 #pragma clang diagnostic push
36 #pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template"
37 #endif
38
39 #ifndef HAVE_EXPR_IN_ARRAY_SIZE
40 #include <vector>
41 #endif
42
43 namespace test {
44 namespace detail {
45
46 enum class Constbuf_mode {
47 BIN,
48 COMMENT,
49 HEX,
50 CHECK,
51 GOTO,
52 };
53
54 template<typename A>
55 constexpr bool
count_comment(A c,Constbuf_mode & mode)56 count_comment(A c, Constbuf_mode &mode)
57 {
58 if (c == '\n')
59 mode = Constbuf_mode::BIN;
60 return false;
61 }
62
63 template<typename A>
64 constexpr bool
count_hex(A c,Constbuf_mode & mode,std::size_t & bits)65 count_hex(A c, Constbuf_mode &mode, std::size_t &bits)
66 {
67 if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
68 (c >= 'A' && c <= 'F')) {
69 if (bits % 4 != 0)
70 throw "unaligned hex digit";
71 bits += 4;
72 return false;
73 }
74 if (c == ':')
75 return false;
76 mode = Constbuf_mode::BIN;
77 return true;
78 }
79
80 template<typename A, typename U>
81 constexpr bool
handle_hex(A c,Constbuf_mode & mode,std::size_t & bit,U & n)82 handle_hex(A c, Constbuf_mode &mode, std::size_t &bit, U &n)
83 {
84 if (c >= '0' && c <= '9') {
85 n[bit / 8] |= ((c - '0') << 4) >> (bit % 8);
86 bit += 4;
87 return false;
88 }
89 if (c >= 'a' && c <= 'f') {
90 n[bit / 8] |= ((c - 'a' + 10) << 4) >> (bit % 8);
91 bit += 4;
92 return false;
93 }
94 if (c >= 'A' && c <= 'F') {
95 n[bit / 8] |= ((c - 'A' + 10) << 4) >> (bit % 8);
96 bit += 4;
97 return false;
98 }
99 if (c == ':')
100 return false;
101 mode = Constbuf_mode::BIN;
102 return true;
103 }
104
105 template<typename A>
106 constexpr bool
count_check(A c,Constbuf_mode & mode,std::size_t &)107 count_check(A c, Constbuf_mode &mode, std::size_t &)
108 {
109 if (c >= '0' && c <= '9')
110 return false;
111 mode = Constbuf_mode::BIN;
112 return true;
113 }
114
115 template<typename A>
116 constexpr bool
handle_check(A c,Constbuf_mode & mode,std::size_t & bits,std::size_t & addr)117 handle_check(A c, Constbuf_mode &mode, std::size_t &bits, std::size_t &addr)
118 {
119 if (c >= '0' && c <= '9') {
120 addr = 10 * addr + c - '0';
121 return false;
122 }
123 if (bits % 8 != 0 || bits / 8 != addr)
124 throw "address check failed";
125 mode = Constbuf_mode::BIN;
126 return true;
127 }
128
129 template<typename A>
130 constexpr bool
count_goto(A c,Constbuf_mode & mode,std::size_t & bits,std::size_t & addr)131 count_goto(A c, Constbuf_mode &mode, std::size_t &bits, std::size_t &addr)
132 {
133 if (c >= '0' && c <= '9') {
134 addr = 10 * addr + c - '0';
135 return false;
136 }
137 if (8 * addr < bits)
138 throw "cannot go backwards";
139 bits = 8 * addr;
140 mode = Constbuf_mode::BIN;
141 return true;
142 }
143
144 template<typename A>
145 constexpr bool
count_bin(A c,Constbuf_mode & mode,std::size_t & bits,std::size_t & addr)146 count_bin(A c, Constbuf_mode &mode, std::size_t &bits, std::size_t &addr)
147 {
148 if (c == ' ' || c == '\t' || c == '\n')
149 /* just ignore */
150 return false;
151 if (c == ';') {
152 mode = Constbuf_mode::COMMENT;
153 return false;
154 }
155 if (c == 'x' || c == 'X') {
156 mode = Constbuf_mode::HEX;
157 return false;
158 }
159 if (c == '!') {
160 mode = Constbuf_mode::CHECK;
161 return false;
162 }
163 if (c == '@') {
164 mode = Constbuf_mode::GOTO;
165 addr = 0;
166 return false;
167 }
168 if (c == '0' || c == '1' || c == '.') {
169 bits++;
170 return false;
171 }
172 throw "bad character";
173 }
174
175 template<typename A, typename U>
176 constexpr bool
handle_bin(A c,Constbuf_mode & mode,std::size_t & bit,std::size_t & addr,U & n)177 handle_bin(A c, Constbuf_mode &mode, std::size_t &bit, std::size_t &addr, U &n)
178 {
179 if (c == ' ' || c == '\t' || c == '\n')
180 /* just ignore */
181 return false;
182 if (c == ';') {
183 mode = Constbuf_mode::COMMENT;
184 return false;
185 }
186 if (c == 'x' || c == 'X') {
187 mode = Constbuf_mode::HEX;
188 return false;
189 }
190 if (c == '!') {
191 mode = Constbuf_mode::CHECK;
192 addr = 0;
193 return false;
194 }
195 if (c == '@') {
196 mode = Constbuf_mode::GOTO;
197 addr = 0;
198 return false;
199 }
200 if (c == '0' || c == '.') {
201 bit++;
202 return false;
203 }
204 if (c == '1') {
205 n[bit / 8] |= 0x80 >> (bit % 8);
206 bit++;
207 return false;
208 }
209 throw "bad character";
210 }
211
212 /**
213 * Count the bits in the test buffer. For a syntax see below.
214 *
215 * \tparam A buffer base character type
216 * \tparam a characters
217 *
218 * \return number of bits required
219 */
220 template<typename A, A ...a>
221 constexpr std::size_t
count_bits()222 count_bits()
223 {
224 std::size_t bits {0};
225 std::size_t addr {0};
226 auto mode = Constbuf_mode::BIN;
227
228 for (auto c : {a...}) {
229 for (bool again = true; again; again = false) {
230 switch (mode) {
231 case Constbuf_mode::COMMENT:
232 again = count_comment(c, mode);
233 break;
234 case Constbuf_mode::CHECK:
235 again = count_check(c, mode, bits);
236 break;
237 case Constbuf_mode::GOTO:
238 again = count_goto(c, mode, bits, addr);
239 break;
240 case Constbuf_mode::HEX:
241 again = count_hex(c, mode, bits);
242 break;
243 case Constbuf_mode::BIN:
244 again = count_bin(c, mode, bits, addr);
245 break;
246 }
247 }
248 }
249 return bits;
250 }
251
252 }
253
254 template<typename A, A ...a>
255 #ifdef HAVE_EXPR_IN_ARRAY_SIZE
256 constexpr auto
257 #else
258 auto
259 #endif
constbuf()260 constbuf()
261 {
262 #ifdef HAVE_EXPR_IN_ARRAY_SIZE
263 std::array<uint8_t, (detail::count_bits<A, a...>() + 7) / 8> n {};
264 #else
265 std::vector<uint8_t> n((detail::count_bits<A, a...>() + 7) / 8);
266 #endif
267 using namespace detail;
268
269 std::size_t bit {0};
270 std::size_t addr {0};
271 auto mode = Constbuf_mode::BIN;
272
273 for (auto c : {a...}) {
274 for (bool again = true; again; again = false) {
275 switch (mode) {
276 case Constbuf_mode::COMMENT:
277 again = count_comment(c, mode);
278 break;
279 case Constbuf_mode::CHECK:
280 again = handle_check(c, mode, bit, addr);
281 break;
282 case Constbuf_mode::GOTO:
283 again = count_goto(c, mode, bit, addr);
284 break;
285 case Constbuf_mode::HEX:
286 again = handle_hex(c, mode, bit, n);
287 break;
288 case Constbuf_mode::BIN:
289 again = handle_bin(c, mode, bit, addr, n);
290 break;
291 }
292 }
293 }
294 return n;
295 }
296
297 inline namespace literals {
298 inline namespace cbuf_literals {
299
300 #ifdef HAVE_EXPR_IN_ARRAY_SIZE
301 template<typename A, A ...a>
302 constexpr auto
303 #else
304 template<typename A, A ...a>
305 auto
306 #endif
307 operator ""_cbuf()
308 {
309 return test::constbuf<A, a...>();
310 }
311
312 } /* namespace cbuf_literals */
313 } /* namespace literals */
314
315 } /* namespace test */
316
317 #endif
318