summaryrefslogtreecommitdiff
path: root/tests/constbuf.h
diff options
context:
space:
mode:
Diffstat (limited to 'tests/constbuf.h')
-rw-r--r--tests/constbuf.h317
1 files changed, 317 insertions, 0 deletions
diff --git a/tests/constbuf.h b/tests/constbuf.h
new file mode 100644
index 0000000000000..411e53181d86d
--- /dev/null
+++ b/tests/constbuf.h
@@ -0,0 +1,317 @@
+/**
+ * Copyright (c) 2019-2020 Hartmut Brandt.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef constbuf_h_1578777513
+#define constbuf_h_1578777513
+
+#include <array>
+#include <cassert>
+#include <cstdint>
+
+#if !defined(HAVE_EXPR_IN_ARRAY_SIZE) && (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>= 9 || (__GNUC__ == 9 && __GNUC_MINOR__ >= 1))))
+#define HAVE_EXPR_IN_ARRAY_SIZE 1
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template"
+#endif
+
+#ifndef HAVE_EXPR_IN_ARRAY_SIZE
+#include <vector>
+#endif
+
+namespace test {
+namespace detail {
+
+enum class Constbuf_mode {
+ BIN,
+ COMMENT,
+ HEX,
+ CHECK,
+ GOTO,
+};
+
+template<typename A>
+constexpr bool
+count_comment(A c, Constbuf_mode &mode)
+{
+ if (c == '\n')
+ mode = Constbuf_mode::BIN;
+ return false;
+}
+
+template<typename A>
+constexpr bool
+count_hex(A c, Constbuf_mode &mode, std::size_t &bits)
+{
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F')) {
+ if (bits % 4 != 0)
+ throw "unaligned hex digit";
+ bits += 4;
+ return false;
+ }
+ if (c == ':')
+ return false;
+ mode = Constbuf_mode::BIN;
+ return true;
+}
+
+template<typename A, typename U>
+constexpr bool
+handle_hex(A c, Constbuf_mode &mode, std::size_t &bit, U &n)
+{
+ if (c >= '0' && c <= '9') {
+ n[bit / 8] |= ((c - '0') << 4) >> (bit % 8);
+ bit += 4;
+ return false;
+ }
+ if (c >= 'a' && c <= 'f') {
+ n[bit / 8] |= ((c - 'a' + 10) << 4) >> (bit % 8);
+ bit += 4;
+ return false;
+ }
+ if (c >= 'A' && c <= 'F') {
+ n[bit / 8] |= ((c - 'A' + 10) << 4) >> (bit % 8);
+ bit += 4;
+ return false;
+ }
+ if (c == ':')
+ return false;
+ mode = Constbuf_mode::BIN;
+ return true;
+}
+
+template<typename A>
+constexpr bool
+count_check(A c, Constbuf_mode &mode, std::size_t &)
+{
+ if (c >= '0' && c <= '9')
+ return false;
+ mode = Constbuf_mode::BIN;
+ return true;
+}
+
+template<typename A>
+constexpr bool
+handle_check(A c, Constbuf_mode &mode, std::size_t &bits, std::size_t &addr)
+{
+ if (c >= '0' && c <= '9') {
+ addr = 10 * addr + c - '0';
+ return false;
+ }
+ if (bits % 8 != 0 || bits / 8 != addr)
+ throw "address check failed";
+ mode = Constbuf_mode::BIN;
+ return true;
+}
+
+template<typename A>
+constexpr bool
+count_goto(A c, Constbuf_mode &mode, std::size_t &bits, std::size_t &addr)
+{
+ if (c >= '0' && c <= '9') {
+ addr = 10 * addr + c - '0';
+ return false;
+ }
+ if (8 * addr < bits)
+ throw "cannot go backwards";
+ bits = 8 * addr;
+ mode = Constbuf_mode::BIN;
+ return true;
+}
+
+template<typename A>
+constexpr bool
+count_bin(A c, Constbuf_mode &mode, std::size_t &bits, std::size_t &addr)
+{
+ if (c == ' ' || c == '\t' || c == '\n')
+ /* just ignore */
+ return false;
+ if (c == ';') {
+ mode = Constbuf_mode::COMMENT;
+ return false;
+ }
+ if (c == 'x' || c == 'X') {
+ mode = Constbuf_mode::HEX;
+ return false;
+ }
+ if (c == '!') {
+ mode = Constbuf_mode::CHECK;
+ return false;
+ }
+ if (c == '@') {
+ mode = Constbuf_mode::GOTO;
+ addr = 0;
+ return false;
+ }
+ if (c == '0' || c == '1' || c == '.') {
+ bits++;
+ return false;
+ }
+ throw "bad character";
+}
+
+template<typename A, typename U>
+constexpr bool
+handle_bin(A c, Constbuf_mode &mode, std::size_t &bit, std::size_t &addr, U &n)
+{
+ if (c == ' ' || c == '\t' || c == '\n')
+ /* just ignore */
+ return false;
+ if (c == ';') {
+ mode = Constbuf_mode::COMMENT;
+ return false;
+ }
+ if (c == 'x' || c == 'X') {
+ mode = Constbuf_mode::HEX;
+ return false;
+ }
+ if (c == '!') {
+ mode = Constbuf_mode::CHECK;
+ addr = 0;
+ return false;
+ }
+ if (c == '@') {
+ mode = Constbuf_mode::GOTO;
+ addr = 0;
+ return false;
+ }
+ if (c == '0' || c == '.') {
+ bit++;
+ return false;
+ }
+ if (c == '1') {
+ n[bit / 8] |= 0x80 >> (bit % 8);
+ bit++;
+ return false;
+ }
+ throw "bad character";
+}
+
+/**
+ * Count the bits in the test buffer. For a syntax see below.
+ *
+ * \tparam A buffer base character type
+ * \tparam a characters
+ *
+ * \return number of bits required
+ */
+template<typename A, A ...a>
+constexpr std::size_t
+count_bits()
+{
+ std::size_t bits {0};
+ std::size_t addr {0};
+ auto mode = Constbuf_mode::BIN;
+
+ for (auto c : {a...}) {
+ for (bool again = true; again; again = false) {
+ switch (mode) {
+ case Constbuf_mode::COMMENT:
+ again = count_comment(c, mode);
+ break;
+ case Constbuf_mode::CHECK:
+ again = count_check(c, mode, bits);
+ break;
+ case Constbuf_mode::GOTO:
+ again = count_goto(c, mode, bits, addr);
+ break;
+ case Constbuf_mode::HEX:
+ again = count_hex(c, mode, bits);
+ break;
+ case Constbuf_mode::BIN:
+ again = count_bin(c, mode, bits, addr);
+ break;
+ }
+ }
+ }
+ return bits;
+}
+
+}
+
+template<typename A, A ...a>
+#ifdef HAVE_EXPR_IN_ARRAY_SIZE
+constexpr auto
+#else
+auto
+#endif
+constbuf()
+{
+#ifdef HAVE_EXPR_IN_ARRAY_SIZE
+ std::array<uint8_t, (detail::count_bits<A, a...>() + 7) / 8> n {};
+#else
+ std::vector<uint8_t> n((detail::count_bits<A, a...>() + 7) / 8);
+#endif
+ using namespace detail;
+
+ std::size_t bit {0};
+ std::size_t addr {0};
+ auto mode = Constbuf_mode::BIN;
+
+ for (auto c : {a...}) {
+ for (bool again = true; again; again = false) {
+ switch (mode) {
+ case Constbuf_mode::COMMENT:
+ again = count_comment(c, mode);
+ break;
+ case Constbuf_mode::CHECK:
+ again = handle_check(c, mode, bit, addr);
+ break;
+ case Constbuf_mode::GOTO:
+ again = count_goto(c, mode, bit, addr);
+ break;
+ case Constbuf_mode::HEX:
+ again = handle_hex(c, mode, bit, n);
+ break;
+ case Constbuf_mode::BIN:
+ again = handle_bin(c, mode, bit, addr, n);
+ break;
+ }
+ }
+ }
+ return n;
+}
+
+inline namespace literals {
+inline namespace cbuf_literals {
+
+#ifdef HAVE_EXPR_IN_ARRAY_SIZE
+template<typename A, A ...a>
+constexpr auto
+#else
+template<typename A, A ...a>
+auto
+#endif
+operator ""_cbuf()
+{
+ return test::constbuf<A, a...>();
+}
+
+} /* namespace cbuf_literals */
+} /* namespace literals */
+
+} /* namespace test */
+
+#endif