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