blob: ea18c110e83e59ef19da88949aa46089c93636f2 [file] [log] [blame]
Wyatt Hepler80c6ee52020-01-03 09:54:58 -08001// 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#include "pw_tokenizer/internal/decode.h"
16
17#include <algorithm>
18#include <array>
19#include <cctype>
20#include <cstring>
21
22#include "pw_varint/varint.h"
23
24namespace pw::tokenizer {
25namespace {
26
27// Functions for parsing a printf format specifier.
28size_t SkipFlags(const char* str) {
29 size_t i = 0;
30 while (str[i] == '-' || str[i] == '+' || str[i] == '#' || str[i] == ' ' ||
31 str[i] == '0') {
32 i += 1;
33 }
34 return i;
35}
36
37size_t SkipAsteriskOrInteger(const char* str) {
38 if (str[0] == '*') {
39 return 1;
40 }
41
42 size_t i = (str[0] == '-' || str[0] == '+') ? 1 : 0;
43
44 while (std::isdigit(str[i])) {
45 i += 1;
46 }
47 return i;
48}
49
50std::array<char, 2> ReadLengthModifier(const char* str) {
51 // Check for ll or hh.
52 if (str[0] == str[1] && (str[0] == 'l' || str[0] == 'h')) {
53 return {str[0], str[1]};
54 }
55 if (std::strchr("hljztL", str[0]) != nullptr) {
56 return {str[0]};
57 }
58 return {};
59}
60
61// Returns the error message that is used in place of a decoded arg when an
62// error occurs.
63std::string ErrorMessage(ArgStatus status,
64 const std::string_view& spec,
65 const std::string_view& value) {
66 const char* message;
67 if (status.HasError(ArgStatus::kSkipped)) {
68 message = "SKIPPED";
69 } else if (status.HasError(ArgStatus::kMissing)) {
70 message = "MISSING";
71 } else if (status.HasError(ArgStatus::kDecodeError)) {
72 message = "ERROR";
73 } else {
74 message = "INTERNAL ERROR";
75 }
76
77 std::string result(PW_TOKENIZER_ARG_DECODING_ERROR_PREFIX);
78 result.append(spec);
79 result.push_back(' ');
80 result.append(message);
81
82 if (!value.empty()) {
83 result.push_back(' ');
84 result.push_back('(');
85 result.append(value);
86 result.push_back(')');
87 }
88
89 result.append(PW_TOKENIZER_ARG_DECODING_ERROR_SUFFIX);
90 return result;
91}
92
93} // namespace
94
95DecodedArg::DecodedArg(ArgStatus error,
96 const std::string_view& spec,
97 size_t raw_size_bytes,
98 const std::string_view& value)
99 : value_(ErrorMessage(error, spec, value)),
100 spec_(spec),
101 raw_data_size_bytes_(raw_size_bytes),
102 status_(error) {}
103
104StringSegment StringSegment::ParseFormatSpec(const char* format) {
105 if (format[0] != '%' || format[1] == '\0') {
106 return StringSegment();
107 }
108
109 // Parse the format specifier.
110 size_t i = 1;
111
112 // Skip the flags.
113 i += SkipFlags(&format[i]);
114
115 // Skip the field width.
116 i += SkipAsteriskOrInteger(&format[i]);
117
118 // Skip the precision.
119 if (format[i] == '.') {
120 i += 1;
121 i += SkipAsteriskOrInteger(&format[i]);
122 }
123
124 // Read the length modifier.
125 const std::array<char, 2> length = ReadLengthModifier(&format[i]);
126 i += (length[0] == '\0' ? 0 : 1) + (length[1] == '\0' ? 0 : 1);
127
128 // Read the conversion specifier.
129 const char spec = format[i];
130
131 Type type;
132 if (spec == 's') {
133 type = kString;
134 } else if (spec == 'c' || spec == 'd' || spec == 'i') {
135 type = kSignedInt;
136 } else if (std::strchr("oxXup", spec) != nullptr) {
137 // The source size matters for unsigned integers because they need to be
138 // masked off to their correct length, since zig-zag decode sign extends.
139 // TODO(hepler): 64-bit targets likely have 64-bit l, j, z, and t. Also, p
140 // needs to be 64-bit on these targets.
141 type = length[0] == 'j' || length[1] == 'l' ? kUnsigned64 : kUnsigned32;
142 } else if (std::strchr("fFeEaAgG", spec) != nullptr) {
143 type = kFloatingPoint;
144 } else if (spec == '%' && i == 1) {
145 type = kPercent;
146 } else {
147 return StringSegment();
148 }
149
150 return {std::string_view(format, i + 1), type, VarargSize(length, spec)};
151}
152
153StringSegment::ArgSize StringSegment::VarargSize(std::array<char, 2> length,
154 char spec) {
155 // Use pointer size for %p or any other type (for which this doesn't matter).
156 if (std::strchr("cdioxXu", spec) == nullptr) {
157 return VarargSize<void*>();
158 }
159 if (length[0] == 'l') {
160 return length[1] == 'l' ? VarargSize<long long>() : VarargSize<long>();
161 }
162 if (length[0] == 'j') {
163 return VarargSize<intmax_t>();
164 }
165 if (length[0] == 'z') {
166 return VarargSize<size_t>();
167 }
168 if (length[0] == 't') {
169 return VarargSize<ptrdiff_t>();
170 }
171 return VarargSize<int>();
172}
173
174DecodedArg StringSegment::DecodeString(
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700175 const std::span<const uint8_t>& arguments) const {
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800176 if (arguments.empty()) {
177 return DecodedArg(ArgStatus::kMissing, text_);
178 }
179
180 ArgStatus status =
181 (arguments[0] & 0x80u) == 0u ? ArgStatus::kOk : ArgStatus::kTruncated;
182
183 const uint_fast8_t size = arguments[0] & 0x7Fu;
184
185 if (arguments.size() - 1 < size) {
186 status.Update(ArgStatus::kDecodeError);
187 return DecodedArg(
188 status,
189 text_,
190 arguments.size(),
191 {reinterpret_cast<const char*>(&arguments[1]), arguments.size() - 1});
192 }
193
194 std::string value(reinterpret_cast<const char*>(&arguments[1]), size);
195
196 if (status.HasError(ArgStatus::kTruncated)) {
197 value.append("[...]");
198 }
199
200 return DecodedArg::FromValue(text_.c_str(), value.c_str(), 1 + size, status);
201}
202
203DecodedArg StringSegment::DecodeInteger(
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700204 const std::span<const uint8_t>& arguments) const {
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800205 if (arguments.empty()) {
206 return DecodedArg(ArgStatus::kMissing, text_);
207 }
208
209 int64_t value;
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700210 const size_t bytes = varint::Decode(std::as_bytes(arguments), &value);
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800211
212 if (bytes == 0u) {
Wyatt Hepleraf0aa542021-10-11 18:38:37 -0700213 return DecodedArg(ArgStatus::kDecodeError,
214 text_,
215 std::min(varint::kMaxVarint64SizeBytes,
216 static_cast<size_t>(arguments.size())));
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800217 }
218
219 // Unsigned ints need to be masked to their bit width due to sign extension.
220 if (type_ == kUnsigned32) {
221 value &= 0xFFFFFFFFu;
222 }
223
224 if (local_size_ == k32Bit) {
225 return DecodedArg::FromValue(
226 text_.c_str(), static_cast<uint32_t>(value), bytes);
227 }
228 return DecodedArg::FromValue(text_.c_str(), value, bytes);
229}
230
231DecodedArg StringSegment::DecodeFloatingPoint(
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700232 const std::span<const uint8_t>& arguments) const {
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800233 static_assert(sizeof(float) == 4u);
234 if (arguments.size() < sizeof(float)) {
235 return DecodedArg(ArgStatus::kMissing, text_);
236 }
237
238 float value;
239 std::memcpy(&value, arguments.data(), sizeof(value));
240 return DecodedArg::FromValue(text_.c_str(), value, sizeof(value));
241}
242
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700243DecodedArg StringSegment::Decode(
244 const std::span<const uint8_t>& arguments) const {
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800245 switch (type_) {
246 case kLiteral:
247 return DecodedArg(text_);
248 case kPercent:
249 return DecodedArg("%");
250 case kString:
251 return DecodeString(arguments);
252 case kSignedInt:
253 case kUnsigned32:
254 case kUnsigned64:
255 return DecodeInteger(arguments);
256 case kFloatingPoint:
257 return DecodeFloatingPoint(arguments);
258 }
259
260 return DecodedArg(ArgStatus::kDecodeError, text_);
261}
262
263DecodedArg StringSegment::Skip() const {
264 switch (type_) {
265 case kLiteral:
266 return DecodedArg(text_);
267 case kPercent:
268 return DecodedArg("%");
269 default:
270 return DecodedArg(ArgStatus::kSkipped, text_);
271 }
272}
273
274std::string DecodedFormatString::value() const {
275 std::string output;
276
277 for (const DecodedArg& arg : segments_) {
278 output.append(arg.ok() ? arg.value() : arg.spec());
279 }
280
281 return output;
282}
283
284std::string DecodedFormatString::value_with_errors() const {
285 std::string output;
286
287 for (const DecodedArg& arg : segments_) {
288 output.append(arg.value());
289 }
290
291 return output;
292}
293
294size_t DecodedFormatString::argument_count() const {
295 return std::count_if(segments_.begin(), segments_.end(), [](const auto& arg) {
296 return !arg.spec().empty();
297 });
298}
299
300size_t DecodedFormatString::decoding_errors() const {
301 return std::count_if(segments_.begin(), segments_.end(), [](const auto& arg) {
302 return !arg.ok();
303 });
304}
305
306FormatString::FormatString(const char* format) {
307 const char* text_start = format;
308
309 while (format[0] != '\0') {
310 if (StringSegment spec = StringSegment::ParseFormatSpec(format);
311 !spec.empty()) {
312 // Add the text segment seen so far (if any).
313 if (text_start < format) {
314 segments_.emplace_back(
315 std::string_view(text_start, format - text_start));
316 }
317
318 // Move along the index and text segment start.
319 format += spec.text().size();
320 text_start = format;
321
322 // Add the format specifier that was just found.
323 segments_.push_back(std::move(spec));
324 } else {
325 format += 1;
326 }
327 }
328
329 if (text_start < format) {
330 segments_.emplace_back(std::string_view(text_start, format - text_start));
331 }
332}
333
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700334DecodedFormatString FormatString::Format(
335 std::span<const uint8_t> arguments) const {
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800336 std::vector<DecodedArg> results;
337 bool skip = false;
338
339 for (const auto& segment : segments_) {
340 if (skip) {
341 results.push_back(segment.Skip());
342 } else {
343 results.push_back(segment.Decode(arguments));
344 arguments = arguments.subspan(results.back().raw_size_bytes());
345
346 // If an error occurred, skip decoding the remaining arguments.
347 if (!results.back().ok()) {
348 skip = true;
349 }
350 }
351 }
352
353 return DecodedFormatString(std::move(results), arguments.size());
354}
355
356} // namespace pw::tokenizer