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