diff --git a/include/nil/crypto3/block/accumulators/block.hpp b/include/nil/crypto3/block/accumulators/block.hpp index e919dee..f36658d 100644 --- a/include/nil/crypto3/block/accumulators/block.hpp +++ b/include/nil/crypto3/block/accumulators/block.hpp @@ -70,7 +70,7 @@ namespace nil { constexpr static const std::size_t value_bits = sizeof(typename block_type::value_type) * CHAR_BIT; constexpr static const std::size_t block_values = block_bits / value_bits; - typedef ::nil::crypto3::detail::injector + typedef ::nil::crypto3::detail::injector injector_type; public: diff --git a/include/nil/crypto3/detail/endian_shift.hpp b/include/nil/crypto3/detail/endian_shift.hpp index 6d6fd12..f5e5e03 100644 --- a/include/nil/crypto3/detail/endian_shift.hpp +++ b/include/nil/crypto3/detail/endian_shift.hpp @@ -102,8 +102,8 @@ namespace nil { std::size_t sz[2] = {UnitBits - shift_rem, shift_rem}; word_type masks[2] = { - unbounded_shr(high_bits(~word_type(), sz[0]), shift_unit_bits), - unbounded_shr(high_bits(~word_type(), sz[1]), shift_unit_bits + UnitBits + sz[0])}; + unbounded_shr(high_bits(~word_type(), sz[0]), shift_unit_bits), + unbounded_shr(high_bits(~word_type(), sz[1]), shift_unit_bits + UnitBits + sz[0])}; std::size_t bits_left = word_bits - shift; word_type w_combined = 0; diff --git a/include/nil/crypto3/detail/inject.hpp b/include/nil/crypto3/detail/inject.hpp index a8f09fb..43c328b 100644 --- a/include/nil/crypto3/detail/inject.hpp +++ b/include/nil/crypto3/detail/inject.hpp @@ -1,6 +1,7 @@ //---------------------------------------------------------------------------// // Copyright (c) 2020 Nikita Kaskov // Copyright (c) 2020 Alexander Sokolov +// Copyright (c) 2024 Iosif (x-mass) // // MIT License // @@ -26,283 +27,167 @@ #ifndef CRYPTO3_INJECT_HASH_HPP #define CRYPTO3_INJECT_HASH_HPP -#include #include -#include #include +#include +#include +#include namespace nil { namespace crypto3 { namespace detail { - template + // Word injectors inject first n_bits of w_src words into b_dst block. + // Bits are inserted to block's [b_dst_cursor, b_dst_cursor + n_bits - 1] positions + template struct word_injector; - template - struct word_injector, WordBits, BlockWords, BlockBits> + template class InputEndian, template class OutputEndian, std::size_t WordBits, std::size_t BlockWords> + struct word_injector, OutputEndian, WordBits, BlockWords> : public basic_functions { constexpr static const std::size_t word_bits = basic_functions::word_bits; + constexpr static const std::size_t block_bits = WordBits * BlockWords; typedef typename basic_functions::word_type word_type; typedef std::array block_type; - static void inject(word_type w, std::size_t word_seen, block_type &b, std::size_t &block_seen) { - // Insert word_seen-bit part of word into the block b according to endianness - + static void inject(word_type w_src, std::size_t n_bits, block_type &b_dst, std::size_t &b_dst_cursor) { // Check whether we fall out of the block - if (block_seen + word_seen <= BlockBits) { - std::size_t last_word_ind = block_seen / word_bits; - std::size_t last_word_seen = block_seen % word_bits; - - // Remove garbage - w &= high_bits(~word_type(), word_seen); - b[last_word_ind] &= high_bits(~word_type(), last_word_seen); - - // Add significant word bits to block word - b[last_word_ind] |= unbounded_shr(w, last_word_seen); - - // If we fall out of the block word, push the remainder of element to the next block word - if (last_word_seen + word_seen > word_bits) - b[last_word_ind + 1] = unbounded_shl(w, word_bits - last_word_seen); - - block_seen += word_seen; + if (b_dst_cursor + n_bits > block_bits) { + return; } - } - }; - - template - struct word_injector, WordBits, BlockWords, BlockBits> - : public basic_functions { - - constexpr static const std::size_t word_bits = basic_functions::word_bits; - typedef typename basic_functions::word_type word_type; - typedef std::array block_type; + // Calculate start and end words in destination block that will be affected + std::size_t start_word_index = b_dst_cursor / WordBits; + std::size_t end_word_index = (b_dst_cursor + n_bits - 1) / WordBits; - static void inject(word_type w, std::size_t word_seen, block_type &b, std::size_t &block_seen) { - // Insert word_seen-bit part of word into the block b according to endianness + using operation_endian = stream_endian::big_unit_big_bit; - // Check whether we fall out of the block - if (block_seen + word_seen <= BlockBits) { - std::size_t last_word_ind = block_seen / word_bits; - std::size_t last_word_seen = block_seen % word_bits; - - // Remove garbage - std::size_t w_rem = word_seen % UnitBits; - std::size_t w_unit_bits = word_seen - w_rem; - word_type mask = - low_bits(~word_type(), w_unit_bits) | - unbounded_shl(low_bits(~word_type(), w_rem), w_unit_bits + UnitBits - w_rem); - w &= mask; - - std::size_t b_rem = last_word_seen % UnitBits; - std::size_t b_unit_bits = last_word_seen - b_rem; - mask = low_bits(~word_type(), b_unit_bits) | - unbounded_shl(low_bits(~word_type(), b_rem), b_unit_bits + UnitBits - b_rem); - b[last_word_ind] &= mask; - - // Split and combine parts of unit values - std::size_t sz[2] = {UnitBits - b_rem, b_rem}; - word_type masks[2]; - masks[0] = unbounded_shl(low_bits(~word_type(), UnitBits - b_rem), b_rem); - masks[1] = low_bits(~word_type(), b_rem); - std::size_t bw_space = word_bits - last_word_seen; - std::size_t w_space = word_seen; - word_type w_split = 0; - std::size_t sz_ind = 0; - - while (bw_space && w_space) { - w_split |= (!sz_ind ? unbounded_shr(w & masks[0], b_rem) : - unbounded_shl(w & masks[1], UnitBits + sz[0])); - bw_space -= sz[sz_ind]; - w_space -= (w_space >= sz[sz_ind]) ? sz[sz_ind] : w_space; - masks[sz_ind] = unbounded_shl(masks[sz_ind], UnitBits); - sz_ind = 1 - sz_ind; - } - - // Add significant word bits to block word - b[last_word_ind] |= unbounded_shl(w_split, b_unit_bits); - - // If we fall out of the block word, push the remainder of element to the next block word - if (last_word_seen + word_seen > word_bits) { - w = unbounded_shr(w, word_bits - b_unit_bits - UnitBits); - w_split = 0; - masks[0] = - unbounded_shl(low_bits(~word_type(), UnitBits - b_rem), b_rem + UnitBits); - masks[1] = low_bits(~word_type(), b_rem); - - while (w_space) { - w_split |= (!sz_ind ? unbounded_shr(w & masks[0], b_rem) : - unbounded_shl(w & masks[1], UnitBits + sz[0])); - w_space -= (w_space >= sz[sz_ind]) ? sz[sz_ind] : w_space; - masks[sz_ind] = unbounded_shl(masks[sz_ind], UnitBits); - sz_ind = 1 - sz_ind; - } - - b[last_word_ind + 1] = unbounded_shr(w_split, UnitBits); - } + using block_to_operation_unit_reverser = unit_reverser, operation_endian, UnitBits>; + using block_to_operation_bit_reverser = bit_reverser, operation_endian, UnitBits>; - block_seen += word_seen; + // Convert affected destination words to big endian (no-op if already so) + for (std::size_t i = start_word_index; i <= end_word_index; ++i) { + block_to_operation_bit_reverser::reverse(b_dst[i]); + block_to_operation_unit_reverser::reverse(b_dst[i]); } - } - }; - - template - struct word_injector, WordBits, BlockWords, BlockBits> - : public basic_functions { - - constexpr static const std::size_t word_bits = basic_functions::word_bits; - typedef typename basic_functions::word_type word_type; - typedef std::array block_type; - - static void inject(word_type w, std::size_t word_seen, block_type &b, std::size_t &block_seen) { - // Insert word_seen-bit part of word into the block b according to endianness - - // Check whether we fall out of the block - if (block_seen + word_seen <= BlockBits) { - std::size_t last_word_ind = block_seen / word_bits; - std::size_t last_word_seen = block_seen % word_bits; - - // Remove garbage - std::size_t w_rem = word_seen % UnitBits; - std::size_t w_unit_bits = word_seen - w_rem; - word_type mask = - high_bits(~word_type(), w_unit_bits) | - unbounded_shr(high_bits(~word_type(), w_rem), w_unit_bits + UnitBits - w_rem); - w &= mask; - std::size_t b_rem = last_word_seen % UnitBits; - std::size_t b_unit_bits = last_word_seen - b_rem; - mask = high_bits(~word_type(), b_unit_bits) | - unbounded_shr(high_bits(~word_type(), b_rem), b_unit_bits + UnitBits - b_rem); - b[last_word_ind] &= mask; - - // Split and combine parts of unit values - std::size_t sz[2] = {UnitBits - b_rem, b_rem}; - word_type masks[2] = { - unbounded_shr(high_bits(~word_type(), UnitBits - b_rem), b_rem), - high_bits(~word_type(), b_rem)}; - std::size_t bw_space = word_bits - last_word_seen; - std::size_t w_space = word_seen; - word_type w_split = 0; - std::size_t sz_ind = 0; - - while (bw_space && w_space) { - w_split |= (!sz_ind ? unbounded_shl(w & masks[0], b_rem) : - unbounded_shr(w & masks[1], UnitBits + sz[0])); - bw_space -= sz[sz_ind]; - w_space -= (w_space >= sz[sz_ind]) ? sz[sz_ind] : w_space; - masks[sz_ind] = unbounded_shr(masks[sz_ind], UnitBits); - sz_ind = 1 - sz_ind; - } - - // Add significant word bits to block word - b[last_word_ind] |= unbounded_shr(w_split, b_unit_bits); - - // If we fall out of the block word, push the remainder of element to the next block word - if (last_word_seen + word_seen > word_bits) { - w = unbounded_shl(w, word_bits - b_unit_bits - UnitBits); - w_split = 0; - masks[0] = - unbounded_shr(high_bits(~word_type(), UnitBits - b_rem), b_rem + UnitBits); - masks[1] = high_bits(~word_type(), b_rem); - - while (w_space) { - w_split |= (!sz_ind ? unbounded_shl(w & masks[0], b_rem) : - unbounded_shr(w & masks[1], UnitBits + sz[0])); - w_space -= (w_space >= sz[sz_ind]) ? sz[sz_ind] : w_space; - masks[sz_ind] = unbounded_shr(masks[sz_ind], UnitBits); - sz_ind = 1 - sz_ind; - } - - b[last_word_ind + 1] = unbounded_shl(w_split, UnitBits); - } - block_seen += word_seen; + using word_to_operation_unit_reverser = unit_reverser, operation_endian, UnitBits>; + using word_to_operation_bit_reverser = bit_reverser, operation_endian, UnitBits>; + word_to_operation_unit_reverser::reverse(w_src); + word_to_operation_bit_reverser::reverse(w_src); + + while (n_bits > 0) { + // Calculate current word and bit offset in destination block + std::size_t cur_word_idx = b_dst_cursor / WordBits; + std::size_t cur_word_bits_offset = b_dst_cursor % WordBits; + + // Determine how many bits can be injected to the current word + std::size_t bits_this_round = std::min(n_bits, WordBits - cur_word_bits_offset); + + // Extract bits_this_round bits from w_src + // We have to pass `word_type` to bits funcs, so small types (e.g. uint8) are not promoted to int after ~() + word_type src_mask = high_bits(~word_type(), bits_this_round); + word_type bits_to_inject = w_src & src_mask; + // Shift bits_to_inject to align with the destination position + bits_to_inject = unbounded_shr(bits_to_inject, cur_word_bits_offset); + + // Which bits in dst word we have to keep + word_type dst_mask = high_bits(~word_type(), cur_word_bits_offset) | + low_bits(~word_type(), WordBits - cur_word_bits_offset - bits_this_round); + + b_dst[cur_word_idx] &= dst_mask; // Clear the bits at the destination + b_dst[cur_word_idx] |= bits_to_inject; // Set the new bits + + // Update cursor and remaining bits for next round, shift w_src + w_src = unbounded_shl(w_src, bits_this_round); + b_dst_cursor += bits_this_round; + n_bits -= bits_this_round; } - } - }; - - template - struct word_injector, WordBits, BlockWords, BlockBits> - : public basic_functions { - - constexpr static const std::size_t word_bits = basic_functions::word_bits; - typedef typename basic_functions::word_type word_type; - typedef std::array block_type; - - static void inject(word_type w, std::size_t word_seen, block_type &b, std::size_t &block_seen) { - // Insert word_seen-bit part of word into the block b according to endianness - - // Check whether we fall out of the block - if (block_seen + word_seen <= BlockBits) { - std::size_t last_word_ind = block_seen / word_bits; - std::size_t last_word_seen = block_seen % word_bits; - - // Remove garbage - w &= low_bits(~word_type(), word_seen); - b[last_word_ind] &= low_bits(~word_type(), last_word_seen); - - // Add significant word bits to block word - b[last_word_ind] |= unbounded_shl(w, last_word_seen); - - // If we fall out of the block word, push the remainder of element to the next block word - if (last_word_seen + word_seen > word_bits) - b[last_word_ind + 1] = unbounded_shr(w, word_bits - last_word_seen); - - block_seen += word_seen; + // Convert affected destination words back to their original endian + for (std::size_t i = start_word_index; i <= end_word_index; ++i) { + block_to_operation_bit_reverser::reverse(b_dst[i]); + block_to_operation_unit_reverser::reverse(b_dst[i]); } } }; - template - struct injector : word_injector { + template + struct injector : word_injector { constexpr static const std::size_t word_bits = basic_functions::word_bits; + constexpr static const std::size_t block_bits = WordBits * BlockWords; typedef typename basic_functions::word_type word_type; typedef std::array block_type; - static void inject(const block_type &b_src, std::size_t b_src_seen, block_type &b_dst, - std::size_t &b_dst_seen, std::size_t block_shift = 0) { - // Insert word_seen-bit part of word into the block b according to endianness - - // Check whether we fall out of the block - if (b_src_seen + b_dst_seen <= BlockBits) { - - std::size_t first_word_ind = block_shift / word_bits; - std::size_t word_shift = block_shift % word_bits; + /** + * @brief Injects a portion of one block into another. + * + * Injects a specified number of bits from the source block into the destination block + * according to endianness. `b_dst_cursor` is updated to reflect the new position + * after injection. + * + * @param b_src Source block from which bits are to be injected. + * @param n_bits Number of bits in `b_src` that are to be injected. + * @param b_dst Destination block where bits from `b_src` will be injected. + * @param b_dst_cursor Reference to the running count of bits injected into `b_dst`. + * @param b_src_offset_bits The bit position within `b_src` from where to start reading bits (default is 0). + * + * @note Checks if the total bits (`n_bits` + `b_dst_cursor`) exceed the size of `b_dst` + * and does nothing in this case. + */ + static void inject(const block_type &b_src, std::size_t n_bits, block_type &b_dst, + std::size_t &b_dst_cursor, const std::size_t b_src_offset_bits = 0) { + if (n_bits + b_dst_cursor <= block_bits) { + + std::size_t first_word_ind = b_src_offset_bits / word_bits; + std::size_t word_shift = b_src_offset_bits % word_bits; std::size_t first_word_seen = - (word_bits - word_shift) > b_src_seen ? b_src_seen : (word_bits - word_shift); + (word_bits - word_shift) > n_bits ? n_bits : (word_bits - word_shift); - inject(b_src[first_word_ind], first_word_seen, b_dst, b_dst_seen, word_shift); + inject(b_src[first_word_ind], first_word_seen, b_dst, b_dst_cursor, word_shift); - b_src_seen -= first_word_seen; + n_bits -= first_word_seen; - for (std::size_t i = 0; i < (b_src_seen / word_bits); i++) { - inject(b_src[first_word_ind + 1 + i], word_bits, b_dst, b_dst_seen); + for (std::size_t i = 0; i < (n_bits / word_bits); i++) { + inject(b_src[first_word_ind + 1 + i], word_bits, b_dst, b_dst_cursor); } - if (b_src_seen % word_bits) { - inject(b_src[first_word_ind + 1 + b_src_seen / word_bits], b_src_seen % word_bits, b_dst, - b_dst_seen); + if (n_bits % word_bits) { + inject(b_src[first_word_ind + 1 + n_bits / word_bits], n_bits % word_bits, b_dst, + b_dst_cursor); } } } - static void inject(word_type w, std::size_t word_seen, block_type &b, std::size_t &block_seen, - std::size_t word_shift = 0) { - - word_type word_shifted = w; - - if (word_shift > 0) { - endian_shift::to_msb(word_shifted, word_shift); + /** + * @brief Injects a portion of a word into a block. + * + * Injects a specified number of bits from a source word into the destination block (b_dst) + * according to endianness. `b_dst_cursor` is updated to reflect the new position + * after injection. + * + * @param w_src The source word from which bits are to be injected. + * @param n_bits The number of bits in w_src to be injected into b_dst. + * @param b_dst The destination block where bits from w_src will be injected. + * @param b_dst_cursor A reference to the running position in b_dst where the next bit will be injected. + * @param src_word_offset The bit position within w_src from where to start reading bits (default is 0). + */ + static void inject(const word_type w_src, const std::size_t n_bits, block_type &b_dst, std::size_t &b_dst_cursor, + std::size_t src_word_offset = 0) { + + word_type shifted_word = w_src; + + if (src_word_offset > 0) { + endian_shift::to_msb(shifted_word, src_word_offset); } - word_injector::inject(word_shifted, word_seen, b, - block_seen); + word_injector::inject(shifted_word, n_bits, b_dst, + b_dst_cursor); } }; } // namespace detail diff --git a/include/nil/crypto3/detail/reverser.hpp b/include/nil/crypto3/detail/reverser.hpp index 47ea915..906c28c 100644 --- a/include/nil/crypto3/detail/reverser.hpp +++ b/include/nil/crypto3/detail/reverser.hpp @@ -66,7 +66,7 @@ namespace nil { b = unbounded_shr<16>(((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU); #elif (BOOST_ARCH_CURRENT_WORD_BITS == 64) b = (b * 0x0202020202ULL & 0x010884422010ULL) % 1023; -#else +#else #error "BOOST_ARCH_CURRENT_WORD_BITS not set" #endif } @@ -156,7 +156,7 @@ namespace nil { /*! * @brief bit_in_unit_reverser transforms the sequence of bits in each unit of - * the input value into reversed sequence of bytes in each unit of the output value. + * the input value into reversed sequence of bits in each unit of the output value. * The function reverse is recursively invoked and the parameter k is used to track * the number of already processed input units. The recursion ends, when all input * units have been processed, i.e. when k == InputBits. @@ -510,6 +510,38 @@ namespace nil { return out; } }; + + /*! + * @brief reverser reverses both the sequence of units in the given value and with within a unit, if InputEndianness + * and OutputEndianness endiannesses have different unit orders, and the sequence of bits in each unit of the given value, + * if InputEndianness and OutputEndianness endiannesses have different bit orders. + * + * @ingroup reverser + * + * @tparam InputEndianness + * @tparam OutputEndianness + * @tparam UnitBits + */ + template + class reverser { + private: + using unit_reverser_specified = unit_reverser; + using bit_reverser_specified = bit_reverser; + public: + template + inline static void reverse(ValueType &val) { + unit_reverser_specified::reverse(val); + bit_reverser_specified::reverse(val); + } + + template + inline static ValueType reverse(ValueType const &val) { + ValueType out = unit_reverser_specified::reverse(val); + bit_reverser_specified::reverse(out); + return out; + } + }; + } // namespace detail } // namespace crypto3 } // namespace nil diff --git a/include/nil/crypto3/detail/stream_endian.hpp b/include/nil/crypto3/detail/stream_endian.hpp index 6498d5a..cdb584e 100644 --- a/include/nil/crypto3/detail/stream_endian.hpp +++ b/include/nil/crypto3/detail/stream_endian.hpp @@ -25,6 +25,7 @@ #ifndef CRYPTO3_STREAM_ENDIAN_HPP #define CRYPTO3_STREAM_ENDIAN_HPP +#include #include #include @@ -34,6 +35,7 @@ namespace nil { namespace stream_endian { // General versions; There should be no need to use these directly + // TODO: int -> std::size_t template struct big_unit_big_bit { }; template @@ -65,6 +67,19 @@ namespace nil { typedef host_unit host_byte; + using host_endian = + #ifdef BOOST_ENDIAN_BIG_BYTE_AVAILABLE + stream_endian::big_octet_big_bit; + #elif defined(BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE) + stream_endian::little_octet_big_bit; + #elif defined(BOOST_ENDIAN_BIG_WORD_AVAILABLE) + stream_endian::big_unit_big_bit; + #elif defined(BOOST_ENDIAN_LITTLE_WORD_AVAILABLE) + stream_endian::little_unit_big_bit; + #else + #error "Unknown endianness" + #endif + } // namespace stream_endian } // namespace crypto3 } // namespace nil diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e429a86..4718a14 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -40,11 +40,12 @@ macro(define_block_cipher_test name) endmacro() set(TESTS_NAMES - "pack" - "rijndael" + "injector" "kasumi" "md4" "md5" + "pack" + "rijndael" "shacal" "shacal2") diff --git a/test/injector.cpp b/test/injector.cpp new file mode 100644 index 0000000..e67295a --- /dev/null +++ b/test/injector.cpp @@ -0,0 +1,308 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Iosif (x-mass) +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE injector_test + +#include +#include + +#include + +#include + +using namespace nil::crypto3; + +template +using fixed_uint = typename boost::uint_t::exact; + +template +void verify_injection( + Src& src, + size_t n_bits, + size_t src_block_offset, + size_t dst_coursor, + Block& initial_dst, + const Block& expected_dst +) { + Block b_dst = initial_dst; + size_t b_dst_coursor = dst_coursor; + + detail::injector::inject(src, n_bits, b_dst, b_dst_coursor, src_block_offset); + + BOOST_CHECK_EQUAL_COLLECTIONS(b_dst.begin(), b_dst.end(), expected_dst.begin(), expected_dst.end()); + BOOST_CHECK_EQUAL(b_dst_coursor, dst_coursor + n_bits); +} + +BOOST_AUTO_TEST_SUITE(InjectorTestSuit) + +BOOST_AUTO_TEST_CASE(FirstBitInjectionBOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::big_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b10001111, 0b11111111}; + word_type w_src = 0b11111111; + + verify_injection(w_src, 1, 0, 0, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(SecondBitInjectionBOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::big_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b01001111, 0b11111111}; + word_type w_src = 0b11111111; + + verify_injection(w_src, 1, 0, 1, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToStartBOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::big_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b10101111, 0b11111111}; + word_type w_src = 0b10101010; + + verify_injection(w_src, 3, 0, 0, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToMidBOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::big_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b01011111, 0b11111111}; + word_type w_src = 0b10101010; + + verify_injection(w_src, 3, 0, 1, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToMidCrossWordBOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::big_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b00001110, 0b10111111}; + word_type w_src = 0b10101010; + + verify_injection(w_src, 5, 0, 6, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToMidCrossLongWordBOBB) { + using word_type = fixed_uint<16>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::big_octet_big_bit; + + block_type initial_dst = {0b00000000'00000000, 0b00000000'00000000}; + block_type expected_dst = {0b00000000'00000101, 0b10111011'10000000}; + word_type w_src = 0b10110111'01111011; + + verify_injection(w_src, 12, 0, 13, initial_dst, expected_dst); +} + +// Since there is not difference between big and little octet for 8-bit values, tests are the same +BOOST_AUTO_TEST_CASE(FirstBitInjectionLOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::little_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b10001111, 0b11111111}; + word_type w_src = 0b11111111; + + verify_injection(w_src, 1, 0, 0, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(SecondBitInjectionLOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::little_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b01001111, 0b11111111}; + word_type w_src = 0b11111111; + + verify_injection(w_src, 1, 0, 1, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToStartLOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::little_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b10101111, 0b11111111}; + word_type w_src = 0b10101010; + + verify_injection(w_src, 3, 0, 0, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToMidLOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::little_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b01011111, 0b11111111}; + word_type w_src = 0b10101010; + + verify_injection(w_src, 3, 0, 1, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToMidCrossWordLOBB) { + using word_type = fixed_uint<8>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::little_octet_big_bit; + + block_type initial_dst = {0b00001111, 0b11111111}; + block_type expected_dst = {0b00001110, 0b10111111}; + word_type w_src = 0b10101010; + + verify_injection(w_src, 5, 0, 6, initial_dst, expected_dst); +} + +// From here LOBB tests differs from BOBB as words are long +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToMidCrossLongWordLOBB) { + using word_type = fixed_uint<16>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::little_octet_big_bit; + + block_type initial_dst = {0b00000000'00000000, 0b00000000'00000000}; + block_type expected_dst = {0b00000101'00000000, 0b10000000'10111011}; + word_type w_src = 0b10110111'01111011; + + verify_injection(w_src, 12, 0, 13, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToMidCrossLongWordBOLB) { + using word_type = fixed_uint<16>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::big_octet_little_bit; + + block_type initial_dst = {0b00000000'00000000, 0b00000000'00000000}; + block_type expected_dst = {0b00000000'10100000, 0b11011101'00000001}; + word_type w_src = 0b10110111'01111011; + + verify_injection(w_src, 12, 0, 13, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(BOBBMultipleBitToMidCrossLongWordLOLB) { + using word_type = fixed_uint<16>; + using block_type = std::array; + using input_endian_type = stream_endian::big_octet_big_bit; + using output_endian_type = stream_endian::little_octet_little_bit; + + block_type initial_dst = {0b00000000'00000000, 0b00000000'00000000}; + block_type expected_dst = {0b10100000'00000000, 0b00000001'11011101}; + word_type w_src = 0b10110111'01111011; + + verify_injection(w_src, 12, 0, 13, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(LOBBMultipleBitToMidCrossLongWordLOLB) { + using word_type = fixed_uint<16>; + using block_type = std::array; + using input_endian_type = stream_endian::little_octet_big_bit; + using output_endian_type = stream_endian::little_octet_little_bit; + + block_type initial_dst = {0b00001001'10011001, 0b10010000'11111111}; + block_type expected_dst = {0b11001001'10011001, 0b10010001'10111011}; + word_type w_src = 0b10110111'01111011; + + verify_injection(w_src, 12, 0, 13, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(LOBBMultipleBitToMidCrossLongWordBOLB) { + using word_type = fixed_uint<16>; + using block_type = std::array; + using input_endian_type = stream_endian::little_octet_big_bit; + using output_endian_type = stream_endian::big_octet_little_bit; + + block_type initial_dst = {0b10011001'00001001, 0b00000000'10010000}; + block_type expected_dst = {0b10011001'11001001, 0b10111011'10010001}; + word_type w_src = 0b10110111'01111011; + + verify_injection(w_src, 12, 0, 13, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(LOBBOffsetMultipleBitToMidCrossLongWordBOLB) { + using word_type = fixed_uint<16>; + using block_type = std::array; + using input_endian_type = stream_endian::little_octet_big_bit; + using output_endian_type = stream_endian::big_octet_little_bit; + + block_type initial_dst = {0b10011001'00001001, 0b00000000'10010000}; + block_type expected_dst = {0b10011001'01101001, 0b10110111'10010001}; + word_type w_src = 0b10110111'01111011; + + verify_injection(w_src, 12, 3, 13, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(LOBBOffsetMultipleBlockBitToMidCrossLongWordBOLB) { + using word_type = fixed_uint<16>; + using block_type = std::array; + using input_endian_type = stream_endian::little_octet_big_bit; + using output_endian_type = stream_endian::big_octet_little_bit; + + block_type initial_dst = {0b00000000'00000000, 0b00000000'10011001}; + block_type expected_dst = {0b11101100'11110110, 0b00011011'10011001}; + block_type b_src = {0b10110111'01111011, 0b11011110'11101101}; + + verify_injection(b_src, 20, 3, 2, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_CASE(LOBBOffsetMultipleBlockBitToMidCrossLongWordBOLB16Unit) { + using word_type = fixed_uint<32>; + using block_type = std::array; + using input_endian_type = stream_endian::little_unit_big_bit<16>; + using output_endian_type = stream_endian::big_unit_little_bit<16>; + + block_type initial_dst = {0b0000000000000000'0000000000000000, 0b0000000000000000'0000000000000000}; + block_type expected_dst = {0b0000000000000000'1110111100000000, 0b1110111011010110'0111011110111101}; + block_type b_src = {0b1011011101111011'1110111101110110, 0b0110111011110111'1101111011101101}; + + verify_injection(b_src, 40, 3, 23, initial_dst, expected_dst); +} + +BOOST_AUTO_TEST_SUITE_END()