xref: /freebsd/contrib/bsnmp/tests/constbuf.h (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
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
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
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
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
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
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
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
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
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
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
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