]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/log/src/named_scope_format_parser.cpp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / log / src / named_scope_format_parser.cpp
1 /*
2 * Copyright Andrey Semashev 2007 - 2015.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
6 */
7 /*!
8 * \file named_scope_format_parser.cpp
9 * \author Andrey Semashev
10 * \date 14.11.2012
11 *
12 * \brief This header is the Boost.Log library implementation, see the library documentation
13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
14 */
15
16 #include <boost/log/detail/config.hpp>
17 #include <cstddef>
18 #include <cstring>
19 #include <string>
20 #include <vector>
21 #include <limits>
22 #include <algorithm>
23 #include <boost/cstdint.hpp>
24 #include <boost/move/core.hpp>
25 #include <boost/move/utility_core.hpp>
26 #include <boost/spirit/include/karma_uint.hpp>
27 #include <boost/spirit/include/karma_generate.hpp>
28 #include <boost/log/attributes/named_scope.hpp>
29 #include <boost/log/expressions/formatters/named_scope.hpp>
30 #include <boost/log/utility/formatting_ostream.hpp>
31 #include <boost/log/detail/header.hpp>
32
33 namespace karma = boost::spirit::karma;
34
35 namespace boost {
36
37 BOOST_LOG_OPEN_NAMESPACE
38
39 namespace expressions {
40
41 namespace aux {
42
43 BOOST_LOG_ANONYMOUS_NAMESPACE {
44
45 //! The function skips any spaces from the current position
46 BOOST_FORCEINLINE const char* skip_spaces(const char* p, const char* end)
47 {
48 while (p < end && *p == ' ')
49 ++p;
50 return p;
51 }
52
53 //! The function checks if the given character can be part of a function/type/namespace name
54 BOOST_FORCEINLINE bool is_name_character(char c)
55 {
56 return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z');
57 }
58
59 //! The function checks if there is 'operator' keyword at the specified position
60 BOOST_FORCEINLINE bool is_operator_keyword(const char* p)
61 {
62 #if defined(__i386__) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_IX86)
63 // Intel architecture allows unaligned accesses, so just compare with the whole keyword at once
64 return *reinterpret_cast< const uint64_t* >(p) == UINT64_C(0x726f74617265706f);
65 #else
66 return std::memcmp(p, "operator", 8) == 0;
67 #endif
68 }
69
70 //! The function tries to parse operator signature
71 bool detect_operator(const char* begin, const char* end, const char* operator_keyword, const char*& operator_end)
72 {
73 if (end - operator_keyword < 9 || !is_operator_keyword(operator_keyword))
74 return false;
75 // Check that it's not a function name ending with 'operator', like detect_operator
76 if (operator_keyword > begin && is_name_character(*(operator_keyword - 1)))
77 return false;
78
79 const char* p = skip_spaces(operator_keyword + 8, end);
80 if (p == end)
81 return false;
82
83 // Check to see where the operator token ends
84 switch (*p)
85 {
86 case '(':
87 // Handle operator()
88 p = skip_spaces(++p, end);
89 if (p < end && *p == ')')
90 {
91 operator_end = p + 1;
92 return true;
93 }
94
95 return false;
96
97 case '[':
98 // Handle operator[]
99 p = skip_spaces(++p, end);
100 if (p < end && *p == ']')
101 {
102 operator_end = p + 1;
103 return true;
104 }
105
106 return false;
107
108 case '>':
109 case '<':
110 // Handle operator<=, operator>=, operator<<, operator>>, operator<<=, operator>>=
111 if (end - p >= 3 && (p[0] == p[1] && p[2] == '='))
112 operator_end = p + 3;
113 else if (end - p >= 2 && (p[0] == p[1] || p[1] == '='))
114 operator_end = p + 2;
115 else
116 operator_end = p + 1;
117
118 return true;
119
120 case '-':
121 // Handle operator->, operator->*
122 if (end - p >= 2 && p[1] == '>')
123 {
124 if (end - p >= 3 && p[2] == '*')
125 operator_end = p + 3;
126 else
127 operator_end = p + 2;
128
129 return true;
130 }
131 // Fall through to other cases involving '-'
132
133 case '=':
134 case '|':
135 case '&':
136 case '+':
137 // Handle operator=, operator==, operator+=, operator++, operator||, opeartor&&, etc.
138 if (end - p >= 2 && (p[0] == p[1] || p[1] == '='))
139 operator_end = p + 2;
140 else
141 operator_end = p + 1;
142
143 return true;
144
145 case '*':
146 case '/':
147 case '%':
148 case '^':
149 // Handle operator*, operator*=, etc.
150 if (end - p >= 2 && p[1] == '=')
151 operator_end = p + 2;
152 else
153 operator_end = p + 1;
154
155 return true;
156
157 case ',':
158 case '~':
159 case '!':
160 // Handle operator,, operator~, etc.
161 operator_end = p + 1;
162 return true;
163
164 case '"':
165 // Handle operator""
166 if (end - p >= 2 && p[0] == p[1])
167 {
168 p = skip_spaces(p + 2, end);
169 // Skip through the literal suffix
170 while (p < end && is_name_character(*p))
171 ++p;
172 operator_end = p;
173 return true;
174 }
175
176 return false;
177
178 default:
179 // Handle type conversion operators. We can't find the end of the type reliably here.
180 operator_end = p;
181 return true;
182 }
183 }
184
185 //! The function skips all template parameters
186 inline const char* skip_template_parameters(const char* begin, const char* end)
187 {
188 unsigned int depth = 1;
189 const char* p = begin;
190 while (depth > 0 && p != end)
191 {
192 switch (*p)
193 {
194 case '>':
195 --depth;
196 break;
197
198 case '<':
199 ++depth;
200 break;
201
202 case 'o':
203 {
204 // Skip operators (e.g. when an operator is a non-type template parameter)
205 const char* operator_end;
206 if (detect_operator(begin, end, p, operator_end))
207 {
208 p = operator_end;
209 continue;
210 }
211 }
212 break;
213
214 default:
215 break;
216 }
217
218 ++p;
219 }
220
221 return p;
222 }
223
224 //! The function seeks for the opening parenthesis and also tries to find the function name beginning
225 inline const char* find_opening_parenthesis(const char* begin, const char* end, const char*& first_name_begin, const char*& last_name_begin)
226 {
227 enum sequence_state
228 {
229 not_started, // no significant (non-space) characters have been encountered so far
230 started, // some name has started; the name is a contiguous sequence of characters that may constitute a function or scope name
231 continued, // the previous characters were the scope operator ("::"), so the name is not finished yet
232 ended, // the name has ended; in particular, this means that there were significant characters previously in the string
233 operator_detected // operator has been found in the string, don't parse for scopes anymore; this is needed for conversion operators
234 };
235 sequence_state state = not_started;
236
237 const char* p = begin;
238 while (p != end)
239 {
240 char c = *p;
241 switch (c)
242 {
243 case '(':
244 if (state == not_started)
245 {
246 // If the opening brace is the first meaningful character in the string then this can't be a function signature.
247 // Pretend we didn't find the paranthesis to fail the parsing process.
248 return end;
249 }
250 return p;
251
252 case '<':
253 if (state == not_started)
254 {
255 // Template parameters cannot start as the first meaningful character in the signature.
256 // Pretend we didn't find the paranthesis to fail the parsing process.
257 return end;
258 }
259 p = skip_template_parameters(p + 1, end);
260 if (state != operator_detected)
261 state = ended;
262 continue;
263
264 case ' ':
265 if (state == started)
266 state = ended;
267 break;
268
269 case ':':
270 ++p;
271 if (p != end && *p == ':')
272 {
273 if (state == not_started)
274 {
275 // Include the starting "::" in the full name
276 first_name_begin = p - 1;
277 }
278 if (state != operator_detected)
279 state = continued;
280 ++p;
281 }
282 else if (state != operator_detected)
283 {
284 // Weird case, a single colon. Maybe, some compilers would put things like "public:" in front of the signature.
285 state = ended;
286 }
287 continue;
288
289 case 'o':
290 {
291 const char* operator_end;
292 if (detect_operator(begin, end, p, operator_end))
293 {
294 if (state == not_started || state == ended)
295 first_name_begin = p;
296 last_name_begin = p;
297 p = operator_end;
298 state = operator_detected;
299 continue;
300 }
301 }
302 // Fall through to process this character as other characters
303
304 default:
305 if (state != operator_detected)
306 {
307 if (is_name_character(c) || c == '~') // check for '~' in case of a destructor
308 {
309 if (state != started)
310 {
311 if (state == not_started || state == ended)
312 first_name_begin = p;
313 last_name_begin = p;
314 state = started;
315 }
316 }
317 else
318 {
319 state = ended;
320 }
321 }
322 break;
323 }
324
325 ++p;
326 }
327
328 return p;
329 }
330
331 //! The function seeks for the closing parenthesis
332 inline const char* find_closing_parenthesis(const char* begin, const char* end, char& first_char)
333 {
334 bool found_first_meaningful_char = false;
335 unsigned int depth = 1;
336 const char* p = begin;
337 while (p != end)
338 {
339 char c = *p;
340 switch (c)
341 {
342 case ')':
343 --depth;
344 if (depth == 0)
345 return p;
346 break;
347
348 case '(':
349 ++depth;
350 break;
351
352 case '<':
353 p = skip_template_parameters(p + 1, end);
354 continue;
355
356 case 'o':
357 {
358 const char* operator_end;
359 if (detect_operator(begin, end, p, operator_end))
360 {
361 p = operator_end;
362 continue;
363 }
364 }
365 // Fall through to process this character as other characters
366
367 default:
368 if (!found_first_meaningful_char && c != ' ')
369 {
370 found_first_meaningful_char = true;
371 first_char = c;
372 }
373 break;
374 }
375
376 ++p;
377 }
378
379 return p;
380 }
381
382 bool parse_function_name(const char*& begin, const char*& end, bool include_scope)
383 {
384 // The algorithm tries to match several patterns to recognize function signatures. The most obvious is:
385 //
386 // A B(C)
387 //
388 // or just:
389 //
390 // B(C)
391 //
392 // in case of constructors, destructors and type conversion operators. The algorithm looks for the opening parenthesis and while doing that
393 // it detects the beginning of B. As a result B is the function name.
394 //
395 // The first significant complication is function and array return types, in which case the syntax becomes nested:
396 //
397 // A (*B(C))(D)
398 // A (&B(C))[D]
399 //
400 // In addition to that MSVC adds calling convention, such as __cdecl, to function types. In order to detect these cases the algorithm
401 // seeks for the closing parenthesis after the opening one. If there is an opening parenthesis or square bracket after the closing parenthesis
402 // then this is a function or array return type. The case of arrays is additionally complicated by GCC output:
403 //
404 // A B(C) [D]
405 //
406 // where D is template parameters description and is not part of the signature. To discern this special case from the array return type, the algorithm
407 // checks for the first significant character within the parenthesis. This character is '&' in case of arrays and something else otherwise.
408 //
409 // Speaking of template parameters, the parsing algorithm ignores them completely, assuming they are part of the name being parsed. This includes
410 // any possible parenthesis, nested template parameters and even operators, which may be present there as non-type template parameters.
411 //
412 // Operators pose another problem. This is especially the case for type conversion operators, and even more so for conversion operators to
413 // function types. In this latter case at least MSVC is known to produce incomprehensible strings which we cannot parse. In other cases it is
414 // too difficult to parse the type correctly. So we cheat a little. Whenever we find "operator", we know that we've found the function name
415 // already, and the name ends at the opening parenthesis. For other operators we are able to parse them correctly but that doesn't really matter.
416 //
417 // Note that the algorithm should be tolerant to different flavors of the input strings from different compilers, so we can't rely on spaces
418 // delimiting function names and other elements. Also, the algorithm should behave well in case of the fallback string generated by
419 // BOOST_CURRENT_FUNCTION (which is "(unknown)" currently). In case of any parsing failure the algorithm should return false, in which case the
420 // full original string will be used as the output.
421
422 const char* b = begin;
423 const char* e = end;
424 while (b != e)
425 {
426 // Find the opening parenthesis. While looking for it, also find the function name.
427 // first_name_begin is the beginning of the function scope, last_name_begin is the actual function name.
428 const char* first_name_begin = NULL, *last_name_begin = NULL;
429 const char* paren_open = find_opening_parenthesis(b, e, first_name_begin, last_name_begin);
430 if (paren_open == e)
431 return false;
432 // Find the closing parenthesis. Also peek at the first character in the parenthesis, which we'll use to detect array return types.
433 char first_char_in_parenthesis = 0;
434 const char* paren_close = find_closing_parenthesis(paren_open + 1, e, first_char_in_parenthesis);
435 if (paren_close == e)
436 return false;
437
438 const char* p = skip_spaces(paren_close + 1, e);
439
440 // Detect function and array return types
441 if (p < e && (*p == '(' || (*p == '[' && first_char_in_parenthesis == '&')))
442 {
443 // This is a function or array return type, the actual function name is within the parenthesis.
444 // Re-parse the string within the parenthesis as a function signature.
445 b = paren_open + 1;
446 e = paren_close;
447 continue;
448 }
449
450 // We found something that looks like a function signature
451 if (include_scope)
452 {
453 if (!first_name_begin)
454 return false;
455
456 begin = first_name_begin;
457 }
458 else
459 {
460 if (!last_name_begin)
461 return false;
462
463 begin = last_name_begin;
464 }
465
466 end = paren_open;
467
468 return true;
469 }
470
471 return false;
472 }
473
474 template< typename CharT >
475 class named_scope_formatter
476 {
477 BOOST_COPYABLE_AND_MOVABLE_ALT(named_scope_formatter)
478
479 public:
480 typedef void result_type;
481
482 typedef CharT char_type;
483 typedef std::basic_string< char_type > string_type;
484 typedef basic_formatting_ostream< char_type > stream_type;
485 typedef attributes::named_scope::value_type::value_type value_type;
486
487 struct literal
488 {
489 typedef void result_type;
490
491 explicit literal(string_type& lit) { m_literal.swap(lit); }
492
493 result_type operator() (stream_type& strm, value_type const&) const
494 {
495 strm << m_literal;
496 }
497
498 private:
499 string_type m_literal;
500 };
501
502 struct scope_name
503 {
504 typedef void result_type;
505
506 result_type operator() (stream_type& strm, value_type const& value) const
507 {
508 strm << value.scope_name;
509 }
510 };
511
512 struct function_name
513 {
514 typedef void result_type;
515
516 explicit function_name(bool include_scope) : m_include_scope(include_scope)
517 {
518 }
519
520 result_type operator() (stream_type& strm, value_type const& value) const
521 {
522 if (value.type == attributes::named_scope_entry::function)
523 {
524 const char* begin = value.scope_name.c_str();
525 const char* end = begin + value.scope_name.size();
526 if (parse_function_name(begin, end, m_include_scope))
527 {
528 strm.write(begin, end - begin);
529 return;
530 }
531 }
532
533 strm << value.scope_name;
534 }
535
536 private:
537 const bool m_include_scope;
538 };
539
540 struct full_file_name
541 {
542 typedef void result_type;
543
544 result_type operator() (stream_type& strm, value_type const& value) const
545 {
546 strm << value.file_name;
547 }
548 };
549
550 struct file_name
551 {
552 typedef void result_type;
553
554 result_type operator() (stream_type& strm, value_type const& value) const
555 {
556 std::size_t n = value.file_name.size(), i = n;
557 for (; i > 0; --i)
558 {
559 const char c = value.file_name[i - 1];
560 #if defined(BOOST_WINDOWS)
561 if (c == '\\')
562 break;
563 #endif
564 if (c == '/')
565 break;
566 }
567 strm.write(value.file_name.c_str() + i, n - i);
568 }
569 };
570
571 struct line_number
572 {
573 typedef void result_type;
574
575 result_type operator() (stream_type& strm, value_type const& value) const
576 {
577 strm.flush();
578
579 char_type buf[std::numeric_limits< unsigned int >::digits10 + 2];
580 char_type* p = buf;
581
582 typedef karma::uint_generator< unsigned int, 10 > uint_gen;
583 karma::generate(p, uint_gen(), value.line);
584
585 typedef typename stream_type::streambuf_type streambuf_type;
586 static_cast< streambuf_type* >(strm.rdbuf())->append(buf, static_cast< std::size_t >(p - buf));
587 }
588 };
589
590 private:
591 typedef boost::log::aux::light_function< void (stream_type&, value_type const&) > formatter_type;
592 typedef std::vector< formatter_type > formatters;
593
594 private:
595 formatters m_formatters;
596
597 public:
598 BOOST_DEFAULTED_FUNCTION(named_scope_formatter(), {})
599 named_scope_formatter(named_scope_formatter const& that) : m_formatters(that.m_formatters) {}
600 named_scope_formatter(BOOST_RV_REF(named_scope_formatter) that) { m_formatters.swap(that.m_formatters); }
601
602 named_scope_formatter& operator= (named_scope_formatter that)
603 {
604 this->swap(that);
605 return *this;
606 }
607
608 result_type operator() (stream_type& strm, value_type const& value) const
609 {
610 for (typename formatters::const_iterator it = m_formatters.begin(), end = m_formatters.end(); strm.good() && it != end; ++it)
611 {
612 (*it)(strm, value);
613 }
614 }
615
616 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
617 template< typename FunT >
618 void add_formatter(FunT&& fun)
619 {
620 m_formatters.emplace_back(boost::forward< FunT >(fun));
621 }
622 #else
623 template< typename FunT >
624 void add_formatter(FunT const& fun)
625 {
626 m_formatters.push_back(formatter_type(fun));
627 }
628 #endif
629
630 void swap(named_scope_formatter& that)
631 {
632 m_formatters.swap(that.m_formatters);
633 }
634 };
635
636 //! Parses the named scope format string and constructs the formatter function
637 template< typename CharT >
638 BOOST_FORCEINLINE boost::log::aux::light_function< void (basic_formatting_ostream< CharT >&, attributes::named_scope::value_type::value_type const&) >
639 do_parse_named_scope_format(const CharT* begin, const CharT* end)
640 {
641 typedef CharT char_type;
642 typedef boost::log::aux::light_function< void (basic_formatting_ostream< char_type >&, attributes::named_scope::value_type::value_type const&) > result_type;
643 typedef named_scope_formatter< char_type > formatter_type;
644 formatter_type fmt;
645
646 std::basic_string< char_type > literal;
647
648 while (begin != end)
649 {
650 const char_type* p = std::find(begin, end, static_cast< char_type >('%'));
651 literal.append(begin, p);
652
653 if ((end - p) >= 2)
654 {
655 switch (p[1])
656 {
657 case '%':
658 literal.push_back(static_cast< char_type >('%'));
659 break;
660
661 case 'n':
662 if (!literal.empty())
663 fmt.add_formatter(typename formatter_type::literal(literal));
664 fmt.add_formatter(typename formatter_type::scope_name());
665 break;
666
667 case 'c':
668 if (!literal.empty())
669 fmt.add_formatter(typename formatter_type::literal(literal));
670 fmt.add_formatter(typename formatter_type::function_name(true));
671 break;
672
673 case 'C':
674 if (!literal.empty())
675 fmt.add_formatter(typename formatter_type::literal(literal));
676 fmt.add_formatter(typename formatter_type::function_name(false));
677 break;
678
679 case 'f':
680 if (!literal.empty())
681 fmt.add_formatter(typename formatter_type::literal(literal));
682 fmt.add_formatter(typename formatter_type::full_file_name());
683 break;
684
685 case 'F':
686 if (!literal.empty())
687 fmt.add_formatter(typename formatter_type::literal(literal));
688 fmt.add_formatter(typename formatter_type::file_name());
689 break;
690
691 case 'l':
692 if (!literal.empty())
693 fmt.add_formatter(typename formatter_type::literal(literal));
694 fmt.add_formatter(typename formatter_type::line_number());
695 break;
696
697 default:
698 literal.append(p, p + 2);
699 break;
700 }
701
702 begin = p + 2;
703 }
704 else
705 {
706 if (p != end)
707 literal.push_back(static_cast< char_type >('%')); // a single '%' character at the end of the string
708 begin = end;
709 }
710 }
711
712 if (!literal.empty())
713 fmt.add_formatter(typename formatter_type::literal(literal));
714
715 return result_type(boost::move(fmt));
716 }
717
718 } // namespace
719
720
721 #ifdef BOOST_LOG_USE_CHAR
722
723 //! Parses the named scope format string and constructs the formatter function
724 BOOST_LOG_API boost::log::aux::light_function< void (basic_formatting_ostream< char >&, attributes::named_scope::value_type::value_type const&) >
725 parse_named_scope_format(const char* begin, const char* end)
726 {
727 return do_parse_named_scope_format(begin, end);
728 }
729
730 #endif // BOOST_LOG_USE_CHAR
731
732 #ifdef BOOST_LOG_USE_WCHAR_T
733
734 //! Parses the named scope format string and constructs the formatter function
735 BOOST_LOG_API boost::log::aux::light_function< void (basic_formatting_ostream< wchar_t >&, attributes::named_scope::value_type::value_type const&) >
736 parse_named_scope_format(const wchar_t* begin, const wchar_t* end)
737 {
738 return do_parse_named_scope_format(begin, end);
739 }
740
741 #endif // BOOST_LOG_USE_WCHAR_T
742
743 } // namespace aux
744
745 } // namespace expressions
746
747 BOOST_LOG_CLOSE_NAMESPACE // namespace log
748
749 } // namespace boost
750
751 #include <boost/log/detail/footer.hpp>