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 // Selects the hash macro implementation to use. The implementation selected
16 // depends on the language (C or C++) and value of
17 // PW_TOKENIZER_CFG_C_HASH_LENGTH. The options are:
18 //
19 // - C++ hash constexpr function, which works for any hash length
20 // - C 80-character hash macro
21 // - C 96-character hash macro
22 // - C 128-character hash macro
23 //
24 // C hash macros for other lengths may be generated using generate_hash_macro.py
25 // and added to this file.
26 #pragma once
27
28 #include <stdint.h>
29
30 #define _PW_TOKENIZER_ENTRY_MAGIC 0xBAA98DEE
31
32 // DOCSTAG: [pw_tokenizer-elf-entry]
33 // Tokenizer entries are stored sequentially in an ELF section. Each entry has a
34 // header with a magic number, token, domain length, string length. The domain
35 // and tokenized string follow next. The next entry's header follows, with no
36 // padding for alignment.
37 //
38 // The domain and string lengths include a null terminator, which is not
39 // considered part of the domain or string. Use (domain_length - 1) and
40 // (string_length - 1) for the domain and string length when decoding.
41 //
42 // All header entries are stored little-endian.
43 typedef struct {
44 uint32_t magic; // Must be _PW_TOKENIZER_ENTRY_MAGIC (0xBAA98DEE).
45 uint32_t token; // The token that represents this string.
46 uint32_t domain_length; // Domain string length, including a null terminator.
47 uint32_t string_length; // String entry length, including a null terminator.
48 } _pw_tokenizer_EntryHeader;
49 // DOCSTAG: [pw_tokenizer-elf-entry]
50
51 #ifdef __cplusplus
52
53 #include "pw_containers/to_array.h"
54 #include "pw_preprocessor/compiler.h"
55
56 namespace pw::tokenizer::internal {
57
58 static_assert(sizeof(_pw_tokenizer_EntryHeader) == 4 * sizeof(uint32_t));
59
ValidDomainChar(char ch)60 constexpr bool ValidDomainChar(char ch) {
61 return ('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') ||
62 ('0' <= ch && ch <= '9') || ch == '_' || ch == ':' || ch == ' ' ||
63 ch == '\t' || ch == '\n';
64 }
65
66 template <size_t kSize>
ValidDomain(const char (& domain)[kSize])67 constexpr bool ValidDomain(const char (&domain)[kSize]) {
68 static_assert(kSize > 0u);
69 for (size_t i = 0; i < kSize - 1; ++i) {
70 if (!ValidDomainChar(domain[i])) {
71 return false;
72 }
73 }
74 return domain[kSize - 1] == '\0' && !('0' <= domain[0] && domain[0] <= '9');
75 }
76
77 // The C++ tokenzied string entry supports both string literals and char arrays,
78 // such as __func__.
79 template <uint32_t kDomainSize, uint32_t kStringSize>
PW_PACKED(class)80 PW_PACKED(class)
81 Entry {
82 public:
83 constexpr Entry(uint32_t token,
84 const char (&domain)[kDomainSize],
85 const char (&string)[kStringSize])
86 : header_{.magic = _PW_TOKENIZER_ENTRY_MAGIC,
87 .token = token,
88 .domain_length = kDomainSize,
89 .string_length = kStringSize},
90 domain_(containers::to_array(domain)),
91 string_(containers::to_array(string)) {}
92
93 private:
94 static_assert(kStringSize > 0u && kDomainSize > 0u,
95 "The string and domain must have at least a null terminator");
96
97 _pw_tokenizer_EntryHeader header_;
98 std::array<char, kDomainSize> domain_;
99 std::array<char, kStringSize> string_;
100 };
101
102 // Use this MakeEntry function so that the type doesn't have to be specified in
103 // the macro. Specifying the type causes problems when the tokenization macro is
104 // used as an argument to another macro because it requires template arguments,
105 // which the preprocessor misinterprets as macro arguments.
106 template <uint32_t kDomainSize, uint32_t kStringSize>
MakeEntry(uint32_t token,const char (& domain)[kDomainSize],const char (& string)[kStringSize])107 constexpr Entry<kDomainSize, kStringSize> MakeEntry(
108 uint32_t token,
109 const char (&domain)[kDomainSize],
110 const char (&string)[kStringSize]) {
111 return {token, domain, string};
112 }
113
114 } // namespace pw::tokenizer::internal
115
116 #else // In C, define a struct inline with appropriately-sized string members.
117
118 #define _PW_TOKENIZER_STRING_ENTRY( \
119 calculated_token, domain_literal, string_literal) \
120 PW_PACKED(struct) { \
121 _pw_tokenizer_EntryHeader header; \
122 char domain[sizeof(domain_literal)]; \
123 char string[sizeof(string_literal)]; \
124 } \
125 _PW_TOKENIZER_UNIQUE(_pw_tokenizer_string_entry_) \
126 _PW_TOKENIZER_SECTION = { \
127 { \
128 .magic = _PW_TOKENIZER_ENTRY_MAGIC, \
129 .token = calculated_token, \
130 .domain_length = sizeof(domain_literal), \
131 .string_length = sizeof(string_literal), \
132 }, \
133 domain_literal, \
134 string_literal, \
135 }
136
137 #endif // __cplusplus
138
139 // In C++17, use a constexpr function to calculate the hash.
140 #if defined(__cpp_constexpr) && __cpp_constexpr >= 201304L && \
141 defined(__cpp_inline_variables)
142
143 #include "pw_tokenizer/hash.h"
144
145 #define PW_TOKENIZER_STRING_TOKEN(format) ::pw::tokenizer::Hash(format)
146
147 #else // In C or older C++ code, use the hashing macro.
148
149 #if PW_TOKENIZER_CFG_C_HASH_LENGTH == 80
150
151 #include "pw_tokenizer/internal/pw_tokenizer_65599_fixed_length_80_hash_macro.h"
152 #define PW_TOKENIZER_STRING_TOKEN PW_TOKENIZER_65599_FIXED_LENGTH_80_HASH
153
154 #elif PW_TOKENIZER_CFG_C_HASH_LENGTH == 96
155
156 #include "pw_tokenizer/internal/pw_tokenizer_65599_fixed_length_96_hash_macro.h"
157 #define PW_TOKENIZER_STRING_TOKEN PW_TOKENIZER_65599_FIXED_LENGTH_96_HASH
158
159 #elif PW_TOKENIZER_CFG_C_HASH_LENGTH == 128
160
161 #include "pw_tokenizer/internal/pw_tokenizer_65599_fixed_length_128_hash_macro.h"
162 #define PW_TOKENIZER_STRING_TOKEN PW_TOKENIZER_65599_FIXED_LENGTH_128_HASH
163
164 #elif PW_TOKENIZER_CFG_C_HASH_LENGTH == 256
165
166 #include "pw_tokenizer/internal/pw_tokenizer_65599_fixed_length_256_hash_macro.h"
167 #define PW_TOKENIZER_STRING_TOKEN PW_TOKENIZER_65599_FIXED_LENGTH_256_HASH
168
169 #else // unsupported hash length
170
171 // Only hash lengths for which there is a corresponding macro header
172 // (pw_tokenizer/internal/mash_macro_#.h) are supported. Additional macros may
173 // be generated with the generate_hash_macro.py function. New macro headers must
174 // be added to this file.
175 #error "Unsupported value for PW_TOKENIZER_CFG_C_HASH_LENGTH"
176
177 // Define a placeholder macro to give clearer compilation errors.
178 #define PW_TOKENIZER_STRING_TOKEN(unused) 0u
179
180 #endif // PW_TOKENIZER_CFG_C_HASH_LENGTH
181
182 #endif // __cpp_constexpr >= 201304L && defined(__cpp_inline_variables)
183