Pull from FB rev 63ce89e2f2301e6bba44a111cc7d4218022156f6
[folly.git] / folly / test / FormatTest.cpp
1 /*
2  * Copyright 2012 Facebook, Inc.
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 #include "folly/Format.h"
18
19 #include <glog/logging.h>
20 #include <gtest/gtest.h>
21
22 #include "folly/FBVector.h"
23 #include "folly/Benchmark.h"
24 #include "folly/dynamic.h"
25 #include "folly/json.h"
26
27 using namespace folly;
28
29 template <class... Args>
30 std::string fstr(StringPiece fmt, Args&&... args) {
31   return format(fmt, std::forward<Args>(args)...).str();
32 }
33
34 template <class C>
35 std::string vstr(StringPiece fmt, const C& c) {
36   return vformat(fmt, c).str();
37 }
38
39 template <class Uint>
40 void compareOctal(Uint u) {
41   char buf1[detail::kMaxOctalLength + 1];
42   buf1[detail::kMaxOctalLength] = '\0';
43   char* p = buf1 + detail::uintToOctal(buf1, detail::kMaxOctalLength, u);
44
45   char buf2[detail::kMaxOctalLength + 1];
46   sprintf(buf2, "%jo", static_cast<uintmax_t>(u));
47
48   EXPECT_EQ(std::string(buf2), std::string(p));
49 }
50
51 template <class Uint>
52 void compareHex(Uint u) {
53   char buf1[detail::kMaxHexLength + 1];
54   buf1[detail::kMaxHexLength] = '\0';
55   char* p = buf1 + detail::uintToHexLower(buf1, detail::kMaxHexLength, u);
56
57   char buf2[detail::kMaxHexLength + 1];
58   sprintf(buf2, "%jx", static_cast<uintmax_t>(u));
59
60   EXPECT_EQ(std::string(buf2), std::string(p));
61 }
62
63 template <class Uint>
64 void compareBinary(Uint u) {
65   char buf[detail::kMaxBinaryLength + 1];
66   buf[detail::kMaxBinaryLength] = '\0';
67   char* p = buf + detail::uintToBinary(buf, detail::kMaxBinaryLength, u);
68
69   std::string repr;
70   if (u == 0) {
71     repr = '0';
72   } else {
73     std::string tmp;
74     for (; u; u >>= 1) {
75       tmp.push_back(u & 1 ? '1' : '0');
76     }
77     repr.assign(tmp.rbegin(), tmp.rend());
78   }
79
80   EXPECT_EQ(repr, std::string(p));
81 }
82
83 TEST(Format, uintToOctal) {
84   for (unsigned i = 0; i < (1u << 16) + 2; i++) {
85     compareOctal(i);
86   }
87 }
88
89 TEST(Format, uintToHex) {
90   for (unsigned i = 0; i < (1u << 16) + 2; i++) {
91     compareHex(i);
92   }
93 }
94
95 TEST(Format, uintToBinary) {
96   for (unsigned i = 0; i < (1u << 16) + 2; i++) {
97     compareBinary(i);
98   }
99 }
100
101 TEST(Format, Simple) {
102   EXPECT_EQ("hello", fstr("hello"));
103   EXPECT_EQ("42", fstr("{}", 42));
104   EXPECT_EQ("42 42", fstr("{0} {0}", 42));
105   EXPECT_EQ("00042  23   42", fstr("{0:05} {1:3} {0:4}", 42, 23));
106   EXPECT_EQ("hello world hello 42",
107             fstr("{0} {1} {0} {2}", "hello", "world", 42));
108   EXPECT_EQ("XXhelloXX", fstr("{:X^9}", "hello"));
109   EXPECT_EQ("XXX42XXXX", fstr("{:X^9}", 42));
110   EXPECT_EQ("-0xYYYY2a", fstr("{:Y=#9x}", -42));
111   EXPECT_EQ("*", fstr("{}", '*'));
112   EXPECT_EQ("42", fstr("{}", 42));
113   EXPECT_EQ("0042", fstr("{:04}", 42));
114
115   EXPECT_EQ("hello  ", fstr("{:7}", "hello"));
116   EXPECT_EQ("hello  ", fstr("{:<7}", "hello"));
117   EXPECT_EQ("  hello", fstr("{:>7}", "hello"));
118
119   std::vector<int> v1 {10, 20, 30};
120   EXPECT_EQ("0020", fstr("{0[1]:04}", v1));
121   EXPECT_EQ("0020", vstr("{1:04}", v1));
122   EXPECT_EQ("10 20", vstr("{} {}", v1));
123
124   const std::vector<int> v2 = v1;
125   EXPECT_EQ("0020", fstr("{0[1]:04}", v2));
126   EXPECT_EQ("0020", vstr("{1:04}", v2));
127
128   const int p[] = {10, 20, 30};
129   const int* q = p;
130   EXPECT_EQ("0020", fstr("{0[1]:04}", p));
131   EXPECT_EQ("0020", vstr("{1:04}", p));
132   EXPECT_EQ("0020", fstr("{0[1]:04}", q));
133   EXPECT_EQ("0020", vstr("{1:04}", q));
134
135   EXPECT_EQ("0x", fstr("{}", p).substr(0, 2));
136   EXPECT_EQ("10", vstr("{}", p));
137   EXPECT_EQ("0x", fstr("{}", q).substr(0, 2));
138   EXPECT_EQ("10", vstr("{}", q));
139   q = nullptr;
140   EXPECT_EQ("(null)", fstr("{}", q));
141
142   std::map<int, std::string> m { {10, "hello"}, {20, "world"} };
143   EXPECT_EQ("worldXX", fstr("{[20]:X<7}", m));
144   EXPECT_EQ("worldXX", vstr("{20:X<7}", m));
145
146   std::map<std::string, std::string> m2 { {"hello", "world"} };
147   EXPECT_EQ("worldXX", fstr("{[hello]:X<7}", m2));
148   EXPECT_EQ("worldXX", vstr("{hello:X<7}", m2));
149
150   // Test indexing in strings
151   EXPECT_EQ("61 62", fstr("{0[0]:x} {0[1]:x}", "abcde"));
152   EXPECT_EQ("61 62", vstr("{0:x} {1:x}", "abcde"));
153   EXPECT_EQ("61 62", fstr("{0[0]:x} {0[1]:x}", std::string("abcde")));
154   EXPECT_EQ("61 62", vstr("{0:x} {1:x}", std::string("abcde")));
155
156   // Test booleans
157   EXPECT_EQ("true", fstr("{}", true));
158   EXPECT_EQ("1", fstr("{:d}", true));
159   EXPECT_EQ("false", fstr("{}", false));
160   EXPECT_EQ("0", fstr("{:d}", false));
161
162   // Test pairs
163   {
164     std::pair<int, std::string> p {42, "hello"};
165     EXPECT_EQ("    42 hello ", fstr("{0[0]:6} {0[1]:6}", p));
166     EXPECT_EQ("    42 hello ", vstr("{:6} {:6}", p));
167   }
168
169   // Test tuples
170   {
171     std::tuple<int, std::string, int> t { 42, "hello", 23 };
172     EXPECT_EQ("    42 hello      23", fstr("{0[0]:6} {0[1]:6} {0[2]:6}", t));
173     EXPECT_EQ("    42 hello      23", vstr("{:6} {:6} {:6}", t));
174   }
175
176   // Test writing to stream
177   std::ostringstream os;
178   os << format("{} {}", 42, 23);
179   EXPECT_EQ("42 23", os.str());
180
181   // Test appending to string
182   std::string s;
183   format(&s, "{} {}", 42, 23);
184   format(&s, " hello {:X<7}", "world");
185   EXPECT_EQ("42 23 hello worldXX", s);
186 }
187
188 namespace {
189 void testFloat(const char* fmt, double val) {
190   char buf[100];
191   sprintf(buf, to<std::string>("%", fmt).c_str(), val);
192
193   EXPECT_EQ(buf, fstr(to<std::string>("{:", fmt, "}"), val));
194 }
195 }  // namespace
196
197 TEST(Format, Float) {
198   double d = 1;
199   EXPECT_EQ("1", fstr("{}", 1.0));
200   EXPECT_EQ("0.1", fstr("{}", 0.1));
201   EXPECT_EQ("0.01", fstr("{}", 0.01));
202   EXPECT_EQ("0.001", fstr("{}", 0.001));
203   EXPECT_EQ("0.0001", fstr("{}", 0.0001));
204   EXPECT_EQ("1e-5", fstr("{}", 0.00001));
205   EXPECT_EQ("1e-6", fstr("{}", 0.000001));
206
207   EXPECT_EQ("10", fstr("{}", 10.0));
208   EXPECT_EQ("100", fstr("{}", 100.0));
209   EXPECT_EQ("1000", fstr("{}", 1000.0));
210   EXPECT_EQ("10000", fstr("{}", 10000.0));
211   EXPECT_EQ("100000", fstr("{}", 100000.0));
212   EXPECT_EQ("1e+6", fstr("{}", 1000000.0));
213   EXPECT_EQ("1e+7", fstr("{}", 10000000.0));
214
215   EXPECT_EQ("1.00", fstr("{:.2f}", 1.0));
216   EXPECT_EQ("0.10", fstr("{:.2f}", 0.1));
217   EXPECT_EQ("0.01", fstr("{:.2f}", 0.01));
218   EXPECT_EQ("0.00", fstr("{:.2f}", 0.001));
219 }
220
221 TEST(Format, MultiLevel) {
222   std::vector<std::map<std::string, std::string>> v = {
223     {
224       {"hello", "world"},
225     },
226   };
227
228   EXPECT_EQ("world", fstr("{[0.hello]}", v));
229 }
230
231 TEST(Format, dynamic) {
232   auto dyn = parseJson(
233       "{\n"
234       "  \"hello\": \"world\",\n"
235       "  \"x\": [20, 30],\n"
236       "  \"y\": {\"a\" : 42}\n"
237       "}");
238
239   EXPECT_EQ("world", fstr("{0[hello]}", dyn));
240   EXPECT_EQ("20", fstr("{0[x.0]}", dyn));
241   EXPECT_EQ("42", fstr("{0[y.a]}", dyn));
242
243   EXPECT_EQ("(null)", fstr("{}", dynamic(nullptr)));
244 }
245
246 namespace {
247
248 struct KeyValue {
249   std::string key;
250   int value;
251 };
252
253 }  // namespace
254
255 namespace folly {
256
257 template <> class FormatValue<KeyValue> {
258  public:
259   explicit FormatValue(const KeyValue& kv) : kv_(kv) { }
260
261   template <class FormatCallback>
262   void format(FormatArg& arg, FormatCallback& cb) const {
263     format_value::formatFormatter(
264         folly::format("<key={}, value={}>", kv_.key, kv_.value),
265         arg, cb);
266   }
267
268  private:
269   const KeyValue& kv_;
270 };
271
272 }  // namespace
273
274 TEST(Format, Custom) {
275   KeyValue kv { "hello", 42 };
276
277   EXPECT_EQ("<key=hello, value=42>", fstr("{}", kv));
278   EXPECT_EQ("<key=hello, value=42>", fstr("{:10}", kv));
279   EXPECT_EQ("<key=hello", fstr("{:.10}", kv));
280   EXPECT_EQ("<key=hello, value=42>XX", fstr("{:X<23}", kv));
281   EXPECT_EQ("XX<key=hello, value=42>", fstr("{:X>23}", kv));
282 }
283
284 namespace {
285
286 char bigBuf[300];
287
288 }  // namespace
289
290 BENCHMARK(octal_sprintf, iters) {
291   while (iters--) {
292     sprintf(bigBuf, "%o", static_cast<unsigned int>(iters));
293   }
294 }
295
296 BENCHMARK_RELATIVE(octal_uintToOctal, iters) {
297   while (iters--) {
298     detail::uintToOctal(bigBuf, detail::kMaxOctalLength,
299                         static_cast<unsigned int>(iters));
300   }
301 }
302
303 BENCHMARK_DRAW_LINE()
304
305 BENCHMARK(hex_sprintf, iters) {
306   while (iters--) {
307     sprintf(bigBuf, "%x", static_cast<unsigned int>(iters));
308   }
309 }
310
311 BENCHMARK_RELATIVE(hex_uintToHex, iters) {
312   while (iters--) {
313     detail::uintToHexLower(bigBuf, detail::kMaxHexLength,
314                            static_cast<unsigned int>(iters));
315   }
316 }
317
318 BENCHMARK_DRAW_LINE()
319
320 BENCHMARK(intAppend_sprintf) {
321   fbstring out;
322   for (int i = -1000; i < 1000; i++) {
323     sprintf(bigBuf, "%d", i);
324     out.append(bigBuf);
325   }
326 }
327
328 BENCHMARK_RELATIVE(intAppend_to) {
329   fbstring out;
330   for (int i = -1000; i < 1000; i++) {
331     toAppend(i, &out);
332   }
333 }
334
335 BENCHMARK_RELATIVE(intAppend_format) {
336   fbstring out;
337   for (int i = -1000; i < 1000; i++) {
338     format(&out, "{}", i);
339   }
340 }
341
342 BENCHMARK_DRAW_LINE()
343
344 BENCHMARK(bigFormat_sprintf, iters) {
345   while (iters--) {
346     for (int i = -100; i < 100; i++) {
347       sprintf(bigBuf,
348               "%d %d %d %d %d"
349               "%d %d %d %d %d"
350               "%d %d %d %d %d"
351               "%d %d %d %d %d",
352               i, i+1, i+2, i+3, i+4,
353               i+5, i+6, i+7, i+8, i+9,
354               i+10, i+11, i+12, i+13, i+14,
355               i+15, i+16, i+17, i+18, i+19);
356     }
357   }
358 }
359
360 BENCHMARK_RELATIVE(bigFormat_format, iters) {
361   char* p;
362   auto writeToBuf = [&p] (StringPiece sp) mutable {
363     memcpy(p, sp.data(), sp.size());
364     p += sp.size();
365   };
366
367   while (iters--) {
368     for (int i = -100; i < 100; i++) {
369       p = bigBuf;
370       format("{} {} {} {} {}"
371              "{} {} {} {} {}"
372              "{} {} {} {} {}"
373              "{} {} {} {} {}",
374               i, i+1, i+2, i+3, i+4,
375               i+5, i+6, i+7, i+8, i+9,
376               i+10, i+11, i+12, i+13, i+14,
377               i+15, i+16, i+17, i+18, i+19)(writeToBuf);
378     }
379   }
380 }
381
382 // Benchmark results on my dev server (dual-CPU Xeon L5520 @ 2.7GHz)
383 //
384 // ============================================================================
385 // folly/test/FormatTest.cpp                         relative  ns/iter  iters/s
386 // ============================================================================
387 // octal_sprintf                                               100.57     9.94M
388 // octal_uintToOctal                                 2599.47%    3.87   258.46M
389 // ----------------------------------------------------------------------------
390 // hex_sprintf                                                 100.13     9.99M
391 // hex_uintToHex                                     3331.75%    3.01   332.73M
392 // ----------------------------------------------------------------------------
393 // intAppend_sprintf                                           406.07K    2.46K
394 // intAppend_to                                       166.03%  244.58K    4.09K
395 // intAppend_format                                   147.57%  275.17K    3.63K
396 // ----------------------------------------------------------------------------
397 // bigFormat_sprintf                                           255.40K    3.92K
398 // bigFormat_format                                   102.18%  249.94K    4.00K
399 // ============================================================================
400
401 int main(int argc, char *argv[]) {
402   testing::InitGoogleTest(&argc, argv);
403   google::ParseCommandLineFlags(&argc, &argv, true);
404   auto ret = RUN_ALL_TESTS();
405   if (!ret) {
406     runBenchmarksOnFlag();
407   }
408   return ret;
409 }
410