libtcspc C++ API
Streaming TCSPC and time tag data processing
Loading...
Searching...
No Matches
read_integers.hpp
1/*
2 * This file is part of libtcspc
3 * Copyright 2019-2026 Board of Regents of the University of Wisconsin System
4 * SPDX-License-Identifier: MIT
5 */
6
7#pragma once
8
9#include "int_types.hpp"
10#include "npint.hpp"
11
12#include <array>
13#include <bit>
14#include <concepts>
15#include <cstddef>
16#include <span>
17
18namespace tcspc {
19
20namespace internal {
21
22// Read little-endian integers from raw bytes, regardless of source alignment.
23//
24// On little-endian targets we use std::bit_cast. On big-endian targets we fall
25// back to a shift-based composition. The shift form is endian-agnostic and
26// would produce correct results everywhere, but at the time this code was
27// originally written some compilers (notably MSVC) generated poor code for it;
28// std::bit_cast reliably compiles to a single load.
29//
30// For the shift-based composition see
31// https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
32// (note the explicit casts, which are needed to suppress integer promotions
33// that would otherwise mangle the result).
34
35constexpr auto read_u16le(std::span<std::byte const, 2> bytes) noexcept
36 -> u16np {
37 if constexpr (std::endian::native == std::endian::little) {
38 return u16np(std::bit_cast<u16>(std::array{bytes[0], bytes[1]}));
39 } else {
40 return (u16np(u16(bytes[0])) << 0) | (u16np(u16(bytes[1])) << 8);
41 }
42}
43
44constexpr auto read_u32le(std::span<std::byte const, 4> bytes) noexcept
45 -> u32np {
46 if constexpr (std::endian::native == std::endian::little) {
47 return u32np(std::bit_cast<u32>(
48 std::array{bytes[0], bytes[1], bytes[2], bytes[3]}));
49 } else {
50 return (u32np(u32(bytes[0])) << 0) | (u32np(u32(bytes[1])) << 8) |
51 (u32np(u32(bytes[2])) << 16) | (u32np(u32(bytes[3])) << 24);
52 }
53}
54
55constexpr auto read_u64le(std::span<std::byte const, 8> bytes) noexcept
56 -> u64np {
57 if constexpr (std::endian::native == std::endian::little) {
58 return u64np(std::bit_cast<u64>(
59 std::array{bytes[0], bytes[1], bytes[2], bytes[3], bytes[4],
60 bytes[5], bytes[6], bytes[7]}));
61 } else {
62 return (u64np(u64(bytes[0])) << 0) | (u64np(u64(bytes[1])) << 8) |
63 (u64np(u64(bytes[2])) << 16) | (u64np(u64(bytes[3])) << 24) |
64 (u64np(u64(bytes[4])) << 32) | (u64np(u64(bytes[5])) << 40) |
65 (u64np(u64(bytes[6])) << 48) | (u64np(u64(bytes[7])) << 56);
66 }
67}
68
69} // namespace internal
70
76template <std::size_t Offset, std::convertible_to<std::byte> T, std::size_t N>
77constexpr auto read_u8_at(std::span<T, N> bytes) noexcept -> u8np {
78 static_assert(Offset + 1 <= N);
79 auto const s = bytes.template subspan<Offset, 1>();
80 auto const b = std::span<std::byte const, 1>(s);
81 return u8np(u8(b.front()));
82}
83
89template <std::size_t Offset, std::convertible_to<std::byte> T, std::size_t N>
90constexpr auto read_u16le_at(std::span<T, N> bytes) noexcept -> u16np {
91 static_assert(Offset + 2 <= N);
92 auto const s = bytes.template subspan<Offset, 2>();
93 return internal::read_u16le(std::span<std::byte const, 2>(s));
94}
95
101template <std::size_t Offset, std::convertible_to<std::byte> T, std::size_t N>
102constexpr auto read_u32le_at(std::span<T, N> bytes) noexcept -> u32np {
103 static_assert(Offset + 4 <= N);
104 auto const s = bytes.template subspan<Offset, 4>();
105 return internal::read_u32le(std::span<std::byte const, 4>(s));
106}
107
113template <std::size_t Offset, std::convertible_to<std::byte> T, std::size_t N>
114constexpr auto read_u64le_at(std::span<T, N> bytes) noexcept -> u64np {
115 static_assert(Offset + 8 <= N);
116 auto const s = bytes.template subspan<Offset, 8>();
117 return internal::read_u64le(std::span<std::byte const, 8>(s));
118}
119
125template <std::size_t Offset, std::convertible_to<std::byte> T, std::size_t N>
126constexpr auto read_i8_at(std::span<T, N> bytes) noexcept -> i8np {
127 return i8np(read_u8_at<Offset, T, N>(bytes));
128}
129
135template <std::size_t Offset, std::convertible_to<std::byte> T, std::size_t N>
136constexpr auto read_i16le_at(std::span<T, N> bytes) noexcept -> i16np {
137 return i16np(read_u16le_at<Offset, T, N>(bytes));
138}
139
145template <std::size_t Offset, std::convertible_to<std::byte> T, std::size_t N>
146constexpr auto read_i32le_at(std::span<T, N> bytes) noexcept -> i32np {
147 return i32np(read_u32le_at<Offset, T, N>(bytes));
148}
149
155template <std::size_t Offset, std::convertible_to<std::byte> T, std::size_t N>
156constexpr auto read_i64le_at(std::span<T, N> bytes) noexcept -> i64np {
157 return i64np(read_u64le_at<Offset, T, N>(bytes));
158}
159
160} // namespace tcspc
constexpr auto read_i32le_at(std::span< T, N > bytes) noexcept -> i32np
Read a little-endian 32-bit signed integer from bytes at offset.
Definition read_integers.hpp:146
npint< u32 > u32np
Non-promoted unsigned 32-bit integer.
Definition npint.hpp:306
npint< i64 > i64np
Non-promoted signed 64-bit integer.
Definition npint.hpp:341
constexpr auto read_u8_at(std::span< T, N > bytes) noexcept -> u8np
Read an 8-bit unsigned integer from bytes at offset.
Definition read_integers.hpp:77
constexpr auto read_i64le_at(std::span< T, N > bytes) noexcept -> i64np
Read a little-endian 64-bit signed integer from bytes at offset.
Definition read_integers.hpp:156
npint< i32 > i32np
Non-promoted signed 32-bit integer.
Definition npint.hpp:334
constexpr auto read_u32le_at(std::span< T, N > bytes) noexcept -> u32np
Read a little-endian 32-bit unsigned integer from bytes at offset.
Definition read_integers.hpp:102
npint< u8 > u8np
Non-promoted unsigned 8-bit integer.
Definition npint.hpp:292
npint< i8 > i8np
Non-promoted signed 8-bit integer.
Definition npint.hpp:320
constexpr auto read_u64le_at(std::span< T, N > bytes) noexcept -> u64np
Read a little-endian 64-bit unsigned integer from bytes at offset.
Definition read_integers.hpp:114
npint< u64 > u64np
Non-promoted unsigned 64-bit integer.
Definition npint.hpp:313
npint< u16 > u16np
Non-promoted unsigned 16-bit integer.
Definition npint.hpp:299
constexpr auto read_i8_at(std::span< T, N > bytes) noexcept -> i8np
Read an 8-bit signed integer from bytes at offset.
Definition read_integers.hpp:126
constexpr auto read_i16le_at(std::span< T, N > bytes) noexcept -> i16np
Read a little-endian 16-bit signed integer from bytes at offset.
Definition read_integers.hpp:136
npint< i16 > i16np
Non-promoted signed 16-bit integer.
Definition npint.hpp:327
constexpr auto read_u16le_at(std::span< T, N > bytes) noexcept -> u16np
Read a little-endian 16-bit unsigned integer from bytes at offset.
Definition read_integers.hpp:90
std::uint64_t u64
Short name for uint64_t.
Definition int_types.hpp:33
std::uint8_t u8
Short name for uint8_t.
Definition int_types.hpp:24
std::uint16_t u16
Short name for uint16_t.
Definition int_types.hpp:27
std::uint32_t u32
Short name for uint32_t.
Definition int_types.hpp:30
libtcspc namespace.
Definition acquire.hpp:29