1 // Copyright 2019 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 // This size report uses pw::string::Format and std::snprintf to build a string
16 // in a buffer with multiple calls. The process aborts if an error occurs and
17 // the total string size is returned.
18 //
19 // This compares the overhead of using pw::string::Format to directly calling
20 // std::snprintf and doing error handling. It demonstrates that the code for
21 // using pw::string::Format is much simpler.
22 
23 #include "pw_bloat/bloat_this_binary.h"
24 
25 #ifndef USE_FORMAT
26 #error "USE_FORMAT must be defined"
27 #endif  // USE_FORMAT
28 
29 #if USE_FORMAT
30 
31 #include "pw_string/format.h"
32 
33 #define FORMAT_FUNCTION(...) \
34   pw::string::Format(std::span(buffer, buffer_size - string_size), __VA_ARGS__)
35 #define CHECK_RESULT(result) ProcessResult(&string_size, result)
36 
37 namespace {
38 
ProcessResult(unsigned * total,pw::StatusWithSize result)39 bool ProcessResult(unsigned* total, pw::StatusWithSize result) {
40   *total += result.size();
41   return result.ok();
42 }
43 
44 }  // namespace
45 
46 #else  // std::snprintf
47 
48 #include <cstdio>
49 
50 #define FORMAT_FUNCTION(...) std::snprintf(buffer, buffer_size, __VA_ARGS__)
51 #define CHECK_RESULT(result) \
52   ProcessResult(buffer, &string_size, buffer_size - string_size, result)
53 
54 namespace {
55 
ProcessResult(char * buffer,unsigned * total,unsigned size,int result)56 bool ProcessResult(char* buffer, unsigned* total, unsigned size, int result) {
57   if (result < 0) {
58     // Null-terminate since snprintf doesn't report the written count.
59     buffer[size] = '\0';
60     return false;
61   }
62 
63   if (static_cast<unsigned>(result) >= size) {
64     if (size > 0u) {
65       *total += size - 1;
66     }
67     return false;
68   }
69 
70   *total += result;
71   return true;
72 }
73 
74 }  // namespace
75 
76 #endif  // USE_FORMAT
77 
78 #define FORMAT_CASE(...)                             \
79   if (!CHECK_RESULT(FORMAT_FUNCTION(__VA_ARGS__))) { \
80     return string_size;                              \
81   }
82 
83 namespace pw::string {
84 
85 char buffer_1[128];
86 char buffer_2[128];
87 
88 char* volatile get_buffer_1 = buffer_1;
89 char* volatile get_buffer_2 = buffer_2;
90 volatile unsigned get_size;
91 
OutputStringsToBuffer()92 unsigned OutputStringsToBuffer() {
93   char* buffer = get_buffer_1;
94   const char* string = get_buffer_2;
95 
96   unsigned buffer_size = get_size;
97   unsigned string_size = 0;
98 
99   FORMAT_CASE("The quick brown");
100   FORMAT_CASE("%s", string);
101   FORMAT_CASE("jumped over the %s d%ug.", string, buffer_size);
102   FORMAT_CASE("One two %s %d %s", "three", 4, "five");
103   FORMAT_CASE("a %c %x d %s %f g", 'b', 0xc, "e", 0.0f);
104 
105   FORMAT_CASE("The quick brown");
106   FORMAT_CASE("%s", string);
107   FORMAT_CASE("jumped over the %s d%ug.", string, buffer_size);
108   FORMAT_CASE("One two %s %d %s", "three", 4, "five");
109   FORMAT_CASE("a %c %x d %s %f g", 'b', 0xc, "e", 0.0f);
110 
111   return string_size;
112 }
113 
114 }  // namespace pw::string
115 
main()116 int main() {
117   pw::bloat::BloatThisBinary();
118   return pw::string::OutputStringsToBuffer();
119 }
120