• 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 // 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