15concept Scalar = ((std::integral<T> && !std::same_as<std::remove_cv_t<T>,
bool>) || std::floating_point<T>) &&
16 (
sizeof(T) == 1 ||
sizeof(T) == 2 ||
sizeof(T) == 4 ||
sizeof(T) == 8);
19using Bits = std::conditional_t<
20 sizeof(T) == 1, uint8_t,
21 std::conditional_t<
sizeof(T) == 2, uint16_t, std::conditional_t<
sizeof(T) == 4, uint32_t, uint64_t>>>;
25[[nodiscard]]
constexpr std::optional<T>
read(std::span<const uint8_t> bytes, std::size_t offset = 0)
27 static_assert(!std::floating_point<T> || std::numeric_limits<T>::is_iec559);
28 if (offset > bytes.size() ||
sizeof(T) > bytes.size() - offset) {
33 for (std::size_t i = 0; i <
sizeof(T); ++i) {
34 bits |= uint64_t{bytes[offset + i]} << (8 * i);
36 return std::bit_cast<T>(
static_cast<Bits<T>>(bits));
41[[nodiscard]]
constexpr bool write(std::span<uint8_t> bytes, std::size_t offset, T value)
43 static_assert(!std::floating_point<T> || std::numeric_limits<T>::is_iec559);
44 if (offset > bytes.size() ||
sizeof(T) > bytes.size() - offset) {
48 const auto bits = std::bit_cast<Bits<T>>(value);
49 for (std::size_t i = 0; i <
sizeof(T); ++i) {
50 bytes[offset + i] =
static_cast<uint8_t
>(bits >> (8 * i));
constexpr std::optional< T > read(std::span< const uint8_t > bytes, std::size_t offset=0)
Decode an unaligned little-endian scalar, returning nullopt when its bytes are unavailable.
std::conditional_t< sizeof(T)==1, uint8_t, std::conditional_t< sizeof(T)==2, uint16_t, std::conditional_t< sizeof(T)==4, uint32_t, uint64_t > > > Bits
constexpr bool write(std::span< uint8_t > bytes, std::size_t offset, T value)
Encode an unaligned little-endian scalar. Insufficient space leaves the destination unchanged.