1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9
10 #ifndef _LIBCPP___FORMAT_FORMAT_STRING_H
11 #define _LIBCPP___FORMAT_FORMAT_STRING_H
12
13 #include <__assert>
14 #include <__config>
15 #include <__format/format_error.h>
16 #include <cstddef>
17 #include <cstdint>
18
19 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
20 # pragma GCC system_header
21 #endif
22
23 _LIBCPP_BEGIN_NAMESPACE_STD
24
25 #if _LIBCPP_STD_VER > 17
26
27 namespace __format {
28
29 template <class _CharT>
30 struct _LIBCPP_TEMPLATE_VIS __parse_number_result {
31 const _CharT* __ptr;
32 uint32_t __value;
33 };
34
35 template <class _CharT>
36 _LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
37 __parse_number(const _CharT* __begin, const _CharT* __end);
38
39 /**
40 * The maximum value of a numeric argument.
41 *
42 * This is used for:
43 * * arg-id
44 * * width as value or arg-id.
45 * * precision as value or arg-id.
46 *
47 * The value is compatible with the maximum formatting width and precision
48 * using the `%*` syntax on a 32-bit system.
49 */
50 inline constexpr uint32_t __number_max = INT32_MAX;
51
52 namespace __detail {
53 template <class _CharT>
54 _LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
__parse_zero(const _CharT * __begin,const _CharT *,auto & __parse_ctx)55 __parse_zero(const _CharT* __begin, const _CharT*, auto& __parse_ctx) {
56 __parse_ctx.check_arg_id(0);
57 return {++__begin, 0}; // can never be larger than the maximum.
58 }
59
60 template <class _CharT>
61 _LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
__parse_automatic(const _CharT * __begin,const _CharT *,auto & __parse_ctx)62 __parse_automatic(const _CharT* __begin, const _CharT*, auto& __parse_ctx) {
63 size_t __value = __parse_ctx.next_arg_id();
64 _LIBCPP_ASSERT(__value <= __number_max,
65 "Compilers don't support this number of arguments");
66
67 return {__begin, uint32_t(__value)};
68 }
69
70 template <class _CharT>
71 _LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
__parse_manual(const _CharT * __begin,const _CharT * __end,auto & __parse_ctx)72 __parse_manual(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) {
73 __parse_number_result<_CharT> __r = __parse_number(__begin, __end);
74 __parse_ctx.check_arg_id(__r.__value);
75 return __r;
76 }
77
78 } // namespace __detail
79
80 /**
81 * Parses a number.
82 *
83 * The number is used for the 31-bit values @em width and @em precision. This
84 * allows a maximum value of 2147483647.
85 */
86 template <class _CharT>
87 _LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
__parse_number(const _CharT * __begin,const _CharT * __end_input)88 __parse_number(const _CharT* __begin, const _CharT* __end_input) {
89 static_assert(__format::__number_max == INT32_MAX,
90 "The algorithm is implemented based on this value.");
91 /*
92 * Limit the input to 9 digits, otherwise we need two checks during every
93 * iteration:
94 * - Are we at the end of the input?
95 * - Does the value exceed width of an uint32_t? (Switching to uint64_t would
96 * have the same issue, but with a higher maximum.)
97 */
98 const _CharT* __end = __end_input - __begin > 9 ? __begin + 9 : __end_input;
99 uint32_t __value = *__begin - _CharT('0');
100 while (++__begin != __end) {
101 if (*__begin < _CharT('0') || *__begin > _CharT('9'))
102 return {__begin, __value};
103
104 __value = __value * 10 + *__begin - _CharT('0');
105 }
106
107 if (__begin != __end_input && *__begin >= _CharT('0') &&
108 *__begin <= _CharT('9')) {
109
110 /*
111 * There are more than 9 digits, do additional validations:
112 * - Does the 10th digit exceed the maximum allowed value?
113 * - Are there more than 10 digits?
114 * (More than 10 digits always overflows the maximum.)
115 */
116 uint64_t __v = uint64_t(__value) * 10 + *__begin++ - _CharT('0');
117 if (__v > __number_max ||
118 (__begin != __end_input && *__begin >= _CharT('0') &&
119 *__begin <= _CharT('9')))
120 __throw_format_error("The numeric value of the format-spec is too large");
121
122 __value = __v;
123 }
124
125 return {__begin, __value};
126 }
127
128 /**
129 * Multiplexer for all parse functions.
130 *
131 * The parser will return a pointer beyond the last consumed character. This
132 * should be the closing '}' of the arg-id.
133 */
134 template <class _CharT>
135 _LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
__parse_arg_id(const _CharT * __begin,const _CharT * __end,auto & __parse_ctx)136 __parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) {
137 switch (*__begin) {
138 case _CharT('0'):
139 return __detail::__parse_zero(__begin, __end, __parse_ctx);
140
141 case _CharT(':'):
142 // This case is conditionally valid. It's allowed in an arg-id in the
143 // replacement-field, but not in the std-format-spec. The caller can
144 // provide a better diagnostic, so accept it here unconditionally.
145 case _CharT('}'):
146 return __detail::__parse_automatic(__begin, __end, __parse_ctx);
147 }
148 if (*__begin < _CharT('0') || *__begin > _CharT('9'))
149 __throw_format_error(
150 "The arg-id of the format-spec starts with an invalid character");
151
152 return __detail::__parse_manual(__begin, __end, __parse_ctx);
153 }
154
155 } // namespace __format
156
157 #endif //_LIBCPP_STD_VER > 17
158
159 _LIBCPP_END_NAMESPACE_STD
160
161 #endif // _LIBCPP___FORMAT_FORMAT_STRING_H
162