• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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