| |
| |
| |
| |
| |
| |
| |
|
|
| |
|
|
| #include <__assert> |
| #include <algorithm> |
| #include <cctype> |
| #include <chrono> |
| #include <filesystem> |
| #include <fstream> |
| #include <stdexcept> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
|
|
| #include "include/tzdb/time_zone_private.h" |
| #include "include/tzdb/types_private.h" |
| #include "include/tzdb/tzdb_list_private.h" |
| #include "include/tzdb/tzdb_private.h" |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| _LIBCPP_BEGIN_NAMESPACE_STD |
|
|
| namespace chrono { |
|
|
| |
| |
| _LIBCPP_WEAK string_view __libcpp_tzdb_directory() { |
| #if defined(__linux__) |
| return "/usr/share/zoneinfo/"; |
| #else |
| # error "unknown path to the IANA Time Zone Database" |
| #endif |
| } |
|
|
| |
| |
| |
|
|
| [[nodiscard]] static bool __is_whitespace(int __c) { return __c == ' ' || __c == '\t'; } |
|
|
| static void __skip_optional_whitespace(istream& __input) { |
| while (chrono::__is_whitespace(__input.peek())) |
| __input.get(); |
| } |
|
|
| static void __skip_mandatory_whitespace(istream& __input) { |
| if (!chrono::__is_whitespace(__input.get())) |
| std::__throw_runtime_error("corrupt tzdb: expected whitespace"); |
|
|
| chrono::__skip_optional_whitespace(__input); |
| } |
|
|
| [[nodiscard]] static bool __is_eol(int __c) { return __c == '\n' || __c == std::char_traits<char>::eof(); } |
|
|
| static void __skip_line(istream& __input) { |
| while (!chrono::__is_eol(__input.peek())) { |
| __input.get(); |
| } |
| __input.get(); |
| } |
|
|
| static void __skip(istream& __input, char __suffix) { |
| if (std::tolower(__input.peek()) == __suffix) |
| __input.get(); |
| } |
|
|
| static void __skip(istream& __input, string_view __suffix) { |
| for (auto __c : __suffix) |
| if (std::tolower(__input.peek()) == __c) |
| __input.get(); |
| } |
|
|
| static void __matches(istream& __input, char __expected) { |
| _LIBCPP_ASSERT_INTERNAL(!std::isalpha(__expected) || std::islower(__expected), "lowercase characters only here!"); |
| char __c = __input.get(); |
| if (std::tolower(__c) != __expected) |
| std::__throw_runtime_error( |
| (string("corrupt tzdb: expected character '") + __expected + "', got '" + __c + "' instead").c_str()); |
| } |
|
|
| static void __matches(istream& __input, string_view __expected) { |
| for (auto __c : __expected) { |
| _LIBCPP_ASSERT_INTERNAL(!std::isalpha(__c) || std::islower(__c), "lowercase strings only here!"); |
| char __actual = __input.get(); |
| if (std::tolower(__actual) != __c) |
| std::__throw_runtime_error( |
| (string("corrupt tzdb: expected character '") + __c + "' from string '" + string(__expected) + "', got '" + |
| __actual + "' instead") |
| .c_str()); |
| } |
| } |
|
|
| [[nodiscard]] static string __parse_string(istream& __input) { |
| string __result; |
| while (true) { |
| int __c = __input.get(); |
| switch (__c) { |
| case ' ': |
| case '\t': |
| case '\n': |
| __input.unget(); |
| [[fallthrough]]; |
| case istream::traits_type::eof(): |
| if (__result.empty()) |
| std::__throw_runtime_error("corrupt tzdb: expected a string"); |
|
|
| return __result; |
|
|
| default: |
| __result.push_back(__c); |
| } |
| } |
| } |
|
|
| [[nodiscard]] static int64_t __parse_integral(istream& __input, bool __leading_zero_allowed) { |
| int64_t __result = __input.get(); |
| if (__leading_zero_allowed) { |
| if (__result < '0' || __result > '9') |
| std::__throw_runtime_error("corrupt tzdb: expected a digit"); |
| } else { |
| if (__result < '1' || __result > '9') |
| std::__throw_runtime_error("corrupt tzdb: expected a non-zero digit"); |
| } |
| __result -= '0'; |
| while (true) { |
| if (__input.peek() < '0' || __input.peek() > '9') |
| return __result; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (__result > (std::numeric_limits<int64_t>::max() / 16)) |
| std::__throw_runtime_error("corrupt tzdb: integral too large"); |
|
|
| __result *= 10; |
| __result += __input.get() - '0'; |
| } |
| } |
|
|
| |
| |
| |
|
|
| [[nodiscard]] static day __parse_day(istream& __input) { |
| unsigned __result = chrono::__parse_integral(__input, false); |
| if (__result > 31) |
| std::__throw_runtime_error("corrupt tzdb day: value too large"); |
| return day{__result}; |
| } |
|
|
| [[nodiscard]] static weekday __parse_weekday(istream& __input) { |
| |
| switch (std::tolower(__input.get())) { |
| case 'f': |
| chrono::__skip(__input, "riday"); |
| return Friday; |
|
|
| case 'm': |
| chrono::__skip(__input, "onday"); |
| return Monday; |
|
|
| case 's': |
| switch (std::tolower(__input.get())) { |
| case 'a': |
| chrono::__skip(__input, "turday"); |
| return Saturday; |
|
|
| case 'u': |
| chrono::__skip(__input, "nday"); |
| return Sunday; |
| } |
| break; |
|
|
| case 't': |
| switch (std::tolower(__input.get())) { |
| case 'h': |
| chrono::__skip(__input, "ursday"); |
| return Thursday; |
|
|
| case 'u': |
| chrono::__skip(__input, "esday"); |
| return Tuesday; |
| } |
| break; |
| case 'w': |
| chrono::__skip(__input, "ednesday"); |
| return Wednesday; |
| } |
|
|
| std::__throw_runtime_error("corrupt tzdb weekday: invalid name"); |
| } |
|
|
| [[nodiscard]] static month __parse_month(istream& __input) { |
| |
| switch (std::tolower(__input.get())) { |
| case 'a': |
| switch (std::tolower(__input.get())) { |
| case 'p': |
| chrono::__skip(__input, "ril"); |
| return April; |
|
|
| case 'u': |
| chrono::__skip(__input, "gust"); |
| return August; |
| } |
| break; |
|
|
| case 'd': |
| chrono::__skip(__input, "ecember"); |
| return December; |
|
|
| case 'f': |
| chrono::__skip(__input, "ebruary"); |
| return February; |
|
|
| case 'j': |
| switch (std::tolower(__input.get())) { |
| case 'a': |
| chrono::__skip(__input, "nuary"); |
| return January; |
|
|
| case 'u': |
| switch (std::tolower(__input.get())) { |
| case 'n': |
| chrono::__skip(__input, 'e'); |
| return June; |
|
|
| case 'l': |
| chrono::__skip(__input, 'y'); |
| return July; |
| } |
| } |
| break; |
|
|
| case 'm': |
| if (std::tolower(__input.get()) == 'a') |
| switch (std::tolower(__input.get())) { |
| case 'y': |
| return May; |
|
|
| case 'r': |
| chrono::__skip(__input, "ch"); |
| return March; |
| } |
| break; |
|
|
| case 'n': |
| chrono::__skip(__input, "ovember"); |
| return November; |
|
|
| case 'o': |
| chrono::__skip(__input, "ctober"); |
| return October; |
|
|
| case 's': |
| chrono::__skip(__input, "eptember"); |
| return September; |
| } |
| std::__throw_runtime_error("corrupt tzdb month: invalid name"); |
| } |
|
|
| [[nodiscard]] static year __parse_year_value(istream& __input) { |
| bool __negative = __input.peek() == '-'; |
| if (__negative) [[unlikely]] |
| __input.get(); |
|
|
| int64_t __result = __parse_integral(__input, true); |
| if (__result > static_cast<int>(year::max())) { |
| if (__negative) |
| std::__throw_runtime_error("corrupt tzdb year: year is less than the minimum"); |
|
|
| std::__throw_runtime_error("corrupt tzdb year: year is greater than the maximum"); |
| } |
|
|
| return year{static_cast<int>(__negative ? -__result : __result)}; |
| } |
|
|
| [[nodiscard]] static year __parse_year(istream& __input) { |
| if (std::tolower(__input.peek()) != 'm') [[likely]] |
| return chrono::__parse_year_value(__input); |
|
|
| __input.get(); |
| switch (std::tolower(__input.peek())) { |
| case 'i': |
| __input.get(); |
| chrono::__skip(__input, 'n'); |
| [[fallthrough]]; |
|
|
| case ' ': |
| |
| return year::min(); |
|
|
| case 'a': |
| __input.get(); |
| chrono::__skip(__input, 'x'); |
| return year::max(); |
| } |
|
|
| std::__throw_runtime_error("corrupt tzdb year: expected 'min' or 'max'"); |
| } |
|
|
| |
| |
| |
|
|
| [[nodiscard]] static year __parse_to(istream& __input, year __only) { |
| if (std::tolower(__input.peek()) != 'o') |
| return chrono::__parse_year(__input); |
|
|
| __input.get(); |
| chrono::__skip(__input, "nly"); |
| return __only; |
| } |
|
|
| [[nodiscard]] static __tz::__constrained_weekday::__comparison_t __parse_comparison(istream& __input) { |
| switch (__input.get()) { |
| case '>': |
| chrono::__matches(__input, '='); |
| return __tz::__constrained_weekday::__ge; |
|
|
| case '<': |
| chrono::__matches(__input, '='); |
| return __tz::__constrained_weekday::__le; |
| } |
| std::__throw_runtime_error("corrupt tzdb on: expected '>=' or '<='"); |
| } |
|
|
| [[nodiscard]] static __tz::__on __parse_on(istream& __input) { |
| if (std::isdigit(__input.peek())) |
| return chrono::__parse_day(__input); |
|
|
| if (std::tolower(__input.peek()) == 'l') { |
| chrono::__matches(__input, "last"); |
| return weekday_last(chrono::__parse_weekday(__input)); |
| } |
|
|
| return __tz::__constrained_weekday{ |
| chrono::__parse_weekday(__input), chrono::__parse_comparison(__input), chrono::__parse_day(__input)}; |
| } |
|
|
| [[nodiscard]] static seconds __parse_duration(istream& __input) { |
| seconds __result{0}; |
| int __c = __input.peek(); |
| bool __negative = __c == '-'; |
| if (__negative) { |
| __input.get(); |
| |
| |
| if (!std::isdigit(__input.peek())) |
| return __result; |
| } |
|
|
| __result += hours(__parse_integral(__input, true)); |
| if (__input.peek() != ':') |
| return __negative ? -__result : __result; |
|
|
| __input.get(); |
| __result += minutes(__parse_integral(__input, true)); |
| if (__input.peek() != ':') |
| return __negative ? -__result : __result; |
|
|
| __input.get(); |
| __result += seconds(__parse_integral(__input, true)); |
| if (__input.peek() != '.') |
| return __negative ? -__result : __result; |
|
|
| __input.get(); |
| (void)__parse_integral(__input, true); |
|
|
| return __negative ? -__result : __result; |
| } |
|
|
| [[nodiscard]] static __tz::__clock __parse_clock(istream& __input) { |
| switch (__input.get()) { |
| case 'w': |
| return __tz::__clock::__local; |
| case 's': |
| return __tz::__clock::__standard; |
|
|
| case 'u': |
| case 'g': |
| case 'z': |
| return __tz::__clock::__universal; |
| } |
|
|
| __input.unget(); |
| return __tz::__clock::__local; |
| } |
|
|
| [[nodiscard]] static bool __parse_dst(istream& __input, seconds __offset) { |
| switch (__input.get()) { |
| case 's': |
| return false; |
|
|
| case 'd': |
| return true; |
| } |
|
|
| __input.unget(); |
| return __offset != 0s; |
| } |
|
|
| [[nodiscard]] static __tz::__at __parse_at(istream& __input) { |
| return {__parse_duration(__input), __parse_clock(__input)}; |
| } |
|
|
| [[nodiscard]] static __tz::__save __parse_save(istream& __input) { |
| seconds __time = chrono::__parse_duration(__input); |
| return {__time, chrono::__parse_dst(__input, __time)}; |
| } |
|
|
| [[nodiscard]] static string __parse_letters(istream& __input) { |
| string __result = __parse_string(__input); |
| |
| return __result != "-" ? __result : ""; |
| } |
|
|
| [[nodiscard]] static __tz::__continuation::__rules_t __parse_rules(istream& __input) { |
| int __c = __input.peek(); |
| |
| if (__c == '-') { |
| __input.get(); |
| if (chrono::__is_whitespace(__input.peek())) |
| return monostate{}; |
| __input.unget(); |
| return chrono::__parse_save(__input); |
| } |
|
|
| if (std::isdigit(__c) || __c == '+') |
| return chrono::__parse_save(__input); |
|
|
| return chrono::__parse_string(__input); |
| } |
|
|
| [[nodiscard]] static __tz::__continuation __parse_continuation(__tz::__rules_storage_type& __rules, istream& __input) { |
| __tz::__continuation __result; |
|
|
| __result.__rule_database_ = std::addressof(__rules); |
|
|
| |
| |
| |
| |
|
|
| __result.__stdoff = chrono::__parse_duration(__input); |
| chrono::__skip_mandatory_whitespace(__input); |
| __result.__rules = chrono::__parse_rules(__input); |
| chrono::__skip_mandatory_whitespace(__input); |
| __result.__format = chrono::__parse_string(__input); |
| chrono::__skip_optional_whitespace(__input); |
|
|
| if (chrono::__is_eol(__input.peek())) |
| return __result; |
| __result.__year = chrono::__parse_year(__input); |
| chrono::__skip_optional_whitespace(__input); |
|
|
| if (chrono::__is_eol(__input.peek())) |
| return __result; |
| __result.__in = chrono::__parse_month(__input); |
| chrono::__skip_optional_whitespace(__input); |
|
|
| if (chrono::__is_eol(__input.peek())) |
| return __result; |
| __result.__on = chrono::__parse_on(__input); |
| chrono::__skip_optional_whitespace(__input); |
|
|
| if (chrono::__is_eol(__input.peek())) |
| return __result; |
| __result.__at = __parse_at(__input); |
|
|
| return __result; |
| } |
|
|
| |
| |
| |
|
|
| static string __parse_version(istream& __input) { |
| |
| |
| |
| |
| |
| chrono::__matches(__input, '#'); |
| chrono::__skip_optional_whitespace(__input); |
| chrono::__matches(__input, "version"); |
| chrono::__skip_mandatory_whitespace(__input); |
| return chrono::__parse_string(__input); |
| } |
|
|
| [[nodiscard]] |
| static __tz::__rule& __create_entry(__tz::__rules_storage_type& __rules, const string& __name) { |
| auto __result = [&]() -> __tz::__rule& { |
| auto& __rule = __rules.emplace_back(__name, vector<__tz::__rule>{}); |
| return __rule.second.emplace_back(); |
| }; |
|
|
| if (__rules.empty()) |
| return __result(); |
|
|
| |
| |
| if (__rules.back().first == __name) |
| return __rules.back().second.emplace_back(); |
|
|
| if (auto __it = ranges::find(__rules, __name, [](const auto& __r) { return __r.first; }); |
| __it != ranges::end(__rules)) |
| return __it->second.emplace_back(); |
|
|
| return __result(); |
| } |
|
|
| static void __parse_rule(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) { |
| chrono::__skip_mandatory_whitespace(__input); |
| string __name = chrono::__parse_string(__input); |
|
|
| __tz::__rule& __rule = __create_entry(__rules, __name); |
|
|
| chrono::__skip_mandatory_whitespace(__input); |
| __rule.__from = chrono::__parse_year(__input); |
| chrono::__skip_mandatory_whitespace(__input); |
| __rule.__to = chrono::__parse_to(__input, __rule.__from); |
| chrono::__skip_mandatory_whitespace(__input); |
| chrono::__matches(__input, '-'); |
| chrono::__skip_mandatory_whitespace(__input); |
| __rule.__in = chrono::__parse_month(__input); |
| chrono::__skip_mandatory_whitespace(__input); |
| __rule.__on = chrono::__parse_on(__input); |
| chrono::__skip_mandatory_whitespace(__input); |
| __rule.__at = __parse_at(__input); |
| chrono::__skip_mandatory_whitespace(__input); |
| __rule.__save = __parse_save(__input); |
| chrono::__skip_mandatory_whitespace(__input); |
| __rule.__letters = chrono::__parse_letters(__input); |
| chrono::__skip_line(__input); |
| } |
|
|
| static void __parse_zone(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) { |
| chrono::__skip_mandatory_whitespace(__input); |
| auto __p = std::make_unique<time_zone::__impl>(chrono::__parse_string(__input), __rules); |
| vector<__tz::__continuation>& __continuations = __p->__continuations(); |
| chrono::__skip_mandatory_whitespace(__input); |
|
|
| do { |
| |
| __continuations.emplace_back(__parse_continuation(__rules, __input)); |
| chrono::__skip_line(__input); |
| chrono::__skip_optional_whitespace(__input); |
| } while (std::isdigit(__input.peek()) || __input.peek() == '-'); |
|
|
| __tzdb.zones.emplace_back(time_zone::__create(std::move(__p))); |
| } |
|
|
| static void __parse_link(tzdb& __tzdb, istream& __input) { |
| chrono::__skip_mandatory_whitespace(__input); |
| string __target = chrono::__parse_string(__input); |
| chrono::__skip_mandatory_whitespace(__input); |
| string __name = chrono::__parse_string(__input); |
| chrono::__skip_line(__input); |
|
|
| __tzdb.links.emplace_back(std::__private_constructor_tag{}, std::move(__name), std::move(__target)); |
| } |
|
|
| static void __parse_tzdata(tzdb& __db, __tz::__rules_storage_type& __rules, istream& __input) { |
| while (true) { |
| int __c = std::tolower(__input.get()); |
|
|
| switch (__c) { |
| case istream::traits_type::eof(): |
| return; |
|
|
| case ' ': |
| case '\t': |
| case '\n': |
| break; |
|
|
| case '#': |
| chrono::__skip_line(__input); |
| break; |
|
|
| case 'r': |
| chrono::__skip(__input, "ule"); |
| chrono::__parse_rule(__db, __rules, __input); |
| break; |
|
|
| case 'z': |
| chrono::__skip(__input, "one"); |
| chrono::__parse_zone(__db, __rules, __input); |
| break; |
|
|
| case 'l': |
| chrono::__skip(__input, "ink"); |
| chrono::__parse_link(__db, __input); |
| break; |
|
|
| default: |
| std::__throw_runtime_error("corrupt tzdb: unexpected input"); |
| } |
| } |
| } |
|
|
| static void __parse_leap_seconds(vector<leap_second>& __leap_seconds, istream&& __input) { |
| |
| |
| constexpr auto __offset = sys_days{1970y / January / 1} - sys_days{1900y / January / 1}; |
|
|
| struct __entry { |
| sys_seconds __timestamp; |
| seconds __value; |
| }; |
| vector<__entry> __entries; |
| [&] { |
| while (true) { |
| switch (__input.peek()) { |
| case istream::traits_type::eof(): |
| return; |
|
|
| case ' ': |
| case '\t': |
| case '\n': |
| __input.get(); |
| continue; |
|
|
| case '#': |
| chrono::__skip_line(__input); |
| continue; |
| } |
|
|
| sys_seconds __date = sys_seconds{seconds{chrono::__parse_integral(__input, false)}} - __offset; |
| chrono::__skip_mandatory_whitespace(__input); |
| seconds __value{chrono::__parse_integral(__input, false)}; |
| chrono::__skip_line(__input); |
|
|
| __entries.emplace_back(__date, __value); |
| } |
| }(); |
| |
| |
| |
| ranges::sort(__entries, {}, &__entry::__timestamp); |
|
|
| |
| |
| |
| (void)ranges::adjacent_find(__entries, [&](const __entry& __first, const __entry& __second) { |
| __leap_seconds.emplace_back( |
| std::__private_constructor_tag{}, __second.__timestamp, __second.__value - __first.__value); |
| return false; |
| }); |
| } |
|
|
| void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) { |
| filesystem::path __root = chrono::__libcpp_tzdb_directory(); |
| ifstream __tzdata{__root / "tzdata.zi"}; |
|
|
| __tzdb.version = chrono::__parse_version(__tzdata); |
| chrono::__parse_tzdata(__tzdb, __rules, __tzdata); |
| ranges::sort(__tzdb.zones); |
| ranges::sort(__tzdb.links); |
| ranges::sort(__rules, {}, [](const auto& p) { return p.first; }); |
|
|
| |
| |
| |
| |
| |
| chrono::__parse_leap_seconds(__tzdb.leap_seconds, ifstream{__root / "leap-seconds.list"}); |
| } |
|
|
| #ifdef _WIN32 |
| [[nodiscard]] static const time_zone* __current_zone_windows(const tzdb& tzdb) { |
| |
| std::__throw_runtime_error("unknown time zone"); |
| } |
| #else |
|
|
| [[nodiscard]] static string __current_zone_environment() { |
| if (const char* __tz = std::getenv("TZ")) |
| return __tz; |
|
|
| return {}; |
| } |
|
|
| [[nodiscard]] static string __current_zone_etc_localtime() { |
| filesystem::path __path = "/etc/localtime"; |
| if (!filesystem::exists(__path) || !filesystem::is_symlink(__path)) |
| return {}; |
|
|
| filesystem::path __tz = filesystem::read_symlink(__path); |
| |
| |
| if (__tz.is_relative()) |
| __tz = filesystem::canonical("/etc" / __tz); |
|
|
| return filesystem::relative(__tz, "/usr/share/zoneinfo/"); |
| } |
|
|
| [[nodiscard]] static string __current_zone_etc_timezone() { |
| filesystem::path __path = "/etc/timezone"; |
| if (!filesystem::exists(__path)) |
| return {}; |
|
|
| ifstream __f(__path); |
| string __name; |
| std::getline(__f, __name); |
| return __name; |
| } |
|
|
| [[nodiscard]] static const time_zone* __current_zone_posix(const tzdb& tzdb) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| string __name = chrono::__current_zone_environment(); |
|
|
| |
| if (!__name.empty()) |
| if (const time_zone* __result = tzdb.__locate_zone(__name)) |
| return __result; |
|
|
| __name = chrono::__current_zone_etc_localtime(); |
| if (__name.empty()) |
| __name = chrono::__current_zone_etc_timezone(); |
|
|
| if (__name.empty()) |
| std::__throw_runtime_error("tzdb: unable to determine the name of the current time zone"); |
|
|
| if (const time_zone* __result = tzdb.__locate_zone(__name)) |
| return __result; |
|
|
| std::__throw_runtime_error(("tzdb: the time zone '" + __name + "' is not found in the database").c_str()); |
| } |
| #endif |
|
|
| |
| |
| |
|
|
| _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list() { |
| static tzdb_list __result{new tzdb_list::__impl()}; |
| return __result; |
| } |
|
|
| [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const time_zone* tzdb::__current_zone() const { |
| #ifdef _WIN32 |
| return chrono::__current_zone_windows(*this); |
| #else |
| return chrono::__current_zone_posix(*this); |
| #endif |
| } |
|
|
| _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const tzdb& reload_tzdb() { |
| if (chrono::remote_version() == chrono::get_tzdb().version) |
| return chrono::get_tzdb(); |
|
|
| return chrono::get_tzdb_list().__implementation().__load(); |
| } |
|
|
| _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version() { |
| filesystem::path __root = chrono::__libcpp_tzdb_directory(); |
| ifstream __tzdata{__root / "tzdata.zi"}; |
| return chrono::__parse_version(__tzdata); |
| } |
|
|
| } |
|
|
| _LIBCPP_END_NAMESPACE_STD |
|
|