aboutsummaryrefslogtreecommitdiffstats
path: root/test/test_basictypeconv.cpp
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2021-02-08 15:02:43 +0100
committerSven Gothel <[email protected]>2021-02-08 15:02:43 +0100
commitb218b12a155a2f07eabbd02e3fbada3feed3aab7 (patch)
treeb432c68247f08810927520b99c3b03af45c05492 /test/test_basictypeconv.cpp
parent112528c73f09b262bd53f817f9d9ff9343af0ee9 (diff)
basic_types.hpp: Cleanup; Add constexpr 'enum class endian', 'pointer_cast()' and 'bit_cast()' and have all byte-order conversion and get/set functions be of constexpr; Add generalized template [get|put]_value(..) and to_hex_string(..)
Cleanup - Split basic_types.hpp -> basic_types.hpp + byte_util.hpp + int_types.hpp + string_util.hpp - Moved nsize_t, snsize_t to int_types.hpp and using 'uint_fast32_t' and 'int_fast32_t' as natural types. - Renamed cpp_lang_macros.hpp -> cpp_lang_util.hpp +++ Add constexpr 'enum class endian', 'pointer_cast()' and 'bit_cast()' and have all byte-order conversion and get/set functions be of constexpr. - Exposing '__builtin_bit_cast(Dest_type, arg)' via 'constexpr bool is_builtin_bit_cast_available()' and type traits. - Adding constexpr bit_cast<>() template for C++17 (a C++20 std), allowing constexpr type conversion using '__builtin_bit_cast(Dest_type, arg)', if the latter is available. - Adding constexpr pointer_cast<>() template, allowing constexpr pointer type conversion. Either using '__builtin_bit_cast(Dest_type, arg)' or reinterpret_cast<>(). - Add constexpr 'enum class endian' API, providing compile-time C++ endian evaluation w/o predefined macros. Inspired by C++20. - Replace linux bswap_[16,32,64] with either __builtin_bswap[16,32,64] or own const definition, both allowing constexpr - Add unified overloaded 'constexpr bswap(uint[16,32,64,128,192,256]_t const &)' using constexpr endian API. - Have all [get|put]_<type>(..) operations be of constexpr, using pointer_cast<>() instead of plain reinterpret_cast<>() and the new 'constexpr bswap(..)' methods. +++ Add generalized template [get|put]_value(..) and to_hex_string(..) - Add generalized template 'constexpr T [get|put]_value(..) {}' for std::is_standard_layout_v<T> - Add generalized template 'inline to_hex_string(T const &) {}' for std::is_standard_layout_v<T> +++ All of the above is covered by unit test 'test_basictypecon.cpp' '-std=c++17': - gcc 8.3.0 on arm32, arm64: __builtin_bit_cast() not available - gcc 10.2.1 on amd64: __builtin_bit_cast() not available - clang 9.0.1 on amd64, arm64: __builtin_bit_cast() is available - clang 11.0.1 on amd64: __builtin_bit_cast() is available Full build time (user) incl unit tests (C++ and Java) on GCC - amd64 gcc 11.0.1, 32 cores: 1m38s - arm64 gcc 8.3.0, 4 cores: 13m30s - arm32 gcc 8.3.0, 4 cores: 17m58s
Diffstat (limited to 'test/test_basictypeconv.cpp')
-rw-r--r--test/test_basictypeconv.cpp346
1 files changed, 346 insertions, 0 deletions
diff --git a/test/test_basictypeconv.cpp b/test/test_basictypeconv.cpp
new file mode 100644
index 0000000..6d56287
--- /dev/null
+++ b/test/test_basictypeconv.cpp
@@ -0,0 +1,346 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2021 Gothel Software e.K.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <iostream>
+#include <cassert>
+#include <cinttypes>
+#include <cstring>
+
+#define CATCH_CONFIG_MAIN
+#include <catch2/catch_amalgamated.hpp>
+
+#include <jau/basic_types.hpp>
+
+static constexpr inline bool VERBOSE = false;
+
+/**
+ * Test private impl namespace
+ */
+namespace test_impl {
+ template<class Dummy_type>
+ constexpr bool isLittleEndian2_impl(std::enable_if_t<jau::has_endian_little_v<Dummy_type>, bool> = true) noexcept {
+ return true;
+ }
+
+ template<class Dummy_type>
+ constexpr bool isLittleEndian2_impl(std::enable_if_t<!jau::has_endian_little_v<Dummy_type>, bool> = true) noexcept {
+ return false;
+ }
+}
+
+/**
+ * Just demonstrating usage of our type-traits
+ * in a convenient API manner w/o requiring to add the dummy template type.
+ */
+constexpr bool isLittleEndian2() noexcept {
+ return test_impl::isLittleEndian2_impl<bool>();
+}
+
+
+TEST_CASE( "Endianess Test 00", "[endian]" ) {
+ fprintf(stderr, "********************************************************************************\n");
+ fprintf(stderr, "is_builtin_bit_cast_available: %d\n", jau::is_builtin_bit_cast_available());
+ fprintf(stderr, "endian: %s\n", jau::to_string(jau::endian::native).c_str());
+ fprintf(stderr, "********************************************************************************\n");
+
+ const bool cpp_is_little =
+ #if BYTE_ORDER == LITTLE_ENDIAN
+ true;
+ #else
+ false;
+ #endif
+ const bool cpp_is_big =
+ #if BYTE_ORDER == BIG_ENDIAN
+ true;
+ #else
+ false;
+ #endif
+ const bool is_little = jau::endian::little == jau::endian::native;
+ const bool is_big = jau::endian::big == jau::endian::native;
+ REQUIRE( cpp_is_little == is_little );
+ REQUIRE( cpp_is_little == jau::isLittleEndian() );
+ REQUIRE( cpp_is_big == is_big );
+ REQUIRE( is_little == isLittleEndian2());
+}
+
+template<typename Value_type>
+static void print(const Value_type a) {
+ const uint8_t * pa = reinterpret_cast<const uint8_t *>(&a);
+ for(std::size_t i=0; i<sizeof(Value_type); i++) {
+ fprintf(stderr, "a[%zu] 0x%X, ", i, pa[i]);
+ }
+}
+
+template<typename Value_type>
+static bool compare(const Value_type a, const Value_type b) {
+ const uint8_t * pa = reinterpret_cast<const uint8_t *>(&a);
+ const uint8_t * pb = reinterpret_cast<const uint8_t *>(&b);
+ bool res = true;
+ for(std::size_t i=0; i<sizeof(Value_type) && res; i++) {
+ res = pa[i] == pb[i];
+ if( !res ) {
+ fprintf(stderr, "pa[%zu] 0x%X != pb[%zu] 0x%X\n", i, pa[i], i, pb[i]);
+ }
+ }
+ return res;
+}
+
+template<typename Value_type>
+static void test_byteorder(const Value_type v_cpu,
+ const Value_type v_le,
+ const Value_type v_be)
+{
+ if( VERBOSE ) {
+ fprintf(stderr, "test_byteorder: sizeof %zu; platform littleEndian %d", sizeof(Value_type), jau::isLittleEndian());
+ fprintf(stderr, "\ncpu: %s: ", jau::to_hex_string(v_cpu).c_str()); print(v_cpu);
+ fprintf(stderr, "\nle_: %s: ", jau::to_hex_string(v_le).c_str()); print(v_le);
+ fprintf(stderr, "\nbe_: %s: ", jau::to_hex_string(v_be).c_str()); print(v_be);
+ fprintf(stderr, "\n");
+ }
+ {
+ Value_type r1_le = jau::bswap(v_be);
+ REQUIRE( r1_le == v_le );
+ Value_type r1_be = jau::bswap(v_le);
+ REQUIRE( r1_be == v_be );
+ }
+ {
+ #if BYTE_ORDER == LITTLE_ENDIAN
+ REQUIRE( compare(v_le, v_cpu) == true );
+ Value_type r1_cpu = jau::bswap(v_be);
+ REQUIRE( r1_cpu == v_cpu );
+ #else
+ REQUIRE( compare(v_be, v_cpu) == true );
+ Value_type r1_cpu = jau::bswap(v_le);
+ REQUIRE( r1_cpu == v_cpu );
+ #endif
+ }
+ {
+ Value_type r1_cpu = jau::le_to_cpu(v_le);
+ Value_type r2_cpu = jau::be_to_cpu(v_be);
+ REQUIRE( r1_cpu == v_cpu );
+ REQUIRE( r2_cpu == v_cpu );
+ }
+}
+
+static uint16_t compose(const uint8_t n1, const uint8_t n2) {
+ uint16_t dest;
+ uint8_t * p_dest = reinterpret_cast<uint8_t*>(&dest);
+ p_dest[0] = n1;
+ p_dest[1] = n2;
+ return dest;
+}
+static uint32_t compose(const uint8_t n1, const uint8_t n2, const uint8_t n3, const uint8_t n4) {
+ uint32_t dest;
+ uint8_t * p_dest = reinterpret_cast<uint8_t*>(&dest);
+ p_dest[0] = n1;
+ p_dest[1] = n2;
+ p_dest[2] = n3;
+ p_dest[3] = n4;
+ return dest;
+}
+static uint64_t compose(const uint8_t n1, const uint8_t n2, const uint8_t n3, const uint8_t n4,
+ const uint8_t n5, const uint8_t n6, const uint8_t n7, const uint8_t n8) {
+ uint64_t dest;
+ uint8_t * p_dest = reinterpret_cast<uint8_t*>(&dest);
+ p_dest[0] = n1;
+ p_dest[1] = n2;
+ p_dest[2] = n3;
+ p_dest[3] = n4;
+ p_dest[4] = n5;
+ p_dest[5] = n6;
+ p_dest[6] = n7;
+ p_dest[7] = n8;
+ return dest;
+}
+
+template<typename Value_type>
+static Value_type compose(const uint8_t lowest_value, const bool little_endian) {
+ Value_type dest;
+ uint8_t * p_dest = reinterpret_cast<uint8_t*>(&dest);
+ uint8_t byte_value = lowest_value;
+ if( little_endian ) {
+ for(size_t i=0; i<sizeof(dest); i++, byte_value++) {
+ p_dest[i] = byte_value;
+ }
+ } else {
+ for(ssize_t i=sizeof(dest)-1; i>=0; i--, byte_value++) {
+ p_dest[i] = byte_value;
+ }
+ }
+ return dest;
+}
+
+TEST_CASE( "Integer Type Byte Order Test 01", "[byteorder][bswap]" ) {
+ {
+ uint16_t cpu = 0x3210U;
+ uint16_t le = compose(0x10, 0x32); // stream: 1032
+ uint16_t be = compose(0x32, 0x10); // stream: 3210
+ test_byteorder(cpu, le, be);
+ }
+ {
+ uint32_t cpu = 0x76543210U;
+ uint32_t le = compose(0x10, 0x32, 0x54, 0x76); // stream: 10325476
+ uint32_t be = compose(0x76, 0x54, 0x32, 0x10); // stream: 76543210
+ test_byteorder(cpu, le, be);
+ }
+ {
+ uint64_t cpu = 0xfedcba9876543210ULL;
+ uint64_t le = compose(0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe); // stream: 1032547698badcfe
+ uint64_t be = compose(0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10); // stream: fedcba9876543210
+ test_byteorder(cpu, le, be);
+ }
+ {
+ jau::uint128_t le = compose<jau::uint128_t>(0x01, true /* little_endian */);
+ jau::uint128_t be = compose<jau::uint128_t>(0x01, false /* little_endian */);
+ jau::uint128_t cpu = jau::isLittleEndian() ? le : be;
+ test_byteorder(cpu, le, be);
+ }
+ {
+ jau::uint192_t le = compose<jau::uint192_t>(0x01, true /* little_endian */);
+ jau::uint192_t be = compose<jau::uint192_t>(0x01, false /* little_endian */);
+ jau::uint192_t cpu = jau::isLittleEndian() ? le : be;
+ test_byteorder(cpu, le, be);
+ }
+ {
+ jau::uint256_t le = compose<jau::uint256_t>(0x01, true /* little_endian */);
+ jau::uint256_t be = compose<jau::uint256_t>(0x01, false /* little_endian */);
+ jau::uint256_t cpu = jau::isLittleEndian() ? le : be;
+ test_byteorder(cpu, le, be);
+ }
+}
+
+template<typename Value_type>
+static void test_value_cpu(const Value_type v1, const Value_type v2, const Value_type v3) {
+ uint8_t buffer[3 * sizeof(Value_type)];
+ jau::put_value(buffer, sizeof(Value_type)*0, v1);
+ jau::put_value(buffer, sizeof(Value_type)*1, v2);
+ jau::put_value(buffer, sizeof(Value_type)*2, v3);
+ const Value_type r1 = jau::get_value<Value_type>(buffer, sizeof(Value_type)*0);
+ const Value_type r2 = jau::get_value<Value_type>(buffer, sizeof(Value_type)*1);
+ const Value_type r3 = jau::get_value<Value_type>(buffer, sizeof(Value_type)*2);
+ REQUIRE( r1 == v1);
+ REQUIRE( r2 == v2);
+ REQUIRE( r3 == v3);
+}
+
+TEST_CASE( "Integer Get/Put in CPU Byte Order Test 02", "[byteorder][get][put]" ) {
+ {
+ uint8_t a = 0x01, b = 0x11, c = 0xff;
+ test_value_cpu(a, b, c);
+ }
+ {
+ uint16_t a = 0x0123, b = 0x1122, c = 0xffee;
+ test_value_cpu(a, b, c);
+ }
+ {
+ uint32_t a = 0x01234567U, b = 0x11223344U, c = 0xffeeddccU;
+ test_value_cpu(a, b, c);
+ }
+ {
+ uint64_t a = 0x0123456789abcdefULL, b = 0x1122334455667788ULL, c = 0xffeeddcc99887766ULL;
+ test_value_cpu(a, b, c);
+ }
+ {
+ jau::uint128_t a = compose<jau::uint128_t>(0x01, jau::isLittleEndian());
+ jau::uint128_t b = compose<jau::uint128_t>(0x20, jau::isLittleEndian());
+ jau::uint128_t c = compose<jau::uint128_t>(0x40, jau::isLittleEndian());
+ test_value_cpu(a, b, c);
+ }
+ {
+ jau::uint192_t a = compose<jau::uint192_t>(0x01, jau::isLittleEndian());
+ jau::uint192_t b = compose<jau::uint192_t>(0x20, jau::isLittleEndian());
+ jau::uint192_t c = compose<jau::uint192_t>(0x40, jau::isLittleEndian());
+ test_value_cpu(a, b, c);
+ }
+ {
+ jau::uint256_t a = compose<jau::uint256_t>(0x01, jau::isLittleEndian());
+ jau::uint256_t b = compose<jau::uint256_t>(0x20, jau::isLittleEndian());
+ jau::uint256_t c = compose<jau::uint256_t>(0x40, jau::isLittleEndian());
+ test_value_cpu(a, b, c);
+ }
+}
+
+template<typename Value_type>
+static void test_value_littlebig(const Value_type v_cpu, const Value_type v_le, const Value_type v_be) {
+ if( VERBOSE ) {
+ fprintf(stderr, "test_value_littlebig: sizeof %zu; platform littleEndian %d", sizeof(Value_type), jau::isLittleEndian());
+ fprintf(stderr, "\ncpu: %s: ", jau::to_hex_string(v_cpu).c_str()); print(v_cpu);
+ fprintf(stderr, "\nle_: %s: ", jau::to_hex_string(v_le).c_str()); print(v_le);
+ fprintf(stderr, "\nbe_: %s: ", jau::to_hex_string(v_be).c_str()); print(v_be);
+ fprintf(stderr, "\n");
+ }
+ uint8_t buffer[2 * sizeof(Value_type)];
+
+ jau::put_value(buffer, sizeof(Value_type)*0, v_cpu, true /* little_endian */);
+ jau::put_value(buffer, sizeof(Value_type)*1, v_cpu, false /* little_endian */);
+
+ const Value_type rle_raw = jau::get_value<Value_type>(buffer, sizeof(Value_type)*0);
+ const Value_type rle_cpu = jau::get_value<Value_type>(buffer, sizeof(Value_type)*0, true /* little_endian */);
+ REQUIRE( rle_raw == v_le);
+ REQUIRE( rle_cpu == v_cpu);
+
+ const Value_type rbe_raw = jau::get_value<Value_type>(buffer, sizeof(Value_type)*1);
+ const Value_type rbe_cpu = jau::get_value<Value_type>(buffer, sizeof(Value_type)*1, false /* little_endian */);
+ REQUIRE( rbe_raw == v_be);
+ REQUIRE( rbe_cpu == v_cpu);
+}
+
+TEST_CASE( "Integer Get/Put in explicit Byte Order Test 03", "[byteorder][get][put]" ) {
+ {
+ uint16_t cpu = 0x3210U;
+ uint16_t le = compose(0x10, 0x32); // stream: 1032
+ uint16_t be = compose(0x32, 0x10); // stream: 3210
+ test_value_littlebig(cpu, le, be);
+ }
+ {
+ uint32_t cpu = 0x76543210U;
+ uint32_t le = compose(0x10, 0x32, 0x54, 0x76); // stream: 10325476
+ uint32_t be = compose(0x76, 0x54, 0x32, 0x10); // stream: 76543210
+ test_value_littlebig(cpu, le, be);
+ }
+ {
+ uint64_t cpu = 0xfedcba9876543210ULL;
+ uint64_t le = compose(0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe); // stream: 1032547698badcfe
+ uint64_t be = compose(0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10); // stream: fedcba9876543210
+ test_value_littlebig(cpu, le, be);
+ }
+ {
+ jau::uint128_t le = compose<jau::uint128_t>(0x01, true /* little_endian */);
+ jau::uint128_t be = compose<jau::uint128_t>(0x01, false /* little_endian */);
+ jau::uint128_t cpu = jau::isLittleEndian() ? le : be;
+ test_value_littlebig(cpu, le, be);
+ }
+ {
+ jau::uint192_t le = compose<jau::uint192_t>(0x01, true /* little_endian */);
+ jau::uint192_t be = compose<jau::uint192_t>(0x01, false /* little_endian */);
+ jau::uint192_t cpu = jau::isLittleEndian() ? le : be;
+ test_value_littlebig(cpu, le, be);
+ }
+ {
+ jau::uint256_t le = compose<jau::uint256_t>(0x01, true /* little_endian */);
+ jau::uint256_t be = compose<jau::uint256_t>(0x01, false /* little_endian */);
+ jau::uint256_t cpu = jau::isLittleEndian() ? le : be;
+ test_value_littlebig(cpu, le, be);
+ }
+}
+