forked from OSchip/llvm-project
170 lines
5.4 KiB
C++
170 lines
5.4 KiB
C++
// -*- C++ -*-
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef _LIBCPP___FORMAT_FORMAT_STRING_H
|
|
#define _LIBCPP___FORMAT_FORMAT_STRING_H
|
|
|
|
#include <__config>
|
|
#include <__debug>
|
|
#include <__format/format_error.h>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
|
#pragma GCC system_header
|
|
#endif
|
|
|
|
_LIBCPP_BEGIN_NAMESPACE_STD
|
|
|
|
#if _LIBCPP_STD_VER > 17
|
|
|
|
// TODO FMT Remove this once we require compilers with proper C++20 support.
|
|
// If the compiler has no concepts support, the format header will be disabled.
|
|
// Without concepts support enable_if needs to be used and that too much effort
|
|
// to support compilers with partial C++20 support.
|
|
#if !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
|
|
|
namespace __format {
|
|
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS __parse_number_result {
|
|
const _CharT* __ptr;
|
|
uint32_t __value;
|
|
};
|
|
|
|
template <class _CharT>
|
|
_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
|
|
__parse_number(const _CharT* __begin, const _CharT* __end);
|
|
|
|
/**
|
|
* The maximum value of a numeric argument.
|
|
*
|
|
* This is used for:
|
|
* * arg-id
|
|
* * width as value or arg-id.
|
|
* * precision as value or arg-id.
|
|
*
|
|
* The value is compatible with the maximum formatting width and precision
|
|
* using the `%*` syntax on a 32-bit system.
|
|
*/
|
|
inline constexpr uint32_t __number_max = INT32_MAX;
|
|
|
|
namespace __detail {
|
|
template <class _CharT>
|
|
_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
|
|
__parse_zero(const _CharT* __begin, const _CharT*, auto& __parse_ctx) {
|
|
__parse_ctx.check_arg_id(0);
|
|
return {++__begin, 0}; // can never be larger than the maximum.
|
|
}
|
|
|
|
template <class _CharT>
|
|
_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
|
|
__parse_automatic(const _CharT* __begin, const _CharT*, auto& __parse_ctx) {
|
|
size_t __value = __parse_ctx.next_arg_id();
|
|
_LIBCPP_ASSERT(__value <= __number_max,
|
|
"Compilers don't support this number of arguments");
|
|
|
|
return {__begin, uint32_t(__value)};
|
|
}
|
|
|
|
template <class _CharT>
|
|
_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
|
|
__parse_manual(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) {
|
|
__parse_number_result<_CharT> __r = __parse_number(__begin, __end);
|
|
__parse_ctx.check_arg_id(__r.__value);
|
|
return __r;
|
|
}
|
|
|
|
} // namespace __detail
|
|
|
|
/**
|
|
* Parses a number.
|
|
*
|
|
* The number is used for the 31-bit values @em width and @em precision. This
|
|
* allows a maximum value of 2147483647.
|
|
*/
|
|
template <class _CharT>
|
|
_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
|
|
__parse_number(const _CharT* __begin, const _CharT* __end_input) {
|
|
static_assert(__format::__number_max == INT32_MAX,
|
|
"The algorithm is implemented based on this value.");
|
|
/*
|
|
* Limit the input to 9 digits, otherwise we need two checks during every
|
|
* iteration:
|
|
* - Are we at the end of the input?
|
|
* - Does the value exceed width of an uint32_t? (Switching to uint64_t would
|
|
* have the same issue, but with a higher maximum.)
|
|
*/
|
|
const _CharT* __end = __end_input - __begin > 9 ? __begin + 9 : __end_input;
|
|
uint32_t __value = *__begin - _CharT('0');
|
|
while (++__begin != __end) {
|
|
if (*__begin < _CharT('0') || *__begin > _CharT('9'))
|
|
return {__begin, __value};
|
|
|
|
__value = __value * 10 + *__begin - _CharT('0');
|
|
}
|
|
|
|
if (__begin != __end_input && *__begin >= _CharT('0') &&
|
|
*__begin <= _CharT('9')) {
|
|
|
|
/*
|
|
* There are more than 9 digits, do additional validations:
|
|
* - Does the 10th digit exceed the maximum allowed value?
|
|
* - Are there more than 10 digits?
|
|
* (More than 10 digits always overflows the maximum.)
|
|
*/
|
|
uint64_t __v = uint64_t(__value) * 10 + *__begin++ - _CharT('0');
|
|
if (__v > __number_max ||
|
|
(__begin != __end_input && *__begin >= _CharT('0') &&
|
|
*__begin <= _CharT('9')))
|
|
__throw_format_error("The numeric value of the format-spec is too large");
|
|
|
|
__value = __v;
|
|
}
|
|
|
|
return {__begin, __value};
|
|
}
|
|
|
|
/**
|
|
* Multiplexer for all parse functions.
|
|
*
|
|
* The parser will return a pointer beyond the last consumed character. This
|
|
* should be the closing '}' of the arg-id.
|
|
*/
|
|
template <class _CharT>
|
|
_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT>
|
|
__parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) {
|
|
switch (*__begin) {
|
|
case _CharT('0'):
|
|
return __detail::__parse_zero(__begin, __end, __parse_ctx);
|
|
|
|
case _CharT(':'):
|
|
// This case is conditionally valid. It's allowed in an arg-id in the
|
|
// replacement-field, but not in the std-format-spec. The caller can
|
|
// provide a better diagnostic, so accept it here unconditionally.
|
|
case _CharT('}'):
|
|
return __detail::__parse_automatic(__begin, __end, __parse_ctx);
|
|
}
|
|
if (*__begin < _CharT('0') || *__begin > _CharT('9'))
|
|
__throw_format_error(
|
|
"The arg-id of the format-spec starts with an invalid character");
|
|
|
|
return __detail::__parse_manual(__begin, __end, __parse_ctx);
|
|
}
|
|
|
|
} // namespace __format
|
|
|
|
#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
|
|
|
#endif //_LIBCPP_STD_VER > 17
|
|
|
|
_LIBCPP_END_NAMESPACE_STD
|
|
|
|
#endif // _LIBCPP___FORMAT_FORMAT_STRING_H
|