1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef BERBERIS_BASE_FORMAT_BUFFER_H_
18 #define BERBERIS_BASE_FORMAT_BUFFER_H_
19
20 #include <limits.h> // CHAR_BIT
21 #include <stdarg.h>
22 #include <stddef.h> // size_t
23 #include <stdint.h> // (u)intmax_t, uintptr_t
24
25 #include <berberis/base/arena_string.h>
26
27 namespace berberis {
28
29 // FormatBuffer functions are reentrant and safe to call from signal handlers.
30 // Implementation avoids using libc to ensure this.
31
32 // FormatBufferImpl is a template building block that extracts arguments from a custom list
33 // and appends formatted output to a custom buffer.
34 //
35 // Output buffer type requirements:
36 // bool Put(char c) - add one char to buffer, return true if added OK
37 //
38 // Argument list type requirements:
39 // (u)intmax_t Get(U)Int() - extract argument for %d, %u, %x
40 // (u)intmax_t Get(U)Long() - extract argument for %ld, %lu, %lx
41 // (u)intmax_t Get(U)LongLong() - extract argument for %lld, %llu, %llx
42 // const char* GetCStr() - extract argument for %s
43 // uintmax_t GetPtrAsUInt() - extract argument for %p
44 // intmax_t GetChar() - extract argument for %c
45 // uintmax_t GetSizeT() - extract argument for %zu, %zx
46
47 namespace format_buffer_internal {
48
49 // printf format specifier is
50 // %[flags][width][.precision][length]specifier
51 //
52 // length and specifier are captured by argument extraction and printing helper
53 // flags, width and precision are captured in style
54 struct Style {
55 size_t width = 0;
56 bool pad_number = false;
57 };
58
59 template <typename Out>
PutString(Out * out,const Style & style,const char * s)60 bool PutString(Out* out, const Style& style, const char* s) {
61 if (!s) {
62 s = "(null)";
63 }
64
65 if (style.width) {
66 size_t n = 0;
67 for (const char* p = s; *p; ++p, ++n) {
68 }
69 for (; n < style.width; ++n) {
70 if (!out->Put(' ')) {
71 return false;
72 }
73 }
74 }
75
76 for (const char* p = s; *p; ++p) {
77 if (!out->Put(*p)) {
78 return false;
79 }
80 }
81
82 return true;
83 }
84
85 template <typename Out>
PutChar(Out * out,const Style & style,char c)86 bool PutChar(Out* out, const Style& style, char c) {
87 for (size_t n = 1; n < style.width; ++n) {
88 if (!out->Put(' ')) {
89 return false;
90 }
91 }
92 return out->Put(c);
93 }
94
95 template <typename Out>
PutUInt(Out * out,const Style & style,const char * prefix,size_t prefix_size,uintmax_t v,size_t base)96 bool PutUInt(Out* out,
97 const Style& style,
98 const char* prefix,
99 size_t prefix_size,
100 uintmax_t v,
101 size_t base) {
102 // Reserve for max possible count of binary digits.
103 char buf[sizeof(uintmax_t) * CHAR_BIT];
104 char* p = buf;
105
106 // Generate digits in reverse order.
107 if (v == 0) {
108 *p++ = '0';
109 } else {
110 while (v) {
111 size_t d = v % base;
112 v /= base;
113 if (d < 10) {
114 *p++ = '0' + d;
115 } else {
116 *p++ = 'a' + (d - 10);
117 }
118 }
119 }
120
121 size_t n_print = prefix_size + (p - buf);
122 size_t n_pad = style.width > n_print ? style.width - n_print : 0;
123
124 // Pad with spaces before prefix.
125 if (!style.pad_number) {
126 for (; n_pad > 0; --n_pad) {
127 if (!out->Put(' ')) {
128 return false;
129 }
130 }
131 }
132
133 for (size_t i = 0; i < prefix_size; ++i) {
134 if (!out->Put(prefix[i])) {
135 return false;
136 }
137 }
138
139 // Pad with zeros after prefix.
140 if (style.pad_number) {
141 for (; n_pad > 0; --n_pad) {
142 if (!out->Put('0')) {
143 return false;
144 }
145 }
146 }
147
148 while (p != buf) {
149 if (!out->Put(*--p)) {
150 return false;
151 }
152 }
153
154 return true;
155 }
156
157 template <typename Out>
PutInt(Out * out,const Style & style,intmax_t v,size_t base)158 bool PutInt(Out* out, const Style& style, intmax_t v, size_t base) {
159 if (v < 0) {
160 return PutUInt(out, style, "-", 1, -v, base);
161 }
162 return PutUInt(out, style, nullptr, 0, v, base);
163 }
164
165 // This class is a syntax sugar to avoid passing format pointer and argument list by reference.
166 template <typename Out, typename Args>
167 class FormatAndArgs {
168 public:
FormatAndArgs(const char * format,Args * args)169 FormatAndArgs(const char* format, Args* args) : format_(format), args_(args) {}
170
Put(Out * out)171 bool Put(Out* out) {
172 while (char c = *format_++) {
173 if (c == '%') {
174 if (!PutSpec(out)) {
175 return false;
176 }
177 } else {
178 if (!out->Put(c)) {
179 return false;
180 }
181 }
182 }
183 return true;
184 }
185
186 private:
PutSpec(Out * out)187 bool PutSpec(Out* out) {
188 Style style = ParseStyle();
189 switch (*format_++) {
190 case '%':
191 return out->Put('%');
192 case 'c':
193 return PutChar(out, style, args_->GetChar());
194 case 's':
195 return PutString(out, style, args_->GetCStr());
196 case 'p':
197 return PutUInt(out, style, "0x", 2, args_->GetPtrAsUInt(), 16);
198 case 'd':
199 return PutInt(out, style, args_->GetInt(), 10);
200 case 'u':
201 return PutUInt(out, style, nullptr, 0, args_->GetUInt(), 10);
202 case 'x':
203 return PutUInt(out, style, nullptr, 0, args_->GetUInt(), 16);
204 case 'l':
205 return PutLongSpec(out, style);
206 case 'z':
207 return PutSizeTSpec(out, style);
208 default:
209 return false;
210 }
211 }
212
PutLongSpec(Out * out,const Style & style)213 bool PutLongSpec(Out* out, const Style& style) {
214 switch (*format_++) {
215 case 'd':
216 return PutInt(out, style, args_->GetLong(), 10);
217 case 'u':
218 return PutUInt(out, style, nullptr, 0, args_->GetULong(), 10);
219 case 'x':
220 return PutUInt(out, style, nullptr, 0, args_->GetULong(), 16);
221 case 'l':
222 return PutLongLongSpec(out, style);
223 default:
224 return false;
225 }
226 }
227
PutLongLongSpec(Out * out,const Style & style)228 bool PutLongLongSpec(Out* out, const Style& style) {
229 switch (*format_++) {
230 case 'd':
231 return PutInt(out, style, args_->GetLongLong(), 10);
232 case 'u':
233 return PutUInt(out, style, nullptr, 0, args_->GetULongLong(), 10);
234 case 'x':
235 return PutUInt(out, style, nullptr, 0, args_->GetULongLong(), 16);
236 default:
237 return false;
238 }
239 }
240
PutSizeTSpec(Out * out,const Style & style)241 bool PutSizeTSpec(Out* out, const Style& style) {
242 switch (*format_++) {
243 case 'u':
244 return PutUInt(out, style, nullptr, 0, args_->GetSizeT(), 10);
245 case 'x':
246 return PutUInt(out, style, nullptr, 0, args_->GetSizeT(), 16);
247 default:
248 return false;
249 }
250 }
251
ParseStyle()252 Style ParseStyle() {
253 Style style;
254 char c = *format_;
255 if (c == '0') {
256 c = *++format_;
257 style.pad_number = true;
258 }
259 if (c == '*') {
260 ++format_;
261 style.width = args_->GetInt();
262 } else {
263 style.width = 0;
264 for (; c >= '0' && c <= '9'; c = *++format_) {
265 style.width = style.width * 10 + (c - '0');
266 }
267 }
268 return style;
269 }
270
271 const char* format_;
272 Args* args_;
273 };
274
275 } // namespace format_buffer_internal
276
277 // Output to char array.
278 class CStrBuffer {
279 public:
280 // TODO(eaeltsin): check 'buf' is not null!
CStrBuffer(char * buf,size_t buf_size)281 CStrBuffer(char* buf, size_t buf_size) : start_(buf), cur_(buf), end_(buf + buf_size) {}
282
Put(char c)283 bool Put(char c) {
284 if (cur_ == end_) {
285 return false;
286 }
287 *cur_++ = c;
288 return true;
289 }
290
Size()291 size_t Size() const { return cur_ - start_; }
292
293 private:
294 char* const start_;
295 char* cur_;
296 char* const end_;
297 };
298
299 // Output to a dynamic char array.
300 class DynamicCStrBuffer {
301 public:
DynamicCStrBuffer()302 DynamicCStrBuffer() : static_cur_(static_buf_), dynamic_buf_(&arena_) {}
303
Put(char c)304 bool Put(char c) {
305 if (IsDynamic()) {
306 dynamic_buf_ += c;
307 return true;
308 }
309 if (static_cur_ == (static_buf_ + kStaticSize)) {
310 dynamic_buf_.append(static_buf_, kStaticSize);
311 return Put(c);
312 }
313 *static_cur_++ = c;
314 return true;
315 }
316
Size()317 size_t Size() const { return IsDynamic() ? dynamic_buf_.size() : (static_cur_ - static_buf_); }
318
Data()319 const char* Data() const { return IsDynamic() ? dynamic_buf_.data() : static_buf_; }
320
IsDynamicForTesting()321 bool IsDynamicForTesting() const { return IsDynamic(); }
322
323 private:
IsDynamic()324 bool IsDynamic() const { return !dynamic_buf_.empty(); }
325
326 static constexpr size_t kStaticSize = 256;
327 char static_buf_[kStaticSize];
328 char* static_cur_;
329 // The buffer is static initially, but if statically allocated storage is
330 // exhausted we use dynamic ArenaString.
331 Arena arena_;
332 // Do not use std::string to avoid mallocs.
333 ArenaString dynamic_buf_;
334 };
335
336 // Extract arguments from va_list.
337 class FormatBufferVaListArgs {
338 public:
FormatBufferVaListArgs(va_list ap)339 explicit FormatBufferVaListArgs(va_list ap) { va_copy(ap_, ap); }
340
GetCStr()341 const char* GetCStr() { return va_arg(ap_, const char*); }
GetPtrAsUInt()342 uintmax_t GetPtrAsUInt() { return reinterpret_cast<uintptr_t>(va_arg(ap_, void*)); }
GetInt()343 intmax_t GetInt() { return va_arg(ap_, int); }
GetLong()344 intmax_t GetLong() { return va_arg(ap_, long); }
GetLongLong()345 intmax_t GetLongLong() { return va_arg(ap_, long long); }
GetUInt()346 uintmax_t GetUInt() { return va_arg(ap_, unsigned int); }
GetULong()347 uintmax_t GetULong() { return va_arg(ap_, unsigned long); }
GetULongLong()348 uintmax_t GetULongLong() { return va_arg(ap_, unsigned long long); }
GetChar()349 intmax_t GetChar() { return va_arg(ap_, int); }
GetSizeT()350 uintmax_t GetSizeT() { return va_arg(ap_, size_t); }
351
352 private:
353 va_list ap_;
354 };
355
356 template <typename Out, typename Args>
FormatBufferImpl(Out * out,const char * format,Args * args)357 bool FormatBufferImpl(Out* out, const char* format, Args* args) {
358 return format_buffer_internal::FormatAndArgs<Out, Args>(format, args).Put(out);
359 }
360
361 template <typename Out>
362 bool __attribute__((__format__(printf, 2, 3)))
FormatBufferImplF(Out * out,const char * format,...)363 FormatBufferImplF(Out* out, const char* format, ...) {
364 va_list ap;
365 va_start(ap, format);
366 FormatBufferVaListArgs args(ap);
367 bool status = FormatBufferImpl(out, format, &args);
368 va_end(ap);
369 return status;
370 }
371
372 // Writes at most 'buf_size' characters, INcluding '\0' terminator.
373 // Returns number of written characters, EXcluding '\0' terminator.
374 // Does NOT report errors, just stops printing.
375 size_t FormatBuffer(char* buf, size_t buf_size, const char* format, ...);
376 size_t FormatBufferV(char* buf, size_t buf_size, const char* format, va_list ap);
377
378 } // namespace berberis
379
380 #endif // BERBERIS_BASE_FORMAT_BUFFER_H_
381