Files
clang-p2996/libcxx/include/__chrono/parser_std_format_spec.h
Louis Dionne 87d56c59f5 [libc++][modules] Guard carved-out headers more consistently (#108637)
Several headers that should not be provided when localization or threads
are disabled were not guarded. That works until one tries to build with
modules and these headers get pulled in.

Note that this could be cleaned up further into something more
systematic, but this patch solves the immediate problems I ran into with
the monolithic modulemap and doesn't create any new inconsistency that
wasn't already there.
2024-09-16 08:15:38 -04:00

422 lines
12 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___CHRONO_PARSER_STD_FORMAT_SPEC_H
#define _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H
#include <__config>
#if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <__format/concepts.h>
# include <__format/format_error.h>
# include <__format/format_parse_context.h>
# include <__format/formatter_string.h>
# include <__format/parser_std_format_spec.h>
# include <string_view>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
# endif
_LIBCPP_BEGIN_NAMESPACE_STD
# if _LIBCPP_STD_VER >= 20
namespace __format_spec {
// By not placing this constant in the formatter class it's not duplicated for char and wchar_t
inline constexpr __fields __fields_chrono_fractional{
.__precision_ = true, .__locale_specific_form_ = true, .__type_ = false};
inline constexpr __fields __fields_chrono{.__locale_specific_form_ = true, .__type_ = false};
/// Flags available or required in a chrono type.
///
/// The caller of the chrono formatter lists the types it has available and the
/// validation tests whether the requested type spec (e.g. %M) is available in
/// the formatter.
/// When the type in the chrono-format-spec isn't present in the data a
/// \ref format_error is thrown.
enum class __flags {
__second = 0x1,
__minute = 0x2,
__hour = 0x4,
__time = __hour | __minute | __second,
__day = 0x8,
__month = 0x10,
__year = 0x20,
__weekday = 0x40,
__month_day = __day | __month,
__month_weekday = __weekday | __month,
__year_month = __month | __year,
__date = __day | __month | __year | __weekday,
__date_time = __date | __time,
__duration = 0x80 | __time,
__time_zone = 0x100,
__clock = __date_time | __time_zone
};
_LIBCPP_HIDE_FROM_ABI constexpr __flags operator&(__flags __lhs, __flags __rhs) {
return static_cast<__flags>(static_cast<unsigned>(__lhs) & static_cast<unsigned>(__rhs));
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_second(__flags __flags) {
if ((__flags & __flags::__second) != __flags::__second)
std::__throw_format_error("The supplied date time doesn't contain a second");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_minute(__flags __flags) {
if ((__flags & __flags::__minute) != __flags::__minute)
std::__throw_format_error("The supplied date time doesn't contain a minute");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_hour(__flags __flags) {
if ((__flags & __flags::__hour) != __flags::__hour)
std::__throw_format_error("The supplied date time doesn't contain an hour");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_time(__flags __flags) {
if ((__flags & __flags::__time) != __flags::__time)
std::__throw_format_error("The supplied date time doesn't contain a time");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_day(__flags __flags) {
if ((__flags & __flags::__day) != __flags::__day)
std::__throw_format_error("The supplied date time doesn't contain a day");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_month(__flags __flags) {
if ((__flags & __flags::__month) != __flags::__month)
std::__throw_format_error("The supplied date time doesn't contain a month");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_year(__flags __flags) {
if ((__flags & __flags::__year) != __flags::__year)
std::__throw_format_error("The supplied date time doesn't contain a year");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_date(__flags __flags) {
if ((__flags & __flags::__date) != __flags::__date)
std::__throw_format_error("The supplied date time doesn't contain a date");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_date_or_duration(__flags __flags) {
if (((__flags & __flags::__date) != __flags::__date) && ((__flags & __flags::__duration) != __flags::__duration))
std::__throw_format_error("The supplied date time doesn't contain a date or duration");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_date_time(__flags __flags) {
if ((__flags & __flags::__date_time) != __flags::__date_time)
std::__throw_format_error("The supplied date time doesn't contain a date and time");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_weekday(__flags __flags) {
if ((__flags & __flags::__weekday) != __flags::__weekday)
std::__throw_format_error("The supplied date time doesn't contain a weekday");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_duration(__flags __flags) {
if ((__flags & __flags::__duration) != __flags::__duration)
std::__throw_format_error("The supplied date time doesn't contain a duration");
}
_LIBCPP_HIDE_FROM_ABI constexpr void __validate_time_zone(__flags __flags) {
if ((__flags & __flags::__time_zone) != __flags::__time_zone)
std::__throw_format_error("The supplied date time doesn't contain a time zone");
}
template <class _CharT>
class _LIBCPP_TEMPLATE_VIS __parser_chrono {
using _ConstIterator = typename basic_format_parse_context<_CharT>::const_iterator;
public:
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator
__parse(_ParseContext& __ctx, __fields __fields, __flags __flags) {
_ConstIterator __begin = __parser_.__parse(__ctx, __fields);
_ConstIterator __end = __ctx.end();
if (__begin == __end)
return __begin;
_ConstIterator __last = __parse_chrono_specs(__begin, __end, __flags);
__chrono_specs_ = basic_string_view<_CharT>{__begin, __last};
return __last;
}
__parser<_CharT> __parser_;
basic_string_view<_CharT> __chrono_specs_;
private:
_LIBCPP_HIDE_FROM_ABI constexpr _ConstIterator
__parse_chrono_specs(_ConstIterator __begin, _ConstIterator __end, __flags __flags) {
_LIBCPP_ASSERT_INTERNAL(__begin != __end,
"When called with an empty input the function will cause "
"undefined behavior by evaluating data not in the input");
if (*__begin != _CharT('%') && *__begin != _CharT('}'))
std::__throw_format_error("The format specifier expects a '%' or a '}'");
do {
switch (*__begin) {
case _CharT('{'):
std::__throw_format_error("The chrono specifiers contain a '{'");
case _CharT('}'):
return __begin;
case _CharT('%'):
__parse_conversion_spec(__begin, __end, __flags);
[[fallthrough]];
default:
// All other literals
++__begin;
}
} while (__begin != __end && *__begin != _CharT('}'));
return __begin;
}
/// \pre *__begin == '%'
/// \post __begin points at the end parsed conversion-spec
_LIBCPP_HIDE_FROM_ABI constexpr void
__parse_conversion_spec(_ConstIterator& __begin, _ConstIterator __end, __flags __flags) {
++__begin;
if (__begin == __end)
std::__throw_format_error("End of input while parsing a conversion specifier");
switch (*__begin) {
case _CharT('n'):
case _CharT('t'):
case _CharT('%'):
break;
case _CharT('S'):
__format_spec::__validate_second(__flags);
break;
case _CharT('M'):
__format_spec::__validate_minute(__flags);
break;
case _CharT('p'): // TODO FMT does the formater require an hour or a time?
case _CharT('H'):
case _CharT('I'):
__parser_.__hour_ = true;
__validate_hour(__flags);
break;
case _CharT('r'):
case _CharT('R'):
case _CharT('T'):
case _CharT('X'):
__parser_.__hour_ = true;
__format_spec::__validate_time(__flags);
break;
case _CharT('d'):
case _CharT('e'):
__format_spec::__validate_day(__flags);
break;
case _CharT('b'):
case _CharT('h'):
case _CharT('B'):
__parser_.__month_name_ = true;
[[fallthrough]];
case _CharT('m'):
__format_spec::__validate_month(__flags);
break;
case _CharT('y'):
case _CharT('C'):
case _CharT('Y'):
__format_spec::__validate_year(__flags);
break;
case _CharT('j'):
__parser_.__day_of_year_ = true;
__format_spec::__validate_date_or_duration(__flags);
break;
case _CharT('g'):
case _CharT('G'):
case _CharT('U'):
case _CharT('V'):
case _CharT('W'):
__parser_.__week_of_year_ = true;
[[fallthrough]];
case _CharT('x'):
case _CharT('D'):
case _CharT('F'):
__format_spec::__validate_date(__flags);
break;
case _CharT('c'):
__format_spec::__validate_date_time(__flags);
break;
case _CharT('a'):
case _CharT('A'):
__parser_.__weekday_name_ = true;
[[fallthrough]];
case _CharT('u'):
case _CharT('w'):
__parser_.__weekday_ = true;
__validate_weekday(__flags);
__format_spec::__validate_weekday(__flags);
break;
case _CharT('q'):
case _CharT('Q'):
__format_spec::__validate_duration(__flags);
break;
case _CharT('E'):
__parse_modifier_E(__begin, __end, __flags);
break;
case _CharT('O'):
__parse_modifier_O(__begin, __end, __flags);
break;
case _CharT('z'):
case _CharT('Z'):
// Currently there's no time zone information. However some clocks have a
// hard-coded "time zone", for these clocks the information can be used.
// TODO FMT implement time zones.
__format_spec::__validate_time_zone(__flags);
break;
default: // unknown type;
std::__throw_format_error("The date time type specifier is invalid");
}
}
/// \pre *__begin == 'E'
/// \post __begin is incremented by one.
_LIBCPP_HIDE_FROM_ABI constexpr void
__parse_modifier_E(_ConstIterator& __begin, _ConstIterator __end, __flags __flags) {
++__begin;
if (__begin == __end)
std::__throw_format_error("End of input while parsing the modifier E");
switch (*__begin) {
case _CharT('X'):
__parser_.__hour_ = true;
__format_spec::__validate_time(__flags);
break;
case _CharT('y'):
case _CharT('C'):
case _CharT('Y'):
__format_spec::__validate_year(__flags);
break;
case _CharT('x'):
__format_spec::__validate_date(__flags);
break;
case _CharT('c'):
__format_spec::__validate_date_time(__flags);
break;
case _CharT('z'):
// Currently there's no time zone information. However some clocks have a
// hard-coded "time zone", for these clocks the information can be used.
// TODO FMT implement time zones.
__format_spec::__validate_time_zone(__flags);
break;
default:
std::__throw_format_error("The date time type specifier for modifier E is invalid");
}
}
/// \pre *__begin == 'O'
/// \post __begin is incremented by one.
_LIBCPP_HIDE_FROM_ABI constexpr void
__parse_modifier_O(_ConstIterator& __begin, _ConstIterator __end, __flags __flags) {
++__begin;
if (__begin == __end)
std::__throw_format_error("End of input while parsing the modifier O");
switch (*__begin) {
case _CharT('S'):
__format_spec::__validate_second(__flags);
break;
case _CharT('M'):
__format_spec::__validate_minute(__flags);
break;
case _CharT('I'):
case _CharT('H'):
__parser_.__hour_ = true;
__format_spec::__validate_hour(__flags);
break;
case _CharT('d'):
case _CharT('e'):
__format_spec::__validate_day(__flags);
break;
case _CharT('m'):
__format_spec::__validate_month(__flags);
break;
case _CharT('y'):
__format_spec::__validate_year(__flags);
break;
case _CharT('U'):
case _CharT('V'):
case _CharT('W'):
__parser_.__week_of_year_ = true;
__format_spec::__validate_date(__flags);
break;
case _CharT('u'):
case _CharT('w'):
__parser_.__weekday_ = true;
__format_spec::__validate_weekday(__flags);
break;
case _CharT('z'):
// Currently there's no time zone information. However some clocks have a
// hard-coded "time zone", for these clocks the information can be used.
// TODO FMT implement time zones.
__format_spec::__validate_time_zone(__flags);
break;
default:
std::__throw_format_error("The date time type specifier for modifier O is invalid");
}
}
};
} // namespace __format_spec
# endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
#endif // !defined(_LIBCPP_HAS_NO_LOCALIZATION)
#endif // _LIBCPP___CHRONO_PARSER_STD_FORMAT_SPEC_H