├── docs ├── news.rst ├── license.rst ├── user_api │ ├── big_endian.rst │ ├── stream_writer.rst │ ├── little_endian.rst │ ├── stream_reader.rst │ ├── is_big_endian.rst │ ├── user_api.rst │ └── network.rst ├── requirements.in ├── examples │ ├── examples.rst │ ├── network.rst │ ├── stream_writer_reader.rst │ └── convert_to_little_endian.rst ├── index.rst ├── requirements.txt └── conf.py ├── waf ├── endian.png ├── test ├── wscript_build ├── endian_tests.cpp └── src │ ├── test_stream_writer.cpp │ ├── detail │ └── test_stream.cpp │ ├── test_stream_reader.cpp │ ├── test_big_endian.cpp │ ├── test_little_endian.cpp │ └── test_stream_writer_reader.cpp ├── examples ├── wscript_build ├── stream_writer_reader.cpp ├── network.cpp └── convert_to_little_endian.cpp ├── resolve.json ├── .github ├── gcc-problem-matcher.json └── workflows │ └── ci.yml ├── .clang-format ├── src └── endian │ ├── is_big_endian.hpp │ ├── network.hpp │ ├── detail │ ├── helpers.hpp │ ├── stream.hpp │ ├── big.hpp │ └── little.hpp │ ├── stream_writer.hpp │ ├── little_endian.hpp │ ├── big_endian.hpp │ └── stream_reader.hpp ├── giit.json ├── .gitignore ├── wscript ├── LICENSE.rst ├── CMakeLists.txt ├── README.rst └── NEWS.rst /docs/news.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../NEWS.rst 2 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steinwurf/endian/HEAD/waf -------------------------------------------------------------------------------- /endian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steinwurf/endian/HEAD/endian.png -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | .. include:: ../LICENSE.rst 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/user_api/big_endian.rst: -------------------------------------------------------------------------------- 1 | .. wurfapi:: class_synopsis.rst 2 | :selector: big_endian 3 | -------------------------------------------------------------------------------- /docs/user_api/stream_writer.rst: -------------------------------------------------------------------------------- 1 | .. wurfapi:: class_synopsis.rst 2 | :selector: stream_writer -------------------------------------------------------------------------------- /docs/user_api/little_endian.rst: -------------------------------------------------------------------------------- 1 | .. wurfapi:: class_synopsis.rst 2 | :selector: little_endian 3 | -------------------------------------------------------------------------------- /docs/user_api/stream_reader.rst: -------------------------------------------------------------------------------- 1 | .. wurfapi:: class_synopsis.rst 2 | :selector: stream_reader 3 | 4 | -------------------------------------------------------------------------------- /docs/requirements.in: -------------------------------------------------------------------------------- 1 | sphinx 2 | versjon 3 | wurfapi 4 | git+https://github.com/steinwurf/guzzle_sphinx_theme@53265a5ccd929ad63b7b0bff029c634be4977a43 5 | giit 6 | -------------------------------------------------------------------------------- /docs/user_api/is_big_endian.rst: -------------------------------------------------------------------------------- 1 | function is_big_endian() 2 | ======================== 3 | 4 | .. wurfapi:: function_synopsis.rst 5 | :selector: is_big_endian() 6 | 7 | -------------------------------------------------------------------------------- /docs/examples/examples.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Examples 3 | ======== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | convert_to_little_endian 9 | stream_writer_reader 10 | network 11 | -------------------------------------------------------------------------------- /docs/examples/network.rst: -------------------------------------------------------------------------------- 1 | Network 2 | ======================== 3 | 4 | This basic example shows how to use the network alias. 5 | 6 | .. literalinclude:: ../../examples/network.cpp 7 | :language: c++ 8 | :linenos: 9 | -------------------------------------------------------------------------------- /test/wscript_build: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | bld.program( 4 | features='cxx test', 5 | source=['endian_tests.cpp'] + bld.path.ant_glob('src/*.cpp'), 6 | target='endian_tests', 7 | use=['endian_includes', 'gtest']) 8 | -------------------------------------------------------------------------------- /docs/examples/stream_writer_reader.rst: -------------------------------------------------------------------------------- 1 | Stream writer and reader 2 | ======================== 3 | 4 | This basic example shows how to use the stream writer and reader. 5 | 6 | .. literalinclude:: ../../examples/stream_writer_reader.cpp 7 | :language: c++ 8 | :linenos: 9 | -------------------------------------------------------------------------------- /docs/user_api/user_api.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _user_api: 3 | 4 | ======== 5 | User API 6 | ======== 7 | 8 | Overview of the public API. 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | is_big_endian 14 | big_endian 15 | little_endian 16 | stream_reader 17 | stream_writer 18 | network 19 | 20 | -------------------------------------------------------------------------------- /docs/examples/convert_to_little_endian.rst: -------------------------------------------------------------------------------- 1 | Convert to little endian 2 | ======================== 3 | 4 | This basic example shows how to convert to little endian. 5 | 6 | The complete example is shown below. 7 | 8 | .. literalinclude:: ../../examples/convert_to_little_endian.cpp 9 | :language: c++ 10 | :linenos: 11 | -------------------------------------------------------------------------------- /test/endian_tests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2016. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | GTEST_API_ int main(int argc, char** argv) 12 | { 13 | srand(static_cast(time(0))); 14 | 15 | testing::InitGoogleTest(&argc, argv); 16 | return RUN_ALL_TESTS(); 17 | } 18 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Endian Documentation 2 | ==================== 3 | 4 | endian is a simple C++ library for conversion between big and little endian data 5 | representations and provide stream-like interface for accessing a fixed-size 6 | buffer. 7 | 8 | 9 | 10 | 11 | Table of Contents 12 | ----------------- 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | 17 | user_api/user_api 18 | examples/examples 19 | license 20 | 21 | 22 | .. toctree:: 23 | :maxdepth: 1 24 | 25 | news 26 | -------------------------------------------------------------------------------- /examples/wscript_build: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | bld.program( 4 | features='cxx', 5 | source=['convert_to_little_endian.cpp'], 6 | target='convert_little_endian_to_big', 7 | use=['endian_includes']) 8 | 9 | 10 | bld.program( 11 | features='cxx', 12 | source=['stream_writer_reader.cpp'], 13 | target='stream_writer_reader', 14 | use=['endian_includes']) 15 | 16 | bld.program( 17 | features='cxx', 18 | source=['network.cpp'], 19 | target='network', 20 | use=['endian_includes']) 21 | -------------------------------------------------------------------------------- /resolve.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "gtest", 4 | "internal": true, 5 | "resolver": "git", 6 | "method": "semver", 7 | "major": 6, 8 | "sources": [ 9 | "github.com/steinwurf/gtest.git" 10 | ] 11 | }, 12 | { 13 | "name": "toolchains", 14 | "internal": true, 15 | "resolver": "git", 16 | "method": "semver", 17 | "major": 1, 18 | "sources": [ 19 | "github.com/steinwurf/cmake-toolchains.git" 20 | ] 21 | } 22 | ] -------------------------------------------------------------------------------- /.github/gcc-problem-matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "gcc-problem-matcher", 5 | "severity": "error", 6 | "pattern": [ 7 | { 8 | "regexp": "^../../(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", 9 | "file": 1, 10 | "line": 2, 11 | "column": 3, 12 | "severity": 4, 13 | "message": 5 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AlignAfterOpenBracket: Align 3 | AlignEscapedNewlinesLeft: 'true' 4 | AlignOperands: 'true' 5 | AlwaysBreakTemplateDeclarations: 'true' 6 | AccessModifierOffset: -4 7 | BreakBeforeBraces: Allman 8 | Standard: Cpp11 9 | IndentWidth: 4 10 | IndentCaseLabels: 'false' 11 | PointerAlignment: Left 12 | TabWidth: 4 13 | UseTab: Never 14 | AllowShortFunctionsOnASingleLine: None 15 | AllowAllParametersOfDeclarationOnNextLine: 'true' 16 | FixNamespaceComments: 'false' 17 | BreakConstructorInitializers: AfterColon 18 | ContinuationIndentWidth: 4 19 | Cpp11BracedListStyle: 'true' 20 | ... 21 | -------------------------------------------------------------------------------- /src/endian/is_big_endian.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2016. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace endian 11 | { 12 | /// Checks if the platform is big- or little-endian. 13 | /// 14 | /// From a test proposed here: 15 | /// http://stackoverflow.com/questions/1001307/ 16 | /// 17 | /// @return True if the platform is big endian otherwise false. 18 | inline bool is_big_endian() 19 | { 20 | union 21 | { 22 | uint32_t i; 23 | uint8_t c[4]; 24 | } test = {0x01020304}; 25 | 26 | return test.c[0] == 1; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /giit.json: -------------------------------------------------------------------------------- 1 | { 2 | "sphinx": [ 3 | { 4 | "scripts": [ 5 | "sphinx-build -b html -D version=${release} . ${build_path}/${release}" 6 | ], 7 | "requirements": "${source_path}/docs/requirements.txt", 8 | "cwd": "${source_path}/docs", 9 | "tags.semver.filters": [ 10 | ">12.0.0" 11 | ], 12 | "workingtree": true, 13 | "variables": { 14 | "tag:release": "${name}", 15 | "workingtree:release": "latest" 16 | } 17 | }, 18 | { 19 | "scripts": [ 20 | "versjon --docs_path ${build_path}" 21 | ] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /src/endian/network.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Steinwurf ApS 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | #include "big_endian.hpp" 8 | 9 | namespace endian 10 | { 11 | /// https://en.wikipedia.org/wiki/Endianness#Networking 12 | /// "Many IETF RFCs use the term network order, meaning the order of 13 | /// transmission for bits and bytes over the wire in network protocols. Among 14 | /// others, the historic RFC 1700 (also known as Internet standard STD 2) has 15 | /// defined the network order for protocols in the Internet protocol suite to be 16 | /// big-endian, hence the use of the term "network byte order" for big-endian 17 | /// byte order." 18 | /// 19 | /// https://datatracker.ietf.org/doc/html/rfc1700#page-3 20 | using network = big_endian; 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | 9 | # Compiled Static libraries 10 | *.lai 11 | *.la 12 | *.a 13 | 14 | # Python 15 | *.pyc 16 | 17 | # Compiled Doxygen documentation 18 | /doxygen/html 19 | 20 | # Waf files 21 | waf-* 22 | waf3-* 23 | .waf-* 24 | .waf3-* 25 | .lock-* 26 | build 27 | build_current 28 | build_cmake 29 | resolve_symlinks 30 | resolved_dependencies 31 | 32 | # Gnu Global tag files 33 | GPATH 34 | GRTAGS 35 | GSYMS 36 | GTAGS 37 | 38 | # Emacs temp / auto save 39 | \#*# 40 | *.#* 41 | *~ 42 | 43 | #Eclipse ignore 44 | .cproject 45 | *.project 46 | .metadata 47 | local.properties 48 | .classpath 49 | .settings/ 50 | 51 | # Visual Studio ignore 52 | *.bat 53 | *.sln 54 | *.suo 55 | *.user 56 | *.ncb 57 | *.sdf 58 | *.opensdf 59 | *.log 60 | *.vcxproj* 61 | VSProjects 62 | -------------------------------------------------------------------------------- /examples/stream_writer_reader.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2016. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | uint16_t a = 111, b = 222; 13 | uint16_t x, y; 14 | 15 | x = y = 0; 16 | std::vector stream_buffer(sizeof(uint16_t) * 2); 17 | endian::stream_writer writer(stream_buffer.data(), 18 | stream_buffer.size()); 19 | writer << a << b; 20 | 21 | endian::stream_reader reader(stream_buffer.data(), 22 | stream_buffer.size()); 23 | reader >> x >> y; 24 | assert(x == a && y == b); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /docs/user_api/network.rst: -------------------------------------------------------------------------------- 1 | Network 2 | ======= 3 | 4 | Many IETF RFCs use the term network order, meaning the order of transmission for bits and bytes over the wire in network 5 | protocols. Among others, the historic RFC 1700 (also known as Internet standard STD 2) has defined the network order for 6 | protocols in the Internet protocol suite to be big-endian, hence the use of the term "network byte order" for big-endian 7 | byte order. 8 | 9 | | ``_ 10 | | ``_ 11 | 12 | For this reason, there is a network alias that can be used instead of having to remember what the standard is. 13 | 14 | See the example below for how to use it, but basically, you just need to replace either ``big_endian`` or 15 | ``little_endian`` with ``network``. 16 | 17 | .. literalinclude:: ../../examples/network.cpp 18 | :language: c++ 19 | :linenos: 20 | -------------------------------------------------------------------------------- /examples/network.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2016. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | uint16_t a = 111, b = 222; 13 | uint16_t x, y; 14 | 15 | auto buffer = new uint8_t[sizeof(uint16_t) * 2]; 16 | 17 | // Usage: `put` and `get` 18 | endian::network::put(a, buffer); 19 | endian::network::put(b, buffer + sizeof(a)); 20 | 21 | endian::network::get(x, buffer); 22 | y = endian::network::get(buffer + sizeof(x)); 23 | assert(x == a && y == b); 24 | 25 | // Usage: `stream_writer` and `stream_reader` 26 | x = y = 0; 27 | std::vector stream_buffer(sizeof(uint16_t) * 2); 28 | endian::stream_writer writer(stream_buffer.data(), 29 | stream_buffer.size()); 30 | writer << a << b; 31 | 32 | endian::stream_reader reader(stream_buffer.data(), 33 | stream_buffer.size()); 34 | reader >> x >> y; 35 | assert(x == a && y == b); 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /examples/convert_to_little_endian.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2016. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | int main() 12 | { 13 | if (endian::is_big_endian()) 14 | { 15 | std::cout << "This machine is big endian." << std::endl; 16 | } 17 | else 18 | { 19 | std::cout << "This machine is little endian." << std::endl; 20 | } 21 | uint8_t data[4]; 22 | uint32_t input = 0x11223344U; 23 | 24 | std::cout << "input is: " << std::hex << input << std::endl; 25 | 26 | // If the host is big endian, the put function 27 | // should change the byte order (no change for little endian) 28 | endian::little_endian::put(input, data); 29 | 30 | // This operation could also have been performed with: 31 | // endian::little_endian::put_bytes<4>(input, data); 32 | 33 | if (0x11u == data[3] && 0x22u == data[2] && 0x33u == data[1] && 34 | 0x44u == data[0]) 35 | { 36 | std::cout << "Success input converted" << std::endl; 37 | } 38 | else 39 | { 40 | std::cout << "Something went wrong" << std::endl; 41 | } 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # encoding: utf-8 3 | 4 | import os 5 | import hashlib 6 | import shutil 7 | import platform 8 | import waflib 9 | import pathlib 10 | import subprocess 11 | 12 | from waflib.Build import BuildContext 13 | 14 | APPNAME = "endian" 15 | VERSION = "14.0.0" 16 | 17 | 18 | def options(opt): 19 | opt.load("cmake") 20 | 21 | 22 | def configure(conf): 23 | conf.load("cmake") 24 | 25 | if conf.is_toplevel(): 26 | conf.cmake_configure() 27 | 28 | 29 | def build(bld): 30 | bld.load("cmake") 31 | 32 | if bld.is_toplevel(): 33 | bld.cmake_build() 34 | 35 | 36 | def docs(ctx): 37 | """Build the documentation in a virtualenv""" 38 | 39 | with ctx.create_virtualenv() as venv: 40 | # To update the requirements.txt just delete it - a fresh one 41 | # will be generated from test/requirements.in 42 | if not os.path.isfile("docs/requirements.txt"): 43 | venv.run("python -m pip install pip-tools") 44 | venv.run("pip-compile docs/requirements.in") 45 | 46 | venv.run("python -m pip install -r docs/requirements.txt") 47 | 48 | build_path = os.path.join(ctx.path.abspath(), "build", "site", "docs") 49 | 50 | venv.run( 51 | "giit clean . --build_path {}".format(build_path), cwd=ctx.path.abspath() 52 | ) 53 | venv.run( 54 | "giit sphinx . --build_path {}".format(build_path), cwd=ctx.path.abspath() 55 | ) 56 | -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Steinwurf ApS 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of endian nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/endian/detail/helpers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Steinwurf ApS 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | namespace endian 11 | { 12 | namespace detail 13 | { 14 | 15 | // Helper to check that unsigned values can fit in the bytes 16 | template 17 | struct check 18 | { 19 | static bool value(ValueType value) 20 | { 21 | (void)value; 22 | return (sizeof(ValueType) == Bytes); 23 | } 24 | }; 25 | 26 | template 27 | struct check 28 | { 29 | static bool value(uint8_t value) 30 | { 31 | (void)value; 32 | // 8 bit values can always fit in 8 bits 33 | return true; 34 | } 35 | }; 36 | 37 | template 38 | struct check 39 | { 40 | static bool value(ValueType value) 41 | { 42 | return value <= 0xFFFFFF; 43 | } 44 | }; 45 | 46 | template 47 | struct check 48 | { 49 | static bool value(ValueType value) 50 | { 51 | return value <= 0xFFFFFFFFFF; 52 | } 53 | }; 54 | 55 | template 56 | struct check 57 | { 58 | static bool value(ValueType value) 59 | { 60 | return value <= 0xFFFFFFFFFFFF; 61 | } 62 | }; 63 | 64 | template 65 | struct check 66 | { 67 | static bool value(ValueType value) 68 | { 69 | return value <= 0xFFFFFFFFFFFFFF; 70 | } 71 | }; 72 | 73 | // Helper to convet floating point type into identically sized unsigned integer 74 | template 75 | struct floating_point 76 | { 77 | }; 78 | 79 | template <> 80 | struct floating_point 81 | { 82 | static_assert(sizeof(float) == 4, "Float type must have a size of 4 bytes"); 83 | using UnsignedType = uint32_t; 84 | }; 85 | 86 | template <> 87 | struct floating_point 88 | { 89 | static_assert(sizeof(double) == 8, 90 | "Float type must have a size of 8 bytes"); 91 | using UnsignedType = uint64_t; 92 | }; 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(sw_endian) 3 | 4 | 5 | # Use waf to resolve dependencies 6 | if(NOT DEFINED STEINWURF_RESOLVE) 7 | message(STATUS "Resolving dependencies...") 8 | execute_process( 9 | COMMAND ${Python_EXECUTABLE} waf resolve ${STEINWURF_RESOLVE_OPTIONS} 10 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 11 | RESULT_VARIABLE STATUS) 12 | 13 | if(STATUS AND NOT STATUS EQUAL 0) 14 | message(FATAL_ERROR "Failed: ${STATUS}") 15 | endif() 16 | 17 | set(STEINWURF_RESOLVE "${CMAKE_CURRENT_SOURCE_DIR}/resolve_symlinks") 18 | endif() 19 | 20 | 21 | # Include common CMake settings 22 | include("${STEINWURF_RESOLVE}/toolchains/common_settings.cmake") 23 | 24 | 25 | # Define library 26 | add_library(sw_endian INTERFACE) 27 | target_compile_features(sw_endian INTERFACE cxx_std_14) 28 | target_include_directories(sw_endian INTERFACE src/) 29 | add_library(steinwurf::endian ALIAS sw_endian) 30 | 31 | # Install headers 32 | install( 33 | DIRECTORY ./src/endian 34 | DESTINATION ${CMAKE_INSTALL_PREFIX}/include 35 | FILES_MATCHING 36 | PATTERN *.hpp) 37 | 38 | # Is top level project? 39 | if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) 40 | 41 | # Include gtest 42 | if(NOT TARGET steinwurf::gtest) 43 | add_subdirectory("${STEINWURF_RESOLVE}/gtest" EXCLUDE_FROM_ALL) 44 | endif() 45 | 46 | file(GLOB_RECURSE endian_test_sources test/**.cpp) 47 | add_executable(sw_endian_tests ${endian_test_sources}) 48 | target_link_libraries(sw_endian_tests ${steinwurf_object_libraries} 49 | steinwurf::gtest steinwurf::endian) 50 | 51 | enable_testing() 52 | add_test(NAME sw_endian_tests COMMAND sw_endian_tests) 53 | 54 | 55 | # Convert to little endian 56 | add_executable(sw_endian_example_convert_to_little_endian 57 | examples/convert_to_little_endian.cpp) 58 | target_link_libraries(sw_endian_example_convert_to_little_endian 59 | ${steinwurf_object_libraries} steinwurf::endian) 60 | 61 | # Stream writer reader 62 | add_executable(sw_endian_example_stream_writer_reader 63 | examples/stream_writer_reader.cpp) 64 | target_link_libraries(sw_endian_example_stream_writer_reader 65 | ${steinwurf_object_libraries} steinwurf::endian) 66 | 67 | # Network 68 | add_executable(sw_endian_example_network examples/network.cpp) 69 | target_link_libraries(sw_endian_example_network ${steinwurf_object_libraries} 70 | steinwurf::endian) 71 | 72 | endif() 73 | -------------------------------------------------------------------------------- /src/endian/stream_writer.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Steinwurf ApS 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "detail/stream.hpp" 13 | 14 | namespace endian 15 | { 16 | /// The stream_writer provides a stream-like interface for writing to a fixed 17 | /// size buffer. All complexity regarding endianness is encapsulated. 18 | template 19 | class stream_writer : public detail::stream 20 | { 21 | public: 22 | /// Creates an endian stream on top of a pre-allocated buffer of the 23 | /// specified size. 24 | /// 25 | /// @param data a data pointer to the buffer 26 | /// @param size the size of the buffer in bytes 27 | stream_writer(uint8_t* data, std::size_t size) noexcept : stream(data, size) 28 | { 29 | } 30 | 31 | /// Writes a Bytes-sized integer to the stream. 32 | /// 33 | /// @param value the value to write. 34 | template 35 | void write_bytes(ValueType value) noexcept 36 | { 37 | assert(Bytes <= remaining_size()); 38 | 39 | EndianType::template put_bytes(value, this->remaining_data()); 40 | skip(Bytes); 41 | } 42 | 43 | /// Writes a Bytes-sized integer to the stream. 44 | /// 45 | /// @param value the value to write. 46 | template 47 | void write(ValueType value) noexcept 48 | { 49 | assert(sizeof(ValueType) <= remaining_size()); 50 | 51 | write_bytes(value); 52 | } 53 | 54 | /// Writes the raw bytes represented by the storage::const_storage 55 | /// object to the stream. 56 | /// 57 | /// Note, that this function is provided only for convenience and 58 | /// it does not perform any endian conversions. 59 | /// 60 | /// @param data Pointer to the data, to be written to the stream. 61 | /// @param size Number of bytes from the data pointer. 62 | void write(const uint8_t* data, std::size_t size) noexcept 63 | { 64 | assert(size <= remaining_size()); 65 | 66 | std::copy_n(data, size, this->remaining_data()); 67 | skip(size); 68 | } 69 | 70 | /// Operator for writing given value to the end of the stream. 71 | /// 72 | /// @param value the value to write. 73 | template 74 | stream_writer& operator<<(ValueType value) 75 | { 76 | write(value); 77 | return *this; 78 | } 79 | }; 80 | } 81 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.9 3 | # by the following command: 4 | # 5 | # pip-compile docs/requirements.in 6 | # 7 | alabaster==0.7.12 8 | # via sphinx 9 | attrs==22.1.0 10 | # via 11 | # jsonschema 12 | # sphobjinv 13 | babel==2.11.0 14 | # via sphinx 15 | beautifulsoup4==4.11.1 16 | # via versjon 17 | certifi==2022.9.24 18 | # via 19 | # requests 20 | # sphobjinv 21 | charset-normalizer==2.1.1 22 | # via requests 23 | click==8.1.3 24 | # via 25 | # giit 26 | # versjon 27 | colorama==0.4.6 28 | # via 29 | # giit 30 | # versjon 31 | contextlib2==21.6.0 32 | # via schema 33 | cssselect==1.2.0 34 | # via pyquery 35 | distlib==0.3.6 36 | # via virtualenv 37 | docutils==0.19 38 | # via sphinx 39 | filelock==3.8.0 40 | # via virtualenv 41 | giit==8.0.0 42 | # via -r docs/requirements.in 43 | guzzle-sphinx-theme @ git+https://github.com/steinwurf/guzzle_sphinx_theme@53265a5ccd929ad63b7b0bff029c634be4977a43 44 | # via -r docs/requirements.in 45 | idna==3.4 46 | # via requests 47 | imagesize==1.4.1 48 | # via sphinx 49 | importlib-metadata==5.1.0 50 | # via sphinx 51 | jinja2==3.1.2 52 | # via sphinx 53 | jsonschema==4.17.3 54 | # via sphobjinv 55 | lxml==4.9.1 56 | # via pyquery 57 | markupsafe==2.1.1 58 | # via jinja2 59 | packaging==21.3 60 | # via sphinx 61 | platformdirs==2.5.4 62 | # via virtualenv 63 | pygments==2.13.0 64 | # via sphinx 65 | pyparsing==3.0.9 66 | # via packaging 67 | pyquery==1.4.3 68 | # via wurfapi 69 | pyrsistent==0.19.2 70 | # via jsonschema 71 | python-archive==0.2 72 | # via wurfapi 73 | python-slugify==7.0.0 74 | # via wurfapi 75 | pytz==2022.6 76 | # via babel 77 | requests==2.28.1 78 | # via sphinx 79 | schema==0.7.5 80 | # via 81 | # giit 82 | # wurfapi 83 | semantic-version==2.10.0 84 | # via 85 | # giit 86 | # versjon 87 | six==1.16.0 88 | # via 89 | # giit 90 | # wurfapi 91 | snowballstemmer==2.2.0 92 | # via sphinx 93 | soupsieve==2.3.2.post1 94 | # via beautifulsoup4 95 | sphinx==5.3.0 96 | # via 97 | # -r docs/requirements.in 98 | # guzzle-sphinx-theme 99 | # wurfapi 100 | sphinxcontrib-applehelp==1.0.2 101 | # via sphinx 102 | sphinxcontrib-devhelp==1.0.2 103 | # via sphinx 104 | sphinxcontrib-htmlhelp==2.0.0 105 | # via sphinx 106 | sphinxcontrib-jsmath==1.0.1 107 | # via sphinx 108 | sphinxcontrib-qthelp==1.0.3 109 | # via sphinx 110 | sphinxcontrib-serializinghtml==1.1.5 111 | # via sphinx 112 | sphobjinv==2.3.1 113 | # via versjon 114 | text-unidecode==1.3 115 | # via python-slugify 116 | urllib3==1.26.13 117 | # via requests 118 | versjon==2.2.0 119 | # via -r docs/requirements.in 120 | virtualenv==20.17.0 121 | # via giit 122 | wurfapi==9.0.0 123 | # via -r docs/requirements.in 124 | zipp==3.11.0 125 | # via importlib-metadata 126 | -------------------------------------------------------------------------------- /src/endian/detail/stream.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Steinwurf ApS 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace endian 15 | { 16 | namespace detail 17 | { 18 | 19 | using non_const_stream = uint8_t*; 20 | using const_stream = const uint8_t*; 21 | 22 | /// @brief Base-class for the endian stream reader and writer. 23 | template 24 | class stream 25 | { 26 | public: 27 | /// Creates an endian stream used to track a buffer of the specified size. 28 | /// 29 | /// @param size the size of the buffer in bytes 30 | stream(data_ptr_type data, std::size_t size) noexcept : 31 | m_data(data), m_size(size) 32 | { 33 | } 34 | 35 | /// Gets the size of the underlying buffer in bytes. 36 | /// 37 | /// @return the size of the buffer 38 | std::size_t size() const noexcept 39 | { 40 | return m_size; 41 | } 42 | 43 | /// Gets the current read/write position in the stream 44 | /// 45 | /// @return the current position. 46 | std::size_t position() const noexcept 47 | { 48 | return m_position; 49 | } 50 | 51 | /// The remaining number of bytes in the stream 52 | /// 53 | /// @return the remaining number of bytes. 54 | std::size_t remaining_size() const noexcept 55 | { 56 | return m_size - m_position; 57 | } 58 | 59 | /// Changes the current read/write position in the stream. The 60 | /// position is absolute i.e. it is always relative to the 61 | /// beginning of the buffer which is position 0. 62 | /// 63 | /// @param new_position the new position 64 | void seek(std::size_t new_position) noexcept 65 | { 66 | assert(new_position <= m_size); 67 | 68 | m_position = new_position; 69 | } 70 | 71 | /// Skips over a given number of bytes in the stream 72 | /// 73 | /// @param bytes_to_skip the bytes to skip 74 | void skip(std::size_t bytes_to_skip) noexcept 75 | { 76 | assert(bytes_to_skip <= m_size - m_position); 77 | 78 | seek(m_position + bytes_to_skip); 79 | } 80 | 81 | /// A pointer to the stream's data. 82 | /// 83 | /// @return pointer to the stream's data. 84 | data_ptr_type data() const noexcept 85 | { 86 | return m_data; 87 | } 88 | 89 | /// A pointer to the stream's data at the current position. 90 | /// 91 | /// @return pointer to the stream's data at the current position. 92 | data_ptr_type remaining_data() const noexcept 93 | { 94 | return m_data + m_position; 95 | } 96 | 97 | private: 98 | /// Data pointer to buffer 99 | data_ptr_type m_data; 100 | 101 | /// The size of the buffer in bytes 102 | std::size_t m_size; 103 | 104 | /// The current position 105 | std::size_t m_position = 0; 106 | }; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /test/src/test_stream_writer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2018. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | template 17 | static void test_basic_api() 18 | { 19 | { 20 | SCOPED_TRACE(testing::Message() << "size 1"); 21 | std::size_t size = 1U; 22 | std::vector buffer(size); 23 | endian::stream_writer stream(buffer.data(), buffer.size()); 24 | 25 | // check initial state 26 | EXPECT_EQ(size, stream.size()); 27 | EXPECT_EQ(0U, stream.position()); 28 | EXPECT_EQ(size, stream.remaining_size()); 29 | EXPECT_EQ(buffer.data(), stream.remaining_data()); 30 | EXPECT_EQ(buffer.data(), stream.data()); 31 | 32 | // check state after seek 33 | stream.seek(1); 34 | EXPECT_EQ(size, stream.size()); 35 | EXPECT_EQ(1U, stream.position()); 36 | EXPECT_EQ(0U, stream.remaining_size()); 37 | EXPECT_EQ(buffer.data() + 1, stream.remaining_data()); 38 | EXPECT_EQ(buffer.data(), stream.data()); 39 | } 40 | 41 | { 42 | SCOPED_TRACE(testing::Message() << "size 1000"); 43 | std::size_t size = 1000U; 44 | std::vector buffer(size); 45 | endian::stream_writer stream(buffer.data(), buffer.size()); 46 | 47 | // check initial state 48 | EXPECT_EQ(size, stream.size()); 49 | EXPECT_EQ(0U, stream.position()); 50 | EXPECT_EQ(size, stream.remaining_size()); 51 | EXPECT_EQ(buffer.data(), stream.remaining_data()); 52 | EXPECT_EQ(buffer.data(), stream.data()); 53 | 54 | // check state after seek 55 | stream.seek(size / 2); 56 | EXPECT_EQ(size, stream.size()); 57 | EXPECT_EQ(size / 2, stream.position()); 58 | EXPECT_EQ(size / 2, stream.remaining_size()); 59 | EXPECT_EQ(buffer.data() + size / 2, stream.remaining_data()); 60 | EXPECT_EQ(buffer.data(), stream.data()); 61 | 62 | // that consecutive seeks doesn't alter state. 63 | stream.seek(size / 2); 64 | stream.seek(size / 2); 65 | stream.seek(size / 2); 66 | EXPECT_EQ(size, stream.size()); 67 | EXPECT_EQ(size / 2, stream.position()); 68 | EXPECT_EQ(size / 2, stream.remaining_size()); 69 | EXPECT_EQ(buffer.data() + size / 2, stream.remaining_data()); 70 | EXPECT_EQ(buffer.data(), stream.data()); 71 | 72 | // seek to end 73 | stream.seek(size); 74 | EXPECT_EQ(size, stream.position()); 75 | EXPECT_EQ(0U, stream.remaining_size()); 76 | EXPECT_EQ(buffer.data() + size, stream.remaining_data()); 77 | EXPECT_EQ(buffer.data(), stream.data()); 78 | } 79 | 80 | { 81 | SCOPED_TRACE(testing::Message() << "vector vs pointer"); 82 | 83 | std::size_t size = 10U; 84 | std::vector buffer(size); 85 | endian::stream_writer stream1(buffer.data(), buffer.size()); 86 | endian::stream_writer stream2(buffer.data(), buffer.size()); 87 | EXPECT_EQ(stream1.size(), stream2.size()); 88 | EXPECT_EQ(stream1.data(), stream2.data()); 89 | } 90 | } 91 | 92 | TEST(test_stream_writer, basic_api_little_endian) 93 | { 94 | test_basic_api(); 95 | } 96 | 97 | TEST(test_stream_writer, basic_api_big_endian) 98 | { 99 | test_basic_api(); 100 | } 101 | -------------------------------------------------------------------------------- /test/src/detail/test_stream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2018. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | TEST(test_stream, basicbuffer) 14 | { 15 | std::vector buffer(1); 16 | endian::detail::stream stream(buffer.data(), buffer.size()); 17 | 18 | // check initial state 19 | EXPECT_EQ(buffer.size(), stream.size()); 20 | EXPECT_EQ(0U, stream.position()); 21 | EXPECT_EQ(buffer.size(), stream.remaining_size()); 22 | 23 | // check state after skip 24 | stream.skip(1); 25 | EXPECT_EQ(buffer.size(), stream.size()); 26 | EXPECT_EQ(1U, stream.position()); 27 | EXPECT_EQ(0U, stream.remaining_size()); 28 | } 29 | 30 | TEST(test_stream, seek) 31 | { 32 | SCOPED_TRACE(testing::Message() << "size 10000"); 33 | std::vector buffer(10000U); 34 | endian::detail::stream stream(buffer.data(), buffer.size()); 35 | 36 | // check initial state 37 | EXPECT_EQ(buffer.size(), stream.size()); 38 | EXPECT_EQ(0U, stream.position()); 39 | EXPECT_EQ(buffer.size(), stream.remaining_size()); 40 | 41 | // check state after seek 42 | stream.seek(buffer.size() / 2); 43 | EXPECT_EQ(buffer.size(), stream.size()); 44 | EXPECT_EQ(buffer.size() / 2, stream.position()); 45 | EXPECT_EQ(buffer.size() / 2, stream.remaining_size()); 46 | 47 | // that consecutive seeks doesn't alter state. 48 | stream.seek(buffer.size() / 2); 49 | stream.seek(buffer.size() / 2); 50 | stream.seek(buffer.size() / 2); 51 | EXPECT_EQ(buffer.size(), stream.size()); 52 | EXPECT_EQ(buffer.size() / 2, stream.position()); 53 | EXPECT_EQ(buffer.size() / 2, stream.remaining_size()); 54 | 55 | // seek to end 56 | stream.seek(buffer.size()); 57 | EXPECT_EQ(buffer.size(), stream.position()); 58 | EXPECT_EQ(0U, stream.remaining_size()); 59 | } 60 | 61 | TEST(test_stream, skip) 62 | { 63 | SCOPED_TRACE(testing::Message() << "size 10000"); 64 | std::vector buffer(10000U); 65 | endian::detail::stream stream(buffer.data(), buffer.size()); 66 | 67 | // check initial state 68 | EXPECT_EQ(buffer.size(), stream.size()); 69 | EXPECT_EQ(0U, stream.position()); 70 | EXPECT_EQ(buffer.size(), stream.remaining_size()); 71 | 72 | // check state after skip 73 | stream.skip(buffer.size() / 2); 74 | EXPECT_EQ(buffer.size(), stream.size()); 75 | EXPECT_EQ(buffer.size() / 2, stream.position()); 76 | EXPECT_EQ(buffer.size() / 2, stream.remaining_size()); 77 | stream.skip(buffer.size() / 2); 78 | EXPECT_EQ(buffer.size(), stream.size()); 79 | EXPECT_EQ(buffer.size(), stream.position()); 80 | EXPECT_EQ(0U, stream.remaining_size()); 81 | 82 | // go back and skip each byte until the end 83 | stream.seek(0); 84 | for (std::size_t i = 0; i < buffer.size(); ++i) 85 | { 86 | EXPECT_EQ(buffer.size() - i, stream.remaining_size()); 87 | ASSERT_NE(0U, stream.remaining_size()); 88 | stream.skip(1); 89 | } 90 | 91 | EXPECT_EQ(buffer.size(), stream.position()); 92 | EXPECT_EQ(0U, stream.remaining_size()); 93 | } 94 | 95 | TEST(test_stream, empty) 96 | { 97 | std::vector buffer; 98 | endian::detail::stream stream(buffer.data(), buffer.size()); 99 | EXPECT_EQ(0U, stream.size()); 100 | EXPECT_EQ(0U, stream.position()); 101 | EXPECT_EQ(0U, stream.remaining_size()); 102 | EXPECT_EQ(nullptr, stream.data()); 103 | EXPECT_EQ(nullptr, stream.remaining_data()); 104 | stream.seek(0); 105 | EXPECT_EQ(0U, stream.position()); 106 | stream.skip(0); 107 | EXPECT_EQ(0U, stream.position()); 108 | } 109 | -------------------------------------------------------------------------------- /src/endian/little_endian.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Steinwurf ApS 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "detail/helpers.hpp" 13 | #include "detail/little.hpp" 14 | 15 | namespace endian 16 | { 17 | 18 | /// Inserts and extracts integers in little-endian format. 19 | struct little_endian 20 | { 21 | /// Inserts a ValueType-sized value into the data buffer. 22 | /// @param value to put in the data buffer 23 | /// @param buffer pointer to the data buffer 24 | template 25 | static void put(ValueType value, uint8_t* buffer) 26 | { 27 | assert(buffer != nullptr && "Nullpointer provided"); 28 | 29 | detail::little::put(value, buffer); 30 | } 31 | 32 | /// Gets a ValueType-sized integer value from a data buffer. 33 | /// @param value variable where to get the value 34 | /// @param buffer pointer to the data buffer 35 | template 36 | static void get(ValueType& value, const uint8_t* buffer) 37 | { 38 | assert(buffer != nullptr && "Nullpointer provided"); 39 | 40 | value = 0; 41 | detail::little::get(value, buffer); 42 | } 43 | 44 | /// Gets a ValueType-sized integer value from a data buffer. 45 | /// @return value variable where to get the value 46 | /// @param buffer pointer to the data buffer 47 | template 48 | static ValueType get(const uint8_t* buffer) 49 | { 50 | assert(buffer != nullptr && "Nullpointer provided"); 51 | 52 | ValueType value = 0; 53 | get(value, buffer); 54 | return value; 55 | } 56 | 57 | /// Inserts a Bytes-sized integer value into the data buffer. 58 | /// @param value to put in the data buffer 59 | /// @param buffer pointer to the data buffer 60 | template 61 | static void put_bytes(ValueType value, uint8_t* buffer) 62 | { 63 | static_assert(Bytes == sizeof(ValueType) || 64 | std::is_unsigned::value, 65 | "Must be unsigned"); 66 | assert(buffer != nullptr && "Nullpointer provided"); 67 | assert((detail::check::value(value)) && 68 | "Value too big to fit in the provided bytes"); 69 | 70 | detail::little::put(value, buffer); 71 | } 72 | 73 | /// Gets a Bytes-sized integer value from a data buffer. 74 | /// @param value variable where to get the value 75 | /// @param buffer pointer to the data buffer 76 | template 77 | static void get_bytes(ValueType& value, const uint8_t* buffer) 78 | { 79 | static_assert(Bytes == sizeof(ValueType) || 80 | std::is_unsigned::value, 81 | "Must be unsigned"); 82 | static_assert(sizeof(ValueType) >= Bytes, "ValueType too small"); 83 | assert(buffer != nullptr && "Nullpointer provided"); 84 | 85 | value = 0; 86 | detail::little::get(value, buffer); 87 | } 88 | 89 | /// Gets a Bytes-sized integer value from a data buffer. 90 | /// @return value variable where to get the value 91 | /// @param buffer pointer to the data buffer 92 | template 93 | static ValueType get_bytes(const uint8_t* buffer) 94 | { 95 | static_assert(Bytes == sizeof(ValueType) || 96 | std::is_unsigned::value, 97 | "Must be unsigned"); 98 | static_assert(sizeof(ValueType) >= Bytes, "ValueType too small"); 99 | assert(buffer != nullptr && "Nullpointer provided"); 100 | 101 | ValueType value = 0; 102 | get_bytes(value, buffer); 103 | return value; 104 | } 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # -- General configuration ------------------------------------------------ 4 | 5 | # Add any Sphinx extension module names here, as strings. They can be 6 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 7 | # ones. 8 | extensions = [ 9 | "sphinx.ext.autodoc", 10 | "sphinx.ext.doctest", 11 | "sphinx.ext.intersphinx", 12 | "sphinx.ext.todo", 13 | "sphinx.ext.coverage", 14 | "sphinx.ext.mathjax", 15 | "sphinx.ext.ifconfig", 16 | "guzzle_sphinx_theme", 17 | "wurfapi", 18 | ] 19 | 20 | # wurfapi options - relative to your docs dir 21 | wurfapi = { 22 | "source_paths": [ 23 | # API 24 | "../src/endian/big_endian.hpp", 25 | "../src/endian/is_big_endian.hpp", 26 | "../src/endian/little_endian.hpp", 27 | "../src/endian/stream_reader.hpp", 28 | "../src/endian/stream_writer.hpp", 29 | ], 30 | "recursive": False, 31 | "include_paths": ["../src"], 32 | "parser": { 33 | "type": "doxygen", 34 | "download": True, 35 | "warnings_as_error": True, 36 | "patch_api": [ 37 | { 38 | "selector": "endian", 39 | "key": "inline", 40 | "value": True, 41 | } 42 | ], 43 | "collapse_inline_namespaces": ["endian"], 44 | }, 45 | } 46 | 47 | # Add any paths that contain templates here, relative to this directory. 48 | templates_path = [] 49 | 50 | # The suffix(es) of source filenames 51 | source_suffix = ".rst" 52 | 53 | # The master toctree document. 54 | master_doc = "index" 55 | 56 | # General information about the project. 57 | project = u"Endian" 58 | copyright = u"2016, Steinwurf" 59 | author = u"Steinwurf" 60 | 61 | # The version info for the project you're documenting, acts as replacement for 62 | # |version| and |release|, also used in various other places throughout the 63 | # built documents. 64 | # 65 | # The short X.Y version. 66 | version = u"" 67 | # The full version, including alpha/beta/rc tags. 68 | release = u"" 69 | 70 | # List of patterns, relative to source directory, that match files and 71 | # directories to ignore when looking for source files. 72 | # This patterns also effect to html_static_path and html_extra_path 73 | exclude_patterns = ["rst_templates"] 74 | 75 | # The name of the Pygments (syntax highlighting) style to use. 76 | pygments_style = "sphinx" 77 | 78 | # -- Options for HTML output ---------------------------------------------- 79 | 80 | try: 81 | import guzzle_sphinx_theme 82 | 83 | html_theme = "guzzle_sphinx_theme" 84 | html_theme_path = guzzle_sphinx_theme.html_theme_path() 85 | except ImportError: 86 | print( 87 | "Unable to import the used theme.\n" 88 | "Please install requirements.txt before building" 89 | ) 90 | pass 91 | 92 | html_sidebars = { 93 | "**": ["logo.html", "logo-text.html", "globaltoc.html", "searchbox.html"] 94 | } 95 | 96 | # Theme options are theme-specific and customize the look and feel of a theme 97 | # further. For a list of options available for each theme, see the 98 | # documentation. 99 | 100 | 101 | # The name of an image file (relative to this directory) to use as a favicon of 102 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 103 | # pixels large. 104 | # 105 | 106 | # Add any paths that contain custom static files (such as style sheets) here, 107 | # relative to this directory. They are copied after the builtin static files, 108 | # so a file named "default.css" will overwrite the builtin "default.css". 109 | html_static_path = [] 110 | 111 | # Add any extra paths that contain custom files (such as robots.txt or 112 | # .htaccess) here, relative to this directory. These files are copied 113 | # directly to the root of the documentation. 114 | # 115 | # html_extra_path = [] 116 | 117 | # If true, the reST sources are included in the HTML build as _sources/name 118 | html_copy_source = False 119 | 120 | # If true, links to the reST sources are added to the pages. 121 | html_show_sourcelink = False 122 | -------------------------------------------------------------------------------- /src/endian/big_endian.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Steinwurf ApS 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "detail/big.hpp" 13 | #include "detail/helpers.hpp" 14 | 15 | namespace endian 16 | { 17 | 18 | /// Inserts and extracts integers in big-endian format. 19 | struct big_endian 20 | { 21 | 22 | /// Inserts a ValueType-sized value into the data buffer. 23 | /// @param value to put in the data buffer 24 | /// @param buffer pointer to the data buffer 25 | template 26 | static void put(ValueType value, uint8_t* buffer) 27 | { 28 | assert(buffer != nullptr && "Nullpointer provided"); 29 | 30 | detail::big::put(value, buffer); 31 | } 32 | 33 | /// Gets a ValueType-sized integer value from a data buffer. 34 | /// @param value variable where to get the value 35 | /// @param buffer pointer to the data buffer 36 | template 37 | static void get(ValueType& value, const uint8_t* buffer) 38 | { 39 | assert(buffer != nullptr && "Nullpointer provided"); 40 | 41 | value = 0; 42 | detail::big::get(value, buffer); 43 | } 44 | 45 | /// Gets a ValueType-sized integer value from a data buffer. 46 | /// @return value variable where to get the value 47 | /// @param buffer pointer to the data buffer 48 | template 49 | static ValueType get(const uint8_t* buffer) 50 | { 51 | assert(buffer != nullptr && "Nullpointer provided"); 52 | 53 | ValueType value = 0; 54 | get(value, buffer); 55 | return value; 56 | } 57 | 58 | /// Inserts a Bytes-sized integer value into the data buffer. 59 | /// @param value to put in the data buffer 60 | /// @param buffer pointer to the data buffer 61 | template 62 | static void put_bytes(ValueType value, uint8_t* buffer) 63 | { 64 | static_assert(Bytes == sizeof(ValueType) || 65 | std::is_unsigned::value, 66 | "Must be unsigned"); 67 | // static_assert(sizeof(ValueType) <= Bytes, "ValueType too large"); 68 | assert(buffer != nullptr && "Nullpointer provided"); 69 | assert((detail::check::value(value)) && 70 | "Value too big to fit in the provided bytes"); 71 | 72 | detail::big::put(value, buffer); 73 | } 74 | 75 | /// Gets a Bytes-sized integer value from a data buffer. 76 | /// @param value variable where to get the value 77 | /// @param buffer pointer to the data buffer 78 | template 79 | static void get_bytes(ValueType& value, const uint8_t* buffer) 80 | { 81 | static_assert(Bytes == sizeof(ValueType) || 82 | std::is_unsigned::value, 83 | "Must be unsigned"); 84 | static_assert(sizeof(ValueType) >= Bytes, "ValueType too small"); 85 | assert(buffer != nullptr && "Nullpointer provided"); 86 | 87 | value = 0; 88 | detail::big::get(value, buffer); 89 | } 90 | 91 | /// Gets a Bytes-sized integer value from a data buffer. 92 | /// @return value variable where to get the value 93 | /// @param buffer pointer to the data buffer 94 | template 95 | static ValueType get_bytes(const uint8_t* buffer) 96 | { 97 | static_assert(Bytes == sizeof(ValueType) || 98 | std::is_unsigned::value, 99 | "Must be unsigned"); 100 | static_assert(sizeof(ValueType) >= Bytes, "ValueType too small"); 101 | assert(buffer != nullptr && "Nullpointer provided"); 102 | 103 | ValueType value = 0; 104 | get_bytes(value, buffer); 105 | return value; 106 | } 107 | }; 108 | } 109 | -------------------------------------------------------------------------------- /src/endian/detail/big.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Steinwurf ApS 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "helpers.hpp" 15 | 16 | namespace endian 17 | { 18 | namespace detail 19 | { 20 | 21 | // Where the actual conversion takes place 22 | template 23 | struct big_impl 24 | { 25 | static void put(ValueType& value, uint8_t* buffer) 26 | { 27 | big_impl::put(value, buffer + 1); 28 | 29 | value = (value >> 8); 30 | *buffer = value & 0xFF; 31 | } 32 | 33 | static void get(ValueType& value, const uint8_t* buffer) 34 | { 35 | value |= ((ValueType)*buffer); 36 | value = (value << 8); 37 | 38 | big_impl::get(value, buffer + 1); 39 | } 40 | }; 41 | 42 | template 43 | struct big_impl 44 | { 45 | static void put(ValueType& value, uint8_t* buffer) 46 | { 47 | *buffer = value & 0xFF; 48 | } 49 | 50 | static void get(ValueType& value, const uint8_t* buffer) 51 | { 52 | value |= ((ValueType)*buffer); 53 | } 54 | }; 55 | 56 | // Helper to delegate to the appropiate specialization depending on the type 57 | // @TODO remove these wrappers when we have CXX17 support and "if constexpr" 58 | template ::value, 60 | bool IsFloat = std::is_floating_point::value> 61 | struct big 62 | { 63 | static void put(ValueType& value, uint8_t* buffer) 64 | { 65 | big::put(value, buffer); 66 | } 67 | 68 | static void get(ValueType& value, const uint8_t* buffer) 69 | { 70 | big::get(value, buffer); 71 | } 72 | }; 73 | 74 | // Unsigned specialization 75 | template 76 | struct big 77 | { 78 | static_assert( 79 | Bytes > sizeof(ValueType) / 2, 80 | "ValueType fits in type of" 81 | "half the size compared to the provide one, use a smaller type"); 82 | 83 | static void put(ValueType& value, uint8_t* buffer) 84 | { 85 | assert((check::value(value)) && 86 | "Value too big to fit in the provided bytes"); 87 | 88 | big_impl::put(value, buffer); 89 | } 90 | 91 | static void get(ValueType& value, const uint8_t* buffer) 92 | { 93 | big_impl::get(value, buffer); 94 | } 95 | }; 96 | 97 | // Signed specialization 98 | template 99 | struct big 100 | { 101 | static_assert(Bytes == sizeof(ValueType), 102 | "The number of bytes must match the size of the signed type"); 103 | 104 | static void put(ValueType& value, uint8_t* buffer) 105 | { 106 | big_impl::put(value, buffer); 107 | } 108 | 109 | static void get(ValueType& value, const uint8_t* buffer) 110 | { 111 | big_impl::get(value, buffer); 112 | } 113 | }; 114 | 115 | // Floating point type specialization 116 | template 117 | struct big 118 | { 119 | static_assert( 120 | std::numeric_limits::is_iec559, 121 | "Platform must be iec559 compliant when floating point types are used"); 122 | 123 | static_assert( 124 | Bytes == sizeof(ValueType), 125 | "The number of bytes must match the size of the floating type"); 126 | 127 | static void put(ValueType& value, uint8_t* buffer) 128 | { 129 | typename floating_point::UnsignedType temp = 0; 130 | memcpy(&temp, &value, sizeof(ValueType)); 131 | big_impl::put(temp, buffer); 132 | } 133 | 134 | static void get(ValueType& value, const uint8_t* buffer) 135 | { 136 | typename floating_point::UnsignedType temp = 0; 137 | big_impl::get(temp, buffer); 138 | memcpy(&value, &temp, sizeof(ValueType)); 139 | } 140 | }; 141 | 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/endian/detail/little.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Steinwurf ApS 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "helpers.hpp" 15 | 16 | namespace endian 17 | { 18 | namespace detail 19 | { 20 | 21 | // Where the actual conversion takes place 22 | template 23 | struct little_impl 24 | { 25 | static void put(ValueType& value, uint8_t* buffer) 26 | { 27 | *buffer = value & 0xFF; 28 | value = (value >> 8); 29 | 30 | little_impl::put(value, buffer + 1); 31 | } 32 | 33 | static void get(ValueType& value, const uint8_t* buffer) 34 | { 35 | little_impl::get(value, buffer + 1); 36 | 37 | value = (value << 8); 38 | value |= ((ValueType)*buffer); 39 | } 40 | }; 41 | 42 | template 43 | struct little_impl 44 | { 45 | static void put(ValueType& value, uint8_t* buffer) 46 | { 47 | *buffer = value & 0xFF; 48 | } 49 | 50 | static void get(ValueType& value, const uint8_t* buffer) 51 | { 52 | value |= ((ValueType)*buffer); 53 | } 54 | }; 55 | 56 | // Helper to delegate to the appropiate specialization depednign on the type 57 | // @TODO remove these wrappers when we have CXX17 support and "if constexpr" 58 | template ::value, 60 | bool IsFloat = std::is_floating_point::value> 61 | struct little 62 | { 63 | static void put(ValueType& value, uint8_t* buffer) 64 | { 65 | little::put(value, buffer); 66 | } 67 | 68 | static void get(ValueType& value, const uint8_t* buffer) 69 | { 70 | little::get(value, buffer); 71 | } 72 | }; 73 | 74 | // Unsigned specialization 75 | template 76 | struct little 77 | { 78 | static_assert( 79 | Bytes > sizeof(ValueType) / 2, 80 | "ValueType fits in type of" 81 | "half the size compared to the provide one, use a smaller type"); 82 | 83 | static void put(ValueType& value, uint8_t* buffer) 84 | { 85 | assert((check::value(value)) && 86 | "Value too big to fit in the provided bytes"); 87 | 88 | little_impl::put(value, buffer); 89 | } 90 | 91 | static void get(ValueType& value, const uint8_t* buffer) 92 | { 93 | little_impl::get(value, buffer); 94 | } 95 | }; 96 | 97 | // Signed specialization 98 | template 99 | struct little 100 | { 101 | static_assert(Bytes == sizeof(ValueType), 102 | "The number of bytes must match the size of the signed type"); 103 | 104 | static void put(ValueType& value, uint8_t* buffer) 105 | { 106 | little_impl::put(value, buffer); 107 | } 108 | 109 | static void get(ValueType& value, const uint8_t* buffer) 110 | { 111 | little_impl::get(value, buffer); 112 | } 113 | }; 114 | 115 | // Floating point type specialization 116 | template 117 | struct little 118 | { 119 | static_assert( 120 | std::numeric_limits::is_iec559, 121 | "Platform must be iec559 compliant when floating point types are used"); 122 | 123 | static_assert( 124 | Bytes == sizeof(ValueType), 125 | "The number of bytes must match the size of the floating type"); 126 | 127 | static void put(ValueType& value, uint8_t* buffer) 128 | { 129 | typename floating_point::UnsignedType temp = 0; 130 | memcpy(&temp, &value, sizeof(ValueType)); 131 | little_impl::put(temp, buffer); 132 | } 133 | 134 | static void get(ValueType& value, const uint8_t* buffer) 135 | { 136 | typename floating_point::UnsignedType temp = 0; 137 | little_impl::get(temp, buffer); 138 | memcpy(&value, &temp, sizeof(ValueType)); 139 | } 140 | }; 141 | 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ====== 2 | endian 3 | ====== 4 | 5 | |Linux make-specs| |Windows make-specs| |MacOS make-specs| |Linux CMake| |Windows CMake| |MacOS CMake| |Raspberry Pi| |Valgrind| |No Assertions| |Clang Format| |Cppcheck| 6 | 7 | .. |Linux make-specs| image:: https://github.com/steinwurf/endian/actions/workflows/linux_mkspecs.yml/badge.svg 8 | :target: https://github.com/steinwurf/endian/actions/workflows/linux_mkspecs.yml 9 | 10 | .. |Windows make-specs| image:: https://github.com/steinwurf/endian/actions/workflows/windows_mkspecs.yml/badge.svg 11 | :target: https://github.com/steinwurf/endian/actions/workflows/windows_mkspecs.yml 12 | 13 | .. |MacOS make-specs| image:: https://github.com/steinwurf/endian/actions/workflows/macos_mkspecs.yml/badge.svg 14 | :target: https://github.com/steinwurf/endian/actions/workflows/macos_mkspecs.yml 15 | 16 | .. |Linux CMake| image:: https://github.com/steinwurf/endian/actions/workflows/linux_cmake.yml/badge.svg 17 | :target: https://github.com/steinwurf/endian/actions/workflows/linux_cmake.yml 18 | 19 | .. |Windows CMake| image:: https://github.com/steinwurf/endian/actions/workflows/windows_cmake.yml/badge.svg 20 | :target: https://github.com/steinwurf/endian/actions/workflows/windows_cmake.yml 21 | 22 | .. |MacOS CMake| image:: https://github.com/steinwurf/endian/actions/workflows/macos_cmake.yml/badge.svg 23 | :target: https://github.com/steinwurf/endian/actions/workflows/macos_cmake.yml 24 | 25 | .. |Raspberry Pi| image:: https://github.com/steinwurf/endian/actions/workflows/raspberry_pi.yml/badge.svg 26 | :target: https://github.com/steinwurf/endian/actions/workflows/raspberry_pi.yml 27 | 28 | .. |Clang Format| image:: https://github.com/steinwurf/endian/actions/workflows/clang-format.yml/badge.svg 29 | :target: https://github.com/steinwurf/endian/actions/workflows/clang-format.yml 30 | 31 | .. |No Assertions| image:: https://github.com/steinwurf/endian/actions/workflows/nodebug.yml/badge.svg 32 | :target: https://github.com/steinwurf/endian/actions/workflows/nodebug.yml 33 | 34 | .. |Valgrind| image:: https://github.com/steinwurf/endian/actions/workflows/valgrind.yml/badge.svg 35 | :target: https://github.com/steinwurf/endian/actions/workflows/valgrind.yml 36 | 37 | .. |Cppcheck| image:: https://github.com/steinwurf/endian/actions/workflows/cppcheck.yml/badge.svg 38 | :target: https://github.com/steinwurf/endian/actions/workflows/cppcheck.yml 39 | 40 | .. image:: https://raw.githubusercontent.com/steinwurf/endian/master/endian.png 41 | :width: 300 42 | 43 | endian is a simple C++ library for conversion between big and little endian 44 | data representations and provide stream-like interface for accessing a 45 | fixed-size buffer. 46 | 47 | .. contents:: Table of Contents: 48 | :local: 49 | 50 | Usage 51 | ----- 52 | 53 | The ``endian::little_endian`` and ``endian::big_endian`` structures acts 54 | like function containers for conversion to little/big endian. In both 55 | structures there are two types of functions `put` and `get`, each have 56 | specializations for different integer types. 57 | 58 | The ``endian::endian_stream`` class is stream-like interface used for 59 | writing and reading data either to or from a stream or storage object. 60 | 61 | Examples for using endian can be found in ``test/src`` and in ``example/src`` 62 | 63 | Header-only 64 | ........... 65 | 66 | The library itself is header-only so essentially to use it you just have to 67 | clone the repository and setup the right include paths in the project where 68 | you would like to use it. 69 | 70 | Unit testing 71 | ------------ 72 | 73 | The unit tests for the endian library are located in the ``test/src`` 74 | folder. 75 | 76 | We use the Google Unit Testing Framwork (gtest) to drive the unit tests. To 77 | build and run the test run:: 78 | 79 | python waf configure 80 | python waf 81 | 82 | Depending on the platform you should see a test binary called 83 | ``endian_test`` in (extension also depends on operating system e.g `.exe` 84 | for Windows):: 85 | 86 | build/platform/test 87 | 88 | Where ``platform`` is typically either linux, win32 or darwin depending on 89 | your operating system 90 | 91 | Use as Dependency in CMake 92 | -------------------------- 93 | 94 | To depend on this project when using the CMake build system, add the following 95 | in your CMake build script:: 96 | 97 | add_subdirectory("/path/to/endian" endian) 98 | target_link_libraries( steinwurf::endian) 99 | 100 | Where ```` is replaced by your target. 101 | 102 | License 103 | ------- 104 | 105 | endian is available under the BSD license. 106 | -------------------------------------------------------------------------------- /NEWS.rst: -------------------------------------------------------------------------------- 1 | News for endian 2 | =============== 3 | 4 | This file lists the major changes between versions. For a more detailed list of 5 | every change, see the Git log. 6 | 7 | Latest 8 | ------ 9 | * tbd 10 | 11 | 14.0.0 12 | ------ 13 | * Major: Upgraded to gtest 6.x.y. 14 | * Major: Build with CMake only. 15 | 16 | 13.0.0 17 | ------ 18 | * Major: Use waf-tools 5. 19 | 20 | 12.1.1 21 | ------ 22 | * Patch: Fix warning in 8-bit value check. 23 | 24 | 12.1.0 25 | ------ 26 | * Minor: Improved api of the detail stream class. 27 | * Minor: Added example and documentation for network. 28 | * Minor: Added network alias methods. 29 | * Minor: Added basic example for stream writer and reader. 30 | * Minor: Added << operator for stream writer and >> for stream reader. 31 | * Minor: Updated waf. 32 | 33 | 12.0.0 34 | ------ 35 | * Major: Change cmake build to be object library based. 36 | 37 | 11.1.0 38 | ------ 39 | * Minor: Added install step to CMake. 40 | 41 | 11.0.0 42 | ------ 43 | * Major: Use std::size_t for size and positions. 44 | 45 | 10.3.0 46 | ------ 47 | * Minor: Lower cmake version requirement. 48 | 49 | 10.2.0 50 | ------ 51 | * Minor: Improve cmake build file. 52 | 53 | 10.1.0 54 | ------ 55 | * Minor: Added cmake. 56 | 57 | 10.0.1 58 | ------ 59 | * Patch: Allow empty streams. 60 | 61 | 10.0.0 62 | ------ 63 | * Major: stream class in detail namespace and folder 64 | * Major: Removed stream_writer(std::vector&) and 65 | stream_reader(const std::vector&) 66 | * Minor: Added ``size_type`` to the API. 67 | 68 | 9.0.0 69 | ----- 70 | * Major: Writing to/from half or less of the bytes of a provided type is now 71 | disallowed, use a smaller type instead and make an explicit cast is needed. 72 | * Patch: Recursive implementation of conversion. 73 | 74 | 8.2.0 75 | ----- 76 | * Minor: Added ``ValueType get()`` and ``ValueType get_bytes()``. 77 | * Minor: Added support for Float type and Double types. 78 | 79 | 8.1.0 80 | ----- 81 | * Minor: Added ``ValueType read()`` and ``ValueType peek()``. 82 | 83 | 8.0.1 84 | ----- 85 | * Patch: Fixed ``stream_reader.read``. 86 | 87 | 8.0.0 88 | ----- 89 | * Major: Removed ``types.hpp``. 90 | * Major: Renamed ``stream_reader.read`` to ``stream_reader.read_bytes`` which 91 | now takes a ``uint8_t`` template argument specifying the number of bytes. 92 | * Major: Renamed ``stream_reader.peek`` to ``stream_reader.peek_bytes`` which 93 | now takes a ``uint8_t`` template argument specifying the number of bytes. 94 | * Major: Renamed ``stream_writer.write`` to ``stream_writer.write_bytes`` which 95 | now takes a ``uint8_t`` template argument specifying the number of bytes. 96 | * Major: Added ``stream_writer.write`` which allows writing ``uint8_t``, 97 | ``uint16_t``, ``uint32_t``, and ``uint64_t``. 98 | * Major: Added ``stream_reader.read`` which allows reading ``uint8_t``, 99 | ``uint16_t``, ``uint32_t``, and ``uint64_t``. 100 | * Major: Added ``stream_reader.peek`` which allows peeking ``uint8_t``, 101 | ``uint16_t``, ``uint32_t``, and ``uint64_t``. 102 | 103 | 7.0.0 104 | ----- 105 | * Major: Renamed ``get`` and ``put`` to ``get_bytes`` and ``put_bytes``. 106 | * Minor: Added possibility to read native types with ``get`` and ``put``. 107 | 108 | 6.2.0 109 | ----- 110 | * Minor: Made ``peek`` const. 111 | * Minor: ``peek`` now takes an offset parameter for peeking further 112 | ahead in the stream. This value is defaulted to 0 so the API hasn't changed. 113 | 114 | 6.1.0 115 | ----- 116 | * Minor: Added `peek` which allows reading without moving the read position. 117 | 118 | 6.0.1 119 | ----- 120 | * Patch: Allow streams of size 0. 121 | 122 | 6.0.0 123 | ----- 124 | * Major: Removed put* and get* static functions. 125 | * Major: Added support for additional byte sized fields. Instead of 126 | directly using the native types. endian now uses the types defined in 127 | ``src/endian/types.hpp`` such as ``u8``, ``u16``, ``u24``, ``u32``, etc. 128 | 129 | 5.0.0 130 | ----- 131 | * Minor: Added functions for reading and writing signed integers. 132 | * Major: Changed size to be of type uint64_t instead of uint32_t. 133 | 134 | 4.2.0 135 | ----- 136 | * Minor: Added ``skip`` function to stream. 137 | 138 | 4.1.0 139 | ----- 140 | * Minor: Added ``stream_writer::constructor(std::vector&)`` and 141 | ``stream_reader::constructor(std::vector&)``. 142 | 143 | 4.0.0 144 | ----- 145 | * Major: Upgrade to waf-tools 4 146 | * Minor: Upgrade to gtest 4 147 | 148 | 3.0.0 149 | ----- 150 | * Major: Renamed ``remaining`` to ``remaining_size``. 151 | * Minor: Added ``data`` and ``remaining_data`` to ``stream_reader`` 152 | and ``stream_writer``. 153 | 154 | 2.1.0 155 | ----- 156 | * Minor: Added ``remaining`` function to ``stream``. 157 | 158 | 2.0.0 159 | ----- 160 | * Major: Removed ``storage`` dependency. 161 | * Major: Renamed use flag ``endian`` to ``endian_includes``. 162 | * Major: Renamed redundant names 163 | ``endian_stream`` to ``stream``, 164 | ``endian_stream_writer`` to ``stream_writer``, and 165 | ``endian_stream_reader`` to ``stream_reader``. 166 | 167 | 1.0.0 168 | ----- 169 | * Major: Initial release. 170 | -------------------------------------------------------------------------------- /src/endian/stream_reader.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Steinwurf ApS 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "detail/stream.hpp" 13 | 14 | namespace endian 15 | { 16 | /// The stream_reader provides a stream-like interface for reading from a 17 | /// fixed-size buffer. All complexity regarding endianness is encapsulated. 18 | template 19 | class stream_reader : public detail::stream 20 | { 21 | public: 22 | /// Creates an endian stream on top of a pre-allocated buffer of the 23 | /// specified size. 24 | /// 25 | /// @param data a data pointer to the buffer 26 | /// @param size the size of the buffer in bytes 27 | stream_reader(const uint8_t* data, std::size_t size) noexcept : 28 | stream(data, size) 29 | { 30 | } 31 | 32 | /// Reads a Bytes-sized integer from the stream and moves the read position. 33 | /// 34 | /// @param value reference to the value to be read 35 | template 36 | void read_bytes(ValueType& value) noexcept 37 | { 38 | assert(Bytes <= remaining_size() && 39 | "Reading over the end of the underlying buffer"); 40 | 41 | peek_bytes(value); 42 | skip(Bytes); 43 | } 44 | 45 | /// Reads a ValueType-sized integer from the stream and moves the read 46 | /// position. 47 | /// 48 | /// @param value reference to the value to be read 49 | template 50 | void read(ValueType& value) noexcept 51 | { 52 | assert(sizeof(ValueType) <= remaining_size() && 53 | "Reading over the end of the underlying buffer"); 54 | 55 | read_bytes(value); 56 | } 57 | 58 | /// Reads a ValueType-sized integer from the stream and moves the read 59 | /// position. 60 | /// 61 | /// @return the read value 62 | template 63 | ValueType read() noexcept 64 | { 65 | assert(sizeof(ValueType) <= remaining_size() && 66 | "Reading over the end of the underlying buffer"); 67 | 68 | ValueType value; 69 | read(value); 70 | return value; 71 | } 72 | 73 | /// Reads raw bytes from the stream to fill a buffer represented by 74 | /// a mutable storage object. 75 | /// 76 | /// Note, that this function is provided only for convenience and 77 | /// it does not perform any endian conversions. 78 | /// 79 | /// @param data The data pointer to fill into 80 | /// @param size The number of bytes to fill. 81 | void read(uint8_t* data, std::size_t size) noexcept 82 | { 83 | assert(size <= remaining_size() && 84 | "Reading over the end of the underlying buffer"); 85 | 86 | std::copy_n(remaining_data(), size, data); 87 | skip(size); 88 | } 89 | 90 | /// Peek a Bytes-sized integer in the stream without moving the read 91 | /// position 92 | /// 93 | /// @param value reference to the value to be read 94 | /// @param offset number of bytes to offset the peeking with 95 | template 96 | void peek_bytes(ValueType& value, std::size_t offset = 0) const noexcept 97 | { 98 | assert(remaining_size() >= offset && "Offset too large"); 99 | assert(Bytes <= remaining_size() - offset && 100 | "Reading over the end of the underlying buffer"); 101 | 102 | const uint8_t* data_position = remaining_data() + offset; 103 | EndianType::template get_bytes(value, data_position); 104 | } 105 | 106 | /// Peek a ValueType-sized integer in the stream without moving the read 107 | /// position 108 | /// 109 | /// @param value reference to the value to be read 110 | /// @param offset number of bytes to offset the peeking with 111 | template 112 | void peek(ValueType& value, std::size_t offset = 0) const noexcept 113 | { 114 | assert(remaining_size() >= offset && "Offset too large"); 115 | assert(sizeof(ValueType) <= remaining_size() - offset && 116 | "Reading over the end of the underlying buffer"); 117 | 118 | peek_bytes(value, offset); 119 | } 120 | 121 | /// Peek a ValueType-sized integer in the stream without moving the read 122 | /// position 123 | /// 124 | /// @param offset number of bytes to offset the peeking with 125 | /// @return the peeked value 126 | template 127 | ValueType peek(std::size_t offset = 0) const noexcept 128 | { 129 | assert(remaining_size() >= offset && "Offset too large"); 130 | assert(sizeof(ValueType) <= remaining_size() - offset && 131 | "Reading over the end of the underlying buffer"); 132 | 133 | ValueType value; 134 | peek(value, offset); 135 | return value; 136 | } 137 | 138 | /// Operator for reading the next value from the stream. 139 | /// 140 | /// @return the read value 141 | template 142 | stream_reader& operator>>(ValueType& value) 143 | { 144 | read(value); 145 | return *this; 146 | } 147 | }; 148 | } 149 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI Pipeline 2 | 3 | on: 4 | schedule: 5 | - cron: 0 1 * * * # Nightly at 01:00 UTC 6 | push: 7 | branches: 8 | - master 9 | pull_request: 10 | workflow_dispatch: 11 | inputs: 12 | extra_resolve_options: 13 | description: Extra Resolve Options 14 | required: false 15 | 16 | jobs: 17 | linux_cmake: 18 | timeout-minutes: 45 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | config: 23 | - runner: ubuntu-current 24 | name: GCC Latest 25 | toolchain: "./resolve_symlinks/toolchains/gcc-toolchain.cmake" 26 | - runner: ubuntu-current 27 | name: Clang Latest 28 | toolchain: "./resolve_symlinks/toolchains/clang-toolchain.cmake" 29 | - runner: ubuntu-current 30 | name: Clang ASAN Latest 31 | toolchain: "./resolve_symlinks/toolchains/clang-asan-toolchain.cmake" 32 | - runner: ubuntu-current 33 | name: Clang TSAN Latest 34 | toolchain: "./resolve_symlinks/toolchains/clang-tsan-toolchain.cmake" 35 | - runner: ubuntu-current 36 | name: Clang UBSAN Latest 37 | toolchain: "./resolve_symlinks/toolchains/clang-ubsan-toolchain.cmake" 38 | - runner: ubuntu-old 39 | name: GCC Oldest 40 | toolchain: "./resolve_symlinks/toolchains/gcc-toolchain.cmake" 41 | - runner: ubuntu-old 42 | name: Clang Oldest 43 | toolchain: "./resolve_symlinks/toolchains/clang-toolchain.cmake" 44 | runs-on: 45 | - self-hosted 46 | - vm 47 | - ${{ matrix.config.runner }} 48 | name: ${{ matrix.config.name }} 49 | steps: 50 | # This is sometimes needed when running docker builds since these 51 | # sometimes produce files with root ownership 52 | - name: Ensure correct owner of repository 53 | run: sudo chown -R actions-runner:actions-runner . 54 | - name: Checkout source code 55 | uses: actions/checkout@v3 56 | - name: Waf Clean 57 | run: python3 waf clean --no_resolve 58 | - name: Waf Configure 59 | run: python3 waf configure --git_protocol=git@ --cmake_toolchain=${{ matrix.config.toolchain }} --cmake_verbose 60 | - name: Waf Build 61 | run: python3 waf build --run_tests 62 | 63 | valgrind: 64 | timeout-minutes: 45 65 | runs-on: 66 | - self-hosted 67 | - vm 68 | - ubuntu-current 69 | name: Valgrind 70 | steps: 71 | - name: Ensure correct owner of repository 72 | run: sudo chown -R actions-runner:actions-runner . 73 | 74 | - name: Checkout source code 75 | uses: actions/checkout@v3 76 | 77 | - name: Waf Clean 78 | run: python3 waf clean --no_resolve 79 | 80 | - name: Waf Configure 81 | run: python3 waf configure --git_protocol=git@ --cmake_toolchain=./resolve_symlinks/toolchains/gcc-toolchain.cmake --cmake_verbose 82 | 83 | - name: Waf Build 84 | run: python3 waf build --run_tests --ctest_valgrind 85 | 86 | zig_toolchain_build: 87 | name: Zig Toolchain Build (Docker) 88 | runs-on: [self-hosted, vm, ubuntu-current] 89 | container: 90 | image: kassany/bookworm-ziglang 91 | options: --user 0:0 92 | volumes: 93 | - /root/.ssh:/root/.ssh 94 | steps: 95 | - name: Checkout source code 96 | uses: actions/checkout@v4 97 | - name: Install dependencies 98 | run: | 99 | apt-get update 100 | apt-get install -y python3 python3-pip git cmake build-essential 101 | - name: Waf Clean 102 | run: python3 waf clean --no_resolve 103 | - name: Waf Configure with Zig Toolchain 104 | run: python3 waf configure --git_protocol=git@ --cmake_toolchain=../resolve_symlinks/toolchains/zig-toolchain-x86_64-linux-musl.cmake --cmake_verbose 105 | - name: Waf Build with Zig Toolchain 106 | run: python3 waf build --run_tests 107 | 108 | macos_cmake: 109 | timeout-minutes: 45 110 | strategy: 111 | fail-fast: false 112 | matrix: 113 | config: 114 | - arch: ARM64 115 | os: big_sur 116 | name: Apple Big Sur (ARM) 117 | toolchain: "./resolve_symlinks/toolchains/clang-toolchain.cmake" 118 | runs-on: 119 | - self-hosted 120 | - macOS 121 | - ${{ matrix.config.os }} 122 | - ${{ matrix.config.arch }} 123 | - cmake 124 | - builder 125 | name: ${{ matrix.config.name }} 126 | steps: 127 | - name: Checkout 128 | uses: actions/checkout@v3 129 | - name: Waf Clean 130 | run: python3 waf clean --no_resolve 131 | - name: Waf Configure 132 | run: python3 waf configure --git_protocol=git@ --cmake_toolchain=${{ matrix.config.toolchain }} --cmake_verbose 133 | - name: Waf Build 134 | run: python3 waf build --run_tests 135 | 136 | windows_cmake: 137 | timeout-minutes: 45 138 | strategy: 139 | fail-fast: false 140 | runs-on: [self-hosted, windows, vm, windows-current] 141 | name: Windows 142 | steps: 143 | - name: Checkout 144 | uses: actions/checkout@v3 145 | - name: Waf Clean 146 | run: python waf clean --no_resolve 147 | - name: Waf Configure 148 | run: python waf configure --git_protocol=git@ --cmake_verbose 149 | - name: Waf Build 150 | run: python waf build --run_tests 151 | 152 | clang-format: 153 | timeout-minutes: 45 154 | name: Clang-Format 155 | runs-on: [self-hosted, vm, ubuntu-current] 156 | steps: 157 | - name: Ensure correct owner of repository 158 | run: sudo chown -R actions-runner:actions-runner . 159 | - name: Clang format version 160 | run: clang-format --version 161 | - name: Checkout source code 162 | uses: actions/checkout@v3 163 | - name: Run Clang-format 164 | run: find ./ -iname *.hpp -o -iname *.cpp -o -iname *.c -o -iname *.h | xargs clang-format --dry-run --Werror 165 | 166 | workflow-keepalive: 167 | if: github.event_name == 'schedule' 168 | runs-on: [self-hosted, vm, ubuntu-current] 169 | permissions: 170 | actions: write 171 | steps: 172 | - name: Install GitHub CLI 173 | run: | 174 | sudo apt update 175 | sudo apt install -y gh 176 | - uses: liskin/gh-workflow-keepalive@v1 177 | 178 | concurrency: 179 | group: ${{ github.workflow }}-${{ github.ref || github.run_id }} 180 | cancel-in-progress: true -------------------------------------------------------------------------------- /test/src/test_stream_reader.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2016. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | template 17 | static void test_basic_api() 18 | { 19 | { 20 | SCOPED_TRACE(testing::Message() << "size 1"); 21 | std::size_t size = 1U; 22 | std::vector buffer; 23 | buffer.resize(size); 24 | endian::stream_reader stream(buffer.data(), buffer.size()); 25 | 26 | // check initial state 27 | EXPECT_EQ(size, stream.size()); 28 | EXPECT_EQ(0U, stream.position()); 29 | EXPECT_EQ(size, stream.remaining_size()); 30 | EXPECT_EQ(buffer.data(), stream.remaining_data()); 31 | EXPECT_EQ(buffer.data(), stream.data()); 32 | 33 | // check state after seek 34 | stream.seek(1); 35 | EXPECT_EQ(size, stream.size()); 36 | EXPECT_EQ(1U, stream.position()); 37 | EXPECT_EQ(0U, stream.remaining_size()); 38 | EXPECT_EQ(buffer.data() + 1, stream.remaining_data()); 39 | EXPECT_EQ(buffer.data(), stream.data()); 40 | } 41 | 42 | { 43 | SCOPED_TRACE(testing::Message() << "size 1000"); 44 | std::size_t size = 1000U; 45 | std::vector buffer; 46 | buffer.resize(size); 47 | endian::stream_reader stream(buffer.data(), buffer.size()); 48 | 49 | // check initial state 50 | EXPECT_EQ(size, stream.size()); 51 | EXPECT_EQ(0U, stream.position()); 52 | EXPECT_EQ(size, stream.remaining_size()); 53 | EXPECT_EQ(buffer.data(), stream.remaining_data()); 54 | EXPECT_EQ(buffer.data(), stream.data()); 55 | 56 | // check state after seek 57 | stream.seek(size / 2); 58 | EXPECT_EQ(size, stream.size()); 59 | EXPECT_EQ(size / 2, stream.position()); 60 | EXPECT_EQ(size / 2, stream.remaining_size()); 61 | EXPECT_EQ(buffer.data() + size / 2, stream.remaining_data()); 62 | EXPECT_EQ(buffer.data(), stream.data()); 63 | 64 | // that consecutive seeks doesn't alter state. 65 | stream.seek(size / 2); 66 | stream.seek(size / 2); 67 | stream.seek(size / 2); 68 | EXPECT_EQ(size, stream.size()); 69 | EXPECT_EQ(size / 2, stream.position()); 70 | EXPECT_EQ(size / 2, stream.remaining_size()); 71 | EXPECT_EQ(buffer.data() + size / 2, stream.remaining_data()); 72 | EXPECT_EQ(buffer.data(), stream.data()); 73 | 74 | // seek to end 75 | stream.seek(size); 76 | EXPECT_EQ(size, stream.position()); 77 | EXPECT_EQ(0U, stream.remaining_size()); 78 | EXPECT_EQ(buffer.data() + size, stream.remaining_data()); 79 | EXPECT_EQ(buffer.data(), stream.data()); 80 | } 81 | 82 | { 83 | SCOPED_TRACE(testing::Message() << "vector vs pointer"); 84 | 85 | std::size_t size = 10U; 86 | std::vector buffer; 87 | buffer.resize(size); 88 | endian::stream_reader stream1(buffer.data(), buffer.size()); 89 | endian::stream_reader stream2(buffer.data(), buffer.size()); 90 | EXPECT_EQ(stream1.size(), stream2.size()); 91 | EXPECT_EQ(stream1.data(), stream2.data()); 92 | } 93 | 94 | { 95 | SCOPED_TRACE(testing::Message() << "peek_bytes"); 96 | std::vector buffer = {1, 2}; 97 | endian::stream_reader stream(buffer.data(), buffer.size()); 98 | uint8_t first_peek = 0; 99 | uint8_t second_peek = 0; 100 | uint8_t first_read = 0; 101 | stream.template peek_bytes<1>(first_peek); 102 | stream.template peek_bytes<1>(second_peek); 103 | stream.template read_bytes<1>(first_read); 104 | EXPECT_EQ(first_peek, second_peek); 105 | EXPECT_EQ(first_peek, first_read); 106 | 107 | uint8_t second_read = 0; 108 | uint8_t third_peek = 0; 109 | stream.template peek_bytes<1>(third_peek); 110 | stream.template read_bytes<1>(second_read); 111 | EXPECT_NE(first_peek, third_peek); 112 | EXPECT_EQ(third_peek, second_read); 113 | } 114 | 115 | { 116 | SCOPED_TRACE(testing::Message() << "peek_read"); 117 | std::vector buffer = {1, 2}; 118 | endian::stream_reader stream(buffer.data(), buffer.size()); 119 | uint8_t first_peek = 0; 120 | uint8_t second_peek = 0; 121 | uint8_t first_read = 0; 122 | stream.peek(first_peek); 123 | stream.peek(second_peek); 124 | stream.read(first_read); 125 | EXPECT_EQ(first_peek, second_peek); 126 | EXPECT_EQ(first_peek, first_read); 127 | 128 | uint8_t second_read = 0; 129 | uint8_t third_peek = 0; 130 | stream.peek(third_peek); 131 | stream.read(second_read); 132 | EXPECT_NE(first_peek, third_peek); 133 | EXPECT_EQ(third_peek, second_read); 134 | } 135 | 136 | { 137 | SCOPED_TRACE(testing::Message() << "peek_read_return_value"); 138 | std::vector buffer = {1, 2}; 139 | endian::stream_reader stream(buffer.data(), buffer.size()); 140 | 141 | uint8_t first_peek = stream.template peek(); 142 | uint8_t second_peek = stream.template peek(); 143 | uint8_t first_read = stream.template read(); 144 | EXPECT_EQ(first_peek, second_peek); 145 | EXPECT_EQ(first_peek, first_read); 146 | 147 | auto third_peek = stream.template peek(); 148 | auto second_read = stream.template read(); 149 | EXPECT_NE(first_peek, third_peek); 150 | EXPECT_EQ(third_peek, second_read); 151 | } 152 | 153 | { 154 | SCOPED_TRACE(testing::Message() << "offset peek"); 155 | std::vector buffer = {1, 2, 3, 4}; 156 | endian::stream_reader stream(buffer.data(), buffer.size()); 157 | uint8_t first_peek_at_3 = 0; 158 | uint8_t second_peek_at_1 = 0; 159 | uint8_t first_read = 0; 160 | stream.template peek_bytes<1>(first_peek_at_3, 2); 161 | stream.template peek_bytes<1>(second_peek_at_1, 0); 162 | stream.template read_bytes<1>(first_read); 163 | EXPECT_NE(first_peek_at_3, second_peek_at_1); 164 | EXPECT_NE(first_peek_at_3, first_read); 165 | EXPECT_EQ(second_peek_at_1, first_read); 166 | } 167 | } 168 | 169 | TEST(test_stream_reader, basic_api_little_endian) 170 | { 171 | test_basic_api(); 172 | } 173 | 174 | TEST(test_stream_reader, basic_api_big_endian) 175 | { 176 | test_basic_api(); 177 | } 178 | -------------------------------------------------------------------------------- /test/src/test_big_endian.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2016. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | TEST(test_big_endian, convert_integers) 15 | { 16 | // Indicate endianness for debugging purposes 17 | SCOPED_TRACE(testing::Message() 18 | << "big_endian: " << endian::is_big_endian()); 19 | 20 | // Test 8-bit integer 21 | { 22 | uint8_t data[1]; 23 | uint8_t input = 0x11U; 24 | 25 | // No change will occur for a single-byte value 26 | endian::big_endian::put_bytes<1>(input, data); 27 | EXPECT_EQ(0x11U, data[0]); 28 | 29 | // No change will occur for a single-byte value 30 | uint8_t out = 0; 31 | endian::big_endian::get_bytes<1>(out, data); 32 | EXPECT_EQ(input, out); 33 | 34 | out = endian::big_endian::get(data); 35 | EXPECT_EQ(input, out); 36 | 37 | out = endian::big_endian::get_bytes(data); 38 | EXPECT_EQ(input, out); 39 | } 40 | 41 | // Test 16-bit integer 42 | { 43 | uint8_t data[2]; 44 | uint16_t input = 0x1122U; 45 | 46 | // If the host is little endian, the put function 47 | // should change the byte order (no change for big endian) 48 | endian::big_endian::put_bytes<2>(input, data); 49 | EXPECT_EQ(0x11U, data[0]); 50 | EXPECT_EQ(0x22U, data[1]); 51 | 52 | // Get should swap the value back (no change for big endian) 53 | uint16_t out = 0; 54 | endian::big_endian::get_bytes<2>(out, data); 55 | EXPECT_EQ(input, out); 56 | 57 | out = endian::big_endian::get(data); 58 | EXPECT_EQ(input, out); 59 | 60 | out = endian::big_endian::get_bytes(data); 61 | EXPECT_EQ(input, out); 62 | } 63 | 64 | // Test 24-bit integer 65 | { 66 | uint8_t data[3]; 67 | uint32_t input = 0x112233U; 68 | 69 | // If the host is little endian, the put function 70 | // should change the byte order (no change for big endian) 71 | endian::big_endian::put_bytes<3>(input, data); 72 | EXPECT_EQ(0x11U, data[0]); 73 | EXPECT_EQ(0x22U, data[1]); 74 | EXPECT_EQ(0x33U, data[2]); 75 | 76 | // Get should swap the value back (no change for big endian) 77 | uint32_t out = 0; 78 | endian::big_endian::get_bytes<3>(out, data); 79 | EXPECT_EQ(input, out); 80 | 81 | out = endian::big_endian::get_bytes<3, decltype(out)>(data); 82 | EXPECT_EQ(input, out); 83 | } 84 | 85 | // Test 32-bit integer 86 | { 87 | uint8_t data[4]; 88 | uint32_t input = 0x11223344U; 89 | 90 | // If the host is little endian, the put function 91 | // should change the byte order (no change for big endian) 92 | endian::big_endian::put_bytes<4>(input, data); 93 | EXPECT_EQ(0x11U, data[0]); 94 | EXPECT_EQ(0x22U, data[1]); 95 | EXPECT_EQ(0x33U, data[2]); 96 | EXPECT_EQ(0x44U, data[3]); 97 | 98 | // Get should swap the value back (no change for big endian) 99 | uint32_t out = 0; 100 | endian::big_endian::get_bytes<4>(out, data); 101 | EXPECT_EQ(input, out); 102 | 103 | out = endian::big_endian::get(data); 104 | EXPECT_EQ(input, out); 105 | 106 | out = endian::big_endian::get_bytes(data); 107 | EXPECT_EQ(input, out); 108 | } 109 | 110 | // Test 40-bit integer 111 | { 112 | uint8_t data[5]; 113 | uint64_t input = 0x1122334455ULL; 114 | 115 | // If the host is little endian, the put function 116 | // should change the byte order (no change for big endian) 117 | endian::big_endian::put_bytes<5>(input, data); 118 | EXPECT_EQ(0x11U, data[0]); 119 | EXPECT_EQ(0x22U, data[1]); 120 | EXPECT_EQ(0x33U, data[2]); 121 | EXPECT_EQ(0x44U, data[3]); 122 | EXPECT_EQ(0x55U, data[4]); 123 | 124 | // Get should swap the value back (no change for big endian) 125 | uint64_t out = 0; 126 | endian::big_endian::get_bytes<5>(out, data); 127 | EXPECT_EQ(input, out); 128 | 129 | out = endian::big_endian::get_bytes<5, decltype(out)>(data); 130 | EXPECT_EQ(input, out); 131 | } 132 | 133 | // Test 48-bit integer 134 | { 135 | uint8_t data[6]; 136 | uint64_t input = 0x112233445566ULL; 137 | 138 | // If the host is little endian, the put function 139 | // should change the byte order (no change for big endian) 140 | endian::big_endian::put_bytes<6>(input, data); 141 | EXPECT_EQ(0x11U, data[0]); 142 | EXPECT_EQ(0x22U, data[1]); 143 | EXPECT_EQ(0x33U, data[2]); 144 | EXPECT_EQ(0x44U, data[3]); 145 | EXPECT_EQ(0x55U, data[4]); 146 | EXPECT_EQ(0x66U, data[5]); 147 | 148 | // Get should swap the value back (no change for big endian) 149 | uint64_t out = 0; 150 | endian::big_endian::get_bytes<6>(out, data); 151 | EXPECT_EQ(input, out); 152 | 153 | out = endian::big_endian::get_bytes<6, decltype(out)>(data); 154 | EXPECT_EQ(input, out); 155 | } 156 | 157 | // Test 56-bit integer 158 | { 159 | uint8_t data[7]; 160 | uint64_t input = 0x11223344556677ULL; 161 | 162 | // If the host is little endian, the put function 163 | // should change the byte order (no change for big endian) 164 | endian::big_endian::put_bytes<7>(input, data); 165 | EXPECT_EQ(0x11U, data[0]); 166 | EXPECT_EQ(0x22U, data[1]); 167 | EXPECT_EQ(0x33U, data[2]); 168 | EXPECT_EQ(0x44U, data[3]); 169 | EXPECT_EQ(0x55U, data[4]); 170 | EXPECT_EQ(0x66U, data[5]); 171 | EXPECT_EQ(0x77U, data[6]); 172 | 173 | // Get should swap the value back (no change for big endian) 174 | uint64_t out = 0; 175 | endian::big_endian::get_bytes<7>(out, data); 176 | EXPECT_EQ(input, out); 177 | 178 | out = endian::big_endian::get_bytes<7, decltype(out)>(data); 179 | EXPECT_EQ(input, out); 180 | } 181 | 182 | // Test 64-bit integer 183 | { 184 | uint8_t data[8]; 185 | uint64_t input = 0x1122334455667788ULL; 186 | 187 | // If the host is little endian, the put function 188 | // should change the byte order (no change for big endian) 189 | endian::big_endian::put_bytes<8>(input, data); 190 | EXPECT_EQ(0x11U, data[0]); 191 | EXPECT_EQ(0x22U, data[1]); 192 | EXPECT_EQ(0x33U, data[2]); 193 | EXPECT_EQ(0x44U, data[3]); 194 | EXPECT_EQ(0x55U, data[4]); 195 | EXPECT_EQ(0x66U, data[5]); 196 | EXPECT_EQ(0x77U, data[6]); 197 | EXPECT_EQ(0x88U, data[7]); 198 | 199 | // Get should swap the value back (no change for big endian) 200 | uint64_t out = 0; 201 | endian::big_endian::get_bytes<8>(out, data); 202 | EXPECT_EQ(input, out); 203 | 204 | out = endian::big_endian::get(data); 205 | EXPECT_EQ(input, out); 206 | 207 | out = endian::big_endian::get_bytes(data); 208 | EXPECT_EQ(input, out); 209 | } 210 | } 211 | 212 | TEST(test_big_endian, convert_float) 213 | { 214 | uint8_t data[4]; 215 | float input = 0.0f; 216 | 217 | endian::big_endian::put_bytes<4>(input, data); 218 | EXPECT_EQ(0x00U, data[0]); 219 | EXPECT_EQ(0x00U, data[1]); 220 | EXPECT_EQ(0x00U, data[2]); 221 | EXPECT_EQ(0x00U, data[3]); 222 | 223 | // Get should swap the value back (no change for big endian) 224 | float out = 0.0f; 225 | endian::big_endian::get_bytes<4>(out, data); 226 | EXPECT_EQ(input, out); 227 | 228 | out = endian::big_endian::get(data); 229 | EXPECT_EQ(input, out); 230 | 231 | out = endian::big_endian::get_bytes(data); 232 | EXPECT_EQ(input, out); 233 | 234 | input = 1.0f; 235 | endian::big_endian::put_bytes<4>(input, data); 236 | EXPECT_EQ(0x3FU, data[0]); 237 | EXPECT_EQ(0x80U, data[1]); 238 | EXPECT_EQ(0x00U, data[2]); 239 | EXPECT_EQ(0x00U, data[3]); 240 | 241 | out = 0.0f; 242 | endian::big_endian::get_bytes<4>(out, data); 243 | EXPECT_EQ(input, out); 244 | 245 | out = endian::big_endian::get(data); 246 | EXPECT_EQ(input, out); 247 | 248 | out = endian::big_endian::get_bytes(data); 249 | EXPECT_EQ(input, out); 250 | 251 | input = 0.1f; 252 | endian::big_endian::put_bytes<4>(input, data); 253 | EXPECT_EQ(0x3DU, data[0]); 254 | EXPECT_EQ(0xCCU, data[1]); 255 | EXPECT_EQ(0xCCU, data[2]); 256 | EXPECT_EQ(0xCDU, data[3]); 257 | 258 | out = 0.0f; 259 | endian::big_endian::get_bytes<4>(out, data); 260 | EXPECT_EQ(input, out); 261 | 262 | out = endian::big_endian::get(data); 263 | EXPECT_EQ(input, out); 264 | 265 | out = endian::big_endian::get_bytes(data); 266 | EXPECT_EQ(input, out); 267 | 268 | input = std::numeric_limits::min(); 269 | endian::big_endian::put_bytes<4>(input, data); 270 | EXPECT_EQ(0x00U, data[0]); 271 | EXPECT_EQ(0x80U, data[1]); 272 | EXPECT_EQ(0x00U, data[2]); 273 | EXPECT_EQ(0x00U, data[3]); 274 | 275 | out = 0; 276 | endian::big_endian::get_bytes<4>(out, data); 277 | EXPECT_EQ(input, out); 278 | 279 | out = endian::big_endian::get(data); 280 | EXPECT_EQ(input, out); 281 | 282 | out = endian::big_endian::get_bytes(data); 283 | EXPECT_EQ(input, out); 284 | 285 | input = std::numeric_limits::max(); 286 | endian::big_endian::put_bytes<4>(input, data); 287 | EXPECT_EQ(0x7FU, data[0]); 288 | EXPECT_EQ(0x7FU, data[1]); 289 | EXPECT_EQ(0xFFU, data[2]); 290 | EXPECT_EQ(0xFFU, data[3]); 291 | 292 | out = 0; 293 | endian::big_endian::get_bytes<4>(out, data); 294 | EXPECT_EQ(input, out); 295 | 296 | out = endian::big_endian::get(data); 297 | EXPECT_EQ(input, out); 298 | 299 | out = endian::big_endian::get_bytes(data); 300 | EXPECT_EQ(input, out); 301 | } 302 | 303 | TEST(test_big_endian, convert_double) 304 | { 305 | uint8_t data[8]; 306 | double input = 0.0; 307 | 308 | endian::big_endian::put_bytes<8>(input, data); 309 | EXPECT_EQ(0x00U, data[0]); 310 | EXPECT_EQ(0x00U, data[1]); 311 | EXPECT_EQ(0x00U, data[2]); 312 | EXPECT_EQ(0x00U, data[3]); 313 | EXPECT_EQ(0x00U, data[4]); 314 | EXPECT_EQ(0x00U, data[5]); 315 | EXPECT_EQ(0x00U, data[6]); 316 | EXPECT_EQ(0x00U, data[7]); 317 | 318 | // Get should swap the value back (no change for big endian) 319 | double out = 0.0; 320 | endian::big_endian::get_bytes<8>(out, data); 321 | EXPECT_EQ(input, out); 322 | 323 | out = endian::big_endian::get(data); 324 | EXPECT_EQ(input, out); 325 | 326 | out = endian::big_endian::get_bytes(data); 327 | EXPECT_EQ(input, out); 328 | 329 | input = 1.0; 330 | endian::big_endian::put_bytes<8>(input, data); 331 | EXPECT_EQ(0x3FU, data[0]); 332 | EXPECT_EQ(0xF0U, data[1]); 333 | EXPECT_EQ(0x00U, data[2]); 334 | EXPECT_EQ(0x00U, data[3]); 335 | EXPECT_EQ(0x00U, data[4]); 336 | EXPECT_EQ(0x00U, data[5]); 337 | EXPECT_EQ(0x00U, data[6]); 338 | EXPECT_EQ(0x00U, data[7]); 339 | 340 | out = 0.0f; 341 | endian::big_endian::get_bytes<8>(out, data); 342 | EXPECT_EQ(input, out); 343 | 344 | out = endian::big_endian::get(data); 345 | EXPECT_EQ(input, out); 346 | 347 | out = endian::big_endian::get_bytes(data); 348 | EXPECT_EQ(input, out); 349 | 350 | input = 0.1; 351 | endian::big_endian::put_bytes<8>(input, data); 352 | EXPECT_EQ(0x3FU, data[0]); 353 | EXPECT_EQ(0xb9U, data[1]); 354 | EXPECT_EQ(0x99U, data[2]); 355 | EXPECT_EQ(0x99U, data[3]); 356 | EXPECT_EQ(0x99U, data[4]); 357 | EXPECT_EQ(0x99U, data[5]); 358 | EXPECT_EQ(0x99U, data[6]); 359 | EXPECT_EQ(0x9AU, data[7]); 360 | 361 | out = 0; 362 | endian::big_endian::get_bytes<8>(out, data); 363 | EXPECT_EQ(input, out); 364 | 365 | out = endian::big_endian::get(data); 366 | EXPECT_EQ(input, out); 367 | 368 | out = endian::big_endian::get_bytes(data); 369 | EXPECT_EQ(input, out); 370 | 371 | input = std::numeric_limits::min(); 372 | endian::big_endian::put_bytes<8>(input, data); 373 | EXPECT_EQ(0x00U, data[0]); 374 | EXPECT_EQ(0x10U, data[1]); 375 | EXPECT_EQ(0x00U, data[2]); 376 | EXPECT_EQ(0x00U, data[3]); 377 | EXPECT_EQ(0x00U, data[4]); 378 | EXPECT_EQ(0x00U, data[5]); 379 | EXPECT_EQ(0x00U, data[6]); 380 | EXPECT_EQ(0x00U, data[7]); 381 | 382 | out = 0; 383 | endian::big_endian::get_bytes<8>(out, data); 384 | EXPECT_EQ(input, out); 385 | 386 | out = endian::big_endian::get(data); 387 | EXPECT_EQ(input, out); 388 | 389 | out = endian::big_endian::get_bytes(data); 390 | EXPECT_EQ(input, out); 391 | 392 | input = std::numeric_limits::max(); 393 | endian::big_endian::put_bytes<8>(input, data); 394 | EXPECT_EQ(0x7FU, data[0]); 395 | EXPECT_EQ(0xEFU, data[1]); 396 | EXPECT_EQ(0xFFU, data[2]); 397 | EXPECT_EQ(0xFFU, data[3]); 398 | EXPECT_EQ(0xFFU, data[4]); 399 | EXPECT_EQ(0xFFU, data[5]); 400 | EXPECT_EQ(0xFFU, data[6]); 401 | EXPECT_EQ(0xFFU, data[7]); 402 | 403 | out = 0; 404 | endian::big_endian::get_bytes<8>(out, data); 405 | EXPECT_EQ(input, out); 406 | 407 | out = endian::big_endian::get(data); 408 | EXPECT_EQ(input, out); 409 | 410 | out = endian::big_endian::get_bytes(data); 411 | EXPECT_EQ(input, out); 412 | } 413 | -------------------------------------------------------------------------------- /test/src/test_little_endian.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2016. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | TEST(test_little_endian, convert) 15 | { 16 | // Indicate endianness for debugging purposes 17 | SCOPED_TRACE(testing::Message() 18 | << "big_endian: " << endian::is_big_endian()); 19 | 20 | // Test 8-bit integer 21 | { 22 | uint8_t data[1]; 23 | uint8_t input = 0x11U; 24 | 25 | // No change will occur for a single-byte value 26 | endian::little_endian::put_bytes<1>(input, data); 27 | EXPECT_EQ(0x11U, data[0]); 28 | 29 | // No change will occur for a single-byte value 30 | uint8_t out = 0; 31 | endian::little_endian::get_bytes<1>(out, data); 32 | EXPECT_EQ(input, out); 33 | 34 | out = endian::little_endian::get(data); 35 | EXPECT_EQ(input, out); 36 | 37 | out = 38 | endian::little_endian::get_bytes(data); 39 | EXPECT_EQ(input, out); 40 | } 41 | 42 | // Test 16-bit integer 43 | { 44 | uint8_t data[2]; 45 | uint16_t input = 0x1122U; 46 | 47 | // If the host is little endian, the put function 48 | // should change the byte order (no change for big endian) 49 | endian::little_endian::put_bytes<2>(input, data); 50 | EXPECT_EQ(0x22U, data[0]); 51 | EXPECT_EQ(0x11U, data[1]); 52 | 53 | // Get should swap the value back (no change for big endian) 54 | uint16_t out = 0; 55 | endian::little_endian::get_bytes<2>(out, data); 56 | EXPECT_EQ(input, out); 57 | 58 | out = endian::little_endian::get(data); 59 | EXPECT_EQ(input, out); 60 | 61 | out = 62 | endian::little_endian::get_bytes(data); 63 | EXPECT_EQ(input, out); 64 | } 65 | 66 | // Test 24-bit integer 67 | { 68 | uint8_t data[3]; 69 | uint32_t input = 0x112233U; 70 | 71 | // If the host is little endian, the put function 72 | // should change the byte order (no change for big endian) 73 | endian::little_endian::put_bytes<3>(input, data); 74 | EXPECT_EQ(0x33U, data[0]); 75 | EXPECT_EQ(0x22U, data[1]); 76 | EXPECT_EQ(0x11U, data[2]); 77 | 78 | // Get should swap the value back (no change for big endian) 79 | uint32_t out = 0; 80 | endian::little_endian::get_bytes<3>(out, data); 81 | EXPECT_EQ(input, out); 82 | 83 | out = endian::little_endian::get_bytes<3, decltype(out)>(data); 84 | EXPECT_EQ(input, out); 85 | } 86 | 87 | // Test 32-bit integer 88 | { 89 | uint8_t data[4]; 90 | uint32_t input = 0x11223344U; 91 | 92 | // If the host is little endian, the put function 93 | // should change the byte order (no change for big endian) 94 | endian::little_endian::put_bytes<4>(input, data); 95 | EXPECT_EQ(0x44U, data[0]); 96 | EXPECT_EQ(0x33U, data[1]); 97 | EXPECT_EQ(0x22U, data[2]); 98 | EXPECT_EQ(0x11U, data[3]); 99 | 100 | // Get should swap the value back (no change for big endian) 101 | uint32_t out = 0; 102 | endian::little_endian::get_bytes<4>(out, data); 103 | EXPECT_EQ(input, out); 104 | 105 | out = endian::little_endian::get(data); 106 | EXPECT_EQ(input, out); 107 | 108 | out = 109 | endian::little_endian::get_bytes(data); 110 | EXPECT_EQ(input, out); 111 | } 112 | 113 | // Test 40-bit integer 114 | { 115 | uint8_t data[5]; 116 | uint64_t input = 0x1122334455ULL; 117 | 118 | // If the host is little endian, the put function 119 | // should change the byte order (no change for big endian) 120 | endian::little_endian::put_bytes<5>(input, data); 121 | EXPECT_EQ(0x55U, data[0]); 122 | EXPECT_EQ(0x44U, data[1]); 123 | EXPECT_EQ(0x33U, data[2]); 124 | EXPECT_EQ(0x22U, data[3]); 125 | EXPECT_EQ(0x11U, data[4]); 126 | 127 | // Get should swap the value back (no change for big endian) 128 | uint64_t out = 0; 129 | endian::little_endian::get_bytes<5>(out, data); 130 | EXPECT_EQ(input, out); 131 | 132 | out = endian::little_endian::get_bytes<5, decltype(out)>(data); 133 | EXPECT_EQ(input, out); 134 | } 135 | 136 | // Test 48-bit integer 137 | { 138 | uint8_t data[6]; 139 | uint64_t input = 0x112233445566ULL; 140 | 141 | // If the host is little endian, the put function 142 | // should change the byte order (no change for big endian) 143 | endian::little_endian::put_bytes<6>(input, data); 144 | EXPECT_EQ(0x66U, data[0]); 145 | EXPECT_EQ(0x55U, data[1]); 146 | EXPECT_EQ(0x44U, data[2]); 147 | EXPECT_EQ(0x33U, data[3]); 148 | EXPECT_EQ(0x22U, data[4]); 149 | EXPECT_EQ(0x11U, data[5]); 150 | 151 | // Get should swap the value back (no change for big endian) 152 | uint64_t out = 0; 153 | endian::little_endian::get_bytes<6>(out, data); 154 | EXPECT_EQ(input, out); 155 | 156 | out = endian::little_endian::get_bytes<6, decltype(out)>(data); 157 | EXPECT_EQ(input, out); 158 | } 159 | 160 | // Test 56-bit integer 161 | { 162 | uint8_t data[7]; 163 | uint64_t input = 0x11223344556677ULL; 164 | 165 | // If the host is little endian, the put function 166 | // should change the byte order (no change for big endian) 167 | endian::little_endian::put_bytes<7>(input, data); 168 | EXPECT_EQ(0x77U, data[0]); 169 | EXPECT_EQ(0x66U, data[1]); 170 | EXPECT_EQ(0x55U, data[2]); 171 | EXPECT_EQ(0x44U, data[3]); 172 | EXPECT_EQ(0x33U, data[4]); 173 | EXPECT_EQ(0x22U, data[5]); 174 | EXPECT_EQ(0x11U, data[6]); 175 | 176 | // Get should swap the value back (no change for big endian) 177 | uint64_t out = 0; 178 | endian::little_endian::get_bytes<7>(out, data); 179 | EXPECT_EQ(input, out); 180 | 181 | out = endian::little_endian::get_bytes<7, decltype(out)>(data); 182 | EXPECT_EQ(input, out); 183 | } 184 | 185 | // Test 64-bit integer 186 | { 187 | uint8_t data[8]; 188 | uint64_t input = 0x1122334455667788ULL; 189 | 190 | // If the host is little endian, the put function 191 | // should change the byte order (no change for big endian) 192 | endian::little_endian::put_bytes<8>(input, data); 193 | EXPECT_EQ(0x88U, data[0]); 194 | EXPECT_EQ(0x77U, data[1]); 195 | EXPECT_EQ(0x66U, data[2]); 196 | EXPECT_EQ(0x55U, data[3]); 197 | EXPECT_EQ(0x44U, data[4]); 198 | EXPECT_EQ(0x33U, data[5]); 199 | EXPECT_EQ(0x22U, data[6]); 200 | EXPECT_EQ(0x11U, data[7]); 201 | 202 | // Get should swap the value back (no change for big endian) 203 | uint64_t out = 0; 204 | endian::little_endian::get_bytes<8>(out, data); 205 | EXPECT_EQ(input, out); 206 | 207 | out = endian::little_endian::get(data); 208 | EXPECT_EQ(input, out); 209 | 210 | out = 211 | endian::little_endian::get_bytes(data); 212 | EXPECT_EQ(input, out); 213 | } 214 | } 215 | 216 | TEST(test_little_endian, convert_float) 217 | { 218 | uint8_t data[4]; 219 | float input = 0.0f; 220 | 221 | endian::little_endian::put_bytes<4>(input, data); 222 | EXPECT_EQ(0x00U, data[0]); 223 | EXPECT_EQ(0x00U, data[1]); 224 | EXPECT_EQ(0x00U, data[2]); 225 | EXPECT_EQ(0x00U, data[3]); 226 | 227 | // Get should swap the value back (no change for big endian) 228 | float out = 0.0f; 229 | endian::little_endian::get_bytes<4>(out, data); 230 | EXPECT_EQ(input, out); 231 | 232 | out = endian::little_endian::get(data); 233 | EXPECT_EQ(input, out); 234 | 235 | out = endian::little_endian::get_bytes(data); 236 | EXPECT_EQ(input, out); 237 | 238 | input = 1.0f; 239 | endian::little_endian::put_bytes<4>(input, data); 240 | EXPECT_EQ(0x00U, data[0]); 241 | EXPECT_EQ(0x00U, data[1]); 242 | EXPECT_EQ(0x80U, data[2]); 243 | EXPECT_EQ(0x3FU, data[3]); 244 | 245 | out = 0.0f; 246 | endian::little_endian::get_bytes<4>(out, data); 247 | EXPECT_EQ(input, out); 248 | 249 | out = endian::little_endian::get(data); 250 | EXPECT_EQ(input, out); 251 | 252 | out = endian::little_endian::get_bytes(data); 253 | EXPECT_EQ(input, out); 254 | 255 | input = 0.1f; 256 | endian::little_endian::put_bytes<4>(input, data); 257 | EXPECT_EQ(0xCDU, data[0]); 258 | EXPECT_EQ(0xCCU, data[1]); 259 | EXPECT_EQ(0xCCU, data[2]); 260 | EXPECT_EQ(0x3DU, data[3]); 261 | 262 | out = 0.0f; 263 | endian::little_endian::get_bytes<4>(out, data); 264 | EXPECT_EQ(input, out); 265 | 266 | out = endian::little_endian::get(data); 267 | EXPECT_EQ(input, out); 268 | 269 | out = endian::little_endian::get_bytes(data); 270 | EXPECT_EQ(input, out); 271 | 272 | input = std::numeric_limits::min(); 273 | endian::little_endian::put_bytes<4>(input, data); 274 | EXPECT_EQ(0x00U, data[0]); 275 | EXPECT_EQ(0x00U, data[1]); 276 | EXPECT_EQ(0x80U, data[2]); 277 | EXPECT_EQ(0x00U, data[3]); 278 | 279 | out = 0.0f; 280 | endian::little_endian::get_bytes<4>(out, data); 281 | EXPECT_EQ(input, out); 282 | 283 | out = endian::little_endian::get(data); 284 | EXPECT_EQ(input, out); 285 | 286 | out = endian::little_endian::get_bytes(data); 287 | EXPECT_EQ(input, out); 288 | 289 | input = std::numeric_limits::max(); 290 | endian::little_endian::put_bytes<4>(input, data); 291 | EXPECT_EQ(0xFFU, data[0]); 292 | EXPECT_EQ(0xFFU, data[1]); 293 | EXPECT_EQ(0x7FU, data[2]); 294 | EXPECT_EQ(0x7FU, data[3]); 295 | 296 | out = 0.0f; 297 | endian::little_endian::get_bytes<4>(out, data); 298 | EXPECT_EQ(input, out); 299 | 300 | out = endian::little_endian::get(data); 301 | EXPECT_EQ(input, out); 302 | 303 | out = endian::little_endian::get_bytes(data); 304 | EXPECT_EQ(input, out); 305 | } 306 | 307 | TEST(test_little_endian, convert_double) 308 | { 309 | uint8_t data[8]; 310 | double input = 0.0; 311 | 312 | endian::little_endian::put_bytes<8>(input, data); 313 | EXPECT_EQ(0x00U, data[0]); 314 | EXPECT_EQ(0x00U, data[1]); 315 | EXPECT_EQ(0x00U, data[2]); 316 | EXPECT_EQ(0x00U, data[3]); 317 | EXPECT_EQ(0x00U, data[4]); 318 | EXPECT_EQ(0x00U, data[5]); 319 | EXPECT_EQ(0x00U, data[6]); 320 | EXPECT_EQ(0x00U, data[7]); 321 | 322 | // Get should swap the value back (no change for big endian) 323 | double out = 0.0; 324 | endian::little_endian::get_bytes<8>(out, data); 325 | EXPECT_EQ(input, out); 326 | 327 | out = endian::little_endian::get(data); 328 | EXPECT_EQ(input, out); 329 | 330 | out = endian::little_endian::get_bytes(data); 331 | EXPECT_EQ(input, out); 332 | 333 | input = 1.0; 334 | endian::little_endian::put_bytes<8>(input, data); 335 | EXPECT_EQ(0x00U, data[0]); 336 | EXPECT_EQ(0x00U, data[1]); 337 | EXPECT_EQ(0x00U, data[2]); 338 | EXPECT_EQ(0x00U, data[3]); 339 | EXPECT_EQ(0x00U, data[4]); 340 | EXPECT_EQ(0x00U, data[5]); 341 | EXPECT_EQ(0xF0U, data[6]); 342 | EXPECT_EQ(0x3FU, data[7]); 343 | 344 | out = 0.0; 345 | endian::little_endian::get_bytes<8>(out, data); 346 | EXPECT_EQ(input, out); 347 | 348 | out = endian::little_endian::get(data); 349 | EXPECT_EQ(input, out); 350 | 351 | out = endian::little_endian::get_bytes(data); 352 | EXPECT_EQ(input, out); 353 | 354 | input = 0.1; 355 | endian::little_endian::put_bytes<8>(input, data); 356 | EXPECT_EQ(0x9AU, data[0]); 357 | EXPECT_EQ(0x99U, data[1]); 358 | EXPECT_EQ(0x99U, data[2]); 359 | EXPECT_EQ(0x99U, data[3]); 360 | EXPECT_EQ(0x99U, data[4]); 361 | EXPECT_EQ(0x99U, data[5]); 362 | EXPECT_EQ(0xB9U, data[6]); 363 | EXPECT_EQ(0x3FU, data[7]); 364 | 365 | out = 0.0; 366 | endian::little_endian::get_bytes<8>(out, data); 367 | EXPECT_EQ(input, out); 368 | 369 | out = endian::little_endian::get(data); 370 | EXPECT_EQ(input, out); 371 | 372 | out = endian::little_endian::get_bytes(data); 373 | EXPECT_EQ(input, out); 374 | 375 | input = std::numeric_limits::min(); 376 | endian::little_endian::put_bytes<8>(input, data); 377 | EXPECT_EQ(0x00U, data[0]); 378 | EXPECT_EQ(0x00U, data[1]); 379 | EXPECT_EQ(0x00U, data[2]); 380 | EXPECT_EQ(0x00U, data[3]); 381 | EXPECT_EQ(0x00U, data[4]); 382 | EXPECT_EQ(0x00U, data[5]); 383 | EXPECT_EQ(0x10U, data[6]); 384 | EXPECT_EQ(0x00U, data[7]); 385 | 386 | out = 0.0; 387 | endian::little_endian::get_bytes<8>(out, data); 388 | EXPECT_EQ(input, out); 389 | 390 | out = endian::little_endian::get(data); 391 | EXPECT_EQ(input, out); 392 | 393 | out = endian::little_endian::get_bytes(data); 394 | EXPECT_EQ(input, out); 395 | 396 | input = std::numeric_limits::max(); 397 | endian::little_endian::put_bytes<8>(input, data); 398 | EXPECT_EQ(0xFFU, data[0]); 399 | EXPECT_EQ(0xFFU, data[1]); 400 | EXPECT_EQ(0xFFU, data[2]); 401 | EXPECT_EQ(0xFFU, data[3]); 402 | EXPECT_EQ(0xFFU, data[4]); 403 | EXPECT_EQ(0xFFU, data[5]); 404 | EXPECT_EQ(0xEFU, data[6]); 405 | EXPECT_EQ(0x7FU, data[7]); 406 | 407 | out = 0.0; 408 | endian::little_endian::get_bytes<8>(out, data); 409 | EXPECT_EQ(input, out); 410 | 411 | out = endian::little_endian::get(data); 412 | EXPECT_EQ(input, out); 413 | 414 | out = endian::little_endian::get_bytes(data); 415 | EXPECT_EQ(input, out); 416 | } 417 | -------------------------------------------------------------------------------- /test/src/test_stream_writer_reader.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Steinwurf ApS 2018. 2 | // All Rights Reserved 3 | // 4 | // Distributed under the "BSD License". See the accompanying LICENSE.rst file. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | template 16 | void write_and_read_value_type(ValueType min, ValueType max) 17 | { 18 | 19 | const std::size_t elements = 1024; 20 | const std::size_t size = elements * sizeof(ValueType); 21 | std::vector buffer(size); 22 | 23 | endian::stream_reader stream_reader(buffer.data(), size); 24 | endian::stream_writer stream_writer(buffer.data(), size); 25 | 26 | for (std::size_t i = 0; i < elements; i++) 27 | { 28 | stream_writer.template write_bytes(max); 29 | } 30 | 31 | stream_reader.seek(0); 32 | for (std::size_t i = 0; i < elements; i++) 33 | { 34 | ValueType value = 0; 35 | stream_reader.template read_bytes(value); 36 | EXPECT_EQ(max, value); 37 | } 38 | 39 | stream_writer.seek(0); 40 | for (std::size_t i = 0; i < elements; i++) 41 | { 42 | stream_writer.template write_bytes(min); 43 | } 44 | 45 | stream_reader.seek(0); 46 | for (std::size_t i = 0; i < elements; i++) 47 | { 48 | ValueType value = 0; 49 | stream_reader.template read_bytes(value); 50 | EXPECT_EQ(min, value); 51 | } 52 | } 53 | 54 | template 55 | void write_and_read_random_value_type(ValueType min, ValueType max) 56 | { 57 | 58 | const std::size_t elements = 1024; 59 | const std::size_t size = elements * sizeof(ValueType); 60 | std::vector buffer(size); 61 | 62 | endian::stream_writer stream_writer(buffer.data(), size); 63 | endian::stream_reader stream_reader(buffer.data(), size); 64 | 65 | std::vector values(elements); 66 | 67 | std::random_device device; 68 | std::mt19937 engine(device()); 69 | 70 | assert(min <= max); 71 | std::uniform_int_distribution distribution(min, max); 72 | 73 | for (std::size_t i = 0; i < elements; i++) 74 | { 75 | values[i] = (ValueType)distribution(engine); 76 | stream_writer.template write_bytes(values[i]); 77 | } 78 | for (std::size_t i = 0; i < elements; i++) 79 | { 80 | ValueType value; 81 | stream_reader.template read_bytes(value); 82 | EXPECT_EQ(values[i], value); 83 | } 84 | } 85 | 86 | template 87 | void run_write_peek_and_read_variadic_bytes() 88 | { 89 | const std::size_t elements = 1024; 90 | const std::size_t size = elements * sizeof(uint64_t); 91 | std::vector buffer(size); 92 | 93 | endian::stream_writer writer(buffer.data(), size); 94 | 95 | std::random_device device; 96 | std::mt19937 engine(device()); 97 | 98 | std::vector values(elements); 99 | for (std::size_t i = 0; i < elements; i++) 100 | { 101 | switch (i % 8) 102 | { 103 | case 0: 104 | { 105 | assert(0 < 0xFF); 106 | std::uniform_int_distribution distribution(0, 0xFF); 107 | values[i] = (uint8_t)distribution(engine); 108 | writer.template write_bytes<1>((uint8_t)values[i]); 109 | break; 110 | } 111 | case 1: 112 | { 113 | assert(0 < 0xFFFF); 114 | std::uniform_int_distribution distribution(0, 0xFFFF); 115 | values[i] = (uint16_t)distribution(engine); 116 | writer.template write_bytes<2>((uint16_t)values[i]); 117 | break; 118 | } 119 | case 2: 120 | { 121 | assert(0 < 0xFFFFFF); 122 | std::uniform_int_distribution distribution(0, 0xFFFFFF); 123 | values[i] = (uint32_t)distribution(engine); 124 | writer.template write_bytes<3>((uint32_t)values[i]); 125 | break; 126 | } 127 | case 3: 128 | { 129 | assert(0 < 0xFFFFFFFF); 130 | std::uniform_int_distribution distribution(0, 0xFFFFFFFF); 131 | values[i] = (uint32_t)distribution(engine); 132 | writer.template write_bytes<4>((uint32_t)values[i]); 133 | break; 134 | } 135 | case 4: 136 | { 137 | assert(0 < 0x000000FFFFFFFFFF); 138 | std::uniform_int_distribution distribution( 139 | 0, 0x000000FFFFFFFFFF); 140 | values[i] = (uint64_t)distribution(engine); 141 | writer.template write_bytes<5>((uint64_t)values[i]); 142 | break; 143 | } 144 | case 5: 145 | { 146 | assert(0 < 0x0000FFFFFFFFFFFF); 147 | std::uniform_int_distribution distribution( 148 | 0, 0x0000FFFFFFFFFFFF); 149 | values[i] = (uint64_t)distribution(engine); 150 | writer.template write_bytes<6>((uint64_t)values[i]); 151 | break; 152 | } 153 | case 6: 154 | { 155 | assert(0 < 0x00FFFFFFFFFFFFFF); 156 | std::uniform_int_distribution distribution( 157 | 0, 0x00FFFFFFFFFFFFFF); 158 | values[i] = (uint64_t)distribution(engine); 159 | writer.template write_bytes<7>((uint64_t)values[i]); 160 | break; 161 | } 162 | case 7: 163 | { 164 | assert(0 < 0x0FFFFFFFFFFFFFFF); 165 | std::uniform_int_distribution distribution( 166 | 0, 0x0FFFFFFFFFFFFFFF); 167 | values[i] = (uint64_t)distribution(engine); 168 | writer.template write_bytes<8>((uint64_t)values[i]); 169 | break; 170 | } 171 | } 172 | } 173 | 174 | uint8_t last_uint8_t = 0; 175 | uint16_t last_uint16_t = 0; 176 | uint32_t last_uint24_t = 0; 177 | uint32_t last_uint32_t = 0; 178 | uint64_t last_uint40_t = 0; 179 | uint64_t last_uint48_t = 0; 180 | uint64_t last_uint56_t = 0; 181 | uint64_t last_uint64_t = 0; 182 | 183 | // create reader 184 | endian::stream_reader reader(buffer.data(), size); 185 | 186 | // Read values in FIFO order 187 | for (std::size_t i = 0; i < elements; i++) 188 | { 189 | switch (i % 8) 190 | { 191 | case 0: 192 | // test peek 193 | reader.template peek_bytes<1>(last_uint8_t); 194 | EXPECT_EQ(values[i], last_uint8_t); 195 | 196 | // test read 197 | last_uint8_t = 0; 198 | reader.template read_bytes<1>(last_uint8_t); 199 | EXPECT_EQ(values[i], last_uint8_t); 200 | break; 201 | case 1: 202 | // test peek 203 | reader.template peek_bytes<2>(last_uint16_t); 204 | EXPECT_EQ(values[i], last_uint16_t); 205 | 206 | // test read 207 | last_uint16_t = 0; 208 | reader.template read_bytes<2>(last_uint16_t); 209 | EXPECT_EQ(values[i], last_uint16_t); 210 | break; 211 | case 2: 212 | // test peek 213 | reader.template peek_bytes<3>(last_uint24_t); 214 | EXPECT_EQ(values[i], last_uint24_t); 215 | 216 | // test read 217 | last_uint24_t = 0; 218 | reader.template read_bytes<3>(last_uint24_t); 219 | EXPECT_EQ(values[i], last_uint24_t); 220 | break; 221 | case 3: 222 | // test peek 223 | reader.template peek_bytes<4>(last_uint32_t); 224 | EXPECT_EQ(values[i], last_uint32_t); 225 | 226 | // test read 227 | last_uint32_t = 0; 228 | reader.template read_bytes<4>(last_uint32_t); 229 | EXPECT_EQ(values[i], last_uint32_t); 230 | break; 231 | case 4: 232 | // test peek 233 | reader.template peek_bytes<5>(last_uint40_t); 234 | EXPECT_EQ(values[i], last_uint40_t); 235 | 236 | // test read 237 | last_uint40_t = 0; 238 | reader.template read_bytes<5>(last_uint40_t); 239 | EXPECT_EQ(values[i], last_uint40_t); 240 | break; 241 | case 5: 242 | // test peek 243 | reader.template peek_bytes<6>(last_uint48_t); 244 | EXPECT_EQ(values[i], last_uint48_t); 245 | 246 | // test read 247 | last_uint48_t = 0; 248 | reader.template read_bytes<6>(last_uint48_t); 249 | EXPECT_EQ(values[i], last_uint48_t); 250 | break; 251 | case 6: 252 | // test peek 253 | reader.template peek_bytes<7>(last_uint56_t); 254 | EXPECT_EQ(values[i], last_uint56_t); 255 | 256 | // test read 257 | last_uint56_t = 0; 258 | reader.template read_bytes<7>(last_uint56_t); 259 | EXPECT_EQ(values[i], last_uint56_t); 260 | break; 261 | case 7: 262 | // test peek 263 | reader.template peek_bytes<8>(last_uint64_t); 264 | EXPECT_EQ(values[i], last_uint64_t); 265 | 266 | // test read 267 | last_uint64_t = 0; 268 | reader.template read_bytes<8>(last_uint64_t); 269 | EXPECT_EQ(values[i], last_uint64_t); 270 | break; 271 | } 272 | } 273 | } 274 | 275 | template 276 | static void run_write_and_read_string_test() 277 | { 278 | const std::size_t size = 1024; 279 | std::vector buffer(size); 280 | 281 | endian::stream_writer writer(buffer.data(), size); 282 | 283 | std::string first("first first first"); 284 | std::string second("second second"); 285 | std::string third("third"); 286 | 287 | // Write the strings together with their lengths 288 | // The uint16_t-bit integers 289 | writer.template write_bytes<2>((uint16_t)first.size()); 290 | writer.write((uint8_t*)first.c_str(), first.size()); 291 | writer.template write_bytes<2>((uint16_t)second.size()); 292 | writer.write((uint8_t*)second.c_str(), second.size()); 293 | writer.template write_bytes<2>((uint16_t)third.size()); 294 | writer.write((uint8_t*)third.c_str(), third.size()); 295 | 296 | // Create reader 297 | endian::stream_reader reader(buffer.data(), size); 298 | 299 | // Read the strings together with their lengths 300 | { 301 | std::string string; 302 | uint16_t length; 303 | reader.template read_bytes<2>(length); 304 | EXPECT_EQ(first.size(), length); 305 | string.resize(length); 306 | reader.read((uint8_t*)string.c_str(), length); 307 | EXPECT_EQ(first, string); 308 | } 309 | { 310 | std::string string; 311 | uint16_t length; 312 | reader.template read_bytes<2>(length); 313 | EXPECT_EQ(second.size(), length); 314 | string.resize(length); 315 | reader.read((uint8_t*)string.c_str(), length); 316 | EXPECT_EQ(second, string); 317 | } 318 | { 319 | std::string string; 320 | uint16_t length; 321 | reader.template read_bytes<2>(length); 322 | EXPECT_EQ(third.size(), length); 323 | string.resize(length); 324 | reader.read((uint8_t*)string.c_str(), length); 325 | EXPECT_EQ(third, string); 326 | } 327 | } 328 | 329 | template 330 | static void run_write_read_vector_test() 331 | { 332 | const std::size_t size = 1024; 333 | std::vector buffer(size); 334 | endian::stream_writer writer(buffer.data(), size); 335 | 336 | using first_type = uint8_t; 337 | std::vector first(100, 'a'); 338 | using second_type = uint32_t; 339 | std::vector second(200, 1234); 340 | 341 | // Write the vectors together with their lengths 342 | // The uint16_t-bit integers 343 | writer.template write_bytes<2>((uint16_t)first.size()); 344 | writer.write((uint8_t*)first.data(), first.size() * sizeof(first_type)); 345 | 346 | // The size here refers to the number of integers 347 | // stored 348 | writer.template write_bytes<2>((uint16_t)second.size()); 349 | writer.write((uint8_t*)second.data(), second.size() * sizeof(second_type)); 350 | 351 | // Temp variables 352 | std::vector first_out; 353 | std::vector second_out; 354 | 355 | // Create reader 356 | endian::stream_reader reader(buffer.data(), size); 357 | 358 | { 359 | uint16_t length; 360 | reader.template read_bytes<2>(length); 361 | EXPECT_EQ(first.size(), length); 362 | 363 | std::vector vector; 364 | vector.resize(length); 365 | reader.read((uint8_t*)vector.data(), length * sizeof(first_type)); 366 | EXPECT_EQ(first, vector); 367 | } 368 | { 369 | uint16_t length; 370 | reader.template read_bytes<2>(length); 371 | EXPECT_EQ(second.size(), length); 372 | 373 | std::vector vector; 374 | vector.resize(length); 375 | reader.read((uint8_t*)vector.data(), length * sizeof(second_type)); 376 | EXPECT_EQ(second, vector); 377 | } 378 | } 379 | 380 | template 381 | static void test_stream_operators() 382 | { 383 | size_t number_of_integers = 16; 384 | auto buffer = std::vector(sizeof(size_t) * number_of_integers); 385 | auto writer = 386 | endian::stream_writer(buffer.data(), buffer.size()); 387 | auto reader = 388 | endian::stream_reader(buffer.data(), buffer.size()); 389 | 390 | for (size_t i = 0; i < number_of_integers; i++) 391 | { 392 | writer << i; 393 | } 394 | 395 | for (size_t i = 0; i < number_of_integers; i++) 396 | { 397 | size_t value; 398 | reader >> value; 399 | EXPECT_EQ(value, i); 400 | } 401 | } 402 | 403 | template 404 | static void test_reader_and_writer_api() 405 | { 406 | // Not allowed by the starndard - maybe will be allowed some day: 407 | // https://stackoverflow.com/a/31460827 408 | // { 409 | // SCOPED_TRACE("u8"); 410 | // using value_type = uint8_t; 411 | // value_type min = std::numeric_limits::min(); 412 | // value_type max = std::numeric_limits::max(); 413 | // write_and_read_value_type(min, max); 414 | // write_and_read_random_value_type(min, 415 | // max); 416 | // } 417 | // { 418 | // SCOPED_TRACE("i8"); 419 | // using value_type = int8_t; 420 | // value_type min = std::numeric_limits::min(); 421 | // value_type max = std::numeric_limits::max(); 422 | // write_and_read_value_type(min, max); 423 | // write_and_read_random_value_type(min, 424 | // max); 425 | // } 426 | { 427 | SCOPED_TRACE("u16"); 428 | using value_type = uint16_t; 429 | value_type min = std::numeric_limits::min(); 430 | value_type max = std::numeric_limits::max(); 431 | write_and_read_value_type(min, max); 432 | write_and_read_random_value_type(min, max); 433 | } 434 | { 435 | SCOPED_TRACE("i16"); 436 | using value_type = int16_t; 437 | value_type min = std::numeric_limits::min(); 438 | value_type max = std::numeric_limits::max(); 439 | write_and_read_value_type(min, max); 440 | write_and_read_random_value_type(min, max); 441 | } 442 | { 443 | SCOPED_TRACE("u24"); 444 | using value_type = uint32_t; 445 | value_type min = 0x00000000; 446 | value_type max = 0x00FFFFFF; 447 | write_and_read_value_type(min, max); 448 | write_and_read_random_value_type(min, max); 449 | } 450 | { 451 | SCOPED_TRACE("u32"); 452 | using value_type = uint32_t; 453 | value_type min = std::numeric_limits::min(); 454 | value_type max = std::numeric_limits::max(); 455 | write_and_read_value_type(min, max); 456 | write_and_read_random_value_type(min, max); 457 | } 458 | { 459 | SCOPED_TRACE("i32"); 460 | using value_type = int32_t; 461 | value_type min = std::numeric_limits::min(); 462 | value_type max = std::numeric_limits::max(); 463 | write_and_read_value_type(min, max); 464 | write_and_read_random_value_type(min, max); 465 | } 466 | { 467 | SCOPED_TRACE("u40"); 468 | using value_type = uint64_t; 469 | value_type min = 0x0000000000000000; 470 | value_type max = 0x000000FFFFFFFFFF; 471 | write_and_read_value_type(min, max); 472 | write_and_read_random_value_type(min, max); 473 | } 474 | { 475 | SCOPED_TRACE("u48"); 476 | using value_type = uint64_t; 477 | value_type min = 0x0000000000000000; 478 | value_type max = 0x0000FFFFFFFFFFFF; 479 | write_and_read_value_type(min, max); 480 | write_and_read_random_value_type(min, max); 481 | } 482 | { 483 | SCOPED_TRACE("u56"); 484 | using value_type = uint64_t; 485 | value_type min = 0x0000000000000000; 486 | value_type max = 0x00FFFFFFFFFFFFFF; 487 | write_and_read_value_type(min, max); 488 | write_and_read_random_value_type(min, max); 489 | } 490 | { 491 | SCOPED_TRACE("u64"); 492 | using value_type = uint64_t; 493 | value_type min = std::numeric_limits::min(); 494 | value_type max = std::numeric_limits::max(); 495 | write_and_read_value_type(min, max); 496 | write_and_read_random_value_type(min, max); 497 | } 498 | { 499 | SCOPED_TRACE("i64"); 500 | using value_type = int64_t; 501 | value_type min = std::numeric_limits::min(); 502 | value_type max = std::numeric_limits::max(); 503 | write_and_read_value_type(min, max); 504 | write_and_read_random_value_type(min, max); 505 | } 506 | 507 | run_write_peek_and_read_variadic_bytes(); 508 | run_write_and_read_string_test(); 509 | run_write_read_vector_test(); 510 | test_stream_operators(); 511 | } 512 | 513 | TEST(test_stream_writer_reader, test_reader_and_writer) 514 | { 515 | { 516 | SCOPED_TRACE("big endian"); 517 | test_reader_and_writer_api(); 518 | } 519 | 520 | { 521 | SCOPED_TRACE("little endian"); 522 | test_reader_and_writer_api(); 523 | } 524 | } 525 | --------------------------------------------------------------------------------