1 // Copyright 2020 The Pigweed Authors
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
7 // https://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
15 // Utilities for building std::byte arrays from strings or integer values at
27 constexpr bool UseBytesDirectly = std::is_integral_v<T> || std::is_enum_v<T>;
29 // Internal implementation functions. CopyBytes copies bytes from an array of
30 // byte-sized elements or the underlying bytes of an integer (as little-endian).
31 // std::memcpy cannot be used since it is not constexpr.
32 template <typename B, typename T, typename... Args>
33 consteval void CopyBytes(B* array, T value, Args... args) {
34 static_assert(sizeof(B) == sizeof(std::byte));
36 if constexpr (UseBytesDirectly<T>) {
37 if constexpr (sizeof(T) == 1u) {
38 *array++ = static_cast<B>(value);
40 for (size_t i = 0; i < sizeof(T); ++i) {
41 *array++ = static_cast<B>(value & 0xFF);
46 static_assert(sizeof(value[0]) == sizeof(B));
47 for (auto b : value) {
48 *array++ = static_cast<B>(b);
52 if constexpr (sizeof...(args) > 0u) {
53 CopyBytes(array, args...);
57 // Evaluates to the size in bytes of an integer or byte array.
59 consteval size_t SizeOfBytes(const T& arg) {
60 if constexpr (UseBytesDirectly<T>) {
63 static_assert(sizeof(arg[0]) == sizeof(std::byte));
64 return std::size(arg);
68 template <typename B, typename T, size_t... kIndex>
69 consteval auto String(const T& array, std::index_sequence<kIndex...>) {
70 return std::array{static_cast<B>(array[kIndex])...};
73 template <typename T, typename U>
74 consteval bool CanBeRepresentedAsByteType(const U& value) {
75 return static_cast<U>(static_cast<T>(value)) == value;
78 } // namespace internal
80 // Concatenates arrays or integers as a byte array at compile time. Integer
81 // values are copied little-endian. Spans are copied byte-for-byte.
82 template <typename B = std::byte, typename... Args>
83 consteval auto Concat(Args... args) {
84 std::array<B, (internal::SizeOfBytes(args) + ...)> bytes{};
85 internal::CopyBytes(bytes.begin(), args...);
89 // Converts a string literal to an array of bytes, without the trailing '\0'.
90 template <typename B = std::byte,
92 typename Indices = std::make_index_sequence<size - 1>>
93 consteval auto String(const char (&str)[size]) {
94 return internal::String<B>(str, Indices{});
97 // String overload for the empty string "".
98 template <typename B = std::byte>
99 consteval auto String(const char (&)[1]) {
100 return std::array<B, 0>{};
103 // Creates an array of bytes from values passed as template parameters. The
104 // values are guaranteed to be representable in the destination byte type.
105 template <typename B, auto... values>
106 consteval auto Array() {
107 static_assert((internal::CanBeRepresentedAsByteType<B>(values) && ...));
108 return std::array<B, sizeof...(values)>{static_cast<B>(values)...};
111 // Array() defaults to using std::byte.
112 template <auto... values>
113 consteval auto Array() {
114 return Array<std::byte, values...>();
117 // Creates an initialized array of bytes. Initializes the array to a value or
118 // the return values from a function that accepts the index as a parameter.
119 template <typename B, size_t size, typename T>
120 constexpr auto Initialized(const T& value_or_function) {
121 std::array<B, size> array{};
123 for (size_t i = 0; i < size; ++i) {
124 if constexpr (std::is_integral_v<T>) {
125 array[i] = static_cast<B>(value_or_function);
127 array[i] = static_cast<B>(value_or_function(i));
133 // Initialized(value_or_function) defaults to using std::byte.
134 template <size_t size, typename T>
135 constexpr auto Initialized(const T& value_or_function) {
136 return Initialized<std::byte, size>(value_or_function);
139 // Creates an array of bytes from a series of function arguments. Unlike
140 // Array(), MakeArray() cannot check if the values fit in the destination type.
141 // MakeArray() should only be used when Array() is not suitable.
142 template <typename B = std::byte, typename... Args>
143 constexpr auto MakeArray(const Args&... args) {
144 return std::array<B, sizeof...(args)>{static_cast<B>(args)...};
147 } // namespace pw::bytes