freebsd-dev/contrib/bsnmp/tests/constbuf.h
2020-04-01 15:25:16 +00:00

318 lines
7.8 KiB
C++

/**
* 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