From 6b12719cc26349564fac9474356b3c76e8d9eca4 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 18 Feb 2026 12:06:48 -0500 Subject: [PATCH 1/6] Fix documentation of run-time vs consteval functions --- doc/modules/ROOT/pages/byte_conversions.adoc | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/doc/modules/ROOT/pages/byte_conversions.adoc b/doc/modules/ROOT/pages/byte_conversions.adoc index 17e4d29..1bba96f 100644 --- a/doc/modules/ROOT/pages/byte_conversions.adoc +++ b/doc/modules/ROOT/pages/byte_conversions.adoc @@ -323,9 +323,12 @@ constexpr auto bytes = to_be_bytes(verified_u32{u32{0x01020304}}); Reconstructs a safe integer value from a big-endian byte array. The bytes are reinterpreted as the underlying type and then converted from big-endian to native byte order using `from_be`. +=== Runtime Overload + [source,c++] ---- template + requires (!is_verified_type_v) constexpr auto from_be_bytes(const std::span bytes) -> T; ---- @@ -366,6 +369,33 @@ auto reconstructed = from_be_bytes(std::span{to_be_byte // reconstructed == original ---- +=== Verified Overload + +[source,c++] +---- +template +consteval auto from_be_bytes(const std::span bytes) -> T; +---- + +Compile-time only overload for verified types. +The span must have a fixed extent matching `sizeof(T)`; a mismatched extent produces a `static_assert` failure. + +==== Example + +[source,c++] +---- +using namespace boost::safe_numbers; + +constexpr std::array bytes {std::byte{0x01}, std::byte{0x02}, std::byte{0x03}, std::byte{0x04}}; +constexpr auto val = from_be_bytes(std::span{bytes}); +// val == verified_u32{u32{0x01020304}} + +// Round-trip +constexpr auto original = verified_u32{u32{0xDEADBEEF}}; +constexpr auto reconstructed = from_be_bytes(std::span{to_be_bytes(original)}); +static_assert(reconstructed == original); +---- + == to_le_bytes Converts a safe integer value to a little-endian byte array. @@ -427,9 +457,12 @@ constexpr auto bytes = to_le_bytes(verified_u32{u32{0x01020304}}); Reconstructs a safe integer value from a little-endian byte array. The bytes are reinterpreted as the underlying type and then converted from little-endian to native byte order using `from_le`. +=== Runtime Overload + [source,c++] ---- template + requires (!is_verified_type_v) constexpr auto from_le_bytes(const std::span bytes) -> T; ---- @@ -469,3 +502,30 @@ auto original = u32{0xDEADBEEF}; auto reconstructed = from_le_bytes(std::span{to_le_bytes(original)}); // reconstructed == original ---- + +=== Verified Overload + +[source,c++] +---- +template +consteval auto from_le_bytes(const std::span bytes) -> T; +---- + +Compile-time only overload for verified types. +The span must have a fixed extent matching `sizeof(T)`; a mismatched extent produces a `static_assert` failure. + +==== Example + +[source,c++] +---- +using namespace boost::safe_numbers; + +constexpr std::array bytes {std::byte{0x04}, std::byte{0x03}, std::byte{0x02}, std::byte{0x01}}; +constexpr auto val = from_le_bytes(std::span{bytes}); +// val == verified_u32{u32{0x01020304}} + +// Round-trip +constexpr auto original = verified_u32{u32{0xDEADBEEF}}; +constexpr auto reconstructed = from_le_bytes(std::span{to_le_bytes(original)}); +static_assert(reconstructed == original); +---- From 5457a5a30ca04b3441d8b62780c34febfc0278d4 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 18 Feb 2026 12:10:53 -0500 Subject: [PATCH 2/6] Add native endian overload --- .../boost/safe_numbers/byte_conversions.hpp | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/include/boost/safe_numbers/byte_conversions.hpp b/include/boost/safe_numbers/byte_conversions.hpp index 7cf3df1..d8b55ff 100644 --- a/include/boost/safe_numbers/byte_conversions.hpp +++ b/include/boost/safe_numbers/byte_conversions.hpp @@ -234,6 +234,60 @@ consteval auto from_le_bytes(const std::span bytes) -> T return from_le(T{std::bit_cast(arr)}); } +template + requires (!detail::is_verified_type_v) +constexpr auto to_ne_bytes(const T value) noexcept -> std::array +{ + if constexpr (std::endian::native == std::endian::little) + { + return to_le_bytes(value); + } + else + { + return to_be_bytes(value); + } +} + +template +consteval auto to_ne_bytes(const detail::verified_type_basis value) noexcept -> std::array +{ + if constexpr (std::endian::native == std::endian::little) + { + return to_le_bytes(value); + } + else + { + return to_be_bytes(value); + } +} + +template + requires (!detail::is_verified_type_v) +constexpr auto from_ne_bytes(const std::span bytes) -> T +{ + if constexpr (std::endian::native == std::endian::little) + { + return from_le_bytes(bytes); + } + else + { + return from_be_bytes(bytes); + } +} + +template +consteval auto from_ne_bytes(const std::span bytes) -> T +{ + if constexpr (std::endian::native == std::endian::little) + { + return from_le_bytes(bytes); + } + else + { + return from_be_bytes(bytes); + } +} + } // namespace boost::safe_numbers #endif //BOOST_SAFE_NUMBERS_BYTE_CONVERSIONS_HPP From 870c6a51ec5c8bbc47e43b37345ef5015a4901bb Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 18 Feb 2026 12:11:03 -0500 Subject: [PATCH 3/6] Test native endian overloads --- test/Jamfile | 1 + test/test_to_from_ne_bytes.cpp | 211 +++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 test/test_to_from_ne_bytes.cpp diff --git a/test/Jamfile b/test/Jamfile index 8948f7f..ed6eb2a 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -136,6 +136,7 @@ run test_to_from_be_bytes.cpp ; compile-fail compile_fail_from_be_bytes_extent.cpp ; run test_to_from_le_bytes.cpp ; compile-fail compile_fail_from_le_bytes_extent.cpp ; +run test_to_from_ne_bytes.cpp ; # Utility function tests run test_isqrt.cpp ; diff --git a/test/test_to_from_ne_bytes.cpp b/test/test_to_from_ne_bytes.cpp new file mode 100644 index 0000000..ac36321 --- /dev/null +++ b/test/test_to_from_ne_bytes.cpp @@ -0,0 +1,211 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE + +import boost.safe_numbers; + +#else + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + +using namespace boost::safe_numbers; + +// ============================================================================= +// to_ne_bytes: the byte representation matches the platform's native layout +// ============================================================================= + +void test_to_ne_bytes_matches_platform() +{ + // On little-endian, to_ne_bytes should match to_le_bytes + // On big-endian, to_ne_bytes should match to_be_bytes + { + const auto ne_bytes = to_ne_bytes(u32{0x01020304U}); + if constexpr (std::endian::native == std::endian::little) + { + const auto le_bytes = to_le_bytes(u32{0x01020304U}); + BOOST_TEST(ne_bytes == le_bytes); + } + else + { + const auto be_bytes = to_be_bytes(u32{0x01020304U}); + BOOST_TEST(ne_bytes == be_bytes); + } + } + { + const auto ne_bytes = to_ne_bytes(u64{0x0102030405060708ULL}); + if constexpr (std::endian::native == std::endian::little) + { + const auto le_bytes = to_le_bytes(u64{0x0102030405060708ULL}); + BOOST_TEST(ne_bytes == le_bytes); + } + else + { + const auto be_bytes = to_be_bytes(u64{0x0102030405060708ULL}); + BOOST_TEST(ne_bytes == be_bytes); + } + } +} + +void test_to_ne_bytes_is_identity() +{ + // to_ne_bytes should produce the same bytes as a bit_cast of the value + { + const auto val = u32{0xDEADBEEFU}; + const auto ne_bytes = to_ne_bytes(val); + const auto raw_bytes = std::bit_cast>(val); + BOOST_TEST(ne_bytes == raw_bytes); + } + { + const auto val = u16{static_cast(0xABCD)}; + const auto ne_bytes = to_ne_bytes(val); + const auto raw_bytes = std::bit_cast>(val); + BOOST_TEST(ne_bytes == raw_bytes); + } + { + const auto val = u64{0x0123456789ABCDEFULL}; + const auto ne_bytes = to_ne_bytes(val); + const auto raw_bytes = std::bit_cast>(val); + BOOST_TEST(ne_bytes == raw_bytes); + } +} + +// ============================================================================= +// from_ne_bytes: reconstruct from native byte patterns +// ============================================================================= + +void test_from_ne_bytes_matches_platform() +{ + { + const std::array bytes {std::byte{0x01}, std::byte{0x02}, std::byte{0x03}, std::byte{0x04}}; + const auto ne_val = from_ne_bytes(std::span{bytes}); + if constexpr (std::endian::native == std::endian::little) + { + const auto le_val = from_le_bytes(std::span{bytes}); + BOOST_TEST(ne_val == le_val); + } + else + { + const auto be_val = from_be_bytes(std::span{bytes}); + BOOST_TEST(ne_val == be_val); + } + } +} + +// ============================================================================= +// Round-trip: to_ne_bytes -> from_ne_bytes == identity +// ============================================================================= + +template +void test_round_trip(T value) +{ + const auto bytes = to_ne_bytes(value); + const auto result = from_ne_bytes(std::span{bytes}); + BOOST_TEST(result == value); +} + +void test_round_trips() +{ + test_round_trip(u8{0}); + test_round_trip(u8{0xFF}); + test_round_trip(u8{0x42}); + + test_round_trip(u16{0}); + test_round_trip(u16{static_cast(0xFFFF)}); + test_round_trip(u16{static_cast(0xABCD)}); + + test_round_trip(u32{0U}); + test_round_trip(u32{0xFFFFFFFFU}); + test_round_trip(u32{0xDEADBEEFU}); + + test_round_trip(u64{0ULL}); + test_round_trip(u64{0xFFFFFFFFFFFFFFFFULL}); + test_round_trip(u64{0x0123456789ABCDEFULL}); +} + +// ============================================================================= +// from_ne_bytes with dynamic extent +// ============================================================================= + +void test_from_ne_bytes_dynamic_size_mismatch() +{ + const std::array bytes {std::byte{0x01}, std::byte{0x02}}; + std::span dynamic_span {bytes}; + + BOOST_TEST_THROWS(from_ne_bytes(dynamic_span), std::domain_error); +} + +void test_from_ne_bytes_dynamic_size_match() +{ + const auto val = u32{0x01020304U}; + const auto bytes = to_ne_bytes(val); + std::span dynamic_span {bytes}; + + const auto result = from_ne_bytes(dynamic_span); + BOOST_TEST(result == val); +} + +// ============================================================================= +// Verified overloads +// ============================================================================= + +void test_verified_to_ne_bytes() +{ + // Verify consteval works and produces correct bytes via round-trip + { + constexpr auto original = verified_u32{u32{0x01020304U}}; + constexpr auto bytes = to_ne_bytes(original); + static_assert(bytes.size() == 4); + } + { + constexpr auto original = verified_u64{u64{0x0102030405060708ULL}}; + constexpr auto bytes = to_ne_bytes(original); + static_assert(bytes.size() == 8); + } +} + +void test_verified_from_ne_bytes() +{ + { + constexpr auto original = verified_u32{u32{0xDEADBEEFU}}; + constexpr auto bytes = to_ne_bytes(original); + constexpr auto reconstructed = from_ne_bytes(std::span{bytes}); + static_assert(reconstructed == original); + } + { + constexpr auto original = verified_u64{u64{0x0123456789ABCDEFULL}}; + constexpr auto bytes = to_ne_bytes(original); + constexpr auto reconstructed = from_ne_bytes(std::span{bytes}); + static_assert(reconstructed == original); + } +} + +int main() +{ + test_to_ne_bytes_matches_platform(); + test_to_ne_bytes_is_identity(); + + test_from_ne_bytes_matches_platform(); + + test_round_trips(); + + test_from_ne_bytes_dynamic_size_mismatch(); + test_from_ne_bytes_dynamic_size_match(); + + test_verified_to_ne_bytes(); + test_verified_from_ne_bytes(); + + return boost::report_errors(); +} From 040dd7fc06ee451caf27c4ccc0685a12caf59491 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 18 Feb 2026 12:11:12 -0500 Subject: [PATCH 4/6] Add docs of ne overloads --- doc/modules/ROOT/pages/api_reference.adoc | 8 +- doc/modules/ROOT/pages/byte_conversions.adoc | 126 +++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/doc/modules/ROOT/pages/api_reference.adoc b/doc/modules/ROOT/pages/api_reference.adoc index ee05a89..aa77884 100644 --- a/doc/modules/ROOT/pages/api_reference.adoc +++ b/doc/modules/ROOT/pages/api_reference.adoc @@ -224,6 +224,12 @@ https://www.boost.org/LICENSE_1_0.txt | xref:byte_conversions.adoc[`from_le_bytes`] | Reconstructs a safe integer from a little-endian byte array + +| xref:byte_conversions.adoc[`to_ne_bytes`] +| Converts a safe integer to a native-endian byte array + +| xref:byte_conversions.adoc[`from_ne_bytes`] +| Reconstructs a safe integer from a native-endian byte array |=== === Arithmetic @@ -296,7 +302,7 @@ This header is not included in the convenience header since it requires external | Bounded unsigned integer type (`bounded_uint`) | `` -| Byte order conversion functions (`to_be`, `from_be`, `to_le`, `from_le`, `to_be_bytes`, `from_be_bytes`, `to_le_bytes`, `from_le_bytes`) +| Byte order conversion functions (`to_be`, `from_be`, `to_le`, `from_le`, `to_be_bytes`, `from_be_bytes`, `to_le_bytes`, `from_le_bytes`, `to_ne_bytes`, `from_ne_bytes`) | `` | Verified integer types (`verified_u8`, `verified_u16`, `verified_u32`, `verified_u64`, `verified_u128`, `verified_bounded_integer`) diff --git a/doc/modules/ROOT/pages/byte_conversions.adoc b/doc/modules/ROOT/pages/byte_conversions.adoc index 1bba96f..084963f 100644 --- a/doc/modules/ROOT/pages/byte_conversions.adoc +++ b/doc/modules/ROOT/pages/byte_conversions.adoc @@ -529,3 +529,129 @@ constexpr auto original = verified_u32{u32{0xDEADBEEF}}; constexpr auto reconstructed = from_le_bytes(std::span{to_le_bytes(original)}); static_assert(reconstructed == original); ---- + +== to_ne_bytes + +Converts a safe integer value to a native-endian byte array. +Delegates to `to_le_bytes` on little-endian platforms and `to_be_bytes` on big-endian platforms. + +The result is equivalent to `std::bit_cast>(value)` -- i.e., the raw in-memory representation. + +=== Runtime Overload + +[source,c++] +---- +template + requires (!is_verified_type_v) +constexpr auto to_ne_bytes(const T value) noexcept -> std::array; +---- + +==== Parameters + +* `value` -- The value to convert. + +==== Return Value + +A `std::array` containing the value's bytes in native byte order. + +==== Complexity + +O(1). + +==== Example + +[source,c++] +---- +using namespace boost::safe_numbers; + +auto bytes = to_ne_bytes(u32{0x01020304}); +// On little-endian: bytes == {0x04, 0x03, 0x02, 0x01} +// On big-endian: bytes == {0x01, 0x02, 0x03, 0x04} +---- + +=== Verified Overload + +[source,c++] +---- +template +consteval auto to_ne_bytes(const verified_type_basis value) noexcept -> std::array; +---- + +Compile-time only overload for verified types. + +==== Example + +[source,c++] +---- +using namespace boost::safe_numbers; + +constexpr auto bytes = to_ne_bytes(verified_u32{u32{0x01020304}}); +---- + +== from_ne_bytes + +Reconstructs a safe integer value from a native-endian byte array. +Delegates to `from_le_bytes` on little-endian platforms and `from_be_bytes` on big-endian platforms. + +=== Runtime Overload + +[source,c++] +---- +template + requires (!is_verified_type_v) +constexpr auto from_ne_bytes(const std::span bytes) -> T; +---- + +==== Parameters + +* `bytes` -- A span of bytes in native byte order. May have a fixed extent or `std::dynamic_extent`. + +==== Return Value + +The reconstructed safe integer value. + +==== Extent Matching + +The function validates that the number of input bytes matches `sizeof(T)`: + +* **Fixed extent matches `sizeof(T)`**: Compiles and executes normally. +* **Fixed extent does not match `sizeof(T)`**: Produces a `static_assert` failure at compile time. +* **Dynamic extent**: Checks at runtime and throws `std::domain_error` if the sizes do not match. + +==== Complexity + +O(1). + +==== Example + +[source,c++] +---- +using namespace boost::safe_numbers; + +// Round-trip +auto original = u32{0xDEADBEEF}; +auto reconstructed = from_ne_bytes(std::span{to_ne_bytes(original)}); +// reconstructed == original +---- + +=== Verified Overload + +[source,c++] +---- +template +consteval auto from_ne_bytes(const std::span bytes) -> T; +---- + +Compile-time only overload for verified types. +The span must have a fixed extent matching `sizeof(T)`; a mismatched extent produces a `static_assert` failure. + +==== Example + +[source,c++] +---- +using namespace boost::safe_numbers; + +constexpr auto original = verified_u32{u32{0xDEADBEEF}}; +constexpr auto reconstructed = from_ne_bytes(std::span{to_ne_bytes(original)}); +static_assert(reconstructed == original); +---- From 01a2490215431dc1f651400b0f0f7b5ed5e08363 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 18 Feb 2026 12:16:41 -0500 Subject: [PATCH 5/6] Add example of the new functions --- examples/byte_conversions.cpp | 155 ++++++++++++++++++++++++++++++++++ test/Jamfile | 1 + 2 files changed, 156 insertions(+) create mode 100644 examples/byte_conversions.cpp diff --git a/examples/byte_conversions.cpp b/examples/byte_conversions.cpp new file mode 100644 index 0000000..125af6d --- /dev/null +++ b/examples/byte_conversions.cpp @@ -0,0 +1,155 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + using namespace boost::safe_numbers; + + // ---- to_be_bytes: convert to big-endian byte array ---- + std::cout << "=== to_be_bytes ===\n"; + { + const auto bytes = to_be_bytes(u32{0x01020304U}); + std::cout << "u32(0x01020304) -> BE bytes: "; + for (const auto& b : bytes) + { + std::cout << std::hex << std::setfill('0') << std::setw(2) + << static_cast(b) << ' '; + } + std::cout << '\n'; + } + { + const auto bytes = to_be_bytes(u16{static_cast(0xABCD)}); + std::cout << "u16(0xABCD) -> BE bytes: "; + for (const auto& b : bytes) + { + std::cout << std::hex << std::setfill('0') << std::setw(2) + << static_cast(b) << ' '; + } + std::cout << '\n'; + } + + // ---- from_be_bytes: reconstruct from big-endian bytes ---- + std::cout << "\n=== from_be_bytes ===\n"; + { + const std::array bytes { + std::byte{0x01}, std::byte{0x02}, std::byte{0x03}, std::byte{0x04} + }; + const auto val = from_be_bytes(std::span{bytes}); + std::cout << "BE bytes {01,02,03,04} -> u32: 0x" + << std::hex << static_cast(val) << '\n'; + } + + // ---- to_le_bytes: convert to little-endian byte array ---- + std::cout << "\n=== to_le_bytes ===\n"; + { + const auto bytes = to_le_bytes(u32{0x01020304U}); + std::cout << "u32(0x01020304) -> LE bytes: "; + for (const auto& b : bytes) + { + std::cout << std::hex << std::setfill('0') << std::setw(2) + << static_cast(b) << ' '; + } + std::cout << '\n'; + } + { + const auto bytes = to_le_bytes(u64{0x0102030405060708ULL}); + std::cout << "u64(0x01..08) -> LE bytes: "; + for (const auto& b : bytes) + { + std::cout << std::hex << std::setfill('0') << std::setw(2) + << static_cast(b) << ' '; + } + std::cout << '\n'; + } + + // ---- from_le_bytes: reconstruct from little-endian bytes ---- + std::cout << "\n=== from_le_bytes ===\n"; + { + const std::array bytes { + std::byte{0x04}, std::byte{0x03}, std::byte{0x02}, std::byte{0x01} + }; + const auto val = from_le_bytes(std::span{bytes}); + std::cout << "LE bytes {04,03,02,01} -> u32: 0x" + << std::hex << static_cast(val) << '\n'; + } + + // ---- to_ne_bytes / from_ne_bytes: native endian round-trip ---- + std::cout << "\n=== to_ne_bytes / from_ne_bytes (native endian) ===\n"; + { + const auto original = u32{0xDEADBEEFU}; + const auto bytes = to_ne_bytes(original); + std::cout << "u32(0xDEADBEEF) -> NE bytes: "; + for (const auto& b : bytes) + { + std::cout << std::hex << std::setfill('0') << std::setw(2) + << static_cast(b) << ' '; + } + std::cout << '\n'; + + const auto reconstructed = from_ne_bytes(std::span{bytes}); + std::cout << "Round-trip: -> u32: 0x" + << std::hex << static_cast(reconstructed) << '\n'; + } + + // ---- BE round-trip with u8 ---- + std::cout << "\n=== u8 round-trip ===\n"; + { + const auto original = u8{0x42}; + const auto be = to_be_bytes(original); + const auto le = to_le_bytes(original); + std::cout << "u8(0x42) -> BE: " << std::hex << std::setfill('0') << std::setw(2) + << static_cast(be[0]) << '\n'; + std::cout << "u8(0x42) -> LE: " << std::hex << std::setfill('0') << std::setw(2) + << static_cast(le[0]) << '\n'; + } + + // ---- Verified types (compile-time) ---- + std::cout << "\n=== Verified types (compile-time) ===\n"; + { + constexpr auto bytes = to_be_bytes(verified_u32{u32{0x01020304U}}); + static_assert(bytes[0] == std::byte{0x01}); + static_assert(bytes[3] == std::byte{0x04}); + std::cout << "verified_u32 to_be_bytes: "; + for (const auto& b : bytes) + { + std::cout << std::hex << std::setfill('0') << std::setw(2) + << static_cast(b) << ' '; + } + std::cout << '\n'; + } + { + constexpr auto original = verified_u32{u32{0xDEADBEEFU}}; + constexpr auto bytes = to_be_bytes(original); + constexpr auto reconstructed = from_be_bytes(std::span{bytes}); + static_assert(reconstructed == original); + std::cout << "verified_u32 BE round-trip: 0x" << std::hex + << static_cast(static_cast(reconstructed)) << '\n'; + } + { + constexpr auto original = verified_u64{u64{0x0123456789ABCDEFULL}}; + constexpr auto bytes = to_le_bytes(original); + constexpr auto reconstructed = from_le_bytes(std::span{bytes}); + static_assert(reconstructed == original); + std::cout << "verified_u64 LE round-trip: 0x" << std::hex + << static_cast(static_cast(reconstructed)) << '\n'; + } + { + constexpr auto original = verified_u32{u32{0xCAFEBABEU}}; + constexpr auto bytes = to_ne_bytes(original); + constexpr auto reconstructed = from_ne_bytes(std::span{bytes}); + static_assert(reconstructed == original); + std::cout << "verified_u32 NE round-trip: 0x" << std::hex + << static_cast(static_cast(reconstructed)) << '\n'; + } + + return 0; +} diff --git a/test/Jamfile b/test/Jamfile index ed6eb2a..2c05578 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -180,3 +180,4 @@ run ../examples/verified_runtime_usage.cpp ; run ../examples/verified_mixed_ops.cpp ; compile-fail ../examples/compile_fail_verified_overflow.cpp ; run ../examples/safe_numerics_comparison.cpp ; +run ../examples/byte_conversions.cpp ; From 20a6f2c3b088124948395b85b99089a0761c43e9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 18 Feb 2026 12:16:51 -0500 Subject: [PATCH 6/6] Add example and xref in the docs --- doc/modules/ROOT/pages/byte_conversions.adoc | 4 ++ doc/modules/ROOT/pages/examples.adoc | 45 ++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/doc/modules/ROOT/pages/byte_conversions.adoc b/doc/modules/ROOT/pages/byte_conversions.adoc index 084963f..3c7b754 100644 --- a/doc/modules/ROOT/pages/byte_conversions.adoc +++ b/doc/modules/ROOT/pages/byte_conversions.adoc @@ -655,3 +655,7 @@ constexpr auto original = verified_u32{u32{0xDEADBEEF}}; constexpr auto reconstructed = from_ne_bytes(std::span{to_ne_bytes(original)}); static_assert(reconstructed == original); ---- + +== Example + +For a complete example demonstrating all byte conversion functions, see xref:examples.adoc#examples_byte_conversions[Byte Conversions]. diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index f4a414d..6973ea2 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -414,6 +414,51 @@ shr(u32(8), 1) = 4 ---- ==== +[#examples_byte_conversions] +== Byte Conversions + +The library provides functions for converting safe integers to and from byte arrays in big-endian, little-endian, or native byte order. +Verified types support the same conversions at compile time via `consteval` overloads. + +.This https://github.com/boostorg/safe_numbers/blob/develop/examples/byte_conversions.cpp[example] demonstrates byte conversion functions with all safe integer types. +==== +[source, c++] +---- +include::example$byte_conversions.cpp[] +---- + +Output: +---- +=== to_be_bytes === +u32(0x01020304) -> BE bytes: 01 02 03 04 +u16(0xABCD) -> BE bytes: ab cd + +=== from_be_bytes === +BE bytes {01,02,03,04} -> u32: 0x1020304 + +=== to_le_bytes === +u32(0x01020304) -> LE bytes: 04 03 02 01 +u64(0x01..08) -> LE bytes: 08 07 06 05 04 03 02 01 + +=== from_le_bytes === +LE bytes {04,03,02,01} -> u32: 0x1020304 + +=== to_ne_bytes / from_ne_bytes (native endian) === +u32(0xDEADBEEF) -> NE bytes: ef be ad de +Round-trip: -> u32: 0xdeadbeef + +=== u8 round-trip === +u8(0x42) -> BE: 42 +u8(0x42) -> LE: 42 + +=== Verified types (compile-time) === +verified_u32 to_be_bytes: 01 02 03 04 +verified_u32 BE round-trip: 0xdeadbeef +verified_u64 LE round-trip: 0x123456789abcdef +verified_u32 NE round-trip: 0xcafebabe +---- +==== + [#examples_verified_construction] == Verified Types: Construction and Runtime Usage