1 // Copyright 2020 The Pigweed Authors
2 //
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
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
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
13 // the License.
14
15 // Utilities for building std::byte arrays from strings or integer values at
16 // compile time.
17 #pragma once
18
19 #include <array>
20 #include <cstddef>
21 #include <iterator>
22
23 namespace pw::bytes {
24 namespace internal {
25
26 template <typename T>
27 constexpr bool UseBytesDirectly = std::is_integral_v<T> || std::is_enum_v<T>;
28
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>
CopyBytes(B * array,T value,Args...args)33 consteval void CopyBytes(B* array, T value, Args... args) {
34 static_assert(sizeof(B) == sizeof(std::byte));
35
36 if constexpr (UseBytesDirectly<T>) {
37 if constexpr (sizeof(T) == 1u) {
38 *array++ = static_cast<B>(value);
39 } else {
40 for (size_t i = 0; i < sizeof(T); ++i) {
41 *array++ = static_cast<B>(value & 0xFF);
42 value >>= 8;
43 }
44 }
45 } else {
46 static_assert(sizeof(value[0]) == sizeof(B));
47 for (auto b : value) {
48 *array++ = static_cast<B>(b);
49 }
50 }
51
52 if constexpr (sizeof...(args) > 0u) {
53 CopyBytes(array, args...);
54 }
55 }
56
57 // Evaluates to the size in bytes of an integer or byte array.
58 template <typename T>
SizeOfBytes(const T & arg)59 consteval size_t SizeOfBytes(const T& arg) {
60 if constexpr (UseBytesDirectly<T>) {
61 return sizeof(arg);
62 } else {
63 static_assert(sizeof(arg[0]) == sizeof(std::byte));
64 return std::size(arg);
65 }
66 }
67
68 template <typename B, typename T, size_t... kIndex>
String(const T & array,std::index_sequence<kIndex...>)69 consteval auto String(const T& array, std::index_sequence<kIndex...>) {
70 return std::array{static_cast<B>(array[kIndex])...};
71 }
72
73 template <typename T, typename U>
CanBeRepresentedAsByteType(const U & value)74 consteval bool CanBeRepresentedAsByteType(const U& value) {
75 return static_cast<U>(static_cast<T>(value)) == value;
76 }
77
78 } // namespace internal
79
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>
Concat(Args...args)83 consteval auto Concat(Args... args) {
84 std::array<B, (internal::SizeOfBytes(args) + ...)> bytes{};
85 internal::CopyBytes(bytes.begin(), args...);
86 return bytes;
87 }
88
89 // Converts a string literal to an array of bytes, without the trailing '\0'.
90 template <typename B = std::byte,
91 size_t kSize,
92 typename Indices = std::make_index_sequence<kSize - 1>>
String(const char (& str)[kSize])93 consteval auto String(const char (&str)[kSize]) {
94 return internal::String<B>(str, Indices{});
95 }
96
97 // String overload for the empty string "".
98 template <typename B = std::byte>
String(const char (&)[1])99 consteval auto String(const char (&)[1]) {
100 return std::array<B, 0>{};
101 }
102
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>
Array()106 consteval auto Array() {
107 static_assert((internal::CanBeRepresentedAsByteType<B>(values) && ...));
108 return std::array<B, sizeof...(values)>{static_cast<B>(values)...};
109 }
110
111 // Array() defaults to using std::byte.
112 template <auto... values>
Array()113 consteval auto Array() {
114 return Array<std::byte, values...>();
115 }
116
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 kSize, typename T>
Initialized(const T & value_or_function)120 constexpr auto Initialized(const T& value_or_function) {
121 std::array<B, kSize> array{};
122
123 for (size_t i = 0; i < kSize; ++i) {
124 if constexpr (std::is_integral_v<T>) {
125 array[i] = static_cast<B>(value_or_function);
126 } else {
127 array[i] = static_cast<B>(value_or_function(i));
128 }
129 }
130 return array;
131 }
132
133 // Initialized(value_or_function) defaults to using std::byte.
134 template <size_t kSize, typename T>
Initialized(const T & value_or_function)135 constexpr auto Initialized(const T& value_or_function) {
136 return Initialized<std::byte, kSize>(value_or_function);
137 }
138
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>
MakeArray(const Args &...args)143 constexpr auto MakeArray(const Args&... args) {
144 return std::array<B, sizeof...(args)>{static_cast<B>(args)...};
145 }
146
147 } // namespace pw::bytes
148