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
43namespace test {
44namespace detail {
45
46enum class Constbuf_mode {
47    BIN,
48    COMMENT,
49    HEX,
50    CHECK,
51    GOTO,
52};
53
54template<typename A>
55constexpr bool
56count_comment(A c, Constbuf_mode &mode)
57{
58    if (c == '\n')
59        mode = Constbuf_mode::BIN;
60    return false;
61}
62
63template<typename A>
64constexpr bool
65count_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
80template<typename A, typename U>
81constexpr bool
82handle_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
105template<typename A>
106constexpr bool
107count_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
115template<typename A>
116constexpr bool
117handle_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
129template<typename A>
130constexpr bool
131count_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
144template<typename A>
145constexpr bool
146count_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
175template<typename A, typename U>
176constexpr bool
177handle_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 */
220template<typename A, A ...a>
221constexpr std::size_t
222count_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
254template<typename A, A ...a>
255#ifdef HAVE_EXPR_IN_ARRAY_SIZE
256constexpr auto
257#else
258auto
259#endif
260constbuf()
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
297inline namespace literals {
298inline namespace cbuf_literals {
299
300#ifdef HAVE_EXPR_IN_ARRAY_SIZE
301template<typename A, A ...a>
302constexpr auto
303#else
304template<typename A, A ...a>
305auto
306#endif
307operator ""_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