├── benchmark ├── InsertSpeed.cpp ├── main.cpp ├── CMakeLists.txt ├── MoveCopy.cpp ├── CreationAndCopy.cpp └── AppendSpeed.cpp ├── .gitignore ├── include └── ba │ ├── endianness.hpp │ ├── bytearray.hpp │ ├── bytearray_reader.hpp │ ├── bytearray_view.hpp │ └── bytearray_processor.hpp ├── tests ├── main.cpp ├── CMakeLists.txt ├── SerializationTest.cpp ├── UtilitiesTest.cpp ├── ConstructTest.cpp ├── SetTest.cpp ├── ReadTest.cpp ├── InsertTest.cpp ├── AppendTest.cpp └── ViewTest.cpp ├── .clang-format ├── .gitmodules ├── CMakeLists.txt ├── .github └── workflows │ └── build.yml ├── LICENSE.MIT └── README.md /benchmark/InsertSpeed.cpp: -------------------------------------------------------------------------------- 1 | // No benchmarks here, sorry -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cmake-build-debug 3 | cmake-build-release -------------------------------------------------------------------------------- /include/ba/endianness.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ba { 4 | enum class endianness { little, big }; 5 | } 6 | -------------------------------------------------------------------------------- /benchmark/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | BENCHMARK_MAIN(); -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) 4 | { 5 | testing::InitGoogleTest(&argc, argv); 6 | 7 | return RUN_ALL_TESTS(); 8 | } -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Chromium 3 | BreakConstructorInitializers: BeforeComma 4 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 5 | ColumnLimit: 140 6 | IndentWidth: 4 7 | AccessModifierOffset: -4 8 | ... 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/googletest"] 2 | path = tests/googletest 3 | url = https://github.com/google/googletest.git 4 | [submodule "benchmark/benchmark"] 5 | path = benchmark/benchmark 6 | url = https://github.com/google/benchmark.git 7 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(bytearray_benchmark) 2 | 3 | if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/benchmark/CMakeLists.txt") 4 | set(BENCHMARK_ENABLE_TESTING OFF) 5 | add_subdirectory(benchmark) 6 | endif() 7 | 8 | add_executable(bytearray_benchmark 9 | main.cpp 10 | AppendSpeed.cpp 11 | MoveCopy.cpp 12 | InsertSpeed.cpp 13 | CreationAndCopy.cpp 14 | ) 15 | 16 | target_link_libraries(bytearray_benchmark 17 | bytearray 18 | benchmark 19 | gtest 20 | ) -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(bytearray_tests) 3 | 4 | add_subdirectory(googletest) 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | include_directories( 8 | googletest/googletest/include 9 | ) 10 | 11 | file(GLOB SOURCE_FILES *Test.cpp) 12 | 13 | add_executable(bytearray_tests ${SOURCE_FILES} main.cpp) 14 | 15 | target_link_libraries(bytearray_tests 16 | bytearray 17 | gtest 18 | ) 19 | 20 | add_test( 21 | NAME bytearray_tests 22 | COMMAND bytearray_tests 23 | ) 24 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(bytearray) 3 | 4 | option(BYTEARRAY_BUILD_TESTS "Build tests" Off) 5 | 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | if (${BYTEARRAY_BUILD_TESTS}) 9 | enable_testing() 10 | add_subdirectory(tests) 11 | add_subdirectory(benchmark) 12 | endif() 13 | 14 | add_library(bytearray STATIC 15 | include/ba/bytearray_reader.hpp 16 | include/ba/bytearray_processor.hpp 17 | include/ba/bytearray.hpp 18 | include/ba/bytearray_view.hpp 19 | ) 20 | 21 | target_include_directories(bytearray PUBLIC include) 22 | set_target_properties(bytearray PROPERTIES LINKER_LANGUAGE CXX) 23 | -------------------------------------------------------------------------------- /tests/SerializationTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | TEST(Serialization, FromString) 6 | { 7 | std::string strict = 8 | "AABBCCDDEE1100"; 9 | 10 | uint8_t strict_expected[] = { 11 | 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x11, 0x00 12 | }; 13 | 14 | // Stricted 15 | { 16 | ba::bytearray v; 17 | 18 | ASSERT_NO_THROW(v.load_from_hex(strict)); 19 | 20 | ASSERT_EQ(v.size(), 7); 21 | 22 | for (ba::bytearray<>::size_type i = 0; i < v.size(); ++i) 23 | { 24 | ASSERT_EQ(strict_expected[i], static_cast(v[i])); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | submodules: true 21 | 22 | - name: Configure CMake 23 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBYTEARRAY_BUILD_TESTS=On -DBENCHMARK_ENABLE_TESTING=Off 24 | 25 | - name: Build 26 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 27 | 28 | - name: Test 29 | working-directory: ${{github.workspace}}/build 30 | run: ctest -C ${{env.BUILD_TYPE}} -VV 31 | -------------------------------------------------------------------------------- /benchmark/MoveCopy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void copy(benchmark::State& state) 5 | { 6 | ba::bytearray array; 7 | 8 | auto resultSize = state.range(0); 9 | 10 | array.push_back_multiple( 11 | 0xFF, resultSize 12 | ); 13 | 14 | for (auto _ : state) 15 | { 16 | benchmark::DoNotOptimize(ba::bytearray<>(array)); 17 | } 18 | 19 | state.SetComplexityN(resultSize); 20 | } 21 | 22 | static void move(benchmark::State& state) 23 | { 24 | ba::bytearray array; 25 | 26 | auto resultSize = state.range(0); 27 | 28 | array.push_back_multiple( 29 | 0xFF, resultSize 30 | ); 31 | 32 | ba::bytearray copy; 33 | 34 | for (auto _ : state) 35 | { 36 | benchmark::DoNotOptimize(copy = std::move(array)); 37 | } 38 | 39 | state.SetComplexityN(resultSize); 40 | } 41 | 42 | BENCHMARK(copy) 43 | ->Range(1, 1 << 20) 44 | ->Complexity(); 45 | 46 | BENCHMARK(move) 47 | ->Range(1, 1 << 20) 48 | ->Complexity(); -------------------------------------------------------------------------------- /LICENSE.MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013-2018 Megaxela 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /benchmark/CreationAndCopy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void creationEmpty(benchmark::State& state) 5 | { 6 | for (auto _ : state) 7 | { 8 | benchmark::DoNotOptimize(ba::bytearray()); 9 | } 10 | } 11 | 12 | static void creationFromArrayOfBytes(benchmark::State& state) 13 | { 14 | auto size = state.range(0); 15 | 16 | auto* data = new std::byte[size]; 17 | 18 | for (auto _ : state) 19 | { 20 | benchmark::DoNotOptimize(ba::bytearray(data, size)); 21 | } 22 | 23 | delete[] data; 24 | 25 | state.SetComplexityN(size); 26 | } 27 | 28 | static void copyEmpty(benchmark::State& state) 29 | { 30 | ba::bytearray byteArray; 31 | 32 | for (auto _ : state) 33 | { 34 | benchmark::DoNotOptimize(ba::bytearray(byteArray)); 35 | } 36 | } 37 | 38 | static void copyWithData(benchmark::State& state) 39 | { 40 | auto size = static_cast::size_type>(state.range(0)); 41 | 42 | ba::bytearray byteArray(size); 43 | 44 | byteArray.push_back_multiple(0x00, size); 45 | 46 | for (auto _ : state) 47 | { 48 | benchmark::DoNotOptimize(ba::bytearray(byteArray)); 49 | } 50 | 51 | state.SetComplexityN(size); 52 | } 53 | 54 | BENCHMARK(creationEmpty); 55 | 56 | BENCHMARK(creationFromArrayOfBytes) 57 | ->Range(1, 1 << 20) 58 | ->Complexity(); 59 | 60 | BENCHMARK(copyEmpty); 61 | 62 | BENCHMARK(copyWithData) 63 | ->Range(1, 1 << 20) 64 | ->Complexity(); -------------------------------------------------------------------------------- /tests/UtilitiesTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | TEST(Utils, LoadFromHexString) 6 | { 7 | ba::bytearray array{}; 8 | 9 | uint8_t expect[] = { 10 | 0xAA, 0xFF, 0xEE, 0xDD 11 | }; 12 | 13 | ASSERT_TRUE(array.load_from_hex(std::string_view("AAFFEEDD"))); 14 | 15 | ASSERT_EQ(array.size(), 4); 16 | 17 | for (decltype(array)::size_type i = 0; i < array.size(); ++i) 18 | { 19 | ASSERT_EQ(uint8_t(array[i]), expect[i]); 20 | } 21 | 22 | ASSERT_TRUE(array.load_from_hex(std::string_view("AA FFEEDD"))); 23 | 24 | ASSERT_EQ(array.size(), 4); 25 | 26 | for (decltype(array)::size_type i = 0; i < array.size(); ++i) 27 | { 28 | ASSERT_EQ(uint8_t(array[i]), expect[i]); 29 | } 30 | 31 | ASSERT_TRUE(array.load_from_hex(std::string_view("AA FF EE DD"))); 32 | 33 | ASSERT_EQ(array.size(), 4); 34 | 35 | for (decltype(array)::size_type i = 0; i < array.size(); ++i) 36 | { 37 | ASSERT_EQ(uint8_t(array[i]), expect[i]); 38 | } 39 | 40 | ASSERT_FALSE(array.load_from_hex(std::string_view("AAFFEE WW"))); 41 | 42 | ASSERT_EQ(array.size(), 4); 43 | 44 | for (decltype(array)::size_type i = 0; i < array.size(); ++i) 45 | { 46 | ASSERT_EQ(uint8_t(array[i]), expect[i]); 47 | } 48 | } 49 | 50 | TEST(Utils, Literal) 51 | { 52 | auto ba = "DEADBEEF"_ba; 53 | 54 | uint8_t expect[] = { 55 | 0xDE, 0xAD, 0xBE, 0xEF 56 | }; 57 | 58 | ASSERT_EQ(ba.size(), 4); 59 | 60 | for (decltype(ba)::size_type i = 0; i < ba.size(); ++i) 61 | { 62 | ASSERT_EQ(uint8_t(ba[i]), expect[i]); 63 | } 64 | } 65 | 66 | TEST(Utils, ToString) 67 | { 68 | auto ba = "DEADBEEF"_ba; 69 | 70 | auto string = std::to_string(ba); 71 | 72 | ASSERT_EQ(string, "DEADBEEF"); 73 | } 74 | 75 | TEST(Utils, ViewToString) 76 | { 77 | auto ba = "DEADBEEF"_ba; 78 | 79 | ba::bytearray_view view(ba, 1, 2); 80 | 81 | auto string = std::to_string(view); 82 | 83 | ASSERT_EQ(string, "ADBE"); 84 | } -------------------------------------------------------------------------------- /tests/ConstructTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | TEST(Constructors, Default) 6 | { 7 | ba::bytearray b{}; 8 | 9 | ASSERT_EQ(b.size(), 0); 10 | ASSERT_EQ(b.capacity(), 0); 11 | } 12 | 13 | TEST(Constructors, InitialFill) 14 | { 15 | ba::bytearray b(1024); 16 | 17 | ASSERT_EQ(b.size(), 1024); 18 | ASSERT_EQ(b.capacity(), 1024); 19 | } 20 | 21 | TEST(Constructors, InitialCapacity) 22 | { 23 | ba::bytearray b{}; 24 | b.reserve(1024); 25 | 26 | ASSERT_EQ(b.size(), 0); 27 | ASSERT_EQ(b.capacity(), 1024); 28 | } 29 | 30 | TEST(Constructors, FromByteArray) 31 | { 32 | uint8_t byteArray[32] = { 33 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 34 | 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 35 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 36 | 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 37 | }; 38 | 39 | ba::bytearray b(reinterpret_cast(byteArray), 32); 40 | 41 | ASSERT_EQ(b.size(), 32); 42 | ASSERT_EQ(b.capacity(), 32); 43 | 44 | for (decltype(b)::size_type i = 0; i < b.size(); ++i) 45 | { 46 | ASSERT_EQ(byteArray[i], uint8_t(b[i])); 47 | } 48 | } 49 | 50 | TEST(Constructors, Copy) 51 | { 52 | uint8_t byteArray[32] = { 53 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 54 | 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 55 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 56 | 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 57 | }; 58 | 59 | ba::bytearray b(reinterpret_cast(byteArray), 32); 60 | 61 | ba::bytearray copy = b; 62 | 63 | for (decltype(b)::size_type i = 0; i < b.size(); ++i) 64 | { 65 | ASSERT_EQ(b[i], copy[i]); 66 | } 67 | } 68 | 69 | TEST(Constructors, Move) 70 | { 71 | uint8_t byteArray[32] = { 72 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 73 | 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 74 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 75 | 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 76 | }; 77 | 78 | ba::bytearray m(reinterpret_cast(byteArray), 32); 79 | 80 | auto b = std::move(m); 81 | 82 | ASSERT_EQ(b.size(), 32); 83 | ASSERT_EQ(b.capacity(), 32); 84 | 85 | for (decltype(b)::size_type i = 0; i < b.size(); ++i) 86 | { 87 | ASSERT_EQ(byteArray[i], uint8_t(b[i])); 88 | } 89 | } -------------------------------------------------------------------------------- /benchmark/AppendSpeed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | static void append(benchmark::State& state) 6 | { 7 | auto resultSize = state.range(0); 8 | 9 | auto count = resultSize / sizeof(T); 10 | 11 | ba::bytearray array; 12 | 13 | for (auto _ : state) 14 | { 15 | array.clear(); 16 | 17 | for (int i = 0; i < count; ++i) 18 | { 19 | array.push_back(T(0xAA)); 20 | } 21 | } 22 | 23 | state.SetComplexityN(resultSize); 24 | } 25 | 26 | template 27 | static void appendMultiple(benchmark::State& state) 28 | { 29 | auto resultSize = state.range(0); 30 | 31 | auto count = resultSize / sizeof(T); 32 | 33 | ba::bytearray array; 34 | 35 | for (auto _ : state) 36 | { 37 | array.clear(); 38 | 39 | array.push_back_multiple(T(0xAA), count); 40 | } 41 | 42 | state.SetComplexityN(resultSize); 43 | } 44 | 45 | static void appendByteArray(benchmark::State& state) 46 | { 47 | auto size = static_cast(state.range(0)); 48 | 49 | std::vector data; 50 | 51 | data.resize(size, std::byte(0x00)); 52 | 53 | ba::bytearray array; 54 | 55 | for (auto _ : state) 56 | { 57 | array.clear(); 58 | array.push_back_multiple(data.begin(), data.end()); 59 | } 60 | 61 | state.SetComplexityN(size); 62 | } 63 | 64 | static void appendPart(benchmark::State& state) 65 | { 66 | uint64_t data = 0x1122334455667788; 67 | 68 | auto size = state.range(0); 69 | auto part = state.range(1); 70 | 71 | auto count = size / part; 72 | 73 | ba::bytearray array; 74 | 75 | for (auto _ : state) 76 | { 77 | array.clear(); 78 | for (auto i = 0; i < count; ++i) 79 | { 80 | array.push_back_part(data, static_cast(part)); 81 | } 82 | } 83 | 84 | state.SetComplexityN(count); 85 | } 86 | 87 | BENCHMARK_TEMPLATE(append, uint8_t) 88 | ->Range(1, 1 << 20) 89 | ->Complexity(); 90 | 91 | BENCHMARK_TEMPLATE(append, uint16_t) 92 | ->Range(1, 1 << 20) 93 | ->Complexity(); 94 | 95 | BENCHMARK_TEMPLATE(append, uint32_t) 96 | ->Range(1, 1 << 20) 97 | ->Complexity(); 98 | 99 | BENCHMARK_TEMPLATE(append, uint64_t) 100 | ->Range(1, 1 << 20) 101 | ->Complexity(); 102 | 103 | 104 | 105 | BENCHMARK_TEMPLATE(appendMultiple, uint8_t) 106 | ->Range(1, 1 << 20) 107 | ->Complexity(); 108 | 109 | BENCHMARK_TEMPLATE(appendMultiple, uint16_t) 110 | ->Range(1, 1 << 20) 111 | ->Complexity(); 112 | 113 | BENCHMARK_TEMPLATE(appendMultiple, uint32_t) 114 | ->Range(1, 1 << 20) 115 | ->Complexity(); 116 | 117 | BENCHMARK_TEMPLATE(appendMultiple, uint64_t) 118 | ->Range(1, 1 << 20) 119 | ->Complexity(); 120 | 121 | 122 | BENCHMARK(appendByteArray) 123 | ->Range(1, 1 << 20) 124 | ->Complexity(); 125 | 126 | BENCHMARK(appendPart) 127 | ->RangeMultiplier(2) 128 | ->Ranges({{1, 1 << 20}, {1, 8}}); -------------------------------------------------------------------------------- /include/ba/bytearray.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bytearray_processor.hpp" 4 | 5 | namespace ba { 6 | /** 7 | * @brief Class, that implements byte array, that 8 | * holds std::vector object and has bytearray_processor 9 | * interface. 10 | * @tparam Allocator Allocator for std::vector. 11 | */ 12 | template > 13 | class bytearray : public bytearray_processor, private std::vector { 14 | using vector = std::vector; 15 | using processor = bytearray_processor; 16 | 17 | public: 18 | using typename processor::size_type; 19 | using typename processor::value_type; 20 | using typename vector::const_iterator; 21 | using typename vector::const_reverse_iterator; 22 | using typename vector::iterator; 23 | using typename vector::reverse_iterator; 24 | 25 | /** 26 | * @brief Default constructor. 27 | */ 28 | bytearray() 29 | : processor(*static_cast(this)) 30 | , vector() {} 31 | 32 | /** 33 | * @brief Move constructor. 34 | */ 35 | bytearray(bytearray&& rhs) noexcept 36 | : processor(*static_cast(this)) 37 | , vector(std::move(rhs.container())) {} 38 | 39 | /** 40 | * @brief Default copy constructor. 41 | */ 42 | bytearray(const bytearray& rhs) 43 | : processor(*static_cast(this)) 44 | , vector(rhs.container()) {} 45 | 46 | /** 47 | * @brief Constructor with initial size. 48 | */ 49 | explicit bytearray(size_type sizeValue) 50 | : processor(*static_cast(this)) 51 | , vector(sizeValue) {} 52 | 53 | /** 54 | * @brief Initializer constructor. 55 | */ 56 | bytearray(const value_type* val, size_type amount) 57 | : processor(*static_cast(this)) 58 | , vector(val, val + amount) {} 59 | 60 | /** 61 | * @brief Copy operator. 62 | */ 63 | bytearray& operator=(const bytearray& rhs) { 64 | vector::operator=(rhs.container()); 65 | 66 | return (*this); 67 | } 68 | 69 | /** 70 | * @brief Move operator. 71 | */ 72 | bytearray& operator=(bytearray&& rhs) noexcept { 73 | vector::operator=(std::move(rhs.container())); 74 | 75 | return (*this); 76 | } 77 | 78 | using vector::operator[]; 79 | using processor::insert; 80 | using processor::push_back; 81 | using vector::at; 82 | using vector::begin; 83 | using vector::capacity; 84 | using vector::cbegin; 85 | using vector::cend; 86 | using vector::clear; 87 | using vector::crbegin; 88 | using vector::crend; 89 | using vector::empty; 90 | using vector::end; 91 | using vector::rbegin; 92 | using vector::rend; 93 | using vector::reserve; 94 | using vector::size; 95 | }; 96 | } // namespace ba 97 | 98 | 99 | static inline ba::bytearray<> operator"" _ba(const char* str, std::size_t len) { 100 | ba::bytearray array{}; 101 | 102 | array.load_from_hex(std::string_view{str, len}); 103 | 104 | return array; 105 | } -------------------------------------------------------------------------------- /tests/SetTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | uint8_t initialData[8] = { 5 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x01, 0x02 6 | }; 7 | 8 | TEST(Set, UInt8) 9 | { 10 | uint8_t expect[8] = { 11 | 0x01, 0x02, 0xDE, 0x04, 0x05, 0x06, 0x01, 0x02 12 | }; 13 | 14 | { 15 | ba::bytearray data(reinterpret_cast(initialData), 8); 16 | 17 | data.set(2, 0xDE, ba::endianness::big); 18 | 19 | ASSERT_EQ(data.size(), 8); 20 | 21 | for (ba::bytearray<>::size_type i = 0; i < data.size(); ++i) 22 | { 23 | ASSERT_EQ(uint8_t(data[i]), expect[i]); 24 | } 25 | } 26 | 27 | { 28 | ba::bytearray data(reinterpret_cast(initialData), 8); 29 | 30 | data.set(2, 0xDE, ba::endianness::little); 31 | 32 | ASSERT_EQ(data.size(), 8); 33 | 34 | for (ba::bytearray<>::size_type i = 0; i < data.size(); ++i) 35 | { 36 | ASSERT_EQ(uint8_t(data[i]), expect[i]); 37 | } 38 | } 39 | } 40 | 41 | TEST(Set, UInt16) 42 | { 43 | uint8_t expect_be[8] = { 44 | 0x01, 0x02, 0xDE, 0xAD, 0x05, 0x06, 0x01, 0x02 45 | }; 46 | uint8_t expect_le[8] = { 47 | 0x01, 0x02, 0xAD, 0xDE, 0x05, 0x06, 0x01, 0x02 48 | }; 49 | 50 | { 51 | ba::bytearray data(reinterpret_cast(initialData), 8); 52 | 53 | data.set(2, 0xDEAD, ba::endianness::big); 54 | 55 | ASSERT_EQ(data.size(), 8); 56 | 57 | for (ba::bytearray<>::size_type i = 0; i < data.size(); ++i) 58 | { 59 | ASSERT_EQ(uint8_t(data[i]), expect_be[i]); 60 | } 61 | } 62 | 63 | { 64 | ba::bytearray data(reinterpret_cast(initialData), 8); 65 | 66 | data.set(2, 0xDEAD, ba::endianness::little); 67 | 68 | ASSERT_EQ(data.size(), 8); 69 | 70 | for (ba::bytearray<>::size_type i = 0; i < data.size(); ++i) 71 | { 72 | ASSERT_EQ(uint8_t(data[i]), expect_le[i]); 73 | } 74 | } 75 | } 76 | 77 | TEST(Set, UInt32) 78 | { 79 | uint8_t expect_be[8] = { 80 | 0x01, 0x02, 0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02 81 | }; 82 | uint8_t expect_le[8] = { 83 | 0x01, 0x02, 0xEF, 0xBE, 0xAD, 0xDE, 0x01, 0x02 84 | }; 85 | 86 | { 87 | ba::bytearray data(reinterpret_cast(initialData), 8); 88 | 89 | data.set(2, 0xDEADBEEF, ba::endianness::big); 90 | 91 | ASSERT_EQ(data.size(), 8); 92 | 93 | for (ba::bytearray<>::size_type i = 0; i < data.size(); ++i) 94 | { 95 | ASSERT_EQ(uint8_t(data[i]), expect_be[i]); 96 | } 97 | } 98 | 99 | { 100 | ba::bytearray data(reinterpret_cast(initialData), 8); 101 | 102 | data.set(2, 0xDEADBEEF, ba::endianness::little); 103 | 104 | ASSERT_EQ(data.size(), 8); 105 | 106 | for (ba::bytearray<>::size_type i = 0; i < data.size(); ++i) 107 | { 108 | ASSERT_EQ(uint8_t(data[i]), expect_le[i]); 109 | } 110 | } 111 | } 112 | 113 | TEST(Set, UInt64) 114 | { 115 | uint8_t expect_be[8] = { 116 | 0xDE, 0xAD, 0xBE, 0xEF, 0xEE, 0xAA, 0xBB, 0xCC 117 | }; 118 | uint8_t expect_le[8] = { 119 | 0xCC, 0xBB, 0xAA, 0xEE, 0xEF, 0xBE, 0xAD, 0xDE 120 | }; 121 | 122 | { 123 | ba::bytearray data(reinterpret_cast(initialData), 8); 124 | 125 | data.set(0, 0xDEADBEEFEEAABBCC, ba::endianness::big); 126 | 127 | ASSERT_EQ(data.size(), 8); 128 | 129 | for (ba::bytearray<>::size_type i = 0; i < data.size(); ++i) 130 | { 131 | ASSERT_EQ(uint8_t(data[i]), expect_be[i]); 132 | } 133 | } 134 | 135 | { 136 | ba::bytearray data(reinterpret_cast(initialData), 8); 137 | 138 | data.set(0, 0xDEADBEEFEEAABBCC, ba::endianness::little); 139 | 140 | ASSERT_EQ(data.size(), 8); 141 | 142 | for (ba::bytearray<>::size_type i = 0; i < data.size(); ++i) 143 | { 144 | ASSERT_EQ(uint8_t(data[i]), expect_le[i]); 145 | } 146 | } 147 | } 148 | 149 | -------------------------------------------------------------------------------- /tests/ReadTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | TEST(Read, ConstVectorForReader) { 6 | const std::vector data = {0xDE, 0xAD, 0xBE, 0xEF}; 7 | 8 | ba::bytearray_reader reader(data); 9 | 10 | ASSERT_EQ(reader.read(0, ba::endianness::big), 0xDE); 11 | ASSERT_EQ(reader.read(0, ba::endianness::big), 0xDEAD); 12 | ASSERT_EQ(reader.read(0, ba::endianness::big), 0xDEADBEEF); 13 | } 14 | 15 | TEST(Read, Uint8) { 16 | const uint8_t value = 0xDE; 17 | 18 | { 19 | ba::bytearray data{}; 20 | 21 | data.push_back(value, ba::endianness::big); 22 | 23 | ASSERT_EQ(data.read(0, ba::endianness::big), value); 24 | } 25 | 26 | { 27 | ba::bytearray data{}; 28 | 29 | data.push_back(value, ba::endianness::little); 30 | 31 | ASSERT_EQ(data.read(0, ba::endianness::little), value); 32 | } 33 | } 34 | 35 | TEST(Read, Uint32) { 36 | const uint32_t value = 0xDEADBEEF; 37 | 38 | { 39 | ba::bytearray data{}; 40 | 41 | data.push_back(value, ba::endianness::big); 42 | 43 | ASSERT_EQ(data.read(0, ba::endianness::big), value); 44 | } 45 | 46 | { 47 | ba::bytearray data{}; 48 | 49 | data.push_back(value, ba::endianness::little); 50 | 51 | ASSERT_EQ(data.read(0, ba::endianness::little), value); 52 | } 53 | } 54 | 55 | TEST(Read, Uint64) { 56 | const uint64_t value = 0xDEADBEEFFFEEFFEE; 57 | 58 | { 59 | ba::bytearray data{}; 60 | 61 | data.push_back(value, ba::endianness::big); 62 | 63 | ASSERT_EQ(data.read(0, ba::endianness::big), value); 64 | } 65 | 66 | { 67 | ba::bytearray data{}; 68 | 69 | data.push_back(value, ba::endianness::little); 70 | 71 | ASSERT_EQ(data.read(0, ba::endianness::little), value); 72 | } 73 | } 74 | 75 | TEST(Read, Int64) { 76 | const int64_t value = 0xDEADBEEFFFEEFFEE; 77 | 78 | { 79 | ba::bytearray data{}; 80 | 81 | data.push_back(value, ba::endianness::big); 82 | 83 | ASSERT_EQ(data.read(0, ba::endianness::big), value); 84 | } 85 | 86 | { 87 | ba::bytearray data{}; 88 | 89 | data.push_back(value, ba::endianness::little); 90 | 91 | ASSERT_EQ(data.read(0, ba::endianness::little), value); 92 | } 93 | } 94 | 95 | struct CustomStructBase { 96 | uint64_t a; 97 | uint32_t c; 98 | char z; 99 | 100 | char message[32]; 101 | }; 102 | 103 | struct CustomStructDerived : CustomStructBase { 104 | char additional[64]; 105 | 106 | bool operator==(const CustomStructDerived& rhs) { 107 | return rhs.a == a && rhs.c == c && rhs.z == z && memcmp(rhs.message, message, 32) == 0 && 108 | memcmp(rhs.additional, additional, 64) == 0; 109 | } 110 | }; 111 | 112 | TEST(Read, Custom) { 113 | CustomStructDerived value = {}; 114 | 115 | value.a = 0xDEADDEADBEEFBEEF; 116 | value.c = 0xFEEDBEEF; 117 | value.z = '%'; 118 | memcpy(value.message, "Sample message", 15); 119 | memcpy(value.additional, "Some actual long additional message", 35); 120 | 121 | { 122 | ba::bytearray data{}; 123 | 124 | data.push_back(value, ba::endianness::big); 125 | 126 | ASSERT_TRUE(data.read(0, ba::endianness::big) == value); 127 | } 128 | 129 | { 130 | ba::bytearray data{}; 131 | 132 | data.push_back(value, ba::endianness::little); 133 | 134 | ASSERT_TRUE(data.read(0, ba::endianness::little) == value); 135 | } 136 | } 137 | 138 | TEST(Read, Part) { 139 | uint64_t value = 0x00ADBEEFFFEEAABB; 140 | 141 | { 142 | ba::bytearray data{}; 143 | 144 | data.push_back_part(value, 7, ba::endianness::big); 145 | 146 | ASSERT_EQ(value, data.read_part(0, 7, ba::endianness::big)); 147 | } 148 | 149 | { 150 | ba::bytearray data{}; 151 | 152 | data.push_back_part(value, 7, ba::endianness::little); 153 | 154 | ASSERT_EQ(value, data.read_part(0, 7, ba::endianness::little)); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Byte Array 2 | It's C++17 STL based set of classes that describes byte array. 3 | 4 | ## Structure 5 | Library contains 3 base classes (See Examples for samples): 6 | * `bytearray_processor` - it's byte array interface with all methods 7 | implementation. It does not contains actual data. 8 | * `bytearray` - it's class, that owns data and has `bytearray_processor` 9 | interface. 10 | * `bytearray_view` - class, that describes object that can refer to some 11 | part of `bytearray`. It's also has `bytearray_processor` interface, but 12 | non constant methods will change parent `bytearray`. 13 | 14 | ## Build 15 | It's header only library, so you may only include headers from `include` 16 | directory. 17 | 18 | You also can use it as CMake submodule. Everything you need is just to add 19 | this project as submodule and link it to target. 20 | 21 | ### Build tests and benchmarks 22 | 1. Load submodules with `git submodule init && git submodule update`. 23 | 1. Pass `BYTEARRAY_BUILD_TESTS` to `cmake` on configuration step (`cmake -DBYTEARRAY_BUILD_TESTS=On`) 24 | 1. Build `cmake --build . -- -j2` 25 | 26 | ## Examples 27 | 28 | Usage of `bytearray_processor`: 29 | ```cpp 30 | #include 31 | 32 | int main(int argc, char** argv) 33 | { 34 | // You may use any numerical type, that has 1 byte size. 35 | std::vector some_vector; 36 | 37 | ba::bytearray_processor processor(some_vector); 38 | 39 | processor.push_back(0xDEADBEEF, ba::endianness::little); 40 | 41 | /* Output: 42 | * ByteArray({ 43 | * #-------------#-------------#-------------#-------------# 44 | * | 00 01 02 03 | 04 05 06 07 | 08 09 0A 0B | 0C 0D 0E 0F | 45 | * #-------------#-------------#-------------#-------------# 46 | * 0x00000000 | EF BE AD DE | | | | .... 47 | * #-------------#-------------#-------------#-------------# 48 | *}, Length: 4, Capacity: 4) 49 | */ 50 | std::cout << processor << std::endl; 51 | 52 | /* Output: 53 | * EFBEADDE 54 | */ 55 | std::cout << std::to_string(processor) << std::endl; 56 | 57 | /* Output: 58 | * true 59 | */ 60 | std::cout 61 | << std::boolalpha 62 | << (processor.read(0, ba::endianness::little) == 0xDEADBEEF) 63 | << std::endl; 64 | 65 | /* Output: 66 | * 4 67 | */ 68 | std::cout << some_vector.size() << std::endl; 69 | } 70 | 71 | ``` 72 | 73 | You may replace 74 | ```cpp 75 | std::vector some_vector; 76 | ba::bytearray_processor processor(some_vector); 77 | ``` 78 | with `ba::bytearray`. You will be able to access vector with `container` method. 79 | 80 | Usage of `ba::bytearray_view`: 81 | 82 | ```cpp 83 | #include 84 | #include 85 | 86 | int main() 87 | { 88 | // It's equal to (this suffix is not `constexpr`): 89 | // bytearray array{}; 90 | // array.load_from_hex("DEADBEEF"); 91 | auto array = "DEADBEEF"_ba; 92 | 93 | ba::bytearray_view view(array, 1, 2); 94 | 95 | /* Output: 96 | * ADBE 97 | */ 98 | std::cout << std::to_string(view) << std::endl; 99 | 100 | view.push_back(0xAAFF); // Big endian by default 101 | 102 | /* Output: 103 | * ADBEAAFF 104 | */ 105 | std::cout << std::to_string(view) << std::endl; 106 | 107 | /* Output: 108 | * DEADBEAAFFEF 109 | */ 110 | std::cout << std::to_string(array) << std::endl; 111 | } 112 | ``` 113 | 114 | ## License 115 | 116 | 117 | Library is licensed under the [MIT License](https://opensource.org/licenses/MIT) 118 | 119 | Permission is hereby granted, free of charge, to any person obtaining a copy 120 | of this software and associated documentation files (the "Software"), to deal 121 | in the Software without restriction, including without limitation the rights 122 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 123 | copies of the Software, and to permit persons to whom the Software is 124 | furnished to do so, subject to the following conditions: 125 | 126 | The above copyright notice and this permission notice shall be included in all 127 | copies or substantial portions of the Software. 128 | 129 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 130 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 131 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 132 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 133 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 134 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 135 | SOFTWARE. -------------------------------------------------------------------------------- /tests/InsertTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(Insert, Normal) 5 | { 6 | uint8_t initial[] = { 7 | 0xFF, 0xFF 8 | }; 9 | 10 | uint8_t expect_le[] = { 11 | 0xFF, 0xEF, 0xBE, 0xAD, 0xDE, 0xFF 12 | }; 13 | 14 | uint8_t expect_be[] = { 15 | 0xFF, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF 16 | }; 17 | 18 | { 19 | ba::bytearray array((std::byte*) initial, 2); 20 | 21 | array.insert(1, 0xDEADBEEF, ba::endianness::big); 22 | 23 | ASSERT_EQ(array.size(), 6); 24 | 25 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) 26 | { 27 | ASSERT_EQ(uint8_t(array[i]), expect_be[i]); 28 | } 29 | } 30 | 31 | { 32 | ba::bytearray array((std::byte*) initial, 2); 33 | 34 | array.insert(1, 0xDEADBEEF, ba::endianness::little); 35 | 36 | ASSERT_EQ(array.size(), 6); 37 | 38 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) 39 | { 40 | ASSERT_EQ(uint8_t(array[i]), expect_le[i]); 41 | } 42 | } 43 | } 44 | 45 | TEST(Insert, Part) 46 | { 47 | uint8_t initial[] = { 48 | 0xFF, 0xFF 49 | }; 50 | 51 | uint8_t expect_le[] = { 52 | 0xFF, 0xEF, 0xBE, 0xAD, 0xFF 53 | }; 54 | 55 | uint8_t expect_be[] = { 56 | 0xFF, 0xAD, 0xBE, 0xEF, 0xFF 57 | }; 58 | 59 | { 60 | ba::bytearray array((std::byte*) initial, 2); 61 | 62 | array.insert_part(1, 0xDEADBEEF, 3, ba::endianness::big); 63 | 64 | ASSERT_EQ(array.size(), 5); 65 | 66 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) 67 | { 68 | ASSERT_EQ(uint8_t(array[i]), expect_be[i]); 69 | } 70 | } 71 | 72 | { 73 | ba::bytearray array((std::byte*) initial, 2); 74 | 75 | array.insert_part(1, 0xDEADBEEF, 3, ba::endianness::little); 76 | 77 | ASSERT_EQ(array.size(), 5); 78 | 79 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) 80 | { 81 | ASSERT_EQ(uint8_t(array[i]), expect_le[i]); 82 | } 83 | } 84 | } 85 | 86 | TEST(Insert, Multiple1) 87 | { 88 | uint8_t initial[] = { 89 | 0xFF, 0xFF 90 | }; 91 | 92 | uint8_t expect_be[] = { 93 | 0xFF, 0xDE, 0xAD, 0xDE, 0xAD, 0xFF 94 | }; 95 | 96 | uint8_t expect_le[] = { 97 | 0xFF, 0xAD, 0xDE, 0xAD, 0xDE, 0xFF 98 | }; 99 | 100 | { 101 | ba::bytearray array(reinterpret_cast(initial), 2); 102 | 103 | array.insert_multiple( 104 | 1, 105 | 0xDEAD, 106 | 2, 107 | ba::endianness::big 108 | ); 109 | 110 | ASSERT_EQ(array.size(), 6); 111 | 112 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) 113 | { 114 | ASSERT_EQ(uint8_t(array[i]), expect_be[i]); 115 | } 116 | } 117 | 118 | { 119 | ba::bytearray array(reinterpret_cast(initial), 2); 120 | 121 | array.insert_multiple( 122 | 1, 123 | 0xDEAD, 124 | 2, 125 | ba::endianness::little 126 | ); 127 | 128 | ASSERT_EQ(array.size(), 6); 129 | 130 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) 131 | { 132 | ASSERT_EQ(uint8_t(array[i]), expect_le[i]); 133 | } 134 | } 135 | } 136 | 137 | 138 | TEST(Insert, Multiple2) 139 | { 140 | uint8_t initial[] = { 141 | 0xFF, 0xFF 142 | }; 143 | 144 | uint8_t expect_be[] = { 145 | 0xFF, 0xDE, 0xAD, 0xDE, 0xAD, 0xFF 146 | }; 147 | 148 | uint8_t expect_le[] = { 149 | 0xFF, 0xAD, 0xDE, 0xAD, 0xDE, 0xFF 150 | }; 151 | 152 | std::vector insertionData = { 153 | 0xDEAD, 0xDEAD 154 | }; 155 | 156 | { 157 | ba::bytearray array(reinterpret_cast(initial), 2); 158 | 159 | array.insert_multiple( 160 | 1, 161 | insertionData.begin(), 162 | insertionData.end(), 163 | ba::endianness::big 164 | ); 165 | 166 | ASSERT_EQ(array.size(), 6); 167 | 168 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) 169 | { 170 | ASSERT_EQ(uint8_t(array[i]), expect_be[i]); 171 | } 172 | } 173 | 174 | { 175 | ba::bytearray array(reinterpret_cast(initial), 2); 176 | 177 | array.insert_multiple( 178 | 1, 179 | insertionData.begin(), 180 | insertionData.end(), 181 | ba::endianness::little 182 | ); 183 | 184 | ASSERT_EQ(array.size(), 6); 185 | 186 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) 187 | { 188 | ASSERT_EQ(uint8_t(array[i]), expect_le[i]); 189 | } 190 | } 191 | } -------------------------------------------------------------------------------- /tests/AppendTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | TEST(Append, InitializerListUint8) { 6 | uint8_t data[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; 7 | 8 | uint8_t expect[12] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xDE, 0xAD, 0xBE, 0xEF}; 9 | 10 | { 11 | ba::bytearray b(reinterpret_cast(data), 8); 12 | 13 | b.push_back_multiple({0xDE, 0xAD, 0xBE, 0xEF}, ba::endianness::big); 14 | 15 | ASSERT_EQ(b.size(), 12); 16 | 17 | for (ba::bytearray<>::size_type i = 0; i < b.size(); ++i) { 18 | ASSERT_EQ(uint8_t(b[i]), expect[i]); 19 | } 20 | } 21 | 22 | { 23 | ba::bytearray b(reinterpret_cast(data), 8); 24 | 25 | b.push_back_multiple({0xDE, 0xAD, 0xBE, 0xEF}, ba::endianness::little); 26 | 27 | ASSERT_EQ(b.size(), 12); 28 | 29 | for (ba::bytearray<>::size_type i = 0; i < b.size(); ++i) { 30 | ASSERT_EQ(uint8_t(b[i]), expect[i]); 31 | } 32 | } 33 | } 34 | 35 | TEST(Append, InitializerListUint32) { 36 | uint8_t data[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; 37 | 38 | uint8_t expect_be[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF}; 39 | 40 | uint8_t expect_le[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE}; 41 | 42 | { 43 | ba::bytearray b(reinterpret_cast(data), 8); 44 | 45 | b.push_back_multiple({0xDEADBEEF, 0xDEADBEEF}, ba::endianness::big); 46 | 47 | ASSERT_EQ(b.size(), 16); 48 | 49 | for (ba::bytearray<>::size_type i = 0; i < b.size(); ++i) { 50 | ASSERT_EQ(uint8_t(b[i]), expect_be[i]); 51 | } 52 | } 53 | 54 | { 55 | ba::bytearray b(reinterpret_cast(data), 8); 56 | 57 | b.push_back_multiple({0xDEADBEEF, 0xDEADBEEF}, ba::endianness::little); 58 | 59 | ASSERT_EQ(b.size(), 16); 60 | 61 | for (ba::bytearray<>::size_type i = 0; i < b.size(); ++i) { 62 | ASSERT_EQ(uint8_t(b[i]), expect_le[i]); 63 | } 64 | } 65 | } 66 | 67 | TEST(Append, Range) { 68 | uint8_t data[8] = { 69 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 70 | }; 71 | 72 | uint8_t appendable[4] = {0xDE, 0xAD, 0xBE, 0xEF}; 73 | 74 | ba::bytearray b(reinterpret_cast(data), 8); 75 | 76 | b.push_back_multiple(appendable, appendable + 4); 77 | 78 | ASSERT_EQ(b.size(), 12); 79 | 80 | for (ba::bytearray<>::size_type i = 0; i < b.size(); ++i) { 81 | if (i >= 8) { 82 | ASSERT_EQ(uint8_t(b[i]), appendable[i - 8]); 83 | } else { 84 | ASSERT_EQ(uint8_t(b[i]), data[i]); 85 | } 86 | } 87 | } 88 | 89 | TEST(Append, Number8bit) { 90 | uint8_t data[8] = { 91 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 92 | }; 93 | 94 | uint8_t expecting_be[9] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xDE}; 95 | 96 | uint8_t expecting_le[9] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xDE}; 97 | 98 | { 99 | ba::bytearray array(reinterpret_cast(data), 8); 100 | 101 | array.push_back(0xDE, ba::endianness::big); 102 | 103 | ASSERT_EQ(array.size(), 9); 104 | 105 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) { 106 | ASSERT_EQ(uint8_t(array[i]), expecting_be[i]); 107 | } 108 | } 109 | 110 | { 111 | ba::bytearray array(reinterpret_cast(data), 8); 112 | 113 | array.push_back(0xDE, ba::endianness::little); 114 | 115 | ASSERT_EQ(array.size(), 9); 116 | 117 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) { 118 | ASSERT_EQ(uint8_t(array[i]), expecting_le[i]); 119 | } 120 | } 121 | } 122 | 123 | TEST(Append, Number16bit) { 124 | uint8_t data[8] = { 125 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 126 | }; 127 | 128 | uint8_t expecting_be[10] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xDE, 0xAD}; 129 | 130 | uint8_t expecting_le[10] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xAD, 0xDE}; 131 | 132 | { 133 | ba::bytearray array(reinterpret_cast(data), 8); 134 | 135 | array.push_back(0xDEAD, ba::endianness::big); 136 | 137 | ASSERT_EQ(array.size(), 10); 138 | 139 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) { 140 | ASSERT_EQ(uint8_t(array[i]), expecting_be[i]); 141 | } 142 | } 143 | 144 | { 145 | ba::bytearray array(reinterpret_cast(data), 8); 146 | 147 | array.push_back(0xDEAD, ba::endianness::little); 148 | 149 | ASSERT_EQ(array.size(), 10); 150 | 151 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) { 152 | ASSERT_EQ(uint8_t(array[i]), expecting_le[i]); 153 | } 154 | } 155 | } 156 | 157 | TEST(Append, Number32bit) { 158 | uint8_t data[8] = { 159 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 160 | }; 161 | 162 | uint8_t expecting_be[12] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xDE, 0xAD, 0xBE, 0xEF}; 163 | 164 | uint8_t expecting_le[12] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xEF, 0xBE, 0xAD, 0xDE}; 165 | 166 | { 167 | ba::bytearray array(reinterpret_cast(data), 8); 168 | 169 | array.push_back(0xDEADBEEF, ba::endianness::big); 170 | 171 | ASSERT_EQ(array.size(), 12); 172 | 173 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) { 174 | ASSERT_EQ(uint8_t(array[i]), expecting_be[i]); 175 | } 176 | } 177 | 178 | { 179 | ba::bytearray array(reinterpret_cast(data), 8); 180 | 181 | array.push_back(0xDEADBEEF, ba::endianness::little); 182 | 183 | ASSERT_EQ(array.size(), 12); 184 | 185 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) { 186 | ASSERT_EQ(uint8_t(array[i]), expecting_le[i]); 187 | } 188 | } 189 | } 190 | 191 | TEST(Append, Number64bit) { 192 | uint8_t data[8] = { 193 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 194 | }; 195 | 196 | uint8_t expecting_be[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF}; 197 | 198 | uint8_t expecting_le[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE}; 199 | 200 | { 201 | ba::bytearray array(reinterpret_cast(data), 8); 202 | 203 | array.push_back(0xDEADBEEFDEADBEEF, ba::endianness::big); 204 | 205 | ASSERT_EQ(array.size(), 16); 206 | 207 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) { 208 | ASSERT_EQ(uint8_t(array[i]), expecting_be[i]); 209 | } 210 | } 211 | 212 | { 213 | ba::bytearray array(reinterpret_cast(data), 8); 214 | 215 | array.push_back(0xDEADBEEFDEADBEEF, ba::endianness::little); 216 | 217 | ASSERT_EQ(array.size(), 16); 218 | 219 | for (ba::bytearray<>::size_type i = 0; i < array.size(); ++i) { 220 | ASSERT_EQ(uint8_t(array[i]), expecting_le[i]); 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /include/ba/bytearray_reader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // ba 4 | #include 5 | 6 | // C++ STL 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace ba { 18 | 19 | /** 20 | * @brief Class, that implements 21 | * read operations with vector like with byte array. 22 | * It holds const reference to std::vector with 1 byte width data. 23 | * (std::byte, uint8_t or unsigned char) 24 | */ 25 | template 26 | class bytearray_reader { 27 | public: 28 | using vector = std::vector; 29 | 30 | private: 31 | /** 32 | * @brief Stream output function. 33 | */ 34 | friend std::ostream& operator<<(std::ostream& ostream, const bytearray_reader& arr) { 35 | // Printing header 36 | ostream << "ByteArray({" << std::endl; 37 | ostream << " #-------------#-------------#-------------#-------------#" << std::endl; 38 | ostream << " | 00 01 02 03 | 04 05 06 07 | 08 09 0A 0B | 0C 0D 0E 0F |" << std::endl; 39 | ostream << " #-------------#-------------#-------------#-------------#"; 40 | 41 | // Saving states 42 | auto oldFlags = ostream.flags(); 43 | auto oldPrec = ostream.precision(); 44 | auto oldFill = ostream.fill(); 45 | 46 | // Changing fill character 47 | ostream.fill('0'); 48 | 49 | const std::vector& container = arr.container(); 50 | 51 | // 52 | size_type index = 0; 53 | for (index = 0; index < container.size() + (16 - (container.size() % 16)); ++index) { 54 | if (!(index % 16)) { 55 | if (index) { 56 | ostream << "| "; 57 | } 58 | 59 | for (std::size_t asc = index - 16; asc < index; ++asc) { 60 | if (container[asc] >= ValueType(' ') && container[asc] <= ValueType('~')) { 61 | ostream << static_cast(container[asc]); 62 | } else { 63 | ostream << '.'; 64 | } 65 | } 66 | 67 | ostream << std::endl << " 0x"; 68 | ostream.width(8); 69 | ostream << std::hex << index << ' '; 70 | } 71 | 72 | if (!(index % 4)) { 73 | ostream << "| "; 74 | } 75 | 76 | if (index < container.size()) { 77 | ostream.width(2); 78 | ostream << std::uppercase << std::hex << static_cast(container[index]) << ' '; 79 | } else { 80 | ostream << " "; 81 | } 82 | } 83 | 84 | if (index) { 85 | ostream << "| "; 86 | } 87 | 88 | for (size_type asc = index - 16; asc < index; ++asc) { 89 | if (asc < container.size()) { 90 | if (container[asc] >= ValueType(' ') && container[asc] <= ValueType('~')) { 91 | ostream << static_cast(container[asc]); 92 | } else { 93 | ostream << '.'; 94 | } 95 | } else { 96 | ostream << ' '; 97 | } 98 | } 99 | 100 | ostream << std::endl 101 | << std::nouppercase << " #-------------#-------------#-------------#-------------#" << std::endl 102 | << "}, Length: " << std::dec << container.size() << ", Capacity: " << std::dec << container.capacity() << ')' << std::endl; 103 | 104 | ostream.flags(oldFlags); 105 | ostream.precision(oldPrec); 106 | ostream.fill(oldFill); 107 | 108 | return ostream; 109 | } 110 | 111 | public: 112 | static_assert(sizeof(ValueType) == 1, "Size of container element has to be 1 byte"); 113 | 114 | using size_type = typename vector::size_type; 115 | using value_type = ValueType; 116 | 117 | /** 118 | * @brief Constructor. Accepts container reference to 119 | * operate with. 120 | * @param container Vector with type. 121 | */ 122 | explicit bytearray_reader(const vector& container) 123 | : m_container(container) {} 124 | 125 | /** 126 | * @brief Method for getting constant reference to internal container. 127 | * @return Constant reference to internal container. 128 | */ 129 | const vector& container() const { return m_container; } 130 | 131 | /** 132 | * @brief Method for performing translation of 133 | * byte array to some trivially copyable type. 134 | * @tparam T Type. 135 | * @param position Value position. 136 | * @param order Read order. 137 | * @return Read value. 138 | */ 139 | template 140 | typename std::enable_if::value, T>::type read(size_type position, 141 | endianness order = endianness::big) const { 142 | assert(position + sizeof(T) <= m_container.size() && "Position + type size is out of bounds."); 143 | 144 | T value; 145 | 146 | if (order == get_system_endianness()) { 147 | for (std::size_t i = 0; i < sizeof(T); ++i) { 148 | ((ValueType*)(&value))[i] = m_container[position + i]; 149 | } 150 | } else { 151 | for (std::size_t i = sizeof(T); i > 0; --i) { 152 | ((ValueType*)(&value))[i - 1] = m_container[position + sizeof(T) - i]; 153 | } 154 | } 155 | 156 | return value; 157 | } 158 | 159 | /** 160 | * @brief Method for performing translation of 161 | * byte array to some part of trivially copyable type. 162 | * @tparam T Type. 163 | * @param position Value position. 164 | * @param order Read order. 165 | * @return Read value. 166 | */ 167 | template 168 | typename std::enable_if::value, T>::type read_part(size_type position, 169 | size_type size, 170 | endianness order = endianness::big) const { 171 | assert(size <= sizeof(T) && "Size if bigger, than types size"); 172 | assert(position + size <= m_container.size() && "Position + size is out of bounds."); 173 | 174 | T value; 175 | std::memset(&value, 0, sizeof(T)); 176 | 177 | if (order == get_system_endianness()) { 178 | for (size_type i = 0; i < size; ++i) { 179 | ((ValueType*)(&value))[i] = m_container[position + i]; 180 | } 181 | } else { 182 | for (size_type i = size; i > 0; --i) { 183 | ((ValueType*)(&value))[i - 1] = m_container[position + size - i]; 184 | } 185 | } 186 | 187 | return value; 188 | } 189 | 190 | private: 191 | /** 192 | * @brief Constexpr function for checking system endianness. 193 | * @return Endianness value. 194 | */ 195 | [[nodiscard]] constexpr endianness get_system_endianness() const { 196 | union { 197 | uint32_t i; 198 | char c[4]; 199 | } example = {0x01020304}; 200 | 201 | return example.c[0] == 1 ? endianness::big : endianness::little; 202 | } 203 | 204 | const vector& m_container; 205 | }; 206 | } // namespace ba 207 | 208 | // Allowed 209 | namespace std { 210 | template 211 | std::string to_string(const ba::bytearray_reader& processor) { 212 | std::stringstream ss; 213 | 214 | for (auto&& b : processor.container()) { 215 | ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << int(b); 216 | } 217 | 218 | return ss.str(); 219 | } 220 | } // namespace std 221 | -------------------------------------------------------------------------------- /include/ba/bytearray_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace ba { 6 | /** 7 | * @brief Class, that describes 8 | * bytearray representation with different size 9 | * based values. 10 | * @tparam Allocator Allocator. 11 | */ 12 | template > 13 | class bytearray_view { 14 | using container = bytearray_processor; 15 | 16 | /** 17 | * @brief Stream output function. 18 | */ 19 | friend std::ostream& operator<<(std::ostream& ostream, const bytearray_view& container) { 20 | // Printing header 21 | ostream << "ByteArray({" << std::endl; 22 | ostream << " #-------------#-------------#-------------#-------------#" << std::endl; 23 | ostream << " | 00 01 02 03 | 04 05 06 07 | 08 09 0A 0B | 0C 0D 0E 0F |" << std::endl; 24 | ostream << " #-------------#-------------#-------------#-------------#"; 25 | 26 | // Saving states 27 | auto oldFlags = ostream.flags(); 28 | auto oldPrec = ostream.precision(); 29 | auto oldFill = ostream.fill(); 30 | 31 | // Changing fill character 32 | ostream.fill('0'); 33 | 34 | // 35 | size_type index = 0; 36 | for (index = 0; index < container.size() + (16 - (container.size() % 16)); ++index) { 37 | if (!(index % 16)) { 38 | if (index) { 39 | ostream << "| "; 40 | } 41 | 42 | for (std::size_t asc = index - 16; asc < index; ++asc) { 43 | if (container[asc] >= ValueType(' ') && container[asc] <= ValueType('~')) { 44 | ostream << static_cast(container[asc]); 45 | } else { 46 | ostream << '.'; 47 | } 48 | } 49 | 50 | ostream << std::endl << " 0x"; 51 | ostream.width(8); 52 | ostream << std::hex << index << ' '; 53 | } 54 | 55 | if (!(index % 4)) { 56 | ostream << "| "; 57 | } 58 | 59 | if (index < container.size()) { 60 | ostream.width(2); 61 | ostream << std::uppercase << std::hex << static_cast(container[index]) << ' '; 62 | } else { 63 | ostream << " "; 64 | } 65 | } 66 | 67 | if (index) { 68 | ostream << "| "; 69 | } 70 | 71 | for (size_type asc = index - 16; asc < index; ++asc) { 72 | if (asc < container.size()) { 73 | if (container[asc] >= ValueType(' ') && container[asc] <= ValueType('~')) { 74 | ostream << static_cast(container[asc]); 75 | } else { 76 | ostream << '.'; 77 | } 78 | } else { 79 | ostream << ' '; 80 | } 81 | } 82 | 83 | ostream << std::endl 84 | << std::nouppercase << " #-------------#-------------#-------------#-------------#" << std::endl 85 | << "}, Length: " << std::dec << container.size() << ')' << std::endl; 86 | 87 | ostream.flags(oldFlags); 88 | ostream.precision(oldPrec); 89 | ostream.fill(oldFill); 90 | 91 | return ostream; 92 | } 93 | 94 | public: 95 | using size_type = typename container::size_type; 96 | using value_type = typename container::value_type; 97 | 98 | /** 99 | * @brief Constructor. 100 | * @param bytearray Base byte array. 101 | */ 102 | explicit bytearray_view(container& bytearray) 103 | : m_byteArray(bytearray) 104 | , m_start(0) 105 | , m_size(m_byteArray.container().size()) {} 106 | 107 | /** 108 | * @brief Constructor with boundaries. 109 | * @param bytearray Initial byte array. 110 | * @param start Start position. 111 | * @param size Size. 112 | */ 113 | bytearray_view(container& bytearray, size_type start, size_type size) 114 | : m_byteArray(bytearray) 115 | , m_start(start) 116 | , m_size(size) { 117 | assert(start <= m_byteArray.container().size()); 118 | assert(start + size <= m_byteArray.container().size()); 119 | } 120 | 121 | /** 122 | * @brief Method for getting initial 123 | * byte array object. 124 | */ 125 | container& bytearray() { return m_byteArray; } 126 | 127 | /** 128 | * @brief Method for getting constant reference 129 | * to initial byte array object. 130 | */ 131 | const container& bytearray() const { return m_byteArray; } 132 | 133 | /** 134 | * @brief Method for getting begin of this 135 | * view. 136 | */ 137 | typename container::vector::iterator begin() { return m_byteArray.container().begin() + m_start; } 138 | 139 | /** 140 | * @brief Method for getting begin of this 141 | * view. 142 | */ 143 | typename container::vector::const_iterator begin() const { return cbegin(); } 144 | 145 | /** 146 | * @brief Method for getting end of this view. 147 | */ 148 | typename container::vector::iterator end() { return m_byteArray.container().begin() + (m_start + m_size); } 149 | 150 | /** 151 | * @brief Method for getting end of this view. 152 | */ 153 | typename container::vector::const_iterator end() const { return cend(); } 154 | 155 | /** 156 | * @brief Method for getting constant begin of this 157 | * view. 158 | */ 159 | typename container::vector::const_iterator cbegin() const { return m_byteArray.container().cbegin() + m_start; } 160 | 161 | /** 162 | * @brief Method for getting constant end of this 163 | * view. 164 | */ 165 | typename container::vector::const_iterator cend() const { return m_byteArray.container().cbegin() + (m_start + m_size); } 166 | 167 | /** 168 | * @brief Method for getting reverse begin of this 169 | * view. 170 | */ 171 | typename container::vector::reverse_iterator rbegin() { 172 | return m_byteArray.container().rbegin() + (m_byteArray.container().size() - (m_start + m_size)); 173 | } 174 | 175 | /** 176 | * @brief Method for getting reverse end of this view. 177 | */ 178 | typename container::vector::reverse_iterator rend() { 179 | return m_byteArray.container().rbegin() + (m_byteArray.container().size() - m_start); 180 | } 181 | 182 | /** 183 | * @brief Method for getting constant reverse begin of this view. 184 | */ 185 | typename container::vector::const_reverse_iterator crbegin() const { 186 | return m_byteArray.container().crbegin() + (m_byteArray.container().size() - (m_start + m_size)); 187 | } 188 | 189 | /** 190 | * @brief Method for getting constant reverse end of this view. 191 | */ 192 | typename container::vector::const_reverse_iterator crend() const { 193 | return m_byteArray.container().crbegin() + (m_byteArray.container().size() - m_start); 194 | } 195 | 196 | /** 197 | * @brief Method for getting view size. 198 | * @return Size in bytes. 199 | */ 200 | typename container::size_type size() const { return m_size; } 201 | 202 | /** 203 | * @brief Method for access to specified element. 204 | * @param i Index. 205 | * @return Reference to value. 206 | */ 207 | typename container::value_type& operator[](size_type i) { return m_byteArray.container()[m_start + i]; } 208 | 209 | /** 210 | * @brief Method for const access to specified element. 211 | * @param i Index. 212 | */ 213 | typename container::value_type operator[](size_type i) const { return m_byteArray.container()[m_start + i]; } 214 | 215 | /** 216 | * @brief Method for access to specified element with 217 | * bounds checking. 218 | * @param i Index. 219 | * @return Reference to value. 220 | */ 221 | typename container::value_type& at(size_type i) { return m_byteArray.container().at(i); } 222 | 223 | /** 224 | * @brief Method for const access to specified element with 225 | * bounds checking. 226 | * @param i Index. 227 | * @return Reference to value. 228 | */ 229 | typename container::value_type at(size_type i) const { return m_byteArray.container().at(i); } 230 | 231 | /** 232 | * @brief Method, that returns maximum number of 233 | * elements. 234 | */ 235 | typename container::size_type max_size() const { return std::numeric_limits::max(); } 236 | 237 | /** 238 | * @brief Method for pushing back some trivially copyable type 239 | * with defined endianness. 240 | * @tparam T Type. 241 | * @param value Value. 242 | * @param order Endianness. 243 | */ 244 | template 245 | typename std::enable_if::value>::type push_back(T value, endianness order = endianness::big) { 246 | m_byteArray.template insert(m_start + m_size, value, order); 247 | 248 | m_size += sizeof(T); 249 | } 250 | 251 | /** 252 | * @brief Method for pushing back some part of trivially copyable type 253 | * value with defined endianness. 254 | * @tparam T Type. 255 | * @param value Value. 256 | * @param size Amount of bytes. 257 | * @param order Endianness. 258 | */ 259 | template 260 | typename std::enable_if::value>::type push_back_part(T value, 261 | size_type size, 262 | endianness order = endianness::big) { 263 | assert(sizeof(T) >= size && "Can't push size bigger, than type."); 264 | 265 | m_byteArray.template insert_part(m_start + m_size, value, size, order); 266 | 267 | m_size += size; 268 | } 269 | 270 | /** 271 | * @brief Method for pushing back multiple instances of some trivially 272 | * copyable type value with defined endiannes for each element. 273 | * @tparam T Type. 274 | * @param value Value. 275 | * @param amount Amount of elements. 276 | * @param order Endianness. 277 | */ 278 | template 279 | typename std::enable_if::value>::type push_back_multiple(T value, 280 | size_type amount, 281 | endianness order = endianness::big) { 282 | m_byteArray.template insert_multiple(m_start + m_size, value, amount, order); 283 | 284 | m_size += amount * sizeof(T); 285 | } 286 | 287 | /** 288 | * @brief Method for insertion some trivially copyable type 289 | * with defined endianness. 290 | * @tparam T Type. 291 | * @param position Position. 292 | * @param value Value. 293 | * @param order Endianness. 294 | */ 295 | template 296 | typename std::enable_if::value>::type insert(size_type position, 297 | T value, 298 | endianness order = endianness::big) { 299 | assert(position <= m_size && "Position is out of bounds."); 300 | 301 | m_byteArray.template insert(m_start + position, value, order); 302 | 303 | m_size += sizeof(T); 304 | } 305 | 306 | /** 307 | * @brief Method for insertion part of some trivially 308 | * copyable value with defined endianness. 309 | * @tparam T Type. 310 | * @param position Position. 311 | * @param value Value. 312 | * @param size Amount of bytes of value. 313 | * @param order Endianness. 314 | */ 315 | template 316 | typename std::enable_if::value>::type insert_part(size_type position, 317 | T value, 318 | size_type size, 319 | endianness order = endianness::big) { 320 | assert(position <= m_size && "Position is out of bounds."); 321 | 322 | m_byteArray.template insert_part(m_start + position, value, size, order); 323 | 324 | m_size += size; 325 | } 326 | 327 | /** 328 | * @brief Method for insertion multiple same elements 329 | * with defined endianness. 330 | * @tparam T Type. 331 | * @param position Position. 332 | * @param value Value. 333 | * @param amount Amount of elements. 334 | * @param order Endianness. 335 | */ 336 | template 337 | typename std::enable_if::value>::type insert_multiple(size_type position, 338 | T value, 339 | size_type amount, 340 | endianness order = endianness::big) { 341 | assert(position <= m_size && "Position is out of bounds."); 342 | 343 | m_byteArray.template insert_multiple(m_start + position, value, amount, order); 344 | 345 | m_size += sizeof(T) * amount; 346 | } 347 | 348 | /** 349 | * @brief Method for insertion multiple elements with 350 | * defined endianness. 351 | * @tparam InputIterator Iterator type. 352 | * @param position Position. 353 | * @param begin Begin iterator. 354 | * @param last Last iterator. 355 | * @param order Endianness. 356 | */ 357 | template 358 | typename std::enable_if::value_type>::value>::type 359 | insert_multiple(size_type position, InputIterator begin, InputIterator last, endianness order = endianness::big) { 360 | m_byteArray.template insert_multiple(m_start + position, begin, last, order); 361 | 362 | m_size += sizeof(typename std::iterator_traits::value_type) * std::distance(begin, last); 363 | } 364 | 365 | /** 366 | * @brief Method for setting value in byte array 367 | * container. 368 | * @tparam T Value type. 369 | * @param position Set position. 370 | * @param value Value. 371 | * @param order Byte order. 372 | */ 373 | template 374 | typename std::enable_if::value>::type set(size_type position, 375 | T value, 376 | endianness order = endianness::big) { 377 | assert(position + sizeof(T) <= size() && "Position + type size is out of bounds."); 378 | 379 | m_byteArray.template set(m_start + position, value, order); 380 | } 381 | 382 | /** 383 | * @brief Method for performing translation of 384 | * byte array to some trivially copyable type. 385 | * @tparam T Type. 386 | * @param position Value position. 387 | * @param order Read order. 388 | * @return Read value. 389 | */ 390 | template 391 | typename std::enable_if::value, T>::type read(size_type position, 392 | endianness order = endianness::big) const { 393 | assert(position + sizeof(T) <= size() && "Position + type size if out of bounde."); 394 | 395 | return m_byteArray.template read(m_start + position, order); 396 | } 397 | 398 | /** 399 | * @brief Method for performing translation of 400 | * byte array to some part of trivially copyable type. 401 | * @tparam T Type. 402 | * @param position Value position. 403 | * @param order Read order. 404 | * @return Read value. 405 | */ 406 | template 407 | typename std::enable_if::value, T>::type read_part(size_type position, 408 | size_type size, 409 | endianness order = endianness::big) const { 410 | assert(size <= sizeof(T) && "Size if bigger, than types size"); 411 | assert(position + size <= this->size() && "Position + size is out of bounds."); 412 | 413 | return m_byteArray.template read_part(m_start + position, size, order); 414 | } 415 | 416 | /** 417 | * @brief Method for checking whether the view is empty. 418 | */ 419 | bool empty() const { return m_size == 0; } 420 | 421 | const ValueType* data() const { return m_byteArray.container().data() + m_start; } 422 | 423 | ValueType* data() { return m_byteArray.container().data() + m_start; } 424 | 425 | /** 426 | * @brief Method for comparison of different views. 427 | */ 428 | template 429 | bool operator==(const bytearray_view& rhs) { 430 | if (size() != rhs.size()) { 431 | return false; 432 | } 433 | 434 | for (size_type i = 0; i < size(); ++i) { 435 | if (operator[](i) != rhs[i]) { 436 | return false; 437 | } 438 | } 439 | 440 | return true; 441 | } 442 | 443 | private: 444 | container& m_byteArray; 445 | size_type m_start; 446 | size_type m_size; 447 | }; 448 | } // namespace ba 449 | 450 | // Allowed 451 | namespace std { 452 | template 453 | std::string to_string(const ba::bytearray_view& processor) { 454 | std::stringstream ss; 455 | 456 | for (auto&& b : processor) { 457 | ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << int(b); 458 | } 459 | 460 | return ss.str(); 461 | } 462 | } // namespace std -------------------------------------------------------------------------------- /tests/ViewTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | TEST(View, ConstructorByteArray) 6 | { 7 | ba::bytearray array{}; 8 | 9 | ba::bytearray_view view(array); 10 | } 11 | 12 | TEST(View, ConstructorByteArrayProcessor) 13 | { 14 | std::vector container; 15 | 16 | ba::bytearray_processor processor(container); 17 | 18 | ba::bytearray_view view(processor); 19 | } 20 | 21 | TEST(View, IndexOperator) 22 | { 23 | ba::bytearray array{}; 24 | 25 | array.push_back(0xDEADBEEFAABBCCDD); 26 | 27 | ba::bytearray_view view(array, 1, 6); // 0xADBEEFAABBCC 28 | 29 | uint8_t expect[] = { 30 | 0xAD, 0xBE, 0xEF, 0xAA, 0xBB, 0xCC 31 | }; 32 | 33 | ASSERT_EQ(view.size(), 6); 34 | 35 | for (decltype(view)::size_type i = 0; i < 6; ++i) 36 | { 37 | ASSERT_EQ(uint8_t(view[i]), expect[i]); 38 | } 39 | } 40 | 41 | TEST(View, Iterators) 42 | { 43 | ba::bytearray array{}; 44 | 45 | array.push_back(0xDEADBEEFAABBCCDD); 46 | 47 | ba::bytearray_view view(array, 1, 6); // 0xADBEEFAABBCC 48 | 49 | uint8_t expect[] = { 50 | 0xAD, 0xBE, 0xEF, 0xAA, 0xBB, 0xCC 51 | }; 52 | 53 | ASSERT_EQ(view.size(), 6); 54 | 55 | for (auto iter = view.begin(), 56 | end = view.end(); 57 | iter != end; 58 | ++iter) 59 | { 60 | ASSERT_EQ(uint8_t(*iter), expect[std::distance(view.begin(), iter)]); 61 | } 62 | } 63 | 64 | TEST(View, ConstantIterators) 65 | { 66 | ba::bytearray array{}; 67 | 68 | array.push_back(0xDEADBEEFAABBCCDD); 69 | 70 | const ba::bytearray_view view(array, 1, 6); // 0xADBEEFAABBCC 71 | 72 | uint8_t expect[] = { 73 | 0xAD, 0xBE, 0xEF, 0xAA, 0xBB, 0xCC 74 | }; 75 | 76 | ASSERT_EQ(view.size(), 6); 77 | 78 | for (auto iter = view.cbegin(), 79 | end = view.cend(); 80 | iter != end; 81 | ++iter) 82 | { 83 | ASSERT_EQ(uint8_t(*iter), expect[std::distance(view.cbegin(), iter)]); 84 | } 85 | } 86 | 87 | TEST(View, ReverseIterators) 88 | { 89 | ba::bytearray array{}; 90 | 91 | array.push_back(0xDEADBEEFAABBCCDD); 92 | 93 | ba::bytearray_view view(array, 1, 6); // 0xADBEEFAABBCC 94 | 95 | uint8_t expect[] = { 96 | 0xCC, 0xBB, 0xAA, 0xEF, 0xBE, 0xAD 97 | }; 98 | 99 | ASSERT_EQ(view.size(), 6); 100 | 101 | for (auto iter = view.rbegin(), 102 | end = view.rend(); 103 | iter != end; 104 | ++iter) 105 | { 106 | ASSERT_EQ(uint8_t(*iter), expect[std::distance(view.rbegin(), iter)]); 107 | } 108 | } 109 | 110 | TEST(View, ConstantReverseIterators) 111 | { 112 | ba::bytearray array{}; 113 | 114 | array.push_back(0xDEADBEEFAABBCCDD); 115 | 116 | const ba::bytearray_view view(array, 1, 6); // 0xADBEEFAABBCC 117 | 118 | uint8_t expect[] = { 119 | 0xCC, 0xBB, 0xAA, 0xEF, 0xBE, 0xAD 120 | }; 121 | 122 | ASSERT_EQ(view.size(), 6); 123 | 124 | for (auto iter = view.crbegin(), 125 | end = view.crend(); 126 | iter != end; 127 | ++iter) 128 | { 129 | ASSERT_EQ(uint8_t(*iter), expect[std::distance(view.crbegin(), iter)]); 130 | } 131 | } 132 | 133 | TEST(View, PushBackUint8) 134 | { 135 | ba::bytearray array{}; 136 | 137 | array.push_back(0xDEADBEEFAABBCCDD); 138 | 139 | ba::bytearray_view view(array, 1, 6); 140 | 141 | uint8_t expect_view[] = { 142 | 0xAD, 0xBE, 0xEF, 0xAA, 0xBB, 0xCC, 0xFF 143 | }; 144 | 145 | uint8_t expect_array[] = { 146 | 0xDE, 0xAD, 0xBE, 0xEF, 0xAA, 0xBB, 0xCC, 0xFF, 0xDD 147 | }; 148 | 149 | view.push_back(0xFF); 150 | 151 | ASSERT_EQ(view.size(), 7); 152 | ASSERT_EQ(array.size(), 9); 153 | 154 | for (decltype(view)::size_type i = 0; i < view.size(); ++i) 155 | { 156 | ASSERT_EQ(uint8_t(view[i]), expect_view[i]); 157 | } 158 | 159 | for (decltype(array)::size_type i = 0; i < array.size(); ++i) 160 | { 161 | ASSERT_EQ(uint8_t(array[i]), expect_array[i]); 162 | } 163 | } 164 | 165 | TEST(View, PushBackPart) 166 | { 167 | uint8_t init[] { 168 | 0xFF, 0xFF, 0xFF, 0xFF 169 | }; 170 | 171 | uint8_t expect_le[] = { 172 | 0xFF, 0xFF, 0xFF, 0xEF, 0xBE, 0xAD, 0xFF 173 | }; 174 | 175 | uint8_t expect_le_view[] = { 176 | 0xFF, 0xFF, 0xEF, 0xBE, 0xAD 177 | }; 178 | 179 | uint8_t expect_be[] = { 180 | 0xFF, 0xFF, 0xFF, 0xAD, 0xBE, 0xEF, 0xFF 181 | }; 182 | 183 | uint8_t expect_be_view[] = { 184 | 0xFF, 0xFF, 0xAD, 0xBE, 0xEF 185 | }; 186 | 187 | { 188 | ba::bytearray array(reinterpret_cast(init), 4); 189 | 190 | ba::bytearray_view view(array, 1, 2); 191 | 192 | view.push_back_part(0xDEADBEEF, 3, ba::endianness::big); 193 | 194 | ASSERT_EQ(array.size(), 7); 195 | ASSERT_EQ(view.size(), 5); 196 | 197 | for (decltype(view)::size_type i = 0; i < view.size(); ++i) 198 | { 199 | ASSERT_EQ(uint8_t(view[i]), expect_be_view[i]); 200 | } 201 | 202 | for (decltype(array)::size_type i = 0; i < array.size(); ++i) 203 | { 204 | ASSERT_EQ(uint8_t(array[i]), expect_be[i]); 205 | } 206 | } 207 | 208 | { 209 | ba::bytearray array(reinterpret_cast(init), 4); 210 | 211 | ba::bytearray_view view(array, 1, 2); 212 | 213 | view.push_back_part(0xDEADBEEF, 3, ba::endianness::little); 214 | 215 | ASSERT_EQ(array.size(), 7); 216 | ASSERT_EQ(view.size(), 5); 217 | 218 | for (decltype(view)::size_type i = 0; i < view.size(); ++i) 219 | { 220 | ASSERT_EQ(uint8_t(view[i]), expect_le_view[i]); 221 | } 222 | 223 | for (decltype(array)::size_type i = 0; i < array.size(); ++i) 224 | { 225 | ASSERT_EQ(uint8_t(array[i]), expect_le[i]); 226 | } 227 | } 228 | } 229 | 230 | TEST(View, PushBackMultiple1) 231 | { 232 | uint8_t init[] { 233 | 0xFF, 0xFF, 0xFF, 0xFF 234 | }; 235 | 236 | uint8_t expect_be[] = { 237 | 0xFF, 0xFF, 0xFF, 0xDE, 0xAD, 0xDE, 0xAD, 0xFF 238 | }; 239 | 240 | uint8_t expect_be_view[] = { 241 | 0xFF, 0xFF, 0xDE, 0xAD, 0xDE, 0xAD 242 | }; 243 | 244 | uint8_t expect_le[] = { 245 | 0xFF, 0xFF, 0xFF, 0xAD, 0xDE, 0xAD, 0xDE, 0xFF 246 | }; 247 | 248 | uint8_t expect_le_view[] = { 249 | 0xFF, 0xFF, 0xAD, 0xDE, 0xAD, 0xDE 250 | }; 251 | 252 | { 253 | ba::bytearray array(reinterpret_cast(init), 4); 254 | 255 | ba::bytearray_view view(array, 1, 2); 256 | 257 | view.push_back_multiple( 258 | 0xDEAD, 2, ba::endianness::big 259 | ); 260 | 261 | ASSERT_EQ(array.size(), 8); 262 | ASSERT_EQ(view.size(), 6); 263 | 264 | for (decltype(array)::size_type i = 0; i < array.size(); ++i) 265 | { 266 | ASSERT_EQ(uint8_t(array[i]), expect_be[i]); 267 | } 268 | 269 | for (decltype(view)::size_type i = 0; i < view.size(); ++i) 270 | { 271 | ASSERT_EQ(uint8_t(view[i]), expect_be_view[i]); 272 | } 273 | } 274 | 275 | { 276 | ba::bytearray array(reinterpret_cast(init), 4); 277 | 278 | ba::bytearray_view view(array, 1, 2); 279 | 280 | view.push_back_multiple( 281 | 0xDEAD, 2, ba::endianness::little 282 | ); 283 | 284 | ASSERT_EQ(array.size(), 8); 285 | ASSERT_EQ(view.size(), 6); 286 | 287 | for (decltype(array)::size_type i = 0; i < array.size(); ++i) 288 | { 289 | ASSERT_EQ(uint8_t(array[i]), expect_le[i]); 290 | } 291 | 292 | for (decltype(view)::size_type i = 0; i < view.size(); ++i) 293 | { 294 | ASSERT_EQ(uint8_t(view[i]), expect_le_view[i]); 295 | } 296 | } 297 | } 298 | 299 | TEST(View, Empty) 300 | { 301 | ba::bytearray array{}; 302 | 303 | ba::bytearray_view view(array); 304 | 305 | ASSERT_TRUE(view.empty()); 306 | 307 | view.push_back(0xFF); 308 | 309 | ASSERT_FALSE(view.empty()); 310 | } 311 | 312 | TEST(View, Comparison) 313 | { 314 | ba::bytearray array1{}; 315 | ba::bytearray array2{}; 316 | 317 | ba::bytearray_view view1(array1); 318 | ba::bytearray_view view2(array2); 319 | 320 | ASSERT_TRUE(view1 == view2); 321 | 322 | view1.push_back(0xDDEEAADDBBEEEEFF); 323 | view2.push_back(0xDDEEAADDBBEEEEFF); 324 | 325 | ASSERT_TRUE(view1 == view2); 326 | 327 | auto tmp = view1[0]; 328 | 329 | view1[0] = std::byte(0xAA); 330 | 331 | ASSERT_FALSE(view1 == view2); 332 | 333 | view1[0] = tmp; 334 | 335 | ASSERT_TRUE(view1 == view2); 336 | 337 | view1.push_back(0xAA); 338 | 339 | ASSERT_FALSE(view1 == view2); 340 | } 341 | 342 | TEST(View, InsertNormal) 343 | { 344 | uint8_t initial[] = { 345 | 0xFF, 0xFF, 0xFF, 0xFF 346 | }; 347 | 348 | uint8_t expect_le[] = { 349 | 0xFF, 0xEF, 0xBE, 0xAD, 0xDE, 0xFF 350 | }; 351 | 352 | uint8_t expect_be[] = { 353 | 0xFF, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF 354 | }; 355 | 356 | { 357 | ba::bytearray array((std::byte*) initial, 4); 358 | 359 | ba::bytearray_view view(array, 1, 2); 360 | 361 | view.insert(1, 0xDEADBEEF, ba::endianness::big); 362 | 363 | ASSERT_EQ(view.size(), 6); 364 | 365 | for (ba::bytearray<>::size_type i = 0; i < view.size(); ++i) 366 | { 367 | ASSERT_EQ(uint8_t(view[i]), expect_be[i]); 368 | } 369 | } 370 | 371 | { 372 | ba::bytearray array((std::byte*) initial, 4); 373 | 374 | ba::bytearray_view view(array, 1, 2); 375 | 376 | view.insert(1, 0xDEADBEEF, ba::endianness::little); 377 | 378 | ASSERT_EQ(view.size(), 6); 379 | 380 | for (ba::bytearray<>::size_type i = 0; i < view.size(); ++i) 381 | { 382 | ASSERT_EQ(uint8_t(view[i]), expect_le[i]); 383 | } 384 | } 385 | } 386 | 387 | TEST(View, InsertPart) 388 | { 389 | uint8_t initial[] = { 390 | 0xFF, 0xFF, 0xFF, 0xFF 391 | }; 392 | 393 | uint8_t expect_le[] = { 394 | 0xFF, 0xEF, 0xBE, 0xAD, 0xFF 395 | }; 396 | 397 | uint8_t expect_be[] = { 398 | 0xFF, 0xAD, 0xBE, 0xEF, 0xFF 399 | }; 400 | 401 | { 402 | ba::bytearray array((std::byte*) initial, 4); 403 | 404 | ba::bytearray_view view(array, 1, 2); 405 | 406 | view.insert_part(1, 0xDEADBEEF, 3, ba::endianness::big); 407 | 408 | ASSERT_EQ(view.size(), 5); 409 | 410 | for (ba::bytearray<>::size_type i = 0; i < view.size(); ++i) 411 | { 412 | ASSERT_EQ(uint8_t(view[i]), expect_be[i]); 413 | } 414 | } 415 | 416 | { 417 | ba::bytearray array((std::byte*) initial, 4); 418 | 419 | ba::bytearray_view view(array, 1, 2); 420 | 421 | view.insert_part(1, 0xDEADBEEF, 3, ba::endianness::little); 422 | 423 | ASSERT_EQ(view.size(), 5); 424 | 425 | for (ba::bytearray<>::size_type i = 0; i < view.size(); ++i) 426 | { 427 | ASSERT_EQ(uint8_t(view[i]), expect_le[i]); 428 | } 429 | } 430 | } 431 | 432 | TEST(View, InsertMultiple1) 433 | { 434 | uint8_t initial[] = { 435 | 0xFF, 0xFF, 0xFF, 0xFF 436 | }; 437 | 438 | uint8_t expect_be[] = { 439 | 0xFF, 0xDE, 0xAD, 0xDE, 0xAD, 0xFF 440 | }; 441 | 442 | uint8_t expect_le[] = { 443 | 0xFF, 0xAD, 0xDE, 0xAD, 0xDE, 0xFF 444 | }; 445 | 446 | { 447 | ba::bytearray array(reinterpret_cast(initial), 4); 448 | 449 | ba::bytearray_view view(array, 1, 2); 450 | 451 | view.insert_multiple( 452 | 1, 453 | 0xDEAD, 454 | 2, 455 | ba::endianness::big 456 | ); 457 | 458 | ASSERT_EQ(view.size(), 6); 459 | 460 | for (ba::bytearray<>::size_type i = 0; i < view.size(); ++i) 461 | { 462 | ASSERT_EQ(uint8_t(view[i]), expect_be[i]); 463 | } 464 | } 465 | 466 | { 467 | ba::bytearray array(reinterpret_cast(initial), 4); 468 | 469 | ba::bytearray_view view(array, 1, 2); 470 | 471 | view.insert_multiple( 472 | 1, 473 | 0xDEAD, 474 | 2, 475 | ba::endianness::little 476 | ); 477 | 478 | ASSERT_EQ(view.size(), 6); 479 | 480 | for (ba::bytearray<>::size_type i = 0; i < view.size(); ++i) 481 | { 482 | ASSERT_EQ(uint8_t(view[i]), expect_le[i]); 483 | } 484 | } 485 | } 486 | 487 | 488 | TEST(View, InsertMultiple2) 489 | { 490 | uint8_t initial[] = { 491 | 0xFF, 0xFF, 0xFF, 0xFF 492 | }; 493 | 494 | uint8_t expect_be[] = { 495 | 0xFF, 0xDE, 0xAD, 0xDE, 0xAD, 0xFF 496 | }; 497 | 498 | uint8_t expect_le[] = { 499 | 0xFF, 0xAD, 0xDE, 0xAD, 0xDE, 0xFF 500 | }; 501 | 502 | std::vector insertionData = { 503 | 0xDEAD, 0xDEAD 504 | }; 505 | 506 | { 507 | ba::bytearray array(reinterpret_cast(initial), 4); 508 | 509 | ba::bytearray_view view(array, 1, 2); 510 | 511 | view.insert_multiple( 512 | 1, 513 | insertionData.begin(), 514 | insertionData.end(), 515 | ba::endianness::big 516 | ); 517 | 518 | ASSERT_EQ(view.size(), 6); 519 | 520 | for (ba::bytearray<>::size_type i = 0; i < view.size(); ++i) 521 | { 522 | ASSERT_EQ(uint8_t(view[i]), expect_be[i]); 523 | } 524 | } 525 | 526 | { 527 | ba::bytearray array(reinterpret_cast(initial), 4); 528 | 529 | ba::bytearray_view view(array, 1, 2); 530 | 531 | view.insert_multiple( 532 | 1, 533 | insertionData.begin(), 534 | insertionData.end(), 535 | ba::endianness::little 536 | ); 537 | 538 | ASSERT_EQ(view.size(), 6); 539 | 540 | for (ba::bytearray<>::size_type i = 0; i < view.size(); ++i) 541 | { 542 | ASSERT_EQ(uint8_t(view[i]), expect_le[i]); 543 | } 544 | } 545 | } 546 | 547 | 548 | TEST(View, ReadUint8) 549 | { 550 | const uint8_t value = 0xDE; 551 | 552 | { 553 | ba::bytearray data{}; 554 | 555 | ba::bytearray_view view(data, 0, 0); 556 | 557 | view.push_back(value, ba::endianness::big); 558 | 559 | ASSERT_EQ( 560 | view.read(0, ba::endianness::big), 561 | value 562 | ); 563 | } 564 | 565 | { 566 | ba::bytearray data{}; 567 | 568 | ba::bytearray_view view(data, 0, 0); 569 | 570 | view.push_back(value, ba::endianness::little); 571 | 572 | ASSERT_EQ( 573 | view.read(0, ba::endianness::little), 574 | value 575 | ); 576 | } 577 | } 578 | 579 | TEST(View, ReadUint32) 580 | { 581 | const uint32_t value = 0xDEADBEEF; 582 | 583 | { 584 | ba::bytearray data{}; 585 | 586 | ba::bytearray_view view(data, 0, 0); 587 | 588 | view.push_back(value, ba::endianness::big); 589 | 590 | ASSERT_EQ( 591 | view.read(0, ba::endianness::big), 592 | value 593 | ); 594 | } 595 | 596 | { 597 | ba::bytearray data{}; 598 | 599 | ba::bytearray_view view(data, 0, 0); 600 | 601 | view.push_back(value, ba::endianness::little); 602 | 603 | ASSERT_EQ( 604 | view.read(0, ba::endianness::little), 605 | value 606 | ); 607 | } 608 | } 609 | 610 | TEST(View, ReadUint64) 611 | { 612 | const uint64_t value = 0xDEADBEEFFFEEFFEE; 613 | 614 | { 615 | ba::bytearray data{}; 616 | 617 | ba::bytearray_view view(data, 0, 0); 618 | 619 | view.push_back(value, ba::endianness::big); 620 | 621 | ASSERT_EQ( 622 | view.read(0, ba::endianness::big), 623 | value 624 | ); 625 | } 626 | 627 | { 628 | ba::bytearray data{}; 629 | 630 | ba::bytearray_view view(data, 0, 0); 631 | 632 | view.push_back(value, ba::endianness::little); 633 | 634 | ASSERT_EQ( 635 | view.read(0, ba::endianness::little), 636 | value 637 | ); 638 | } 639 | } 640 | 641 | TEST(View, ReadInt64) 642 | { 643 | const int64_t value = 0xDEADBEEFFFEEFFEE; 644 | 645 | { 646 | ba::bytearray data{}; 647 | 648 | ba::bytearray_view view(data, 0, 0); 649 | 650 | view.push_back(value, ba::endianness::big); 651 | 652 | ASSERT_EQ( 653 | view.read(0, ba::endianness::big), 654 | value 655 | ); 656 | } 657 | 658 | { 659 | ba::bytearray data{}; 660 | 661 | ba::bytearray_view view(data, 0, 0); 662 | 663 | view.push_back(value, ba::endianness::little); 664 | 665 | ASSERT_EQ( 666 | view.read(0, ba::endianness::little), 667 | value 668 | ); 669 | } 670 | } 671 | 672 | struct CustomStructBase 673 | { 674 | uint64_t a; 675 | uint32_t c; 676 | char z; 677 | 678 | char message[32]; 679 | }; 680 | 681 | struct CustomStructDerived : CustomStructBase 682 | { 683 | char additional[64]; 684 | 685 | bool operator==(const CustomStructDerived& rhs) 686 | { 687 | return 688 | rhs.a == a && 689 | rhs.c == c && 690 | rhs.z == z && 691 | memcmp(rhs.message, message, 32) == 0 && 692 | memcmp(rhs.additional, additional, 64) == 0; 693 | } 694 | }; 695 | 696 | TEST(View, ReadCustom) 697 | { 698 | CustomStructDerived value = {}; 699 | 700 | value.a = 0xDEADDEADBEEFBEEF; 701 | value.c = 0xFEEDBEEF; 702 | value.z = '%'; 703 | memcpy(value.message, "Sample message", 15); 704 | memcpy(value.additional, "Some actual long additional message", 35); 705 | 706 | { 707 | ba::bytearray data{}; 708 | 709 | ba::bytearray_view view(data, 0, 0); 710 | 711 | view.push_back(value, ba::endianness::big); 712 | 713 | ASSERT_TRUE( 714 | view.read(0, ba::endianness::big) == value 715 | ); 716 | } 717 | 718 | { 719 | ba::bytearray data{}; 720 | 721 | ba::bytearray_view view(data, 0, 0); 722 | 723 | view.push_back(value, ba::endianness::little); 724 | 725 | ASSERT_TRUE( 726 | view.read(0, ba::endianness::little) == value 727 | ); 728 | } 729 | } 730 | 731 | TEST(View, ReadPart) 732 | { 733 | uint64_t value = 0x00ADBEEFFFEEAABB; 734 | 735 | { 736 | ba::bytearray data{}; 737 | 738 | ba::bytearray_view view(data, 0, 0); 739 | 740 | view.push_back_part(value, 7, ba::endianness::big); 741 | 742 | ASSERT_EQ(value, view.read_part(0, 7, ba::endianness::big)); 743 | } 744 | 745 | { 746 | ba::bytearray data{}; 747 | 748 | ba::bytearray_view view(data, 0, 0); 749 | 750 | view.push_back_part(value, 7, ba::endianness::little); 751 | 752 | ASSERT_EQ(value, view.read_part(0, 7, ba::endianness::little)); 753 | } 754 | } -------------------------------------------------------------------------------- /include/ba/bytearray_processor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // ba 4 | #include 5 | #include 6 | 7 | // C++ STL 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace ba { 19 | 20 | /** 21 | * @brief Class, that implements 22 | * operations with vector like with byte array. 23 | * It holds reference to std::vector with 1 byte width data. 24 | * (std::byte, uint8_t or unsigned char) 25 | */ 26 | template 27 | class bytearray_processor : public bytearray_reader { 28 | public: 29 | using vector = std::vector; 30 | 31 | private: 32 | /** 33 | * @brief Stream output function. 34 | */ 35 | friend std::ostream& operator<<(std::ostream& ostream, const bytearray_processor& arr) { 36 | // Printing header 37 | ostream << "ByteArray({" << std::endl; 38 | ostream << " #-------------#-------------#-------------#-------------#" << std::endl; 39 | ostream << " | 00 01 02 03 | 04 05 06 07 | 08 09 0A 0B | 0C 0D 0E 0F |" << std::endl; 40 | ostream << " #-------------#-------------#-------------#-------------#"; 41 | 42 | // Saving states 43 | auto oldFlags = ostream.flags(); 44 | auto oldPrec = ostream.precision(); 45 | auto oldFill = ostream.fill(); 46 | 47 | // Changing fill character 48 | ostream.fill('0'); 49 | 50 | const std::vector& container = arr.container(); 51 | 52 | // 53 | size_type index = 0; 54 | for (index = 0; index < container.size() + (16 - (container.size() % 16)); ++index) { 55 | if (!(index % 16)) { 56 | if (index) { 57 | ostream << "| "; 58 | } 59 | 60 | for (std::size_t asc = index - 16; asc < index; ++asc) { 61 | if (container[asc] >= ValueType(' ') && container[asc] <= ValueType('~')) { 62 | ostream << static_cast(container[asc]); 63 | } else { 64 | ostream << '.'; 65 | } 66 | } 67 | 68 | ostream << std::endl << " 0x"; 69 | ostream.width(8); 70 | ostream << std::hex << index << ' '; 71 | } 72 | 73 | if (!(index % 4)) { 74 | ostream << "| "; 75 | } 76 | 77 | if (index < container.size()) { 78 | ostream.width(2); 79 | ostream << std::uppercase << std::hex << static_cast(container[index]) << ' '; 80 | } else { 81 | ostream << " "; 82 | } 83 | } 84 | 85 | if (index) { 86 | ostream << "| "; 87 | } 88 | 89 | for (size_type asc = index - 16; asc < index; ++asc) { 90 | if (asc < container.size()) { 91 | if (container[asc] >= ValueType(' ') && container[asc] <= ValueType('~')) { 92 | ostream << static_cast(container[asc]); 93 | } else { 94 | ostream << '.'; 95 | } 96 | } else { 97 | ostream << ' '; 98 | } 99 | } 100 | 101 | ostream << std::endl 102 | << std::nouppercase << " #-------------#-------------#-------------#-------------#" << std::endl 103 | << "}, Length: " << std::dec << container.size() << ", Capacity: " << std::dec << container.capacity() << ')' << std::endl; 104 | 105 | ostream.flags(oldFlags); 106 | ostream.precision(oldPrec); 107 | ostream.fill(oldFill); 108 | 109 | return ostream; 110 | } 111 | 112 | public: 113 | static_assert(sizeof(ValueType) == 1, "Size of container element has to be 1 byte"); 114 | 115 | using size_type = typename vector::size_type; 116 | using value_type = ValueType; 117 | 118 | /** 119 | * @brief Constructor. Accepts container reference to 120 | * operate with. 121 | * @param container Vector with type. 122 | */ 123 | explicit bytearray_processor(vector& container) 124 | : bytearray_reader(container) 125 | , m_container(container) {} 126 | 127 | /** 128 | * @brief Method for getting reference to internal container. 129 | * @return Reference to internal container. 130 | */ 131 | vector& container() { return m_container; } 132 | 133 | /** 134 | * @brief Method for getting constant reference to internal container. 135 | * @return Constant reference to internal container. 136 | */ 137 | const vector& container() const { return m_container; } 138 | 139 | /** 140 | * @brief Method for loading bytearray data from hex. 141 | * If bytearray already contains any data - it will be erased. 142 | * @param s Hex string. Example: "aabbccdd" or "AABBDDCC" or "AA BB CC DD" 143 | * @return Success. If fail - previous data will not be erased. 144 | */ 145 | bool load_from_hex(const std::string& s) { return load_from_hex(std::string_view(s)); } 146 | 147 | /** 148 | * @brief Method for loading bytearray data from hex. 149 | * If bytearray already contains any data - it will be erased. 150 | * @param sv Hex string view. Example: "aabbccdd" or "AABBDDCC" or "AA BB CC DD" 151 | * @return Success. If fail - previous data will not be erased. 152 | */ 153 | bool load_from_hex(const std::string_view& sv) { 154 | static auto validator = [](char c) { return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'); }; 155 | 156 | static auto char2int = [](char symbol) -> uint8_t { 157 | if (symbol >= '0' && symbol <= '9') { 158 | return static_cast(symbol - '0'); 159 | } else if (symbol >= 'A' && symbol <= 'F') { 160 | return static_cast(symbol - 'A' + 10); 161 | } else if (symbol >= 'a' && symbol <= 'f') { 162 | return static_cast(symbol - 'a' + 10); 163 | } 164 | 165 | throw std::runtime_error("Received unknown symbol"); 166 | }; 167 | 168 | // Checking string 169 | auto found = std::find_if(sv.begin(), sv.end(), [](char c) { 170 | return (c < 'a' || c > 'f') && (c < 'A' || c > 'F') && (c < '0' || c > '9') && (c != ' '); 171 | }); 172 | 173 | if (found != sv.end()) { 174 | return false; 175 | } 176 | 177 | // Parsing 178 | auto count = std::count_if(sv.begin(), sv.end(), validator); 179 | 180 | m_container.clear(); 181 | m_container.reserve(count); 182 | 183 | bool isFirst = true; 184 | uint8_t firstValue = 0; 185 | 186 | for (auto symbol : sv) { 187 | if (!validator(symbol)) { 188 | continue; 189 | } 190 | 191 | if (isFirst) { 192 | firstValue = char2int(symbol); 193 | 194 | isFirst = false; 195 | } else { 196 | m_container.push_back(std::byte(firstValue * 16 + char2int(symbol))); 197 | 198 | isFirst = true; 199 | } 200 | } 201 | 202 | return true; 203 | } 204 | 205 | /** 206 | * @brief Method for pushing back some trivially copyable type 207 | * with defined endianness. 208 | * @tparam T Type. 209 | * @param value Value. 210 | * @param order Endianness. 211 | */ 212 | template 213 | typename std::enable_if::value>::type push_back(T value, endianness order = endianness::big) { 214 | if (order == get_system_endianness()) { 215 | for (std::size_t i = 0; i < sizeof(T); ++i) { 216 | m_container.push_back(((ValueType*)(&value))[i]); 217 | } 218 | } else // big 219 | { 220 | for (std::size_t i = sizeof(T); i > 0; --i) { 221 | m_container.push_back(((ValueType*)(&value))[i - 1]); 222 | } 223 | } 224 | } 225 | 226 | /** 227 | * @brief Method for pushing back some part of trivially copyable type 228 | * value with defined endianness. 229 | * @tparam T Type. 230 | * @param value Value. 231 | * @param order Endianness. 232 | */ 233 | template 234 | typename std::enable_if::value>::type push_back_part(T value, 235 | size_type size, 236 | endianness order = endianness::big) { 237 | assert(sizeof(T) >= size && "Can't push size bigger, than type."); 238 | 239 | if (order == get_system_endianness()) { 240 | for (std::size_t i = 0; i < size; ++i) { 241 | m_container.push_back(((ValueType*)(&value))[i]); 242 | } 243 | } else { 244 | for (std::size_t i = size; i > 0; --i) { 245 | m_container.push_back(((ValueType*)(&value))[i - 1]); 246 | } 247 | } 248 | } 249 | 250 | /** 251 | * @brief Method for pushing back several 252 | * copies of some trivially copyable value. 253 | * @tparam T Value type. 254 | * @param value Value. 255 | * @param amount Amount. 256 | * @param order Endianness. 257 | */ 258 | template 259 | typename std::enable_if::value>::type push_back_multiple(T value, 260 | size_type amount, 261 | endianness order = endianness::big) { 262 | for (size_type i = 0; i < amount; ++i) { 263 | push_back(value, order); 264 | } 265 | } 266 | 267 | /** 268 | * @brief Method for pushing back several 269 | * values from range. 270 | * @tparam InputIterator Input iterator type. 271 | * @param begin Begin iterator. 272 | * @param last Last iterator. 273 | * @param order Endianness. 274 | */ 275 | template 276 | typename std::enable_if::value_type>::value>::type 277 | push_back_multiple(InputIterator begin, InputIterator last, endianness order = endianness::big) { 278 | while (begin != last) { 279 | push_back(*begin, order); 280 | 281 | ++begin; 282 | } 283 | } 284 | 285 | /** 286 | * @brief Method for pushing back data from initializer list. 287 | * @tparam T Initializer list element type. 288 | * @param il Initializer list. 289 | * @param order Element pushing order. 290 | */ 291 | template 292 | typename std::enable_if::value>::type push_back_multiple(std::initializer_list il, 293 | endianness order = endianness::big) { 294 | push_back_multiple(il.begin(), il.end(), order); 295 | } 296 | 297 | /** 298 | * @brief Method for insertion some trivially copyable type 299 | * with defined endianness. 300 | * @tparam T Type. 301 | * @param position Position. 302 | * @param value Value. 303 | * @param order Endianness. 304 | */ 305 | template 306 | typename std::enable_if::value>::type insert(size_type position, 307 | T value, 308 | endianness order = endianness::big) { 309 | assert(position <= m_container.size() && "Position is out of bounds."); 310 | 311 | if (order == get_system_endianness()) { 312 | m_container.insert(m_container.begin() + position, ((ValueType*)&value), ((ValueType*)&value) + sizeof(value)); 313 | } else { 314 | m_container.insert(m_container.begin() + position, std::reverse_iterator(((ValueType*)&value) + sizeof(value)), 315 | std::reverse_iterator((ValueType*)&value)); 316 | } 317 | } 318 | 319 | /** 320 | * @brief Method for insertion part of some trivially 321 | * copyable type with defined endianness. 322 | * @tparam T Type. 323 | * @param position Position. 324 | * @param value Value. 325 | * @param size Amount of bytes of value. 326 | * @param order Endianness. 327 | */ 328 | template 329 | typename std::enable_if::value>::type insert_part(size_type position, 330 | T value, 331 | size_type size, 332 | endianness order = endianness::big) { 333 | assert(sizeof(T) >= size && "Can't insert size bigger, than type."); 334 | 335 | if (order == get_system_endianness()) { 336 | m_container.insert(m_container.begin() + position, ((ValueType*)&value), ((ValueType*)&value) + size); 337 | } else { 338 | m_container.insert(m_container.begin() + position, std::reverse_iterator(((ValueType*)&value) + size), 339 | std::reverse_iterator((ValueType*)&value)); 340 | } 341 | } 342 | 343 | /** 344 | * @brief Method for insertion multiple same elements 345 | * with defined endianness. 346 | * @tparam T Type. 347 | * @param position Position. 348 | * @param value Value. 349 | * @param amount Amount of elements. 350 | * @param order Endianness. 351 | */ 352 | template 353 | typename std::enable_if::value>::type insert_multiple(size_type position, 354 | T value, 355 | size_type amount, 356 | endianness order = endianness::big) { 357 | // Moving everything 358 | m_container.insert(m_container.begin() + position, sizeof(T) * amount, ValueType(0x00)); 359 | 360 | for (size_type i = 0; i < amount; ++i) { 361 | set(position + sizeof(T) * i, value, order); 362 | } 363 | } 364 | 365 | /** 366 | * @brief Method for insertion multiple elements with 367 | * defined endianness. 368 | * @tparam InputIterator Iterator type. 369 | * @param position Position. 370 | * @param begin Begin iterator. 371 | * @param last Last iterator. 372 | * @param order Endianness. 373 | */ 374 | template 375 | typename std::enable_if::value_type>::value>::type 376 | insert_multiple(size_type position, InputIterator begin, InputIterator last, endianness order = endianness::big) { 377 | using val = typename std::iterator_traits::value_type; 378 | 379 | auto count = std::distance(begin, last); 380 | 381 | // Moving everything 382 | m_container.insert(m_container.begin() + position, sizeof(val) * count, ValueType(0x00)); 383 | 384 | size_type index = 0; 385 | 386 | while (begin != last) { 387 | set(position + sizeof(val) * index, *begin, order); 388 | 389 | ++begin; 390 | ++index; 391 | } 392 | } 393 | 394 | /** 395 | * @brief Method for setting value in byte array 396 | * container. 397 | * @tparam T Value type. 398 | * @param position Set position. 399 | * @param value Value. 400 | * @param order Byte order. 401 | */ 402 | template 403 | typename std::enable_if::value>::type set(size_type position, 404 | T value, 405 | endianness order = endianness::big) { 406 | assert(position + sizeof(value) <= m_container.size() && "Position + type size is out of bounds."); 407 | 408 | if (order == get_system_endianness()) { 409 | for (std::size_t i = 0; i < sizeof(T); ++i) { 410 | m_container[position + i] = ((ValueType*)(&value))[i]; 411 | } 412 | } else { 413 | for (std::size_t i = sizeof(T); i > 0; --i) { 414 | m_container[position + sizeof(T) - i] = ((ValueType*)(&value))[i - 1]; 415 | } 416 | } 417 | } 418 | 419 | /** 420 | * @brief Method for performing translation of 421 | * byte array to some trivially copyable type. 422 | * @tparam T Type. 423 | * @param position Value position. 424 | * @param order Read order. 425 | * @return Read value. 426 | */ 427 | template 428 | typename std::enable_if::value, T>::type read(size_type position, 429 | endianness order = endianness::big) const { 430 | assert(position + sizeof(T) <= m_container.size() && "Position + type size is out of bounds."); 431 | 432 | T value; 433 | 434 | if (order == get_system_endianness()) { 435 | for (std::size_t i = 0; i < sizeof(T); ++i) { 436 | ((ValueType*)(&value))[i] = m_container[position + i]; 437 | } 438 | } else { 439 | for (std::size_t i = sizeof(T); i > 0; --i) { 440 | ((ValueType*)(&value))[i - 1] = m_container[position + sizeof(T) - i]; 441 | } 442 | } 443 | 444 | return value; 445 | } 446 | 447 | /** 448 | * @brief Method for performing translation of 449 | * byte array to some part of trivially copyable type. 450 | * @tparam T Type. 451 | * @param position Value position. 452 | * @param order Read order. 453 | * @return Read value. 454 | */ 455 | template 456 | typename std::enable_if::value, T>::type read_part(size_type position, 457 | size_type size, 458 | endianness order = endianness::big) const { 459 | assert(size <= sizeof(T) && "Size if bigger, than types size"); 460 | assert(position + size <= m_container.size() && "Position + size is out of bounds."); 461 | 462 | T value; 463 | std::memset(&value, 0, sizeof(T)); 464 | 465 | if (order == get_system_endianness()) { 466 | for (size_type i = 0; i < size; ++i) { 467 | ((ValueType*)(&value))[i] = m_container[position + i]; 468 | } 469 | } else { 470 | for (size_type i = size; i > 0; --i) { 471 | ((ValueType*)(&value))[i - 1] = m_container[position + size - i]; 472 | } 473 | } 474 | 475 | return value; 476 | } 477 | 478 | private: 479 | /** 480 | * @brief Constexpr function for checking system endianness. 481 | * @return Endianness value. 482 | */ 483 | [[nodiscard]] constexpr endianness get_system_endianness() const { 484 | union { 485 | uint32_t i; 486 | char c[4]; 487 | } example = {0x01020304}; 488 | 489 | return example.c[0] == 1 ? endianness::big : endianness::little; 490 | } 491 | 492 | vector& m_container; 493 | }; 494 | } // namespace ba 495 | 496 | // Allowed 497 | namespace std { 498 | template 499 | std::string to_string(const ba::bytearray_processor& processor) { 500 | std::stringstream ss; 501 | 502 | for (auto&& b : processor.container()) { 503 | ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << int(b); 504 | } 505 | 506 | return ss.str(); 507 | } 508 | } // namespace std 509 | --------------------------------------------------------------------------------