Byte Conversions

Description

The library provides functions for converting safe integer types to and from big-endian or little-endian byte order. These operate on the non-bounded unsigned types (u8, u16, u32, u64, u128).

For big-endian conversions (to_be/from_be): on big-endian platforms these are no-ops; on little-endian platforms they delegate to byteswap.

For little-endian conversions (to_le/from_le): on little-endian platforms these are no-ops; on big-endian platforms they delegate to byteswap.

#include <boost/safe_numbers/byte_conversions.hpp>

to_be

Converts a value from the native byte order to big-endian byte order.

template <non_bounded_integral_library_type T>
[[nodiscard]] constexpr auto to_be(const T value) noexcept -> T;

Parameters

  • value — The value to convert.

Return Value

If std::endian::native == std::endian::big, returns value unchanged. Otherwise, returns byteswap(value).

Complexity

O(1).

from_be

Converts a value from big-endian byte order to the native byte order.

This is the inverse of to_be. Since byte swapping is its own inverse, from_be delegates directly to to_be.

template <non_bounded_integral_library_type T>
[[nodiscard]] constexpr auto from_be(const T value) noexcept -> T;

Parameters

  • value — The big-endian value to convert to native byte order.

Return Value

The value in native byte order. Equivalent to to_be(value).

Complexity

O(1).

to_le

Converts a value from the native byte order to little-endian byte order.

template <non_bounded_integral_library_type T>
[[nodiscard]] constexpr auto to_le(const T value) noexcept -> T;

Parameters

  • value — The value to convert.

Return Value

If std::endian::native == std::endian::little, returns value unchanged. Otherwise, returns byteswap(value).

Complexity

O(1).

from_le

Converts a value from little-endian byte order to the native byte order.

This is the inverse of to_le. Since byte swapping is its own inverse, from_le delegates directly to to_le.

template <non_bounded_integral_library_type T>
[[nodiscard]] constexpr auto from_le(const T value) noexcept -> T;

Parameters

  • value — The little-endian value to convert to native byte order.

Return Value

The value in native byte order. Equivalent to to_le(value).

Complexity

O(1).

to_be_bytes

Converts a safe integer value to a big-endian byte array. The value is first converted to big-endian byte order using to_be, then reinterpreted as an array of std::byte.

template <non_bounded_integral_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto to_be_bytes(const T value) noexcept -> std::array<std::byte, sizeof(T)>;

Parameters

  • value — The value to convert.

Return Value

A std::array<std::byte, sizeof(T)> containing the value’s bytes in big-endian order (most significant byte first).

Complexity

O(1).

from_be_bytes

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.

template <non_bounded_integral_library_type T, std::size_t N>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto from_be_bytes(const std::span<const std::byte, N> bytes) -> T;

Parameters

  • bytes — A span of bytes in big-endian order. May have a fixed extent or std::dynamic_extent.

Return Value

The reconstructed safe integer value in native byte order.

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).

to_le_bytes

Converts a safe integer value to a little-endian byte array. The value is first converted to little-endian byte order using to_le, then reinterpreted as an array of std::byte.

template <non_bounded_integral_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto to_le_bytes(const T value) noexcept -> std::array<std::byte, sizeof(T)>;

Parameters

  • value — The value to convert.

Return Value

A std::array<std::byte, sizeof(T)> containing the value’s bytes in little-endian order (least significant byte first).

Complexity

O(1).

from_le_bytes

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.

template <non_bounded_integral_library_type T, std::size_t N>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto from_le_bytes(const std::span<const std::byte, N> bytes) -> T;

Parameters

  • bytes — A span of bytes in little-endian order. May have a fixed extent or std::dynamic_extent.

Return Value

The reconstructed safe integer value in native byte order.

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).

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<std::array<std::byte, sizeof(T)>>(value) — i.e., the raw in-memory representation.

template <non_bounded_integral_library_type T>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto to_ne_bytes(const T value) noexcept -> std::array<std::byte, sizeof(T)>;

Parameters

  • value — The value to convert.

Return Value

A std::array<std::byte, sizeof(T)> containing the value’s bytes in native byte order.

Complexity

O(1).

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.

template <non_bounded_integral_library_type T, std::size_t N>
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto from_ne_bytes(const std::span<const std::byte, N> 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).

Examples

Example 1. This example demonstrates the byte conversion functions with safe integer types.
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/safe_numbers/byte_conversions.hpp>
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <iomanip>
#include <cstddef>
#include <cstdint>
#include <span>

int main()
{
    using namespace boost::safe_numbers;

    // ---- to_be / from_be / to_le / from_le: scalar byte-order round-trips ----
    // The direct result of to_be / to_le is platform dependent, but a round-trip
    // back through from_be / from_le always recovers the original value.
    std::cout << "=== scalar byte-order round-trips ===\n";
    {
        const auto value = u32{0x01020304U};
        std::cout << std::hex;
        std::cout << "from_be(to_be(0x01020304)) = 0x" << from_be(to_be(value)) << '\n';
        std::cout << "from_le(to_le(0x01020304)) = 0x" << from_le(to_le(value)) << '\n';
        std::cout << std::dec;
    }

    std::cout << '\n';

    // ---- 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<unsigned>(b) << ' ';
        }
        std::cout << '\n';
    }
    {
        const auto bytes = to_be_bytes(u16{static_cast<std::uint16_t>(0xABCD)});
        std::cout << "u16(0xABCD)     -> BE bytes: ";
        for (const auto& b : bytes)
        {
            std::cout << std::hex << std::setfill('0') << std::setw(2)
                      << static_cast<unsigned>(b) << ' ';
        }
        std::cout << '\n';
    }

    // ---- from_be_bytes: reconstruct from big-endian bytes ----
    std::cout << "\n=== from_be_bytes ===\n";
    {
        const std::array<std::byte, 4> bytes {
            std::byte{0x01}, std::byte{0x02}, std::byte{0x03}, std::byte{0x04}
        };
        const auto val = from_be_bytes<u32>(std::span<const std::byte, 4>{bytes});
        std::cout << "BE bytes {01,02,03,04} -> u32: 0x"
                  << std::hex << static_cast<std::uint32_t>(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<unsigned>(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<unsigned>(b) << ' ';
        }
        std::cout << '\n';
    }

    // ---- from_le_bytes: reconstruct from little-endian bytes ----
    std::cout << "\n=== from_le_bytes ===\n";
    {
        const std::array<std::byte, 4> bytes {
            std::byte{0x04}, std::byte{0x03}, std::byte{0x02}, std::byte{0x01}
        };
        const auto val = from_le_bytes<u32>(std::span<const std::byte, 4>{bytes});
        std::cout << "LE bytes {04,03,02,01} -> u32: 0x"
                  << std::hex << static_cast<std::uint32_t>(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<unsigned>(b) << ' ';
        }
        std::cout << '\n';

        const auto reconstructed = from_ne_bytes<u32>(std::span<const std::byte, 4>{bytes});
        std::cout << "Round-trip:      -> u32: 0x"
                  << std::hex << static_cast<std::uint32_t>(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<unsigned>(be[0]) << '\n';
        std::cout << "u8(0x42) -> LE: " << std::hex << std::setfill('0') << std::setw(2)
                  << static_cast<unsigned>(le[0]) << '\n';
    }

    return 0;
}

Output:

=== scalar byte-order round-trips ===
from_be(to_be(0x01020304)) = 0x1020304
from_le(to_le(0x01020304)) = 0x1020304

=== 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