]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/Formatter.cc
add stop-gap to fix compat with CPUs not supporting SSE 4.1
[ceph.git] / ceph / src / common / Formatter.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2011 New Dream Network
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #define LARGE_SIZE 1024
16
17 #include "HTMLFormatter.h"
18 #include "common/escape.h"
19 #include "include/buffer.h"
20
21 #include <fmt/format.h>
22 #include <algorithm>
23 #include <set>
24 #include <limits>
25
26 // -----------------------
27 namespace ceph {
28
29 std::string
30 fixed_u_to_string(uint64_t num, int scale)
31 {
32 std::ostringstream t;
33
34 t.fill('0');
35 t.width(scale + 1);
36 t << num;
37 int len = t.str().size();
38 return t.str().substr(0,len - scale) + "." + t.str().substr(len - scale);
39 }
40
41 std::string
42 fixed_to_string(int64_t num, int scale)
43 {
44 std::ostringstream t;
45 bool neg = num < 0;
46 if (neg) num = -num;
47
48 t.fill('0');
49 t.width(scale + 1);
50 t << num;
51 int len = t.str().size();
52 return (neg ? "-" : "") + t.str().substr(0,len - scale) + "." + t.str().substr(len - scale);
53 }
54
55 /*
56 * FormatterAttrs(const char *attr, ...)
57 *
58 * Requires a list of attrs followed by NULL. The attrs should be char *
59 * pairs, first one is the name, second one is the value. E.g.,
60 *
61 * FormatterAttrs("name1", "value1", "name2", "value2", NULL);
62 */
63 FormatterAttrs::FormatterAttrs(const char *attr, ...)
64 {
65 const char *s = attr;
66 va_list ap;
67 va_start(ap, attr);
68 do {
69 const char *val = va_arg(ap, char *);
70 if (!val)
71 break;
72
73 attrs.push_back(make_pair(std::string(s), std::string(val)));
74 s = va_arg(ap, char *);
75 } while (s);
76 va_end(ap);
77 }
78
79 void Formatter::write_bin_data(const char*, int){}
80
81 Formatter::Formatter() { }
82
83 Formatter::~Formatter() { }
84
85 Formatter *Formatter::create(std::string_view type,
86 std::string_view default_type,
87 std::string_view fallback)
88 {
89 std::string_view mytype(type);
90 if (mytype.empty()) {
91 mytype = default_type;
92 }
93
94 if (mytype == "json")
95 return new JSONFormatter(false);
96 else if (mytype == "json-pretty")
97 return new JSONFormatter(true);
98 else if (mytype == "xml")
99 return new XMLFormatter(false);
100 else if (mytype == "xml-pretty")
101 return new XMLFormatter(true);
102 else if (mytype == "table")
103 return new TableFormatter();
104 else if (mytype == "table-kv")
105 return new TableFormatter(true);
106 else if (mytype == "html")
107 return new HTMLFormatter(false);
108 else if (mytype == "html-pretty")
109 return new HTMLFormatter(true);
110 else if (fallback != "")
111 return create(fallback, "", "");
112 else
113 return (Formatter *) NULL;
114 }
115
116
117 void Formatter::flush(bufferlist &bl)
118 {
119 std::stringstream os;
120 flush(os);
121 bl.append(os.str());
122 }
123
124 void Formatter::dump_format(std::string_view name, const char *fmt, ...)
125 {
126 va_list ap;
127 va_start(ap, fmt);
128 dump_format_va(name, NULL, true, fmt, ap);
129 va_end(ap);
130 }
131
132 void Formatter::dump_format_ns(std::string_view name, const char *ns, const char *fmt, ...)
133 {
134 va_list ap;
135 va_start(ap, fmt);
136 dump_format_va(name, ns, true, fmt, ap);
137 va_end(ap);
138
139 }
140
141 void Formatter::dump_format_unquoted(std::string_view name, const char *fmt, ...)
142 {
143 va_list ap;
144 va_start(ap, fmt);
145 dump_format_va(name, NULL, false, fmt, ap);
146 va_end(ap);
147 }
148
149 // -----------------------
150
151 JSONFormatter::JSONFormatter(bool p)
152 : m_pretty(p), m_is_pending_string(false)
153 {
154 reset();
155 }
156
157 void JSONFormatter::flush(std::ostream& os)
158 {
159 finish_pending_string();
160 os << m_ss.str();
161 if (m_line_break_enabled)
162 os << "\n";
163 m_ss.clear();
164 m_ss.str("");
165 }
166
167 void JSONFormatter::reset()
168 {
169 m_stack.clear();
170 m_ss.clear();
171 m_ss.str("");
172 m_pending_string.clear();
173 m_pending_string.str("");
174 }
175
176 void JSONFormatter::print_comma(json_formatter_stack_entry_d& entry)
177 {
178 if (entry.size) {
179 if (m_pretty) {
180 m_ss << ",\n";
181 for (unsigned i = 1; i < m_stack.size(); i++)
182 m_ss << " ";
183 } else {
184 m_ss << ",";
185 }
186 } else if (m_pretty) {
187 m_ss << "\n";
188 for (unsigned i = 1; i < m_stack.size(); i++)
189 m_ss << " ";
190 }
191 if (m_pretty && entry.is_array)
192 m_ss << " ";
193 }
194
195 void JSONFormatter::print_quoted_string(std::string_view s)
196 {
197 m_ss << '\"' << json_stream_escaper(s) << '\"';
198 }
199
200 void JSONFormatter::print_name(std::string_view name)
201 {
202 finish_pending_string();
203 if (m_stack.empty())
204 return;
205 struct json_formatter_stack_entry_d& entry = m_stack.back();
206 print_comma(entry);
207 if (!entry.is_array) {
208 if (m_pretty) {
209 m_ss << " ";
210 }
211 m_ss << "\"" << name << "\"";
212 if (m_pretty)
213 m_ss << ": ";
214 else
215 m_ss << ':';
216 }
217 ++entry.size;
218 }
219
220 void JSONFormatter::open_section(std::string_view name, const char *ns, bool is_array)
221 {
222 if (handle_open_section(name, ns, is_array)) {
223 return;
224 }
225 if (ns) {
226 std::ostringstream oss;
227 oss << name << " " << ns;
228 print_name(oss.str().c_str());
229 } else {
230 print_name(name);
231 }
232 if (is_array)
233 m_ss << '[';
234 else
235 m_ss << '{';
236
237 json_formatter_stack_entry_d n;
238 n.is_array = is_array;
239 m_stack.push_back(n);
240 }
241
242 void JSONFormatter::open_array_section(std::string_view name)
243 {
244 open_section(name, nullptr, true);
245 }
246
247 void JSONFormatter::open_array_section_in_ns(std::string_view name, const char *ns)
248 {
249 open_section(name, ns, true);
250 }
251
252 void JSONFormatter::open_object_section(std::string_view name)
253 {
254 open_section(name, nullptr, false);
255 }
256
257 void JSONFormatter::open_object_section_in_ns(std::string_view name, const char *ns)
258 {
259 open_section(name, ns, false);
260 }
261
262 void JSONFormatter::close_section()
263 {
264
265 if (handle_close_section()) {
266 return;
267 }
268 ceph_assert(!m_stack.empty());
269 finish_pending_string();
270
271 struct json_formatter_stack_entry_d& entry = m_stack.back();
272 if (m_pretty && entry.size) {
273 m_ss << "\n";
274 for (unsigned i = 1; i < m_stack.size(); i++)
275 m_ss << " ";
276 }
277 m_ss << (entry.is_array ? ']' : '}');
278 m_stack.pop_back();
279 if (m_pretty && m_stack.empty())
280 m_ss << "\n";
281 }
282
283 void JSONFormatter::finish_pending_string()
284 {
285 if (m_is_pending_string) {
286 m_is_pending_string = false;
287 add_value(m_pending_name.c_str(), m_pending_string.str(), true);
288 m_pending_string.str("");
289 }
290 }
291
292 template <class T>
293 void JSONFormatter::add_value(std::string_view name, T val)
294 {
295 std::stringstream ss;
296 ss.precision(std::numeric_limits<T>::max_digits10);
297 ss << val;
298 add_value(name, ss.str(), false);
299 }
300
301 void JSONFormatter::add_value(std::string_view name, std::string_view val, bool quoted)
302 {
303 if (handle_value(name, val, quoted)) {
304 return;
305 }
306 print_name(name);
307 if (!quoted) {
308 m_ss << val;
309 } else {
310 print_quoted_string(val);
311 }
312 }
313
314 void JSONFormatter::dump_unsigned(std::string_view name, uint64_t u)
315 {
316 add_value(name, u);
317 }
318
319 void JSONFormatter::dump_int(std::string_view name, int64_t s)
320 {
321 add_value(name, s);
322 }
323
324 void JSONFormatter::dump_float(std::string_view name, double d)
325 {
326 add_value(name, d);
327 }
328
329 void JSONFormatter::dump_string(std::string_view name, std::string_view s)
330 {
331 add_value(name, s, true);
332 }
333
334 std::ostream& JSONFormatter::dump_stream(std::string_view name)
335 {
336 finish_pending_string();
337 m_pending_name = name;
338 m_is_pending_string = true;
339 return m_pending_string;
340 }
341
342 void JSONFormatter::dump_format_va(std::string_view name, const char *ns, bool quoted, const char *fmt, va_list ap)
343 {
344 char buf[LARGE_SIZE];
345 vsnprintf(buf, LARGE_SIZE, fmt, ap);
346
347 add_value(name, buf, quoted);
348 }
349
350 int JSONFormatter::get_len() const
351 {
352 return m_ss.str().size();
353 }
354
355 void JSONFormatter::write_raw_data(const char *data)
356 {
357 m_ss << data;
358 }
359
360 const char *XMLFormatter::XML_1_DTD =
361 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
362
363 XMLFormatter::XMLFormatter(bool pretty, bool lowercased, bool underscored)
364 : m_pretty(pretty),
365 m_lowercased(lowercased),
366 m_underscored(underscored)
367 {
368 reset();
369 }
370
371 void XMLFormatter::flush(std::ostream& os)
372 {
373 finish_pending_string();
374 std::string m_ss_str = m_ss.str();
375 os << m_ss_str;
376 /* There is a small catch here. If the rest of the formatter had NO output,
377 * we should NOT output a newline. This primarily triggers on HTTP redirects */
378 if (m_pretty && !m_ss_str.empty())
379 os << "\n";
380 else if (m_line_break_enabled)
381 os << "\n";
382 m_ss.clear();
383 m_ss.str("");
384 }
385
386 void XMLFormatter::reset()
387 {
388 m_ss.clear();
389 m_ss.str("");
390 m_pending_string.clear();
391 m_pending_string.str("");
392 m_sections.clear();
393 m_pending_string_name.clear();
394 m_header_done = false;
395 }
396
397 void XMLFormatter::output_header()
398 {
399 if(!m_header_done) {
400 m_header_done = true;
401 write_raw_data(XMLFormatter::XML_1_DTD);
402 if (m_pretty)
403 m_ss << "\n";
404 }
405 }
406
407 void XMLFormatter::output_footer()
408 {
409 while(!m_sections.empty()) {
410 close_section();
411 }
412 }
413
414 void XMLFormatter::open_object_section(std::string_view name)
415 {
416 open_section_in_ns(name, NULL, NULL);
417 }
418
419 void XMLFormatter::open_object_section_with_attrs(std::string_view name, const FormatterAttrs& attrs)
420 {
421 open_section_in_ns(name, NULL, &attrs);
422 }
423
424 void XMLFormatter::open_object_section_in_ns(std::string_view name, const char *ns)
425 {
426 open_section_in_ns(name, ns, NULL);
427 }
428
429 void XMLFormatter::open_array_section(std::string_view name)
430 {
431 open_section_in_ns(name, NULL, NULL);
432 }
433
434 void XMLFormatter::open_array_section_with_attrs(std::string_view name, const FormatterAttrs& attrs)
435 {
436 open_section_in_ns(name, NULL, &attrs);
437 }
438
439 void XMLFormatter::open_array_section_in_ns(std::string_view name, const char *ns)
440 {
441 open_section_in_ns(name, ns, NULL);
442 }
443
444 void XMLFormatter::close_section()
445 {
446 ceph_assert(!m_sections.empty());
447 finish_pending_string();
448
449 std::string section = m_sections.back();
450 std::transform(section.begin(), section.end(), section.begin(),
451 [this](char c) { return this->to_lower_underscore(c); });
452 m_sections.pop_back();
453 print_spaces();
454 m_ss << "</" << section << ">";
455 if (m_pretty)
456 m_ss << "\n";
457 }
458
459 template <class T>
460 void XMLFormatter::add_value(std::string_view name, T val)
461 {
462 std::string e(name);
463 std::transform(e.begin(), e.end(), e.begin(),
464 [this](char c) { return this->to_lower_underscore(c); });
465
466 print_spaces();
467 m_ss.precision(std::numeric_limits<T>::max_digits10);
468 m_ss << "<" << e << ">" << val << "</" << e << ">";
469 if (m_pretty)
470 m_ss << "\n";
471 }
472
473 void XMLFormatter::dump_unsigned(std::string_view name, uint64_t u)
474 {
475 add_value(name, u);
476 }
477
478 void XMLFormatter::dump_int(std::string_view name, int64_t s)
479 {
480 add_value(name, s);
481 }
482
483 void XMLFormatter::dump_float(std::string_view name, double d)
484 {
485 add_value(name, d);
486 }
487
488 void XMLFormatter::dump_string(std::string_view name, std::string_view s)
489 {
490 std::string e(name);
491 std::transform(e.begin(), e.end(), e.begin(),
492 [this](char c) { return this->to_lower_underscore(c); });
493
494 print_spaces();
495 m_ss << "<" << e << ">" << xml_stream_escaper(s) << "</" << e << ">";
496 if (m_pretty)
497 m_ss << "\n";
498 }
499
500 void XMLFormatter::dump_string_with_attrs(std::string_view name, std::string_view s, const FormatterAttrs& attrs)
501 {
502 std::string e(name);
503 std::transform(e.begin(), e.end(), e.begin(),
504 [this](char c) { return this->to_lower_underscore(c); });
505
506 std::string attrs_str;
507 get_attrs_str(&attrs, attrs_str);
508 print_spaces();
509 m_ss << "<" << e << attrs_str << ">" << xml_stream_escaper(s) << "</" << e << ">";
510 if (m_pretty)
511 m_ss << "\n";
512 }
513
514 std::ostream& XMLFormatter::dump_stream(std::string_view name)
515 {
516 print_spaces();
517 m_pending_string_name = name;
518 m_ss << "<" << m_pending_string_name << ">";
519 return m_pending_string;
520 }
521
522 void XMLFormatter::dump_format_va(std::string_view name, const char *ns, bool quoted, const char *fmt, va_list ap)
523 {
524 char buf[LARGE_SIZE];
525 size_t len = vsnprintf(buf, LARGE_SIZE, fmt, ap);
526 std::string e(name);
527 std::transform(e.begin(), e.end(), e.begin(),
528 [this](char c) { return this->to_lower_underscore(c); });
529
530 print_spaces();
531 if (ns) {
532 m_ss << "<" << e << " xmlns=\"" << ns << "\">" << xml_stream_escaper(std::string_view(buf, len)) << "</" << e << ">";
533 } else {
534 m_ss << "<" << e << ">" << xml_stream_escaper(std::string_view(buf, len)) << "</" << e << ">";
535 }
536
537 if (m_pretty)
538 m_ss << "\n";
539 }
540
541 int XMLFormatter::get_len() const
542 {
543 return m_ss.str().size();
544 }
545
546 void XMLFormatter::write_raw_data(const char *data)
547 {
548 m_ss << data;
549 }
550
551 void XMLFormatter::write_bin_data(const char* buff, int buf_len)
552 {
553 std::stringbuf *pbuf = m_ss.rdbuf();
554 pbuf->sputn(buff, buf_len);
555 m_ss.seekg(buf_len);
556 }
557
558 void XMLFormatter::get_attrs_str(const FormatterAttrs *attrs, std::string& attrs_str)
559 {
560 std::stringstream attrs_ss;
561
562 for (std::list<std::pair<std::string, std::string> >::const_iterator iter = attrs->attrs.begin();
563 iter != attrs->attrs.end(); ++iter) {
564 std::pair<std::string, std::string> p = *iter;
565 attrs_ss << " " << p.first << "=" << "\"" << p.second << "\"";
566 }
567
568 attrs_str = attrs_ss.str();
569 }
570
571 void XMLFormatter::open_section_in_ns(std::string_view name, const char *ns, const FormatterAttrs *attrs)
572 {
573 print_spaces();
574 std::string attrs_str;
575
576 if (attrs) {
577 get_attrs_str(attrs, attrs_str);
578 }
579
580 std::string e(name);
581 std::transform(e.begin(), e.end(), e.begin(),
582 [this](char c) { return this->to_lower_underscore(c); });
583
584 if (ns) {
585 m_ss << "<" << e << attrs_str << " xmlns=\"" << ns << "\">";
586 } else {
587 m_ss << "<" << e << attrs_str << ">";
588 }
589 if (m_pretty)
590 m_ss << "\n";
591 m_sections.push_back(std::string(name));
592 }
593
594 void XMLFormatter::finish_pending_string()
595 {
596 if (!m_pending_string_name.empty()) {
597 m_ss << xml_stream_escaper(m_pending_string.str())
598 << "</" << m_pending_string_name << ">";
599 m_pending_string_name.clear();
600 m_pending_string.str(std::string());
601 if (m_pretty) {
602 m_ss << "\n";
603 }
604 }
605 }
606
607 void XMLFormatter::print_spaces()
608 {
609 finish_pending_string();
610 if (m_pretty) {
611 std::string spaces(m_sections.size(), ' ');
612 m_ss << spaces;
613 }
614 }
615
616 char XMLFormatter::to_lower_underscore(char c) const
617 {
618 if (m_underscored && c == ' ') {
619 return '_';
620 } else if (m_lowercased) {
621 return std::tolower(c);
622 }
623 return c;
624 }
625
626 TableFormatter::TableFormatter(bool keyval) : m_keyval(keyval)
627 {
628 reset();
629 }
630
631 void TableFormatter::flush(std::ostream& os)
632 {
633 finish_pending_string();
634 std::vector<size_t> column_size = m_column_size;
635 std::vector<std::string> column_name = m_column_name;
636
637 std::set<int> need_header_set;
638
639 // auto-sizing columns
640 for (size_t i = 0; i < m_vec.size(); i++) {
641 for (size_t j = 0; j < m_vec[i].size(); j++) {
642 column_size.resize(m_vec[i].size());
643 column_name.resize(m_vec[i].size());
644 if (i > 0) {
645 if (m_vec[i - 1][j] != m_vec[i][j]) {
646 // changing row labels require to show the header
647 need_header_set.insert(i);
648 column_name[i] = m_vec[i][j].first;
649 }
650 } else {
651 column_name[i] = m_vec[i][j].first;
652 }
653
654 if (m_vec[i][j].second.length() > column_size[j])
655 column_size[j] = m_vec[i][j].second.length();
656 if (m_vec[i][j].first.length() > column_size[j])
657 column_size[j] = m_vec[i][j].first.length();
658 }
659 }
660
661 bool need_header = false;
662 if ((column_size.size() == m_column_size.size())) {
663 for (size_t i = 0; i < column_size.size(); i++) {
664 if (column_size[i] != m_column_size[i]) {
665 need_header = true;
666 break;
667 }
668 }
669 } else {
670 need_header = true;
671 }
672
673 if (need_header) {
674 // first row always needs a header if there wasn't one before
675 need_header_set.insert(0);
676 }
677
678 m_column_size = column_size;
679 for (size_t i = 0; i < m_vec.size(); i++) {
680 if (i == 0) {
681 if (need_header_set.count(i)) {
682 // print the header
683 if (!m_keyval) {
684 os << "+";
685 for (size_t j = 0; j < m_vec[i].size(); j++) {
686 for (size_t v = 0; v < m_column_size[j] + 3; v++)
687 os << "-";
688 os << "+";
689 }
690 os << "\n";
691 os << "|";
692
693 for (size_t j = 0; j < m_vec[i].size(); j++) {
694 os << fmt::format(" {:<{}}|",
695 m_vec[i][j].first, m_column_size[j] + 2);
696 }
697 os << "\n";
698 os << "+";
699 for (size_t j = 0; j < m_vec[i].size(); j++) {
700 for (size_t v = 0; v < m_column_size[j] + 3; v++)
701 os << "-";
702 os << "+";
703 }
704 os << "\n";
705 }
706 }
707 }
708 // print body
709 if (!m_keyval)
710 os << "|";
711 for (size_t j = 0; j < m_vec[i].size(); j++) {
712 if (!m_keyval)
713 os << " ";
714 if (m_keyval) {
715 os << "key::";
716 os << m_vec[i][j].first;
717 os << "=";
718 os << "\"";
719 os << m_vec[i][j].second;
720 os << "\" ";
721 } else {
722 os << fmt::format("{:<{}}|", m_vec[i][j].second, m_column_size[j] + 2);
723 }
724 }
725
726 os << "\n";
727 if (!m_keyval) {
728 if (i == (m_vec.size() - 1)) {
729 // print trailer
730 os << "+";
731 for (size_t j = 0; j < m_vec[i].size(); j++) {
732 for (size_t v = 0; v < m_column_size[j] + 3; v++)
733 os << "-";
734 os << "+";
735 }
736 os << "\n";
737 }
738 }
739 m_vec[i].clear();
740 }
741 m_vec.clear();
742 }
743
744 void TableFormatter::reset()
745 {
746 m_ss.clear();
747 m_ss.str("");
748 m_section_cnt.clear();
749 m_column_size.clear();
750 m_section_open = 0;
751 }
752
753 void TableFormatter::open_object_section(std::string_view name)
754 {
755 open_section_in_ns(name, NULL, NULL);
756 }
757
758 void TableFormatter::open_object_section_with_attrs(std::string_view name, const FormatterAttrs& attrs)
759 {
760 open_section_in_ns(name, NULL, NULL);
761 }
762
763 void TableFormatter::open_object_section_in_ns(std::string_view name, const char *ns)
764 {
765 open_section_in_ns(name, NULL, NULL);
766 }
767
768 void TableFormatter::open_array_section(std::string_view name)
769 {
770 open_section_in_ns(name, NULL, NULL);
771 }
772
773 void TableFormatter::open_array_section_with_attrs(std::string_view name, const FormatterAttrs& attrs)
774 {
775 open_section_in_ns(name, NULL, NULL);
776 }
777
778 void TableFormatter::open_array_section_in_ns(std::string_view name, const char *ns)
779 {
780 open_section_in_ns(name, NULL, NULL);
781 }
782
783 void TableFormatter::open_section_in_ns(std::string_view name, const char *ns, const FormatterAttrs *attrs)
784 {
785 m_section.push_back(std::string(name));
786 m_section_open++;
787 }
788
789 void TableFormatter::close_section()
790 {
791 //
792 m_section_open--;
793 if (m_section.size()) {
794 m_section_cnt[m_section.back()] = 0;
795 m_section.pop_back();
796 }
797 }
798
799 size_t TableFormatter::m_vec_index(std::string_view name)
800 {
801 std::string key(name);
802
803 size_t i = m_vec.size();
804 if (i)
805 i--;
806
807 // make sure there are vectors to push back key/val pairs
808 if (!m_vec.size())
809 m_vec.resize(1);
810
811 if (m_vec.size()) {
812 if (m_vec[i].size()) {
813 if (m_vec[i][0].first == key) {
814 // start a new column if a key is repeated
815 m_vec.resize(m_vec.size() + 1);
816 i++;
817 }
818 }
819 }
820
821 return i;
822 }
823
824 std::string TableFormatter::get_section_name(std::string_view name)
825 {
826 std::string t_name{name};
827 for (size_t i = 0; i < m_section.size(); i++) {
828 t_name.insert(0, ":");
829 t_name.insert(0, m_section[i]);
830 }
831 if (m_section_open) {
832 std::stringstream lss;
833 lss << t_name;
834 lss << "[";
835 lss << m_section_cnt[t_name]++;
836 lss << "]";
837 return lss.str();
838 } else {
839 return t_name;
840 }
841 }
842
843 template <class T>
844 void TableFormatter::add_value(std::string_view name, T val) {
845 finish_pending_string();
846 size_t i = m_vec_index(name);
847 m_ss.precision(std::numeric_limits<double>::max_digits10);
848 m_ss << val;
849
850 m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
851 m_ss.clear();
852 m_ss.str("");
853 }
854
855 void TableFormatter::dump_unsigned(std::string_view name, uint64_t u)
856 {
857 add_value(name, u);
858 }
859
860 void TableFormatter::dump_int(std::string_view name, int64_t s)
861 {
862 add_value(name, s);
863 }
864
865 void TableFormatter::dump_float(std::string_view name, double d)
866 {
867 add_value(name, d);
868 }
869
870 void TableFormatter::dump_string(std::string_view name, std::string_view s)
871 {
872 finish_pending_string();
873 size_t i = m_vec_index(name);
874 m_ss << s;
875
876 m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
877 m_ss.clear();
878 m_ss.str("");
879 }
880
881 void TableFormatter::dump_string_with_attrs(std::string_view name, std::string_view s, const FormatterAttrs& attrs)
882 {
883 finish_pending_string();
884 size_t i = m_vec_index(name);
885
886 std::string attrs_str;
887 get_attrs_str(&attrs, attrs_str);
888 m_ss << attrs_str << s;
889
890 m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
891 m_ss.clear();
892 m_ss.str("");
893 }
894
895 void TableFormatter::dump_format_va(std::string_view name,
896 const char *ns, bool quoted,
897 const char *fmt, va_list ap)
898 {
899 finish_pending_string();
900 char buf[LARGE_SIZE];
901 vsnprintf(buf, LARGE_SIZE, fmt, ap);
902
903 size_t i = m_vec_index(name);
904 if (ns) {
905 m_ss << ns << "." << buf;
906 } else
907 m_ss << buf;
908
909 m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
910 m_ss.clear();
911 m_ss.str("");
912 }
913
914 std::ostream& TableFormatter::dump_stream(std::string_view name)
915 {
916 finish_pending_string();
917 // we don't support this
918 m_pending_name = name;
919 return m_ss;
920 }
921
922 int TableFormatter::get_len() const
923 {
924 // we don't know the size until flush is called
925 return 0;
926 }
927
928 void TableFormatter::write_raw_data(const char *data) {
929 // not supported
930 }
931
932 void TableFormatter::get_attrs_str(const FormatterAttrs *attrs, std::string& attrs_str)
933 {
934 std::stringstream attrs_ss;
935
936 for (std::list<std::pair<std::string, std::string> >::const_iterator iter = attrs->attrs.begin();
937 iter != attrs->attrs.end(); ++iter) {
938 std::pair<std::string, std::string> p = *iter;
939 attrs_ss << " " << p.first << "=" << "\"" << p.second << "\"";
940 }
941
942 attrs_str = attrs_ss.str();
943 }
944
945 void TableFormatter::finish_pending_string()
946 {
947 if (m_pending_name.length()) {
948 std::string ss = m_ss.str();
949 m_ss.clear();
950 m_ss.str("");
951 std::string pending_name = m_pending_name;
952 m_pending_name = "";
953 dump_string(pending_name.c_str(), ss);
954 }
955 }
956 }
957