├── .gitignore ├── ext └── fmt-7.1.3 │ ├── include │ └── fmt │ │ ├── posix.h │ │ ├── locale.h │ │ ├── ostream.h │ │ ├── ranges.h │ │ ├── os.h │ │ ├── color.h │ │ ├── printf.h │ │ ├── compile.h │ │ └── chrono.h │ └── src │ ├── format.cc │ └── os.cc ├── .gitmodules ├── powmon.hh ├── README.md ├── CMakeLists.txt ├── dsmr-sample.txt ├── minicurl.hh ├── sqlwriter.hh ├── money.cc ├── solcount.cc ├── minicurl.cc ├── sqlwriter.cc └── p1parse.cc /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.d 4 | -------------------------------------------------------------------------------- /ext/fmt-7.1.3/include/fmt/posix.h: -------------------------------------------------------------------------------- 1 | #include "os.h" 2 | #warning "fmt/posix.h is deprecated; use fmt/os.h instead" 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/powerblog"] 2 | path = ext/powerblog 3 | url = https://github.com/ahupowerdns/powerblog.git 4 | -------------------------------------------------------------------------------- /powmon.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | void pricingThread(); 6 | std::optional getPrice(time_t now); 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Two tools to measure power usage using external meters, and publish this in 2 | Prometheus format. 3 | 4 | Both tools require libh2o-evloop. Compiling requires cmake. 5 | 6 | Clone this repo like this: `git clone --recursive 7 | https://github.com/berthubert/powmon.git` 8 | 9 | If you forgot the --recursive, it won't work. 10 | 11 | To compile: 12 | 13 | ``` 14 | mkdir build 15 | cd build 16 | cmake .. 17 | make 18 | ``` 19 | 20 | powmon 21 | ------ 22 | Requires libcurl-dev 23 | 24 | Initializes /dev/ttyUSB0 to 115200 and expects to find DSMR coded data 25 | there. Published on 0.0.0.0:10000/metrics/ 26 | 27 | solcount 28 | -------- 29 | Requires libgpiod-dev 30 | 31 | This tool counts transitions on a selected GPIO chip and pin. 32 | 33 | `solcount ./gpiochip0 3` works for me. 34 | 35 | Publishes on 0.0.0.0:10000/metrics/ 36 | 37 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(powmon VERSION 1.0 4 | DESCRIPTION "Convert DSMR and GPIO pulses to Prometheus" 5 | LANGUAGES CXX) 6 | 7 | 8 | set(CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard to use") 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | set(CMAKE_CXX_EXTENSIONS ON) 11 | 12 | set(CMAKE_THREAD_PREFER_PTHREAD TRUE) 13 | set(THREADS_PREFER_PTHREAD_FLAG TRUE) 14 | find_package(Threads REQUIRED) 15 | 16 | 17 | add_library(support STATIC ext/powerblog/h2o-pp.cc 18 | ext/powerblog/ext/simplesocket/swrappers.cc 19 | ext/powerblog/ext/simplesocket/comboaddress.cc 20 | ext/powerblog/ext/simplesocket/sclasses.cc 21 | ext/fmt-7.1.3/src/format.cc) 22 | 23 | 24 | 25 | target_include_directories(support PUBLIC ext/powerblog/ext/simplesocket ext/powerblog/ext ext/fmt-7.1.3/include/) 26 | target_link_libraries(support PUBLIC -lh2o-evloop -lssl -lcrypto Threads::Threads) 27 | 28 | add_executable(powmon p1parse.cc minicurl.cc money.cc sqlwriter.cc) 29 | target_link_libraries(powmon support -lcurl -lsqlite3) 30 | 31 | add_executable(solcount solcount.cc ) 32 | target_link_libraries(solcount support -lgpiodcxx -latomic) 33 | -------------------------------------------------------------------------------- /dsmr-sample.txt: -------------------------------------------------------------------------------- 1 | /KFM5KAIFA-METER 2 | 3 | Version: 4 | 1-3:0.2.8(42) 5 | 6 | Date stamp: 7 | 0-0:1.0.0(210411201513S) 8 | 9 | Equipment ID: 10 | 0-0:96.1.1(4530303236333030303330333138313136) 11 | 12 | Tariff 1 delivered 13 | 1-0:1.8.1(011295.029*kWh) 14 | Tariff 2 delivered 15 | 1-0:1.8.2(011173.196*kWh) 16 | 17 | Tariff 1 out 18 | 1-0:2.8.1(001286.748*kWh) 19 | Tariff 2 out 20 | 1-0:2.8.2(003042.395*kWh) 21 | 22 | Tariff indicator: 23 | 0-0:96.14.0(0001) 24 | 25 | Power in: 26 | 1-0:1.7.0(02.677*kW) 27 | 28 | Power out: 29 | 1-0:2.7.0(00.000*kW) 30 | 31 | Power failures in any phase 32 | 0-0:96.7.21(00000) 33 | Long power failures in any phase: 34 | 0-0:96.7.9(00000) 35 | 36 | Power failure event log: 37 | 1-0:99.97.0(1)(0-0:96.7.19)(000101000018W)(2147483647*s) 38 | 39 | L1 sags: 40 | 1-0:32.32.0(00000) 41 | L2 sags: 42 | 1-0:52.32.0(00000) 43 | L3 sags: 44 | 1-0:72.32.0(00001) 45 | 46 | L1 swells: 47 | 1-0:32.36.0(00000) 48 | L2 swells: 49 | 1-0:52.36.0(00000) 50 | L3 swells: 51 | 1-0:72.36.0(00000) 52 | 53 | Text message: 54 | 0-0:96.13.1() 55 | Text message: 56 | 0-0:96.13.0() 57 | 58 | L1 amps: 59 | 1-0:31.7.0(002*A) 60 | L2 amps: 61 | 1-0:51.7.0(000*A) 62 | L3 amps: 63 | 1-0:71.7.0(009*A) 64 | 65 | L1 power: 66 | 1-0:21.7.0(00.469*kW) 67 | L2 power: 68 | 1-0:41.7.0(00.000*kW) 69 | L3 power: 70 | 1-0:61.7.0(02.217*kW) 71 | 72 | L1 power OUT: 73 | 1-0:22.7.0(00.000*kW) 74 | L2 power OUT: 75 | 1-0:42.7.0(00.009*kW) 76 | L3 power OUT: 77 | 1-0:62.7.0(00.000*kW) 78 | !1EE8 79 | -------------------------------------------------------------------------------- /minicurl.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018-2019 powerdns.com bv 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | #include 27 | #include 28 | #include "comboaddress.hh" 29 | #include 30 | #include 31 | #include 32 | using std::string; 33 | // turns out 'CURL' is currently typedef for void which means we can't easily forward declare it 34 | 35 | class MiniCurl 36 | { 37 | public: 38 | using MiniCurlHeaders = std::map; 39 | 40 | static void init(); 41 | 42 | MiniCurl(const string& useragent="MiniCurl/0.0"); 43 | ~MiniCurl(); 44 | MiniCurl& operator=(const MiniCurl&) = delete; 45 | std::string getURL(const std::string& str, const ComboAddress* rem=0, const ComboAddress* src=0); 46 | std::string postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers); 47 | 48 | std::string urlEncode(std::string_view str); 49 | private: 50 | CURL *d_curl; 51 | static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata); 52 | std::string d_data; 53 | struct curl_slist* d_header_list = nullptr; 54 | void setupURL(const std::string& str, const ComboAddress* rem=0, const ComboAddress* src=0); 55 | void setHeaders(const MiniCurlHeaders& headers); 56 | void clearHeaders(); 57 | }; 58 | -------------------------------------------------------------------------------- /ext/fmt-7.1.3/include/fmt/locale.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::locale support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_LOCALE_H_ 9 | #define FMT_LOCALE_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | namespace detail { 18 | template 19 | std::basic_string vformat( 20 | const std::locale& loc, basic_string_view format_str, 21 | basic_format_args>> args) { 22 | basic_memory_buffer buffer; 23 | detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc)); 24 | return fmt::to_string(buffer); 25 | } 26 | } // namespace detail 27 | 28 | template > 29 | inline std::basic_string vformat( 30 | const std::locale& loc, const S& format_str, 31 | basic_format_args>> args) { 32 | return detail::vformat(loc, to_string_view(format_str), args); 33 | } 34 | 35 | template > 36 | inline std::basic_string format(const std::locale& loc, 37 | const S& format_str, Args&&... args) { 38 | return detail::vformat(loc, to_string_view(format_str), 39 | fmt::make_args_checked(format_str, args...)); 40 | } 41 | 42 | template , 44 | FMT_ENABLE_IF(detail::is_output_iterator::value)> 45 | inline OutputIt vformat_to( 46 | OutputIt out, const std::locale& loc, const S& format_str, 47 | basic_format_args>> args) { 48 | decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); 49 | vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); 50 | return detail::get_iterator(buf); 51 | } 52 | 53 | template >::value> 55 | inline auto format_to(OutputIt out, const std::locale& loc, 56 | const S& format_str, Args&&... args) -> 57 | typename std::enable_if::type { 58 | const auto& vargs = fmt::make_args_checked(format_str, args...); 59 | return vformat_to(out, loc, to_string_view(format_str), vargs); 60 | } 61 | 62 | FMT_END_NAMESPACE 63 | 64 | #endif // FMT_LOCALE_H_ 65 | -------------------------------------------------------------------------------- /sqlwriter.hh: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct sqlite3; 10 | struct sqlite3_stmt; 11 | 12 | class MiniSQLite 13 | { 14 | public: 15 | MiniSQLite(std::string_view fname); 16 | ~MiniSQLite(); 17 | std::vector> getSchema(); 18 | void addColumn(std::string_view name, std::string_view type); 19 | std::vector> exec(std::string_view query); 20 | void prepare(std::string_view str); 21 | void bindPrep(int idx, bool value); 22 | void bindPrep(int idx, int value); 23 | void bindPrep(int idx, uint32_t value); 24 | void bindPrep(int idx, long value); 25 | void bindPrep(int idx, unsigned long value); 26 | void bindPrep(int idx, long long value); 27 | void bindPrep(int idx, unsigned long long value); 28 | void bindPrep(int idx, double value); 29 | void bindPrep(int idx, const std::string& value); 30 | void execPrep(); 31 | void begin(); 32 | void commit(); 33 | void cycle(); 34 | bool isPrepared() const 35 | { 36 | return d_stmt != nullptr; 37 | } 38 | private: 39 | sqlite3* d_sqlite; 40 | sqlite3_stmt* d_stmt{nullptr}; 41 | 42 | std::vector> d_rows; // for exec() 43 | static int helperFunc(void* ptr, int cols, char** colvals, char** colnames); 44 | bool d_notable; 45 | bool d_intransaction{false}; 46 | }; 47 | 48 | class SQLiteWriter 49 | { 50 | 51 | public: 52 | explicit SQLiteWriter(std::string_view fname) : d_db(fname) 53 | { 54 | d_columns = d_db.getSchema(); 55 | // for(const auto& c : d_columns) 56 | // cout < var_t; 63 | void addValue(const std::initializer_list>& values); 64 | void addValue(const std::vector>& values); 65 | template 66 | void addValueGeneric(const T& values); 67 | ~SQLiteWriter() 68 | { 69 | // std::cerr<<"Destructor called"<> d_columns; 81 | std::vector d_lastsig; 82 | bool haveColumn(std::string_view name); 83 | 84 | }; 85 | -------------------------------------------------------------------------------- /money.cc: -------------------------------------------------------------------------------- 1 | #include "minicurl.hh" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "nlohmann/json.hpp" 8 | #include "fmt/format.h" 9 | #include "fmt/printf.h" 10 | #include "powmon.hh" 11 | using namespace std; 12 | 13 | std::mutex g_prices_mutex; 14 | std::map g_prices; 15 | 16 | void pricingThread() 17 | { 18 | for(;;) { 19 | try { 20 | time_t now = time(0); 21 | time_t from = now - 86400; 22 | time_t till = now + 86400; 23 | struct tm fromtm={}, tilltm={}; 24 | gmtime_r(&from, &fromtm); 25 | gmtime_r(&till, &tilltm); 26 | 27 | std::string url=fmt::sprintf("https://api.energyzero.nl/v1/energyprices?fromDate=%04d-%02d-%02dT22%%3A00%%3A00.000Z&tillDate=%04d-%02d-%02dT21%%3A59%%3A59.999Z&interval=4&usageType=1&inclBtw=false", 28 | fromtm.tm_year+1900, 1+fromtm.tm_mon, fromtm.tm_mday, 29 | tilltm.tm_year+1900, 1+tilltm.tm_mon, tilltm.tm_mday); 30 | cout< pricemap; 39 | for(const auto& p : prices) { 40 | struct tm tm; 41 | // 2022-10-10T22:00:00Z 42 | char* ptr=strptime(p["readingDate"].get().c_str(), "%Y-%m-%dT%H:%M:%S%z", & tm); 43 | if(!ptr || *ptr) { 44 | throw std::runtime_error("Could not parse the whole date: "+p["readingDate"].get()); 45 | } 46 | time_t t = timegm(&tm); 47 | struct tm localtm={}; 48 | localtime_r(&t, &localtm); 49 | pricemap[t] = p["price"].get(); 50 | cout<()<<", "< lock(g_prices_mutex); 54 | g_prices = pricemap; 55 | } 56 | 57 | sleep(3600); 58 | } 59 | catch(std::exception& e) { 60 | cerr<<"Error: "< getPrice(time_t now) 67 | { 68 | decltype(g_prices) prices; 69 | { 70 | std::lock_guard lock(g_prices_mutex); 71 | prices = g_prices; 72 | } 73 | std::optional ret; 74 | if(prices.empty()) // no pricing, sorry 75 | return ret; 76 | // if now is at the beginning of our map, we also have no applicable pricing 77 | if(auto iter = prices.lower_bound(now); iter != prices.begin()) { 78 | --iter; 79 | if(now - iter->first > 7200) // too old 80 | return ret; 81 | return iter->second; 82 | } 83 | return ret; 84 | } 85 | -------------------------------------------------------------------------------- /solcount.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ext/powerblog/h2o-pp.hh" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using Clock = std::chrono::steady_clock; 10 | 11 | static double passedMsec(const Clock::time_point& then, const Clock::time_point& now) 12 | { 13 | return std::chrono::duration_cast(now - then).count()/1000.0; 14 | } 15 | 16 | 17 | static double passedMsec(const Clock::time_point& then) 18 | { 19 | return passedMsec(then, Clock::now()); 20 | } 21 | 22 | 23 | 24 | using namespace std; // No need to keep using 25 | 26 | std::atomic g_pulses; 27 | 28 | void print_event(const ::gpiod::line_event& event) 29 | { 30 | 31 | if (event.event_type == ::gpiod::line_event::RISING_EDGE) 32 | ::std::cout << " RISING EDGE"; 33 | else if (event.event_type == ::gpiod::line_event::FALLING_EDGE) 34 | ::std::cout << "FALLING EDGE"; 35 | else 36 | throw ::std::logic_error("invalid event type"); 37 | 38 | ::std::cout << " "; 39 | 40 | ::std::cout << ::std::chrono::duration_cast<::std::chrono::seconds>(event.timestamp).count(); 41 | ::std::cout << "."; 42 | ::std::cout << event.timestamp.count() % 1000000000; 43 | 44 | ::std::cout << " line: " << event.source.offset(); 45 | 46 | ::std::cout << ::std::endl; 47 | } 48 | 49 | 50 | int main(int argc, char **argv) 51 | { 52 | signal(SIGPIPE, SIG_IGN); // every TCP application needs this 53 | 54 | if (argc < 3) { 55 | cout << "usage: " << argv[0] << " ..." << endl; 56 | return EXIT_FAILURE; 57 | } 58 | 59 | vector offsets; 60 | offsets.reserve(argc); 61 | for (int i = 2; i < argc; i++) 62 | offsets.push_back(stoul(argv[i])); 63 | 64 | ::gpiod::chip chip(argv[1]); 65 | auto lines = chip.get_lines(offsets); 66 | 67 | lines.request({ 68 | argv[0], 69 | ::gpiod::line_request::EVENT_FALLING_EDGE, 70 | 0, 71 | }); 72 | 73 | H2OWebserver h2s("solcount"); 74 | 75 | /* 76 | # HELP http_requests_total The total number of HTTP requests. 77 | # TYPE http_requests_total counter 78 | http_requests_total{method="post",code="200"} 1027 1395066363000 79 | http_requests_total{method="post",code="400"} 3 1395066363000 80 | */ 81 | 82 | h2s.addHandler("/metrics",[](auto handler, auto req) 83 | { 84 | ostringstream ret; 85 | ret << "# HELP power_pulses The total number of power pulses"<("text/plain", ret.str()); 89 | }); 90 | 91 | 92 | std::thread ws([&h2s]() { 93 | auto actx = h2s.addContext(); 94 | ComboAddress listenOn("0.0.0.0:10000"); 95 | h2s.addListener(listenOn, actx); 96 | cout<<"Listening on "<< listenOn.toStringWithPort() < 14 | int format_float(char* buf, std::size_t size, const char* format, int precision, 15 | T value) { 16 | #ifdef FMT_FUZZ 17 | if (precision > 100000) 18 | throw std::runtime_error( 19 | "fuzz mode - avoid large allocation inside snprintf"); 20 | #endif 21 | // Suppress the warning about nonliteral format string. 22 | int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; 23 | return precision < 0 ? snprintf_ptr(buf, size, format, value) 24 | : snprintf_ptr(buf, size, format, precision, value); 25 | } 26 | 27 | template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(float x) 28 | FMT_NOEXCEPT; 29 | template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(double x) 30 | FMT_NOEXCEPT; 31 | 32 | // DEPRECATED! This function exists for ABI compatibility. 33 | template 34 | typename basic_format_context>, 35 | Char>::iterator 36 | vformat_to(buffer& buf, basic_string_view format_str, 37 | basic_format_args>>, 39 | type_identity_t>> 40 | args) { 41 | using iterator = std::back_insert_iterator>; 42 | using context = basic_format_context< 43 | std::back_insert_iterator>>, 44 | type_identity_t>; 45 | auto out = iterator(buf); 46 | format_handler h(out, format_str, args, {}); 47 | parse_format_string(format_str, h); 48 | return out; 49 | } 50 | template basic_format_context>, 51 | char>::iterator 52 | vformat_to(buffer&, string_view, 53 | basic_format_args>>, 55 | type_identity_t>>); 56 | } // namespace detail 57 | 58 | template struct FMT_INSTANTIATION_DEF_API detail::basic_data; 59 | 60 | // Workaround a bug in MSVC2013 that prevents instantiation of format_float. 61 | int (*instantiate_format_float)(double, int, detail::float_specs, 62 | detail::buffer&) = detail::format_float; 63 | 64 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 65 | template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); 66 | template FMT_API std::locale detail::locale_ref::get() const; 67 | #endif 68 | 69 | // Explicit instantiations for char. 70 | 71 | template FMT_API std::string detail::grouping_impl(locale_ref); 72 | template FMT_API char detail::thousands_sep_impl(locale_ref); 73 | template FMT_API char detail::decimal_point_impl(locale_ref); 74 | 75 | template FMT_API void detail::buffer::append(const char*, const char*); 76 | 77 | template FMT_API void detail::vformat_to( 78 | detail::buffer&, string_view, 79 | basic_format_args, detail::locale_ref); 80 | 81 | template FMT_API int detail::snprintf_float(double, int, detail::float_specs, 82 | detail::buffer&); 83 | template FMT_API int detail::snprintf_float(long double, int, 84 | detail::float_specs, 85 | detail::buffer&); 86 | template FMT_API int detail::format_float(double, int, detail::float_specs, 87 | detail::buffer&); 88 | template FMT_API int detail::format_float(long double, int, detail::float_specs, 89 | detail::buffer&); 90 | 91 | // Explicit instantiations for wchar_t. 92 | 93 | template FMT_API std::string detail::grouping_impl(locale_ref); 94 | template FMT_API wchar_t detail::thousands_sep_impl(locale_ref); 95 | template FMT_API wchar_t detail::decimal_point_impl(locale_ref); 96 | 97 | template FMT_API void detail::buffer::append(const wchar_t*, 98 | const wchar_t*); 99 | FMT_END_NAMESPACE 100 | -------------------------------------------------------------------------------- /ext/fmt-7.1.3/include/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | template class basic_printf_parse_context; 18 | template class basic_printf_context; 19 | 20 | namespace detail { 21 | 22 | template class formatbuf : public std::basic_streambuf { 23 | private: 24 | using int_type = typename std::basic_streambuf::int_type; 25 | using traits_type = typename std::basic_streambuf::traits_type; 26 | 27 | buffer& buffer_; 28 | 29 | public: 30 | formatbuf(buffer& buf) : buffer_(buf) {} 31 | 32 | protected: 33 | // The put-area is actually always empty. This makes the implementation 34 | // simpler and has the advantage that the streambuf and the buffer are always 35 | // in sync and sputc never writes into uninitialized memory. The obvious 36 | // disadvantage is that each call to sputc always results in a (virtual) call 37 | // to overflow. There is no disadvantage here for sputn since this always 38 | // results in a call to xsputn. 39 | 40 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 41 | if (!traits_type::eq_int_type(ch, traits_type::eof())) 42 | buffer_.push_back(static_cast(ch)); 43 | return ch; 44 | } 45 | 46 | std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { 47 | buffer_.append(s, s + count); 48 | return count; 49 | } 50 | }; 51 | 52 | struct converter { 53 | template ::value)> converter(T); 54 | }; 55 | 56 | template struct test_stream : std::basic_ostream { 57 | private: 58 | void_t<> operator<<(converter); 59 | }; 60 | 61 | // Hide insertion operators for built-in types. 62 | template 63 | void_t<> operator<<(std::basic_ostream&, Char); 64 | template 65 | void_t<> operator<<(std::basic_ostream&, char); 66 | template 67 | void_t<> operator<<(std::basic_ostream&, char); 68 | template 69 | void_t<> operator<<(std::basic_ostream&, signed char); 70 | template 71 | void_t<> operator<<(std::basic_ostream&, unsigned char); 72 | 73 | // Checks if T has a user-defined operator<< (e.g. not a member of 74 | // std::ostream). 75 | template class is_streamable { 76 | private: 77 | template 78 | static bool_constant&>() 79 | << std::declval()), 80 | void_t<>>::value> 81 | test(int); 82 | 83 | template static std::false_type test(...); 84 | 85 | using result = decltype(test(0)); 86 | 87 | public: 88 | static const bool value = result::value; 89 | }; 90 | 91 | // Write the content of buf to os. 92 | template 93 | void write_buffer(std::basic_ostream& os, buffer& buf) { 94 | const Char* buf_data = buf.data(); 95 | using unsigned_streamsize = std::make_unsigned::type; 96 | unsigned_streamsize size = buf.size(); 97 | unsigned_streamsize max_size = to_unsigned(max_value()); 98 | do { 99 | unsigned_streamsize n = size <= max_size ? size : max_size; 100 | os.write(buf_data, static_cast(n)); 101 | buf_data += n; 102 | size -= n; 103 | } while (size != 0); 104 | } 105 | 106 | template 107 | void format_value(buffer& buf, const T& value, 108 | locale_ref loc = locale_ref()) { 109 | formatbuf format_buf(buf); 110 | std::basic_ostream output(&format_buf); 111 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) 112 | if (loc) output.imbue(loc.get()); 113 | #endif 114 | output << value; 115 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 116 | buf.try_resize(buf.size()); 117 | } 118 | 119 | // Formats an object of type T that has an overloaded ostream operator<<. 120 | template 121 | struct fallback_formatter::value>> 122 | : private formatter, Char> { 123 | FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) 124 | -> decltype(ctx.begin()) { 125 | return formatter, Char>::parse(ctx); 126 | } 127 | template >::value)> 130 | auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) { 131 | return ctx.begin(); 132 | } 133 | 134 | template 135 | auto format(const T& value, basic_format_context& ctx) 136 | -> OutputIt { 137 | basic_memory_buffer buffer; 138 | format_value(buffer, value, ctx.locale()); 139 | basic_string_view str(buffer.data(), buffer.size()); 140 | return formatter, Char>::format(str, ctx); 141 | } 142 | template 143 | auto format(const T& value, basic_printf_context& ctx) 144 | -> OutputIt { 145 | basic_memory_buffer buffer; 146 | format_value(buffer, value, ctx.locale()); 147 | return std::copy(buffer.begin(), buffer.end(), ctx.out()); 148 | } 149 | }; 150 | } // namespace detail 151 | 152 | template 153 | void vprint(std::basic_ostream& os, basic_string_view format_str, 154 | basic_format_args>> args) { 155 | basic_memory_buffer buffer; 156 | detail::vformat_to(buffer, format_str, args); 157 | detail::write_buffer(os, buffer); 158 | } 159 | 160 | /** 161 | \rst 162 | Prints formatted data to the stream *os*. 163 | 164 | **Example**:: 165 | 166 | fmt::print(cerr, "Don't {}!", "panic"); 167 | \endrst 168 | */ 169 | template ::value, char_t>> 171 | void print(std::basic_ostream& os, const S& format_str, Args&&... args) { 172 | vprint(os, to_string_view(format_str), 173 | fmt::make_args_checked(format_str, args...)); 174 | } 175 | FMT_END_NAMESPACE 176 | 177 | #endif // FMT_OSTREAM_H_ 178 | -------------------------------------------------------------------------------- /minicurl.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018-2019 powerdns.com bv 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "minicurl.hh" 26 | #include 27 | #include 28 | #include 29 | #include "fmt/format.h" 30 | #include "fmt/printf.h" 31 | void MiniCurl::init() 32 | { 33 | static std::atomic_flag s_init = ATOMIC_FLAG_INIT; 34 | 35 | if (s_init.test_and_set()) 36 | return; 37 | 38 | CURLcode code = curl_global_init(CURL_GLOBAL_ALL); 39 | if (code != 0) { 40 | throw std::runtime_error("Error initializing libcurl"); 41 | } 42 | } 43 | 44 | MiniCurl::MiniCurl(const string& useragent) 45 | { 46 | d_curl = curl_easy_init(); 47 | if (d_curl == nullptr) { 48 | throw std::runtime_error("Error creating a MiniCurl session"); 49 | } 50 | curl_easy_setopt(d_curl, CURLOPT_USERAGENT, useragent.c_str()); 51 | } 52 | 53 | MiniCurl::~MiniCurl() 54 | { 55 | // NEEDS TO CLEAN HOSTLIST 56 | curl_easy_cleanup(d_curl); 57 | } 58 | 59 | size_t MiniCurl::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) 60 | { 61 | MiniCurl* us = (MiniCurl*)userdata; 62 | us->d_data.append(ptr, size*nmemb); 63 | return size*nmemb; 64 | } 65 | 66 | using namespace std; 67 | 68 | static string extractHostFromURL(const std::string& url) 69 | { 70 | auto pos = url.find("://"); 71 | if(pos == string::npos) 72 | throw std::runtime_error("Can't find host part of '"+url+"'"); 73 | pos += 3; 74 | auto endpos = url.find('/', pos); 75 | if(endpos == string::npos) 76 | return url.substr(pos); 77 | 78 | return url.substr(pos, endpos-pos); 79 | } 80 | 81 | void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src) 82 | { 83 | if(rem) { 84 | struct curl_slist *hostlist = nullptr; // THIS SHOULD BE FREED 85 | 86 | // url = http://hostname.enzo/url 87 | string host4=extractHostFromURL(str); 88 | // doest the host contain port indication 89 | std::size_t found = host4.find(':'); 90 | vector ports{80, 443}; 91 | if (found != std::string::npos) { 92 | int port = std::stoi(host4.substr(found + 1)); 93 | if (port <= 0 || port > 65535) 94 | throw std::overflow_error("Invalid port number"); 95 | ports = {(uint16_t)port}; 96 | host4 = host4.substr(0, found); 97 | } 98 | 99 | for (const auto& port : ports) { 100 | string hcode = fmt::format("%s:%u:%s", host4 , port , rem->toString()); 101 | hostlist = curl_slist_append(hostlist, hcode.c_str()); 102 | } 103 | 104 | curl_easy_setopt(d_curl, CURLOPT_RESOLVE, hostlist); 105 | } 106 | if(src) { 107 | curl_easy_setopt(d_curl, CURLOPT_INTERFACE, src->toString().c_str()); 108 | } 109 | curl_easy_setopt(d_curl, CURLOPT_FOLLOWLOCATION, true); 110 | /* only allow HTTP and HTTPS */ 111 | curl_easy_setopt(d_curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); 112 | curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYPEER, false); 113 | curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYHOST, false); 114 | // curl_easy_setopt(d_curl, CURLOPT_FAILONERROR, true); 115 | curl_easy_setopt(d_curl, CURLOPT_URL, str.c_str()); 116 | curl_easy_setopt(d_curl, CURLOPT_WRITEFUNCTION, write_callback); 117 | curl_easy_setopt(d_curl, CURLOPT_WRITEDATA, this); 118 | curl_easy_setopt(d_curl, CURLOPT_TIMEOUT, 10L); 119 | 120 | clearHeaders(); 121 | d_data.clear(); 122 | } 123 | 124 | std::string MiniCurl::getURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src) 125 | { 126 | setupURL(str, rem, src); 127 | auto res = curl_easy_perform(d_curl); 128 | long http_code = 0; 129 | curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &http_code); 130 | 131 | if(res != CURLE_OK || http_code != 200) { 132 | throw std::runtime_error("Unable to retrieve URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res))); 133 | } 134 | std::string ret=d_data; 135 | d_data.clear(); 136 | return ret; 137 | } 138 | 139 | std::string MiniCurl::postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers) 140 | { 141 | setupURL(str); 142 | setHeaders(headers); 143 | curl_easy_setopt(d_curl, CURLOPT_POSTFIELDSIZE, postdata.size()); 144 | curl_easy_setopt(d_curl, CURLOPT_POSTFIELDS, postdata.c_str()); 145 | 146 | auto res = curl_easy_perform(d_curl); 147 | 148 | long http_code = 0; 149 | curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &http_code); 150 | 151 | if(res != CURLE_OK || http_code >= 300 ) { 152 | cerr<<"Detailed error: "< 3 | #include 4 | #include "sqlite3.h" 5 | using namespace std; 6 | 7 | MiniSQLite::MiniSQLite(std::string_view fname) 8 | { 9 | if ( sqlite3_open(&fname[0], &d_sqlite)!=SQLITE_OK ) { 10 | throw runtime_error("Unable to open "+(string)fname+" for sqlite"); 11 | } 12 | exec("PRAGMA journal_mode='wal'"); 13 | d_notable=getSchema().empty(); 14 | } 15 | 16 | 17 | 18 | //! Get field names and types from a table 19 | vector > MiniSQLite::getSchema() 20 | { 21 | vector> ret; 22 | 23 | auto rows = exec("SELECT cid,name,type FROM pragma_table_xinfo('data')"); 24 | 25 | 26 | for(const auto& r : rows) { 27 | ret.push_back({r[1], r[2]}); 28 | } 29 | sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { 30 | return a.first < b.first; 31 | }); 32 | 33 | return ret; 34 | } 35 | 36 | int MiniSQLite::helperFunc(void* ptr, int cols, char** colvals, char** colnames) 37 | { 38 | vector row; 39 | row.reserve(cols); 40 | for(int n=0; n < cols ; ++n) 41 | row.push_back(colvals[n]); 42 | ((MiniSQLite*)ptr)->d_rows.push_back(row); 43 | return 0; 44 | } 45 | 46 | vector> MiniSQLite::exec(std::string_view str) 47 | { 48 | char *errmsg; 49 | std::string errstr; 50 | // int (*callback)(void*,int,char**,char**) 51 | d_rows.clear(); 52 | int rc = sqlite3_exec(d_sqlite, &str[0], helperFunc, this, &errmsg); 53 | if (rc != SQLITE_OK) { 54 | errstr = errmsg; 55 | sqlite3_free(errmsg); 56 | throw std::runtime_error("Error executing sqlite3 query '"+(string)str+"': "+errstr); 57 | } 58 | return d_rows; 59 | } 60 | 61 | void MiniSQLite::bindPrep(int idx, bool value) { sqlite3_bind_int(d_stmt, idx, value ? 1 : 0); } 62 | void MiniSQLite::bindPrep(int idx, int value) { sqlite3_bind_int(d_stmt, idx, value); } 63 | void MiniSQLite::bindPrep(int idx, uint32_t value) { sqlite3_bind_int64(d_stmt, idx, value); } 64 | void MiniSQLite::bindPrep(int idx, long value) { sqlite3_bind_int64(d_stmt, idx, value); } 65 | void MiniSQLite::bindPrep(int idx, unsigned long value) { sqlite3_bind_int64(d_stmt, idx, value); } 66 | void MiniSQLite::bindPrep(int idx, long long value) { sqlite3_bind_int64(d_stmt, idx, value); } 67 | void MiniSQLite::bindPrep(int idx, unsigned long long value) { sqlite3_bind_int64(d_stmt, idx, value); } 68 | void MiniSQLite::bindPrep(int idx, double value) { sqlite3_bind_double(d_stmt, idx, value); } 69 | void MiniSQLite::bindPrep(int idx, const std::string& value) { sqlite3_bind_text(d_stmt, idx, value.c_str(), value.size(), SQLITE_TRANSIENT); } 70 | 71 | 72 | void MiniSQLite::prepare(string_view str) 73 | { 74 | if(d_stmt) { 75 | sqlite3_finalize(d_stmt); 76 | d_stmt = 0; 77 | } 78 | const char* pTail; 79 | 80 | if (sqlite3_prepare_v2(d_sqlite, &str[0], -1, &d_stmt, &pTail ) != SQLITE_OK) { 81 | throw runtime_error("Unable to prepare query "+(string)str); 82 | } 83 | } 84 | 85 | void MiniSQLite::execPrep() 86 | { 87 | int rc; 88 | for(;;) { 89 | rc = sqlite3_step(d_stmt); // XXX this needs to be an error checking loop 90 | if(rc == SQLITE_DONE) 91 | break; 92 | else 93 | throw runtime_error("Sqlite error: "+std::to_string(rc)); 94 | } 95 | rc= sqlite3_reset(d_stmt); 96 | if(rc != SQLITE_OK) 97 | throw runtime_error("Sqlite error: "+std::to_string(rc)); 98 | sqlite3_clear_bindings(d_stmt); 99 | } 100 | 101 | void MiniSQLite::begin() 102 | { 103 | d_intransaction=true; 104 | exec("begin"); 105 | } 106 | void MiniSQLite::commit() 107 | { 108 | d_intransaction=false; 109 | exec("commit"); 110 | } 111 | 112 | void MiniSQLite::cycle() 113 | { 114 | exec("commit;begin"); 115 | } 116 | 117 | //! Add a column to a atable with a certain type 118 | void MiniSQLite::addColumn(string_view name, string_view type) 119 | { 120 | // SECURITY PROBLEM - somehow we can't do prepared statements here 121 | 122 | if(d_notable) { 123 | #if SQLITE_VERSION_NUMBER >= 3037001 124 | exec("create table if not exists data ( '"+(string)name+"' "+(string)type+" ) STRICT"); 125 | #else 126 | exec("create table if not exists data ( '"+(string)name+"' "+(string)type+" )"); 127 | #endif 128 | d_notable=false; 129 | } else { 130 | exec("ALTER table data add column \""+string(name)+ "\" "+string(type)); 131 | } 132 | } 133 | 134 | 135 | 136 | void SQLiteWriter::commitThread() 137 | { 138 | int n=0; 139 | while(!d_pleasequit) { 140 | usleep(50000); 141 | if(!(n%20)) { 142 | std::lock_guard lock(d_mutex); 143 | d_db.cycle(); 144 | } 145 | n++; 146 | } 147 | // cerr<<"Thread exiting"< cmp{name, std::string()}; 154 | return binary_search(d_columns.begin(), d_columns.end(), cmp, 155 | [](const auto& a, const auto& b) 156 | { 157 | return a.first < b.first; 158 | }); 159 | 160 | } 161 | 162 | 163 | void SQLiteWriter::addValue(const initializer_list>& values) 164 | { 165 | addValueGeneric(values); 166 | } 167 | 168 | void SQLiteWriter::addValue(const std::vector>& values) 169 | { 170 | addValueGeneric(values); 171 | } 172 | 173 | 174 | template 175 | void SQLiteWriter::addValueGeneric(const T& values) 176 | { 177 | std::lock_guard lock(d_mutex); 178 | if(!d_db.isPrepared() || !equal(values.begin(), values.end(), 179 | d_lastsig.cbegin(), d_lastsig.cend(), 180 | [](const auto& a, const auto& b) 181 | { 182 | return a.first == b; 183 | })) { 184 | // cout<<"Starting a new prepared statement"<(&p.second)) 191 | d_db.addColumn(p.first, "REAL"); 192 | else if(std::get_if(&p.second)) 193 | d_db.addColumn(p.first, "TEXT"); 194 | else 195 | d_db.addColumn(p.first, "INT"); 196 | } 197 | if(!first) { 198 | q+=", "; 199 | qmarks += ", "; 200 | } 201 | first=false; 202 | q+="'"+string(p.first)+"'"; 203 | qmarks +="?"; 204 | } 205 | q+= ") values ("+qmarks+")"; 206 | 207 | d_db.prepare(q); 208 | 209 | d_lastsig.clear(); 210 | for(const auto& p : values) 211 | d_lastsig.push_back(p.first); 212 | } 213 | else 214 | ;//cout<<"We can reuse old statement"< 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "ext/powerblog/h2o-pp.hh" 18 | #include 19 | #include "powmon.hh" 20 | #include "sqlwriter.hh" 21 | 22 | int g_baudval{B115200}; 23 | 24 | // basic search and replace 25 | std::string string_replace(const std::string& str, const std::string& match, 26 | const std::string& replacement) 27 | { 28 | size_t pos = 0; 29 | std::string newstr = str; 30 | 31 | while ((pos = newstr.find(match, pos)) != std::string::npos 32 | ) 33 | { 34 | newstr.replace(pos, match.length(), replacement); 35 | pos += replacement.length(); 36 | } 37 | return newstr; 38 | } 39 | 40 | 41 | 42 | // set the termios correctly 43 | static void doTermios(int fd) 44 | { 45 | struct termios newtio; 46 | 47 | bzero(&newtio, sizeof(newtio)); 48 | newtio.c_cflag = CS8 | CLOCAL | CREAD; 49 | newtio.c_iflag = IGNPAR; 50 | newtio.c_oflag = 0; 51 | 52 | /* set input mode (non-canonical, no echo,...) */ 53 | newtio.c_lflag = 0; 54 | 55 | newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ 56 | newtio.c_cc[VMIN] = 4; /* blocking read until 5 chars received */ 57 | 58 | cfsetspeed(&newtio, g_baudval); 59 | if(tcsetattr(fd, TCSANOW, &newtio)) { 60 | perror("tcsetattr"); 61 | exit(-1); 62 | } 63 | } 64 | 65 | using namespace std; 66 | 67 | // parse a DSMR message (badly) 68 | map parseDSMR(const std::string& in) 69 | { 70 | istringstream inp(in); 71 | string line; 72 | map ret; 73 | 74 | while(getline(inp, line)) { 75 | // if(line.find("kW")== string::npos) 76 | // continue; 77 | 78 | /* 79 | 1-0:1.8.1(011295.029*kWh) 80 | 1-0:1.7.0(02.677*kW) 81 | 1-0:21.7.0(00.469*kW) 82 | 1-0:32.32.0(00000) 83 | */ 84 | 85 | if(auto pos1 = line.find(':'); pos1 != string::npos) 86 | if(auto pos2 = line.find('(', pos1); pos2 != string::npos) 87 | if(auto pos3 = line.find(')', pos2); pos3 != string::npos) { 88 | ret[line.substr(pos1+1, pos2-pos1-1)]=atof(&line[pos2+1]); 89 | } 90 | } 91 | return ret; 92 | } 93 | 94 | std::mutex g_metrix_mutex; 95 | std::shared_ptr> g_metrics; 96 | 97 | static void addMetric(map& metrics, ostringstream& ret, std::string_view key, std::string_view desc, std::string_view kind, double factor=1.0) 98 | { 99 | if(!metrics.count((string)key)) 100 | return; 101 | string rkey = string_replace((string)key, ".", "_"); 102 | ret << "# HELP dsmr_" << rkey << " " < metrics; 123 | { 124 | std::lock_guard lock(g_metrix_mutex); 125 | metrics = *g_metrics; 126 | } 127 | 128 | addMetric(metrics, ret, "1.8.1", "Total energy IN low tariff (J)", "counter", 3600*1000); 129 | addMetric(metrics, ret, "1.8.2", "Total energy IN high tariff (J)", "counter", 3600*1000); 130 | addMetric(metrics, ret, "2.8.1", "Total energy OUT low tariff (J)", "counter", 3600*1000); 131 | addMetric(metrics, ret, "2.8.2", "Total energy OUT high tariff (J)", "counter", 3600*1000); 132 | 133 | addMetric(metrics, ret, "1.7.0", "Total power IN (kW)", "gauge"); 134 | addMetric(metrics, ret, "2.7.0", "Total power OUT (kW)", "gauge"); 135 | 136 | addMetric(metrics, ret, "21.7.0", "Total power IN (kW) phase 1", "gauge"); 137 | addMetric(metrics, ret, "41.7.0", "Total power IN (kW) phase 2", "gauge"); 138 | addMetric(metrics, ret, "61.7.0", "Total power IN (kW) phase 3", "gauge"); 139 | 140 | addMetric(metrics, ret, "22.7.0", "Total power OUT (kW) phase 1", "gauge"); 141 | addMetric(metrics, ret, "42.7.0", "Total power OUT (kW) phase 2", "gauge"); 142 | addMetric(metrics, ret, "62.7.0", "Total power OUT (kW) phase 3", "gauge"); 143 | 144 | addMetric(metrics, ret, "96.14.0", "Tariff indicator", "gauge"); 145 | addMetric(metrics, ret, "96.7.21", "Power failires in any phase", "counter"); 146 | addMetric(metrics, ret, "96.7.9", "Long power failures in any phase", "counter"); 147 | 148 | addMetric(metrics, ret, "32.32.0", "L1 sags", "counter"); 149 | addMetric(metrics, ret, "52.32.0", "L2 sags", "counter"); 150 | addMetric(metrics, ret, "72.32.0", "L3 sags", "counter"); 151 | 152 | addMetric(metrics, ret, "32.36.0", "L1 swells", "counter"); 153 | addMetric(metrics, ret, "52.36.0", "L2 swells", "counter"); 154 | addMetric(metrics, ret, "72.36.0", "L3 swells", "counter"); 155 | addMetric(metrics, ret, "euros", "euros all-in", "counter", 1000); 156 | addMetric(metrics, ret, "price", "all-in price", "gauge", 100); 157 | 158 | return pair("text/plain", ret.str()); 159 | }); 160 | 161 | bool first = true; 162 | 163 | SQLiteWriter sqw("electricity.sqlite3"); 164 | 165 | // we passively read DSMR messages 166 | for(;;) { 167 | string msg; 168 | char c; 169 | for(;;) { 170 | int res = read(fd, &c, 1); 171 | if(res != 1) 172 | throw runtime_error("Serial error"); 173 | if(c=='/') 174 | break; 175 | } 176 | for(;;) { 177 | int res = read(fd, &c, 1); 178 | if(res != 1) 179 | throw runtime_error("Serial error"); 180 | if(c=='!') 181 | break; 182 | msg.append(1, c); 183 | } 184 | auto res=parseDSMR(msg); 185 | auto metrics = make_shared>(); 186 | for(const auto& r: res) { 187 | //cerr<< r.first <<" = " < lock(g_metrix_mutex); 193 | static double prevKwh; 194 | static double euros; 195 | static std::optional rprice; 196 | static time_t lastEurosTime; 197 | time_t now = time(0); 198 | double kwh = ((*metrics)["1.8.1"] + (*metrics)["1.8.2"] - (*metrics)["2.8.1"] - (*metrics)["2.8.2"]); 199 | cout<> values; 234 | for(const auto& v : *metrics) 235 | values.emplace_back(v.first.c_str(), v.second); 236 | values.emplace_back("timestamp", time(0)); 237 | sqw.addValue(values); 238 | 239 | g_metrics = metrics; 240 | } 241 | 242 | if(first) { 243 | std::thread ws([&h2s]() { 244 | auto actx = h2s.addContext(); 245 | ComboAddress listenOn("0.0.0.0:10000"); 246 | h2s.addListener(listenOn, actx); 247 | cout<<"Listening on "<< listenOn.toStringWithPort() < 16 | 17 | #if FMT_USE_FCNTL 18 | # include 19 | # include 20 | 21 | # ifndef _WIN32 22 | # include 23 | # else 24 | # ifndef WIN32_LEAN_AND_MEAN 25 | # define WIN32_LEAN_AND_MEAN 26 | # endif 27 | # include 28 | # include 29 | 30 | # define O_CREAT _O_CREAT 31 | # define O_TRUNC _O_TRUNC 32 | 33 | # ifndef S_IRUSR 34 | # define S_IRUSR _S_IREAD 35 | # endif 36 | 37 | # ifndef S_IWUSR 38 | # define S_IWUSR _S_IWRITE 39 | # endif 40 | 41 | # ifdef __MINGW32__ 42 | # define _SH_DENYNO 0x40 43 | # endif 44 | # endif // _WIN32 45 | #endif // FMT_USE_FCNTL 46 | 47 | #ifdef _WIN32 48 | # include 49 | #endif 50 | 51 | #ifdef fileno 52 | # undef fileno 53 | #endif 54 | 55 | namespace { 56 | #ifdef _WIN32 57 | // Return type of read and write functions. 58 | using RWResult = int; 59 | 60 | // On Windows the count argument to read and write is unsigned, so convert 61 | // it from size_t preventing integer overflow. 62 | inline unsigned convert_rwcount(std::size_t count) { 63 | return count <= UINT_MAX ? static_cast(count) : UINT_MAX; 64 | } 65 | #elif FMT_USE_FCNTL 66 | // Return type of read and write functions. 67 | using RWResult = ssize_t; 68 | 69 | inline std::size_t convert_rwcount(std::size_t count) { return count; } 70 | #endif 71 | } // namespace 72 | 73 | FMT_BEGIN_NAMESPACE 74 | 75 | #ifdef _WIN32 76 | detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) { 77 | if (int error_code = convert(s)) { 78 | FMT_THROW(windows_error(error_code, 79 | "cannot convert string from UTF-16 to UTF-8")); 80 | } 81 | } 82 | 83 | int detail::utf16_to_utf8::convert(wstring_view s) { 84 | if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; 85 | int s_size = static_cast(s.size()); 86 | if (s_size == 0) { 87 | // WideCharToMultiByte does not support zero length, handle separately. 88 | buffer_.resize(1); 89 | buffer_[0] = 0; 90 | return 0; 91 | } 92 | 93 | int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0, 94 | nullptr, nullptr); 95 | if (length == 0) return GetLastError(); 96 | buffer_.resize(length + 1); 97 | length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0], 98 | length, nullptr, nullptr); 99 | if (length == 0) return GetLastError(); 100 | buffer_[length] = 0; 101 | return 0; 102 | } 103 | 104 | void windows_error::init(int err_code, string_view format_str, 105 | format_args args) { 106 | error_code_ = err_code; 107 | memory_buffer buffer; 108 | detail::format_windows_error(buffer, err_code, vformat(format_str, args)); 109 | std::runtime_error& base = *this; 110 | base = std::runtime_error(to_string(buffer)); 111 | } 112 | 113 | void detail::format_windows_error(detail::buffer& out, int error_code, 114 | string_view message) FMT_NOEXCEPT { 115 | FMT_TRY { 116 | wmemory_buffer buf; 117 | buf.resize(inline_buffer_size); 118 | for (;;) { 119 | wchar_t* system_message = &buf[0]; 120 | int result = FormatMessageW( 121 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, 122 | error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message, 123 | static_cast(buf.size()), nullptr); 124 | if (result != 0) { 125 | utf16_to_utf8 utf8_message; 126 | if (utf8_message.convert(system_message) == ERROR_SUCCESS) { 127 | format_to(buffer_appender(out), "{}: {}", message, 128 | utf8_message); 129 | return; 130 | } 131 | break; 132 | } 133 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 134 | break; // Can't get error message, report error code instead. 135 | buf.resize(buf.size() * 2); 136 | } 137 | } 138 | FMT_CATCH(...) {} 139 | format_error_code(out, error_code, message); 140 | } 141 | 142 | void report_windows_error(int error_code, 143 | fmt::string_view message) FMT_NOEXCEPT { 144 | report_error(detail::format_windows_error, error_code, message); 145 | } 146 | #endif // _WIN32 147 | 148 | buffered_file::~buffered_file() FMT_NOEXCEPT { 149 | if (file_ && FMT_SYSTEM(fclose(file_)) != 0) 150 | report_system_error(errno, "cannot close file"); 151 | } 152 | 153 | buffered_file::buffered_file(cstring_view filename, cstring_view mode) { 154 | FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 155 | nullptr); 156 | if (!file_) 157 | FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); 158 | } 159 | 160 | void buffered_file::close() { 161 | if (!file_) return; 162 | int result = FMT_SYSTEM(fclose(file_)); 163 | file_ = nullptr; 164 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 165 | } 166 | 167 | // A macro used to prevent expansion of fileno on broken versions of MinGW. 168 | #define FMT_ARGS 169 | 170 | int buffered_file::fileno() const { 171 | int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); 172 | if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); 173 | return fd; 174 | } 175 | 176 | #if FMT_USE_FCNTL 177 | file::file(cstring_view path, int oflag) { 178 | int mode = S_IRUSR | S_IWUSR; 179 | # if defined(_WIN32) && !defined(__MINGW32__) 180 | fd_ = -1; 181 | FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 182 | # else 183 | FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); 184 | # endif 185 | if (fd_ == -1) 186 | FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); 187 | } 188 | 189 | file::~file() FMT_NOEXCEPT { 190 | // Don't retry close in case of EINTR! 191 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 192 | if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 193 | report_system_error(errno, "cannot close file"); 194 | } 195 | 196 | void file::close() { 197 | if (fd_ == -1) return; 198 | // Don't retry close in case of EINTR! 199 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 200 | int result = FMT_POSIX_CALL(close(fd_)); 201 | fd_ = -1; 202 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 203 | } 204 | 205 | long long file::size() const { 206 | # ifdef _WIN32 207 | // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT 208 | // is less than 0x0500 as is the case with some default MinGW builds. 209 | // Both functions support large file sizes. 210 | DWORD size_upper = 0; 211 | HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); 212 | DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); 213 | if (size_lower == INVALID_FILE_SIZE) { 214 | DWORD error = GetLastError(); 215 | if (error != NO_ERROR) 216 | FMT_THROW(windows_error(GetLastError(), "cannot get file size")); 217 | } 218 | unsigned long long long_size = size_upper; 219 | return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; 220 | # else 221 | using Stat = struct stat; 222 | Stat file_stat = Stat(); 223 | if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) 224 | FMT_THROW(system_error(errno, "cannot get file attributes")); 225 | static_assert(sizeof(long long) >= sizeof(file_stat.st_size), 226 | "return type of file::size is not large enough"); 227 | return file_stat.st_size; 228 | # endif 229 | } 230 | 231 | std::size_t file::read(void* buffer, std::size_t count) { 232 | RWResult result = 0; 233 | FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); 234 | if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); 235 | return detail::to_unsigned(result); 236 | } 237 | 238 | std::size_t file::write(const void* buffer, std::size_t count) { 239 | RWResult result = 0; 240 | FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); 241 | if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); 242 | return detail::to_unsigned(result); 243 | } 244 | 245 | file file::dup(int fd) { 246 | // Don't retry as dup doesn't return EINTR. 247 | // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html 248 | int new_fd = FMT_POSIX_CALL(dup(fd)); 249 | if (new_fd == -1) 250 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); 251 | return file(new_fd); 252 | } 253 | 254 | void file::dup2(int fd) { 255 | int result = 0; 256 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 257 | if (result == -1) { 258 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}", 259 | fd_, fd)); 260 | } 261 | } 262 | 263 | void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT { 264 | int result = 0; 265 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 266 | if (result == -1) ec = error_code(errno); 267 | } 268 | 269 | void file::pipe(file& read_end, file& write_end) { 270 | // Close the descriptors first to make sure that assignments don't throw 271 | // and there are no leaks. 272 | read_end.close(); 273 | write_end.close(); 274 | int fds[2] = {}; 275 | # ifdef _WIN32 276 | // Make the default pipe capacity same as on Linux 2.6.11+. 277 | enum { DEFAULT_CAPACITY = 65536 }; 278 | int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); 279 | # else 280 | // Don't retry as the pipe function doesn't return EINTR. 281 | // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html 282 | int result = FMT_POSIX_CALL(pipe(fds)); 283 | # endif 284 | if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe")); 285 | // The following assignments don't throw because read_fd and write_fd 286 | // are closed. 287 | read_end = file(fds[0]); 288 | write_end = file(fds[1]); 289 | } 290 | 291 | buffered_file file::fdopen(const char* mode) { 292 | // Don't retry as fdopen doesn't return EINTR. 293 | # if defined(__MINGW32__) && defined(_POSIX_) 294 | FILE* f = ::fdopen(fd_, mode); 295 | # else 296 | FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); 297 | # endif 298 | if (!f) 299 | FMT_THROW( 300 | system_error(errno, "cannot associate stream with file descriptor")); 301 | buffered_file bf(f); 302 | fd_ = -1; 303 | return bf; 304 | } 305 | 306 | long getpagesize() { 307 | # ifdef _WIN32 308 | SYSTEM_INFO si; 309 | GetSystemInfo(&si); 310 | return si.dwPageSize; 311 | # else 312 | long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); 313 | if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size")); 314 | return size; 315 | # endif 316 | } 317 | 318 | FMT_API void ostream::grow(size_t) { 319 | if (this->size() == this->capacity()) flush(); 320 | } 321 | #endif // FMT_USE_FCNTL 322 | FMT_END_NAMESPACE 323 | -------------------------------------------------------------------------------- /ext/fmt-7.1.3/include/fmt/ranges.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental range support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | // 8 | // Copyright (c) 2018 - present, Remotion (Igor Schulz) 9 | // All Rights Reserved 10 | // {fmt} support for ranges, containers and types tuple interface. 11 | 12 | #ifndef FMT_RANGES_H_ 13 | #define FMT_RANGES_H_ 14 | 15 | #include 16 | #include 17 | 18 | #include "format.h" 19 | 20 | // output only up to N items from the range. 21 | #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT 22 | # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 23 | #endif 24 | 25 | FMT_BEGIN_NAMESPACE 26 | 27 | template struct formatting_base { 28 | template 29 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 30 | return ctx.begin(); 31 | } 32 | }; 33 | 34 | template 35 | struct formatting_range : formatting_base { 36 | static FMT_CONSTEXPR_DECL const size_t range_length_limit = 37 | FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the 38 | // range. 39 | Char prefix; 40 | Char delimiter; 41 | Char postfix; 42 | formatting_range() : prefix('{'), delimiter(','), postfix('}') {} 43 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 44 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 45 | }; 46 | 47 | template 48 | struct formatting_tuple : formatting_base { 49 | Char prefix; 50 | Char delimiter; 51 | Char postfix; 52 | formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} 53 | static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; 54 | static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 55 | }; 56 | 57 | namespace detail { 58 | 59 | template 60 | OutputIterator copy(const RangeT& range, OutputIterator out) { 61 | for (auto it = range.begin(), end = range.end(); it != end; ++it) 62 | *out++ = *it; 63 | return out; 64 | } 65 | 66 | template 67 | OutputIterator copy(const char* str, OutputIterator out) { 68 | while (*str) *out++ = *str++; 69 | return out; 70 | } 71 | 72 | template 73 | OutputIterator copy(char ch, OutputIterator out) { 74 | *out++ = ch; 75 | return out; 76 | } 77 | 78 | /// Return true value if T has std::string interface, like std::string_view. 79 | template class is_like_std_string { 80 | template 81 | static auto check(U* p) 82 | -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); 83 | template static void check(...); 84 | 85 | public: 86 | static FMT_CONSTEXPR_DECL const bool value = 87 | is_string::value || !std::is_void(nullptr))>::value; 88 | }; 89 | 90 | template 91 | struct is_like_std_string> : std::true_type {}; 92 | 93 | template struct conditional_helper {}; 94 | 95 | template struct is_range_ : std::false_type {}; 96 | 97 | #if !FMT_MSC_VER || FMT_MSC_VER > 1800 98 | template 99 | struct is_range_< 100 | T, conditional_t().begin()), 102 | decltype(std::declval().end())>, 103 | void>> : std::true_type {}; 104 | #endif 105 | 106 | /// tuple_size and tuple_element check. 107 | template class is_tuple_like_ { 108 | template 109 | static auto check(U* p) -> decltype(std::tuple_size::value, int()); 110 | template static void check(...); 111 | 112 | public: 113 | static FMT_CONSTEXPR_DECL const bool value = 114 | !std::is_void(nullptr))>::value; 115 | }; 116 | 117 | // Check for integer_sequence 118 | #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 119 | template 120 | using integer_sequence = std::integer_sequence; 121 | template using index_sequence = std::index_sequence; 122 | template using make_index_sequence = std::make_index_sequence; 123 | #else 124 | template struct integer_sequence { 125 | using value_type = T; 126 | 127 | static FMT_CONSTEXPR size_t size() { return sizeof...(N); } 128 | }; 129 | 130 | template using index_sequence = integer_sequence; 131 | 132 | template 133 | struct make_integer_sequence : make_integer_sequence {}; 134 | template 135 | struct make_integer_sequence : integer_sequence {}; 136 | 137 | template 138 | using make_index_sequence = make_integer_sequence; 139 | #endif 140 | 141 | template 142 | void for_each(index_sequence, Tuple&& tup, F&& f) FMT_NOEXCEPT { 143 | using std::get; 144 | // using free function get(T) now. 145 | const int _[] = {0, ((void)f(get(tup)), 0)...}; 146 | (void)_; // blocks warnings 147 | } 148 | 149 | template 150 | FMT_CONSTEXPR make_index_sequence::value> get_indexes( 151 | T const&) { 152 | return {}; 153 | } 154 | 155 | template void for_each(Tuple&& tup, F&& f) { 156 | const auto indexes = get_indexes(tup); 157 | for_each(indexes, std::forward(tup), std::forward(f)); 158 | } 159 | 160 | template 161 | using value_type = remove_cvref_t().begin())>; 162 | 163 | template ::type>::value)> 165 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 166 | return add_space ? " {}" : "{}"; 167 | } 168 | 169 | template ::type>::value)> 171 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { 172 | return add_space ? " \"{}\"" : "\"{}\""; 173 | } 174 | 175 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { 176 | return add_space ? " \"{}\"" : "\"{}\""; 177 | } 178 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { 179 | return add_space ? L" \"{}\"" : L"\"{}\""; 180 | } 181 | 182 | FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { 183 | return add_space ? " '{}'" : "'{}'"; 184 | } 185 | FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { 186 | return add_space ? L" '{}'" : L"'{}'"; 187 | } 188 | } // namespace detail 189 | 190 | template struct is_tuple_like { 191 | static FMT_CONSTEXPR_DECL const bool value = 192 | detail::is_tuple_like_::value && !detail::is_range_::value; 193 | }; 194 | 195 | template 196 | struct formatter::value>> { 197 | private: 198 | // C++11 generic lambda for format() 199 | template struct format_each { 200 | template void operator()(const T& v) { 201 | if (i > 0) { 202 | if (formatting.add_prepostfix_space) { 203 | *out++ = ' '; 204 | } 205 | out = detail::copy(formatting.delimiter, out); 206 | } 207 | out = format_to(out, 208 | detail::format_str_quoted( 209 | (formatting.add_delimiter_spaces && i > 0), v), 210 | v); 211 | ++i; 212 | } 213 | 214 | formatting_tuple& formatting; 215 | size_t& i; 216 | typename std::add_lvalue_reference().out())>::type out; 218 | }; 219 | 220 | public: 221 | formatting_tuple formatting; 222 | 223 | template 224 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 225 | return formatting.parse(ctx); 226 | } 227 | 228 | template 229 | auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { 230 | auto out = ctx.out(); 231 | size_t i = 0; 232 | detail::copy(formatting.prefix, out); 233 | 234 | detail::for_each(values, format_each{formatting, i, out}); 235 | if (formatting.add_prepostfix_space) { 236 | *out++ = ' '; 237 | } 238 | detail::copy(formatting.postfix, out); 239 | 240 | return ctx.out(); 241 | } 242 | }; 243 | 244 | template struct is_range { 245 | static FMT_CONSTEXPR_DECL const bool value = 246 | detail::is_range_::value && !detail::is_like_std_string::value && 247 | !std::is_convertible>::value && 248 | !std::is_constructible, T>::value; 249 | }; 250 | 251 | template 252 | struct formatter< 253 | T, Char, 254 | enable_if_t::value 255 | // Workaround a bug in MSVC 2017 and earlier. 256 | #if !FMT_MSC_VER || FMT_MSC_VER >= 1927 257 | && 258 | (has_formatter, format_context>::value || 259 | detail::has_fallback_formatter, 260 | format_context>::value) 261 | #endif 262 | >> { 263 | formatting_range formatting; 264 | 265 | template 266 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 267 | return formatting.parse(ctx); 268 | } 269 | 270 | template 271 | typename FormatContext::iterator format(const T& values, FormatContext& ctx) { 272 | auto out = detail::copy(formatting.prefix, ctx.out()); 273 | size_t i = 0; 274 | auto it = values.begin(); 275 | auto end = values.end(); 276 | for (; it != end; ++it) { 277 | if (i > 0) { 278 | if (formatting.add_prepostfix_space) *out++ = ' '; 279 | out = detail::copy(formatting.delimiter, out); 280 | } 281 | out = format_to(out, 282 | detail::format_str_quoted( 283 | (formatting.add_delimiter_spaces && i > 0), *it), 284 | *it); 285 | if (++i > formatting.range_length_limit) { 286 | out = format_to(out, " ... "); 287 | break; 288 | } 289 | } 290 | if (formatting.add_prepostfix_space) *out++ = ' '; 291 | return detail::copy(formatting.postfix, out); 292 | } 293 | }; 294 | 295 | template struct tuple_arg_join : detail::view { 296 | const std::tuple& tuple; 297 | basic_string_view sep; 298 | 299 | tuple_arg_join(const std::tuple& t, basic_string_view s) 300 | : tuple{t}, sep{s} {} 301 | }; 302 | 303 | template 304 | struct formatter, Char> { 305 | template 306 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 307 | return ctx.begin(); 308 | } 309 | 310 | template 311 | typename FormatContext::iterator format( 312 | const tuple_arg_join& value, FormatContext& ctx) { 313 | return format(value, ctx, detail::make_index_sequence{}); 314 | } 315 | 316 | private: 317 | template 318 | typename FormatContext::iterator format( 319 | const tuple_arg_join& value, FormatContext& ctx, 320 | detail::index_sequence) { 321 | return format_args(value, ctx, std::get(value.tuple)...); 322 | } 323 | 324 | template 325 | typename FormatContext::iterator format_args( 326 | const tuple_arg_join&, FormatContext& ctx) { 327 | // NOTE: for compilers that support C++17, this empty function instantiation 328 | // can be replaced with a constexpr branch in the variadic overload. 329 | return ctx.out(); 330 | } 331 | 332 | template 333 | typename FormatContext::iterator format_args( 334 | const tuple_arg_join& value, FormatContext& ctx, 335 | const Arg& arg, const Args&... args) { 336 | using base = formatter::type, Char>; 337 | auto out = ctx.out(); 338 | out = base{}.format(arg, ctx); 339 | if (sizeof...(Args) > 0) { 340 | out = std::copy(value.sep.begin(), value.sep.end(), out); 341 | ctx.advance_to(out); 342 | return format_args(value, ctx, args...); 343 | } 344 | return out; 345 | } 346 | }; 347 | 348 | /** 349 | \rst 350 | Returns an object that formats `tuple` with elements separated by `sep`. 351 | 352 | **Example**:: 353 | 354 | std::tuple t = {1, 'a'}; 355 | fmt::print("{}", fmt::join(t, ", ")); 356 | // Output: "1, a" 357 | \endrst 358 | */ 359 | template 360 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 361 | string_view sep) { 362 | return {tuple, sep}; 363 | } 364 | 365 | template 366 | FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, 367 | wstring_view sep) { 368 | return {tuple, sep}; 369 | } 370 | 371 | /** 372 | \rst 373 | Returns an object that formats `initializer_list` with elements separated by 374 | `sep`. 375 | 376 | **Example**:: 377 | 378 | fmt::print("{}", fmt::join({1, 2, 3}, ", ")); 379 | // Output: "1, 2, 3" 380 | \endrst 381 | */ 382 | template 383 | arg_join join(std::initializer_list list, 384 | string_view sep) { 385 | return join(std::begin(list), std::end(list), sep); 386 | } 387 | 388 | template 389 | arg_join join(std::initializer_list list, 390 | wstring_view sep) { 391 | return join(std::begin(list), std::end(list), sep); 392 | } 393 | 394 | FMT_END_NAMESPACE 395 | 396 | #endif // FMT_RANGES_H_ 397 | -------------------------------------------------------------------------------- /ext/fmt-7.1.3/include/fmt/os.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OS_H_ 9 | #define FMT_OS_H_ 10 | 11 | #if defined(__MINGW32__) || defined(__CYGWIN__) 12 | // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. 13 | # undef __STRICT_ANSI__ 14 | #endif 15 | 16 | #include 17 | #include // for locale_t 18 | #include 19 | #include 20 | #include // for strtod_l 21 | 22 | #if defined __APPLE__ || defined(__FreeBSD__) 23 | # include // for LC_NUMERIC_MASK on OS X 24 | #endif 25 | 26 | #include "format.h" 27 | 28 | // UWP doesn't provide _pipe. 29 | #if FMT_HAS_INCLUDE("winapifamily.h") 30 | # include 31 | #endif 32 | #if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ 33 | defined(__linux__)) && \ 34 | (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) 35 | # include // for O_RDONLY 36 | # define FMT_USE_FCNTL 1 37 | #else 38 | # define FMT_USE_FCNTL 0 39 | #endif 40 | 41 | #ifndef FMT_POSIX 42 | # if defined(_WIN32) && !defined(__MINGW32__) 43 | // Fix warnings about deprecated symbols. 44 | # define FMT_POSIX(call) _##call 45 | # else 46 | # define FMT_POSIX(call) call 47 | # endif 48 | #endif 49 | 50 | // Calls to system functions are wrapped in FMT_SYSTEM for testability. 51 | #ifdef FMT_SYSTEM 52 | # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) 53 | #else 54 | # define FMT_SYSTEM(call) ::call 55 | # ifdef _WIN32 56 | // Fix warnings about deprecated symbols. 57 | # define FMT_POSIX_CALL(call) ::_##call 58 | # else 59 | # define FMT_POSIX_CALL(call) ::call 60 | # endif 61 | #endif 62 | 63 | // Retries the expression while it evaluates to error_result and errno 64 | // equals to EINTR. 65 | #ifndef _WIN32 66 | # define FMT_RETRY_VAL(result, expression, error_result) \ 67 | do { \ 68 | (result) = (expression); \ 69 | } while ((result) == (error_result) && errno == EINTR) 70 | #else 71 | # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) 72 | #endif 73 | 74 | #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) 75 | 76 | FMT_BEGIN_NAMESPACE 77 | 78 | /** 79 | \rst 80 | A reference to a null-terminated string. It can be constructed from a C 81 | string or ``std::string``. 82 | 83 | You can use one of the following type aliases for common character types: 84 | 85 | +---------------+-----------------------------+ 86 | | Type | Definition | 87 | +===============+=============================+ 88 | | cstring_view | basic_cstring_view | 89 | +---------------+-----------------------------+ 90 | | wcstring_view | basic_cstring_view | 91 | +---------------+-----------------------------+ 92 | 93 | This class is most useful as a parameter type to allow passing 94 | different types of strings to a function, for example:: 95 | 96 | template 97 | std::string format(cstring_view format_str, const Args & ... args); 98 | 99 | format("{}", 42); 100 | format(std::string("{}"), 42); 101 | \endrst 102 | */ 103 | template class basic_cstring_view { 104 | private: 105 | const Char* data_; 106 | 107 | public: 108 | /** Constructs a string reference object from a C string. */ 109 | basic_cstring_view(const Char* s) : data_(s) {} 110 | 111 | /** 112 | \rst 113 | Constructs a string reference from an ``std::string`` object. 114 | \endrst 115 | */ 116 | basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} 117 | 118 | /** Returns the pointer to a C string. */ 119 | const Char* c_str() const { return data_; } 120 | }; 121 | 122 | using cstring_view = basic_cstring_view; 123 | using wcstring_view = basic_cstring_view; 124 | 125 | // An error code. 126 | class error_code { 127 | private: 128 | int value_; 129 | 130 | public: 131 | explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} 132 | 133 | int get() const FMT_NOEXCEPT { return value_; } 134 | }; 135 | 136 | #ifdef _WIN32 137 | namespace detail { 138 | // A converter from UTF-16 to UTF-8. 139 | // It is only provided for Windows since other systems support UTF-8 natively. 140 | class utf16_to_utf8 { 141 | private: 142 | memory_buffer buffer_; 143 | 144 | public: 145 | utf16_to_utf8() {} 146 | FMT_API explicit utf16_to_utf8(wstring_view s); 147 | operator string_view() const { return string_view(&buffer_[0], size()); } 148 | size_t size() const { return buffer_.size() - 1; } 149 | const char* c_str() const { return &buffer_[0]; } 150 | std::string str() const { return std::string(&buffer_[0], size()); } 151 | 152 | // Performs conversion returning a system error code instead of 153 | // throwing exception on conversion error. This method may still throw 154 | // in case of memory allocation error. 155 | FMT_API int convert(wstring_view s); 156 | }; 157 | 158 | FMT_API void format_windows_error(buffer& out, int error_code, 159 | string_view message) FMT_NOEXCEPT; 160 | } // namespace detail 161 | 162 | /** A Windows error. */ 163 | class windows_error : public system_error { 164 | private: 165 | FMT_API void init(int error_code, string_view format_str, format_args args); 166 | 167 | public: 168 | /** 169 | \rst 170 | Constructs a :class:`fmt::windows_error` object with the description 171 | of the form 172 | 173 | .. parsed-literal:: 174 | **: ** 175 | 176 | where ** is the formatted message and ** is the 177 | system message corresponding to the error code. 178 | *error_code* is a Windows error code as given by ``GetLastError``. 179 | If *error_code* is not a valid error code such as -1, the system message 180 | will look like "error -1". 181 | 182 | **Example**:: 183 | 184 | // This throws a windows_error with the description 185 | // cannot open file 'madeup': The system cannot find the file specified. 186 | // or similar (system message may vary). 187 | const char *filename = "madeup"; 188 | LPOFSTRUCT of = LPOFSTRUCT(); 189 | HFILE file = OpenFile(filename, &of, OF_READ); 190 | if (file == HFILE_ERROR) { 191 | throw fmt::windows_error(GetLastError(), 192 | "cannot open file '{}'", filename); 193 | } 194 | \endrst 195 | */ 196 | template 197 | windows_error(int error_code, string_view message, const Args&... args) { 198 | init(error_code, message, make_format_args(args...)); 199 | } 200 | }; 201 | 202 | // Reports a Windows error without throwing an exception. 203 | // Can be used to report errors from destructors. 204 | FMT_API void report_windows_error(int error_code, 205 | string_view message) FMT_NOEXCEPT; 206 | #endif // _WIN32 207 | 208 | // A buffered file. 209 | class buffered_file { 210 | private: 211 | FILE* file_; 212 | 213 | friend class file; 214 | 215 | explicit buffered_file(FILE* f) : file_(f) {} 216 | 217 | public: 218 | buffered_file(const buffered_file&) = delete; 219 | void operator=(const buffered_file&) = delete; 220 | 221 | // Constructs a buffered_file object which doesn't represent any file. 222 | buffered_file() FMT_NOEXCEPT : file_(nullptr) {} 223 | 224 | // Destroys the object closing the file it represents if any. 225 | FMT_API ~buffered_file() FMT_NOEXCEPT; 226 | 227 | public: 228 | buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { 229 | other.file_ = nullptr; 230 | } 231 | 232 | buffered_file& operator=(buffered_file&& other) { 233 | close(); 234 | file_ = other.file_; 235 | other.file_ = nullptr; 236 | return *this; 237 | } 238 | 239 | // Opens a file. 240 | FMT_API buffered_file(cstring_view filename, cstring_view mode); 241 | 242 | // Closes the file. 243 | FMT_API void close(); 244 | 245 | // Returns the pointer to a FILE object representing this file. 246 | FILE* get() const FMT_NOEXCEPT { return file_; } 247 | 248 | // We place parentheses around fileno to workaround a bug in some versions 249 | // of MinGW that define fileno as a macro. 250 | FMT_API int(fileno)() const; 251 | 252 | void vprint(string_view format_str, format_args args) { 253 | fmt::vprint(file_, format_str, args); 254 | } 255 | 256 | template 257 | inline void print(string_view format_str, const Args&... args) { 258 | vprint(format_str, make_format_args(args...)); 259 | } 260 | }; 261 | 262 | #if FMT_USE_FCNTL 263 | // A file. Closed file is represented by a file object with descriptor -1. 264 | // Methods that are not declared with FMT_NOEXCEPT may throw 265 | // fmt::system_error in case of failure. Note that some errors such as 266 | // closing the file multiple times will cause a crash on Windows rather 267 | // than an exception. You can get standard behavior by overriding the 268 | // invalid parameter handler with _set_invalid_parameter_handler. 269 | class file { 270 | private: 271 | int fd_; // File descriptor. 272 | 273 | // Constructs a file object with a given descriptor. 274 | explicit file(int fd) : fd_(fd) {} 275 | 276 | public: 277 | // Possible values for the oflag argument to the constructor. 278 | enum { 279 | RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. 280 | WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. 281 | RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. 282 | CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. 283 | APPEND = FMT_POSIX(O_APPEND) // Open in append mode. 284 | }; 285 | 286 | // Constructs a file object which doesn't represent any file. 287 | file() FMT_NOEXCEPT : fd_(-1) {} 288 | 289 | // Opens a file and constructs a file object representing this file. 290 | FMT_API file(cstring_view path, int oflag); 291 | 292 | public: 293 | file(const file&) = delete; 294 | void operator=(const file&) = delete; 295 | 296 | file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } 297 | 298 | file& operator=(file&& other) FMT_NOEXCEPT { 299 | close(); 300 | fd_ = other.fd_; 301 | other.fd_ = -1; 302 | return *this; 303 | } 304 | 305 | // Destroys the object closing the file it represents if any. 306 | FMT_API ~file() FMT_NOEXCEPT; 307 | 308 | // Returns the file descriptor. 309 | int descriptor() const FMT_NOEXCEPT { return fd_; } 310 | 311 | // Closes the file. 312 | FMT_API void close(); 313 | 314 | // Returns the file size. The size has signed type for consistency with 315 | // stat::st_size. 316 | FMT_API long long size() const; 317 | 318 | // Attempts to read count bytes from the file into the specified buffer. 319 | FMT_API size_t read(void* buffer, size_t count); 320 | 321 | // Attempts to write count bytes from the specified buffer to the file. 322 | FMT_API size_t write(const void* buffer, size_t count); 323 | 324 | // Duplicates a file descriptor with the dup function and returns 325 | // the duplicate as a file object. 326 | FMT_API static file dup(int fd); 327 | 328 | // Makes fd be the copy of this file descriptor, closing fd first if 329 | // necessary. 330 | FMT_API void dup2(int fd); 331 | 332 | // Makes fd be the copy of this file descriptor, closing fd first if 333 | // necessary. 334 | FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT; 335 | 336 | // Creates a pipe setting up read_end and write_end file objects for reading 337 | // and writing respectively. 338 | FMT_API static void pipe(file& read_end, file& write_end); 339 | 340 | // Creates a buffered_file object associated with this file and detaches 341 | // this file object from the file. 342 | FMT_API buffered_file fdopen(const char* mode); 343 | }; 344 | 345 | // Returns the memory page size. 346 | long getpagesize(); 347 | 348 | namespace detail { 349 | 350 | struct buffer_size { 351 | size_t value = 0; 352 | buffer_size operator=(size_t val) const { 353 | auto bs = buffer_size(); 354 | bs.value = val; 355 | return bs; 356 | } 357 | }; 358 | 359 | struct ostream_params { 360 | int oflag = file::WRONLY | file::CREATE; 361 | size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; 362 | 363 | ostream_params() {} 364 | 365 | template 366 | ostream_params(T... params, int oflag) : ostream_params(params...) { 367 | this->oflag = oflag; 368 | } 369 | 370 | template 371 | ostream_params(T... params, detail::buffer_size bs) 372 | : ostream_params(params...) { 373 | this->buffer_size = bs.value; 374 | } 375 | }; 376 | } // namespace detail 377 | 378 | static constexpr detail::buffer_size buffer_size; 379 | 380 | // A fast output stream which is not thread-safe. 381 | class ostream final : private detail::buffer { 382 | private: 383 | file file_; 384 | 385 | void flush() { 386 | if (size() == 0) return; 387 | file_.write(data(), size()); 388 | clear(); 389 | } 390 | 391 | FMT_API void grow(size_t) override final; 392 | 393 | ostream(cstring_view path, const detail::ostream_params& params) 394 | : file_(path, params.oflag) { 395 | set(new char[params.buffer_size], params.buffer_size); 396 | } 397 | 398 | public: 399 | ostream(ostream&& other) 400 | : detail::buffer(other.data(), other.size(), other.capacity()), 401 | file_(std::move(other.file_)) { 402 | other.set(nullptr, 0); 403 | } 404 | ~ostream() { 405 | flush(); 406 | delete[] data(); 407 | } 408 | 409 | template 410 | friend ostream output_file(cstring_view path, T... params); 411 | 412 | void close() { 413 | flush(); 414 | file_.close(); 415 | } 416 | 417 | template 418 | void print(const S& format_str, const Args&... args) { 419 | format_to(detail::buffer_appender(*this), format_str, args...); 420 | } 421 | }; 422 | 423 | /** 424 | Opens a file for writing. Supported parameters passed in `params`: 425 | * ````: Output flags (``file::WRONLY | file::CREATE`` by default) 426 | * ``buffer_size=``: Output buffer size 427 | */ 428 | template 429 | inline ostream output_file(cstring_view path, T... params) { 430 | return {path, detail::ostream_params(params...)}; 431 | } 432 | #endif // FMT_USE_FCNTL 433 | 434 | #ifdef FMT_LOCALE 435 | // A "C" numeric locale. 436 | class locale { 437 | private: 438 | # ifdef _WIN32 439 | using locale_t = _locale_t; 440 | 441 | static void freelocale(locale_t loc) { _free_locale(loc); } 442 | 443 | static double strtod_l(const char* nptr, char** endptr, _locale_t loc) { 444 | return _strtod_l(nptr, endptr, loc); 445 | } 446 | # endif 447 | 448 | locale_t locale_; 449 | 450 | public: 451 | using type = locale_t; 452 | locale(const locale&) = delete; 453 | void operator=(const locale&) = delete; 454 | 455 | locale() { 456 | # ifndef _WIN32 457 | locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr)); 458 | # else 459 | locale_ = _create_locale(LC_NUMERIC, "C"); 460 | # endif 461 | if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); 462 | } 463 | ~locale() { freelocale(locale_); } 464 | 465 | type get() const { return locale_; } 466 | 467 | // Converts string to floating-point number and advances str past the end 468 | // of the parsed input. 469 | double strtod(const char*& str) const { 470 | char* end = nullptr; 471 | double result = strtod_l(str, &end, locale_); 472 | str = end; 473 | return result; 474 | } 475 | }; 476 | using Locale FMT_DEPRECATED_ALIAS = locale; 477 | #endif // FMT_LOCALE 478 | FMT_END_NAMESPACE 479 | 480 | #endif // FMT_OS_H_ 481 | -------------------------------------------------------------------------------- /ext/fmt-7.1.3/include/fmt/color.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - color support 2 | // 3 | // Copyright (c) 2018 - present, Victor Zverovich and fmt contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_COLOR_H_ 9 | #define FMT_COLOR_H_ 10 | 11 | #include "format.h" 12 | 13 | FMT_BEGIN_NAMESPACE 14 | 15 | enum class color : uint32_t { 16 | alice_blue = 0xF0F8FF, // rgb(240,248,255) 17 | antique_white = 0xFAEBD7, // rgb(250,235,215) 18 | aqua = 0x00FFFF, // rgb(0,255,255) 19 | aquamarine = 0x7FFFD4, // rgb(127,255,212) 20 | azure = 0xF0FFFF, // rgb(240,255,255) 21 | beige = 0xF5F5DC, // rgb(245,245,220) 22 | bisque = 0xFFE4C4, // rgb(255,228,196) 23 | black = 0x000000, // rgb(0,0,0) 24 | blanched_almond = 0xFFEBCD, // rgb(255,235,205) 25 | blue = 0x0000FF, // rgb(0,0,255) 26 | blue_violet = 0x8A2BE2, // rgb(138,43,226) 27 | brown = 0xA52A2A, // rgb(165,42,42) 28 | burly_wood = 0xDEB887, // rgb(222,184,135) 29 | cadet_blue = 0x5F9EA0, // rgb(95,158,160) 30 | chartreuse = 0x7FFF00, // rgb(127,255,0) 31 | chocolate = 0xD2691E, // rgb(210,105,30) 32 | coral = 0xFF7F50, // rgb(255,127,80) 33 | cornflower_blue = 0x6495ED, // rgb(100,149,237) 34 | cornsilk = 0xFFF8DC, // rgb(255,248,220) 35 | crimson = 0xDC143C, // rgb(220,20,60) 36 | cyan = 0x00FFFF, // rgb(0,255,255) 37 | dark_blue = 0x00008B, // rgb(0,0,139) 38 | dark_cyan = 0x008B8B, // rgb(0,139,139) 39 | dark_golden_rod = 0xB8860B, // rgb(184,134,11) 40 | dark_gray = 0xA9A9A9, // rgb(169,169,169) 41 | dark_green = 0x006400, // rgb(0,100,0) 42 | dark_khaki = 0xBDB76B, // rgb(189,183,107) 43 | dark_magenta = 0x8B008B, // rgb(139,0,139) 44 | dark_olive_green = 0x556B2F, // rgb(85,107,47) 45 | dark_orange = 0xFF8C00, // rgb(255,140,0) 46 | dark_orchid = 0x9932CC, // rgb(153,50,204) 47 | dark_red = 0x8B0000, // rgb(139,0,0) 48 | dark_salmon = 0xE9967A, // rgb(233,150,122) 49 | dark_sea_green = 0x8FBC8F, // rgb(143,188,143) 50 | dark_slate_blue = 0x483D8B, // rgb(72,61,139) 51 | dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) 52 | dark_turquoise = 0x00CED1, // rgb(0,206,209) 53 | dark_violet = 0x9400D3, // rgb(148,0,211) 54 | deep_pink = 0xFF1493, // rgb(255,20,147) 55 | deep_sky_blue = 0x00BFFF, // rgb(0,191,255) 56 | dim_gray = 0x696969, // rgb(105,105,105) 57 | dodger_blue = 0x1E90FF, // rgb(30,144,255) 58 | fire_brick = 0xB22222, // rgb(178,34,34) 59 | floral_white = 0xFFFAF0, // rgb(255,250,240) 60 | forest_green = 0x228B22, // rgb(34,139,34) 61 | fuchsia = 0xFF00FF, // rgb(255,0,255) 62 | gainsboro = 0xDCDCDC, // rgb(220,220,220) 63 | ghost_white = 0xF8F8FF, // rgb(248,248,255) 64 | gold = 0xFFD700, // rgb(255,215,0) 65 | golden_rod = 0xDAA520, // rgb(218,165,32) 66 | gray = 0x808080, // rgb(128,128,128) 67 | green = 0x008000, // rgb(0,128,0) 68 | green_yellow = 0xADFF2F, // rgb(173,255,47) 69 | honey_dew = 0xF0FFF0, // rgb(240,255,240) 70 | hot_pink = 0xFF69B4, // rgb(255,105,180) 71 | indian_red = 0xCD5C5C, // rgb(205,92,92) 72 | indigo = 0x4B0082, // rgb(75,0,130) 73 | ivory = 0xFFFFF0, // rgb(255,255,240) 74 | khaki = 0xF0E68C, // rgb(240,230,140) 75 | lavender = 0xE6E6FA, // rgb(230,230,250) 76 | lavender_blush = 0xFFF0F5, // rgb(255,240,245) 77 | lawn_green = 0x7CFC00, // rgb(124,252,0) 78 | lemon_chiffon = 0xFFFACD, // rgb(255,250,205) 79 | light_blue = 0xADD8E6, // rgb(173,216,230) 80 | light_coral = 0xF08080, // rgb(240,128,128) 81 | light_cyan = 0xE0FFFF, // rgb(224,255,255) 82 | light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) 83 | light_gray = 0xD3D3D3, // rgb(211,211,211) 84 | light_green = 0x90EE90, // rgb(144,238,144) 85 | light_pink = 0xFFB6C1, // rgb(255,182,193) 86 | light_salmon = 0xFFA07A, // rgb(255,160,122) 87 | light_sea_green = 0x20B2AA, // rgb(32,178,170) 88 | light_sky_blue = 0x87CEFA, // rgb(135,206,250) 89 | light_slate_gray = 0x778899, // rgb(119,136,153) 90 | light_steel_blue = 0xB0C4DE, // rgb(176,196,222) 91 | light_yellow = 0xFFFFE0, // rgb(255,255,224) 92 | lime = 0x00FF00, // rgb(0,255,0) 93 | lime_green = 0x32CD32, // rgb(50,205,50) 94 | linen = 0xFAF0E6, // rgb(250,240,230) 95 | magenta = 0xFF00FF, // rgb(255,0,255) 96 | maroon = 0x800000, // rgb(128,0,0) 97 | medium_aquamarine = 0x66CDAA, // rgb(102,205,170) 98 | medium_blue = 0x0000CD, // rgb(0,0,205) 99 | medium_orchid = 0xBA55D3, // rgb(186,85,211) 100 | medium_purple = 0x9370DB, // rgb(147,112,219) 101 | medium_sea_green = 0x3CB371, // rgb(60,179,113) 102 | medium_slate_blue = 0x7B68EE, // rgb(123,104,238) 103 | medium_spring_green = 0x00FA9A, // rgb(0,250,154) 104 | medium_turquoise = 0x48D1CC, // rgb(72,209,204) 105 | medium_violet_red = 0xC71585, // rgb(199,21,133) 106 | midnight_blue = 0x191970, // rgb(25,25,112) 107 | mint_cream = 0xF5FFFA, // rgb(245,255,250) 108 | misty_rose = 0xFFE4E1, // rgb(255,228,225) 109 | moccasin = 0xFFE4B5, // rgb(255,228,181) 110 | navajo_white = 0xFFDEAD, // rgb(255,222,173) 111 | navy = 0x000080, // rgb(0,0,128) 112 | old_lace = 0xFDF5E6, // rgb(253,245,230) 113 | olive = 0x808000, // rgb(128,128,0) 114 | olive_drab = 0x6B8E23, // rgb(107,142,35) 115 | orange = 0xFFA500, // rgb(255,165,0) 116 | orange_red = 0xFF4500, // rgb(255,69,0) 117 | orchid = 0xDA70D6, // rgb(218,112,214) 118 | pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) 119 | pale_green = 0x98FB98, // rgb(152,251,152) 120 | pale_turquoise = 0xAFEEEE, // rgb(175,238,238) 121 | pale_violet_red = 0xDB7093, // rgb(219,112,147) 122 | papaya_whip = 0xFFEFD5, // rgb(255,239,213) 123 | peach_puff = 0xFFDAB9, // rgb(255,218,185) 124 | peru = 0xCD853F, // rgb(205,133,63) 125 | pink = 0xFFC0CB, // rgb(255,192,203) 126 | plum = 0xDDA0DD, // rgb(221,160,221) 127 | powder_blue = 0xB0E0E6, // rgb(176,224,230) 128 | purple = 0x800080, // rgb(128,0,128) 129 | rebecca_purple = 0x663399, // rgb(102,51,153) 130 | red = 0xFF0000, // rgb(255,0,0) 131 | rosy_brown = 0xBC8F8F, // rgb(188,143,143) 132 | royal_blue = 0x4169E1, // rgb(65,105,225) 133 | saddle_brown = 0x8B4513, // rgb(139,69,19) 134 | salmon = 0xFA8072, // rgb(250,128,114) 135 | sandy_brown = 0xF4A460, // rgb(244,164,96) 136 | sea_green = 0x2E8B57, // rgb(46,139,87) 137 | sea_shell = 0xFFF5EE, // rgb(255,245,238) 138 | sienna = 0xA0522D, // rgb(160,82,45) 139 | silver = 0xC0C0C0, // rgb(192,192,192) 140 | sky_blue = 0x87CEEB, // rgb(135,206,235) 141 | slate_blue = 0x6A5ACD, // rgb(106,90,205) 142 | slate_gray = 0x708090, // rgb(112,128,144) 143 | snow = 0xFFFAFA, // rgb(255,250,250) 144 | spring_green = 0x00FF7F, // rgb(0,255,127) 145 | steel_blue = 0x4682B4, // rgb(70,130,180) 146 | tan = 0xD2B48C, // rgb(210,180,140) 147 | teal = 0x008080, // rgb(0,128,128) 148 | thistle = 0xD8BFD8, // rgb(216,191,216) 149 | tomato = 0xFF6347, // rgb(255,99,71) 150 | turquoise = 0x40E0D0, // rgb(64,224,208) 151 | violet = 0xEE82EE, // rgb(238,130,238) 152 | wheat = 0xF5DEB3, // rgb(245,222,179) 153 | white = 0xFFFFFF, // rgb(255,255,255) 154 | white_smoke = 0xF5F5F5, // rgb(245,245,245) 155 | yellow = 0xFFFF00, // rgb(255,255,0) 156 | yellow_green = 0x9ACD32 // rgb(154,205,50) 157 | }; // enum class color 158 | 159 | enum class terminal_color : uint8_t { 160 | black = 30, 161 | red, 162 | green, 163 | yellow, 164 | blue, 165 | magenta, 166 | cyan, 167 | white, 168 | bright_black = 90, 169 | bright_red, 170 | bright_green, 171 | bright_yellow, 172 | bright_blue, 173 | bright_magenta, 174 | bright_cyan, 175 | bright_white 176 | }; 177 | 178 | enum class emphasis : uint8_t { 179 | bold = 1, 180 | italic = 1 << 1, 181 | underline = 1 << 2, 182 | strikethrough = 1 << 3 183 | }; 184 | 185 | // rgb is a struct for red, green and blue colors. 186 | // Using the name "rgb" makes some editors show the color in a tooltip. 187 | struct rgb { 188 | FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} 189 | FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} 190 | FMT_CONSTEXPR rgb(uint32_t hex) 191 | : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} 192 | FMT_CONSTEXPR rgb(color hex) 193 | : r((uint32_t(hex) >> 16) & 0xFF), 194 | g((uint32_t(hex) >> 8) & 0xFF), 195 | b(uint32_t(hex) & 0xFF) {} 196 | uint8_t r; 197 | uint8_t g; 198 | uint8_t b; 199 | }; 200 | 201 | namespace detail { 202 | 203 | // color is a struct of either a rgb color or a terminal color. 204 | struct color_type { 205 | FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} 206 | FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), 207 | value{} { 208 | value.rgb_color = static_cast(rgb_color); 209 | } 210 | FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { 211 | value.rgb_color = (static_cast(rgb_color.r) << 16) | 212 | (static_cast(rgb_color.g) << 8) | rgb_color.b; 213 | } 214 | FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), 215 | value{} { 216 | value.term_color = static_cast(term_color); 217 | } 218 | bool is_rgb; 219 | union color_union { 220 | uint8_t term_color; 221 | uint32_t rgb_color; 222 | } value; 223 | }; 224 | } // namespace detail 225 | 226 | // Experimental text formatting support. 227 | class text_style { 228 | public: 229 | FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT 230 | : set_foreground_color(), 231 | set_background_color(), 232 | ems(em) {} 233 | 234 | FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { 235 | if (!set_foreground_color) { 236 | set_foreground_color = rhs.set_foreground_color; 237 | foreground_color = rhs.foreground_color; 238 | } else if (rhs.set_foreground_color) { 239 | if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) 240 | FMT_THROW(format_error("can't OR a terminal color")); 241 | foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; 242 | } 243 | 244 | if (!set_background_color) { 245 | set_background_color = rhs.set_background_color; 246 | background_color = rhs.background_color; 247 | } else if (rhs.set_background_color) { 248 | if (!background_color.is_rgb || !rhs.background_color.is_rgb) 249 | FMT_THROW(format_error("can't OR a terminal color")); 250 | background_color.value.rgb_color |= rhs.background_color.value.rgb_color; 251 | } 252 | 253 | ems = static_cast(static_cast(ems) | 254 | static_cast(rhs.ems)); 255 | return *this; 256 | } 257 | 258 | friend FMT_CONSTEXPR text_style operator|(text_style lhs, 259 | const text_style& rhs) { 260 | return lhs |= rhs; 261 | } 262 | 263 | FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { 264 | if (!set_foreground_color) { 265 | set_foreground_color = rhs.set_foreground_color; 266 | foreground_color = rhs.foreground_color; 267 | } else if (rhs.set_foreground_color) { 268 | if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) 269 | FMT_THROW(format_error("can't AND a terminal color")); 270 | foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; 271 | } 272 | 273 | if (!set_background_color) { 274 | set_background_color = rhs.set_background_color; 275 | background_color = rhs.background_color; 276 | } else if (rhs.set_background_color) { 277 | if (!background_color.is_rgb || !rhs.background_color.is_rgb) 278 | FMT_THROW(format_error("can't AND a terminal color")); 279 | background_color.value.rgb_color &= rhs.background_color.value.rgb_color; 280 | } 281 | 282 | ems = static_cast(static_cast(ems) & 283 | static_cast(rhs.ems)); 284 | return *this; 285 | } 286 | 287 | friend FMT_CONSTEXPR text_style operator&(text_style lhs, 288 | const text_style& rhs) { 289 | return lhs &= rhs; 290 | } 291 | 292 | FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { 293 | return set_foreground_color; 294 | } 295 | FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { 296 | return set_background_color; 297 | } 298 | FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { 299 | return static_cast(ems) != 0; 300 | } 301 | FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { 302 | FMT_ASSERT(has_foreground(), "no foreground specified for this style"); 303 | return foreground_color; 304 | } 305 | FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { 306 | FMT_ASSERT(has_background(), "no background specified for this style"); 307 | return background_color; 308 | } 309 | FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { 310 | FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); 311 | return ems; 312 | } 313 | 314 | private: 315 | FMT_CONSTEXPR text_style(bool is_foreground, 316 | detail::color_type text_color) FMT_NOEXCEPT 317 | : set_foreground_color(), 318 | set_background_color(), 319 | ems() { 320 | if (is_foreground) { 321 | foreground_color = text_color; 322 | set_foreground_color = true; 323 | } else { 324 | background_color = text_color; 325 | set_background_color = true; 326 | } 327 | } 328 | 329 | friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) 330 | FMT_NOEXCEPT; 331 | friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) 332 | FMT_NOEXCEPT; 333 | 334 | detail::color_type foreground_color; 335 | detail::color_type background_color; 336 | bool set_foreground_color; 337 | bool set_background_color; 338 | emphasis ems; 339 | }; 340 | 341 | FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT { 342 | return text_style(/*is_foreground=*/true, foreground); 343 | } 344 | 345 | FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT { 346 | return text_style(/*is_foreground=*/false, background); 347 | } 348 | 349 | FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { 350 | return text_style(lhs) | rhs; 351 | } 352 | 353 | namespace detail { 354 | 355 | template struct ansi_color_escape { 356 | FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, 357 | const char* esc) FMT_NOEXCEPT { 358 | // If we have a terminal color, we need to output another escape code 359 | // sequence. 360 | if (!text_color.is_rgb) { 361 | bool is_background = esc == detail::data::background_color; 362 | uint32_t value = text_color.value.term_color; 363 | // Background ASCII codes are the same as the foreground ones but with 364 | // 10 more. 365 | if (is_background) value += 10u; 366 | 367 | size_t index = 0; 368 | buffer[index++] = static_cast('\x1b'); 369 | buffer[index++] = static_cast('['); 370 | 371 | if (value >= 100u) { 372 | buffer[index++] = static_cast('1'); 373 | value %= 100u; 374 | } 375 | buffer[index++] = static_cast('0' + value / 10u); 376 | buffer[index++] = static_cast('0' + value % 10u); 377 | 378 | buffer[index++] = static_cast('m'); 379 | buffer[index++] = static_cast('\0'); 380 | return; 381 | } 382 | 383 | for (int i = 0; i < 7; i++) { 384 | buffer[i] = static_cast(esc[i]); 385 | } 386 | rgb color(text_color.value.rgb_color); 387 | to_esc(color.r, buffer + 7, ';'); 388 | to_esc(color.g, buffer + 11, ';'); 389 | to_esc(color.b, buffer + 15, 'm'); 390 | buffer[19] = static_cast(0); 391 | } 392 | FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { 393 | uint8_t em_codes[4] = {}; 394 | uint8_t em_bits = static_cast(em); 395 | if (em_bits & static_cast(emphasis::bold)) em_codes[0] = 1; 396 | if (em_bits & static_cast(emphasis::italic)) em_codes[1] = 3; 397 | if (em_bits & static_cast(emphasis::underline)) em_codes[2] = 4; 398 | if (em_bits & static_cast(emphasis::strikethrough)) 399 | em_codes[3] = 9; 400 | 401 | size_t index = 0; 402 | for (int i = 0; i < 4; ++i) { 403 | if (!em_codes[i]) continue; 404 | buffer[index++] = static_cast('\x1b'); 405 | buffer[index++] = static_cast('['); 406 | buffer[index++] = static_cast('0' + em_codes[i]); 407 | buffer[index++] = static_cast('m'); 408 | } 409 | buffer[index++] = static_cast(0); 410 | } 411 | FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } 412 | 413 | FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } 414 | FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { 415 | return buffer + std::char_traits::length(buffer); 416 | } 417 | 418 | private: 419 | Char buffer[7u + 3u * 4u + 1u]; 420 | 421 | static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, 422 | char delimiter) FMT_NOEXCEPT { 423 | out[0] = static_cast('0' + c / 100); 424 | out[1] = static_cast('0' + c / 10 % 10); 425 | out[2] = static_cast('0' + c % 10); 426 | out[3] = static_cast(delimiter); 427 | } 428 | }; 429 | 430 | template 431 | FMT_CONSTEXPR ansi_color_escape make_foreground_color( 432 | detail::color_type foreground) FMT_NOEXCEPT { 433 | return ansi_color_escape(foreground, detail::data::foreground_color); 434 | } 435 | 436 | template 437 | FMT_CONSTEXPR ansi_color_escape make_background_color( 438 | detail::color_type background) FMT_NOEXCEPT { 439 | return ansi_color_escape(background, detail::data::background_color); 440 | } 441 | 442 | template 443 | FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { 444 | return ansi_color_escape(em); 445 | } 446 | 447 | template 448 | inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { 449 | std::fputs(chars, stream); 450 | } 451 | 452 | template <> 453 | inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { 454 | std::fputws(chars, stream); 455 | } 456 | 457 | template inline void reset_color(FILE* stream) FMT_NOEXCEPT { 458 | fputs(detail::data::reset_color, stream); 459 | } 460 | 461 | template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { 462 | fputs(detail::data::wreset_color, stream); 463 | } 464 | 465 | template 466 | inline void reset_color(buffer& buffer) FMT_NOEXCEPT { 467 | const char* begin = data::reset_color; 468 | const char* end = begin + sizeof(data::reset_color) - 1; 469 | buffer.append(begin, end); 470 | } 471 | 472 | template 473 | void vformat_to(buffer& buf, const text_style& ts, 474 | basic_string_view format_str, 475 | basic_format_args>> args) { 476 | bool has_style = false; 477 | if (ts.has_emphasis()) { 478 | has_style = true; 479 | auto emphasis = detail::make_emphasis(ts.get_emphasis()); 480 | buf.append(emphasis.begin(), emphasis.end()); 481 | } 482 | if (ts.has_foreground()) { 483 | has_style = true; 484 | auto foreground = detail::make_foreground_color(ts.get_foreground()); 485 | buf.append(foreground.begin(), foreground.end()); 486 | } 487 | if (ts.has_background()) { 488 | has_style = true; 489 | auto background = detail::make_background_color(ts.get_background()); 490 | buf.append(background.begin(), background.end()); 491 | } 492 | detail::vformat_to(buf, format_str, args); 493 | if (has_style) detail::reset_color(buf); 494 | } 495 | } // namespace detail 496 | 497 | template > 498 | void vprint(std::FILE* f, const text_style& ts, const S& format, 499 | basic_format_args>> args) { 500 | basic_memory_buffer buf; 501 | detail::vformat_to(buf, ts, to_string_view(format), args); 502 | buf.push_back(Char(0)); 503 | detail::fputs(buf.data(), f); 504 | } 505 | 506 | /** 507 | \rst 508 | Formats a string and prints it to the specified file stream using ANSI 509 | escape sequences to specify text formatting. 510 | 511 | **Example**:: 512 | 513 | fmt::print(fmt::emphasis::bold | fg(fmt::color::red), 514 | "Elapsed time: {0:.2f} seconds", 1.23); 515 | \endrst 516 | */ 517 | template ::value)> 519 | void print(std::FILE* f, const text_style& ts, const S& format_str, 520 | const Args&... args) { 521 | vprint(f, ts, format_str, 522 | fmt::make_args_checked(format_str, args...)); 523 | } 524 | 525 | /** 526 | Formats a string and prints it to stdout using ANSI escape sequences to 527 | specify text formatting. 528 | Example: 529 | fmt::print(fmt::emphasis::bold | fg(fmt::color::red), 530 | "Elapsed time: {0:.2f} seconds", 1.23); 531 | */ 532 | template ::value)> 534 | void print(const text_style& ts, const S& format_str, const Args&... args) { 535 | return print(stdout, ts, format_str, args...); 536 | } 537 | 538 | template > 539 | inline std::basic_string vformat( 540 | const text_style& ts, const S& format_str, 541 | basic_format_args>> args) { 542 | basic_memory_buffer buf; 543 | detail::vformat_to(buf, ts, to_string_view(format_str), args); 544 | return fmt::to_string(buf); 545 | } 546 | 547 | /** 548 | \rst 549 | Formats arguments and returns the result as a string using ANSI 550 | escape sequences to specify text formatting. 551 | 552 | **Example**:: 553 | 554 | #include 555 | std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), 556 | "The answer is {}", 42); 557 | \endrst 558 | */ 559 | template > 560 | inline std::basic_string format(const text_style& ts, const S& format_str, 561 | const Args&... args) { 562 | return vformat(ts, to_string_view(format_str), 563 | fmt::make_args_checked(format_str, args...)); 564 | } 565 | 566 | /** 567 | Formats a string with the given text_style and writes the output to ``out``. 568 | */ 569 | template ::value)> 571 | OutputIt vformat_to( 572 | OutputIt out, const text_style& ts, basic_string_view format_str, 573 | basic_format_args>> args) { 574 | decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); 575 | detail::vformat_to(buf, ts, format_str, args); 576 | return detail::get_iterator(buf); 577 | } 578 | 579 | /** 580 | \rst 581 | Formats arguments with the given text_style, writes the result to the output 582 | iterator ``out`` and returns the iterator past the end of the output range. 583 | 584 | **Example**:: 585 | 586 | std::vector out; 587 | fmt::format_to(std::back_inserter(out), 588 | fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); 589 | \endrst 590 | */ 591 | template >::value&& 593 | detail::is_string::value> 594 | inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, 595 | Args&&... args) -> 596 | typename std::enable_if::type { 597 | return vformat_to(out, ts, to_string_view(format_str), 598 | fmt::make_args_checked(format_str, args...)); 599 | } 600 | 601 | FMT_END_NAMESPACE 602 | 603 | #endif // FMT_COLOR_H_ 604 | -------------------------------------------------------------------------------- /ext/fmt-7.1.3/include/fmt/printf.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - legacy printf implementation 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_PRINTF_H_ 9 | #define FMT_PRINTF_H_ 10 | 11 | #include // std::max 12 | #include // std::numeric_limits 13 | 14 | #include "ostream.h" 15 | 16 | FMT_BEGIN_NAMESPACE 17 | namespace detail { 18 | 19 | // Checks if a value fits in int - used to avoid warnings about comparing 20 | // signed and unsigned integers. 21 | template struct int_checker { 22 | template static bool fits_in_int(T value) { 23 | unsigned max = max_value(); 24 | return value <= max; 25 | } 26 | static bool fits_in_int(bool) { return true; } 27 | }; 28 | 29 | template <> struct int_checker { 30 | template static bool fits_in_int(T value) { 31 | return value >= (std::numeric_limits::min)() && 32 | value <= max_value(); 33 | } 34 | static bool fits_in_int(int) { return true; } 35 | }; 36 | 37 | class printf_precision_handler { 38 | public: 39 | template ::value)> 40 | int operator()(T value) { 41 | if (!int_checker::is_signed>::fits_in_int(value)) 42 | FMT_THROW(format_error("number is too big")); 43 | return (std::max)(static_cast(value), 0); 44 | } 45 | 46 | template ::value)> 47 | int operator()(T) { 48 | FMT_THROW(format_error("precision is not integer")); 49 | return 0; 50 | } 51 | }; 52 | 53 | // An argument visitor that returns true iff arg is a zero integer. 54 | class is_zero_int { 55 | public: 56 | template ::value)> 57 | bool operator()(T value) { 58 | return value == 0; 59 | } 60 | 61 | template ::value)> 62 | bool operator()(T) { 63 | return false; 64 | } 65 | }; 66 | 67 | template struct make_unsigned_or_bool : std::make_unsigned {}; 68 | 69 | template <> struct make_unsigned_or_bool { using type = bool; }; 70 | 71 | template class arg_converter { 72 | private: 73 | using char_type = typename Context::char_type; 74 | 75 | basic_format_arg& arg_; 76 | char_type type_; 77 | 78 | public: 79 | arg_converter(basic_format_arg& arg, char_type type) 80 | : arg_(arg), type_(type) {} 81 | 82 | void operator()(bool value) { 83 | if (type_ != 's') operator()(value); 84 | } 85 | 86 | template ::value)> 87 | void operator()(U value) { 88 | bool is_signed = type_ == 'd' || type_ == 'i'; 89 | using target_type = conditional_t::value, U, T>; 90 | if (const_check(sizeof(target_type) <= sizeof(int))) { 91 | // Extra casts are used to silence warnings. 92 | if (is_signed) { 93 | arg_ = detail::make_arg( 94 | static_cast(static_cast(value))); 95 | } else { 96 | using unsigned_type = typename make_unsigned_or_bool::type; 97 | arg_ = detail::make_arg( 98 | static_cast(static_cast(value))); 99 | } 100 | } else { 101 | if (is_signed) { 102 | // glibc's printf doesn't sign extend arguments of smaller types: 103 | // std::printf("%lld", -42); // prints "4294967254" 104 | // but we don't have to do the same because it's a UB. 105 | arg_ = detail::make_arg(static_cast(value)); 106 | } else { 107 | arg_ = detail::make_arg( 108 | static_cast::type>(value)); 109 | } 110 | } 111 | } 112 | 113 | template ::value)> 114 | void operator()(U) {} // No conversion needed for non-integral types. 115 | }; 116 | 117 | // Converts an integer argument to T for printf, if T is an integral type. 118 | // If T is void, the argument is converted to corresponding signed or unsigned 119 | // type depending on the type specifier: 'd' and 'i' - signed, other - 120 | // unsigned). 121 | template 122 | void convert_arg(basic_format_arg& arg, Char type) { 123 | visit_format_arg(arg_converter(arg, type), arg); 124 | } 125 | 126 | // Converts an integer argument to char for printf. 127 | template class char_converter { 128 | private: 129 | basic_format_arg& arg_; 130 | 131 | public: 132 | explicit char_converter(basic_format_arg& arg) : arg_(arg) {} 133 | 134 | template ::value)> 135 | void operator()(T value) { 136 | arg_ = detail::make_arg( 137 | static_cast(value)); 138 | } 139 | 140 | template ::value)> 141 | void operator()(T) {} // No conversion needed for non-integral types. 142 | }; 143 | 144 | // An argument visitor that return a pointer to a C string if argument is a 145 | // string or null otherwise. 146 | template struct get_cstring { 147 | template const Char* operator()(T) { return nullptr; } 148 | const Char* operator()(const Char* s) { return s; } 149 | }; 150 | 151 | // Checks if an argument is a valid printf width specifier and sets 152 | // left alignment if it is negative. 153 | template class printf_width_handler { 154 | private: 155 | using format_specs = basic_format_specs; 156 | 157 | format_specs& specs_; 158 | 159 | public: 160 | explicit printf_width_handler(format_specs& specs) : specs_(specs) {} 161 | 162 | template ::value)> 163 | unsigned operator()(T value) { 164 | auto width = static_cast>(value); 165 | if (detail::is_negative(value)) { 166 | specs_.align = align::left; 167 | width = 0 - width; 168 | } 169 | unsigned int_max = max_value(); 170 | if (width > int_max) FMT_THROW(format_error("number is too big")); 171 | return static_cast(width); 172 | } 173 | 174 | template ::value)> 175 | unsigned operator()(T) { 176 | FMT_THROW(format_error("width is not integer")); 177 | return 0; 178 | } 179 | }; 180 | 181 | template 182 | void vprintf(buffer& buf, basic_string_view format, 183 | basic_format_args args) { 184 | Context(buffer_appender(buf), format, args).format(); 185 | } 186 | } // namespace detail 187 | 188 | // For printing into memory_buffer. 189 | template 190 | FMT_DEPRECATED void printf(detail::buffer& buf, 191 | basic_string_view format, 192 | basic_format_args args) { 193 | return detail::vprintf(buf, format, args); 194 | } 195 | using detail::vprintf; 196 | 197 | template 198 | class basic_printf_parse_context : public basic_format_parse_context { 199 | using basic_format_parse_context::basic_format_parse_context; 200 | }; 201 | template class basic_printf_context; 202 | 203 | /** 204 | \rst 205 | The ``printf`` argument formatter. 206 | \endrst 207 | */ 208 | template 209 | class printf_arg_formatter : public detail::arg_formatter_base { 210 | public: 211 | using iterator = OutputIt; 212 | 213 | private: 214 | using char_type = Char; 215 | using base = detail::arg_formatter_base; 216 | using context_type = basic_printf_context; 217 | 218 | context_type& context_; 219 | 220 | void write_null_pointer(char) { 221 | this->specs()->type = 0; 222 | this->write("(nil)"); 223 | } 224 | 225 | void write_null_pointer(wchar_t) { 226 | this->specs()->type = 0; 227 | this->write(L"(nil)"); 228 | } 229 | 230 | public: 231 | using format_specs = typename base::format_specs; 232 | 233 | /** 234 | \rst 235 | Constructs an argument formatter object. 236 | *buffer* is a reference to the output buffer and *specs* contains format 237 | specifier information for standard argument types. 238 | \endrst 239 | */ 240 | printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx) 241 | : base(iter, &specs, detail::locale_ref()), context_(ctx) {} 242 | 243 | template ::value)> 244 | iterator operator()(T value) { 245 | // MSVC2013 fails to compile separate overloads for bool and char_type so 246 | // use std::is_same instead. 247 | if (std::is_same::value) { 248 | format_specs& fmt_specs = *this->specs(); 249 | if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0); 250 | fmt_specs.type = 0; 251 | this->write(value != 0); 252 | } else if (std::is_same::value) { 253 | format_specs& fmt_specs = *this->specs(); 254 | if (fmt_specs.type && fmt_specs.type != 'c') 255 | return (*this)(static_cast(value)); 256 | fmt_specs.sign = sign::none; 257 | fmt_specs.alt = false; 258 | fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. 259 | // align::numeric needs to be overwritten here since the '0' flag is 260 | // ignored for non-numeric types 261 | if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) 262 | fmt_specs.align = align::right; 263 | return base::operator()(value); 264 | } else { 265 | return base::operator()(value); 266 | } 267 | return this->out(); 268 | } 269 | 270 | template ::value)> 271 | iterator operator()(T value) { 272 | return base::operator()(value); 273 | } 274 | 275 | /** Formats a null-terminated C string. */ 276 | iterator operator()(const char* value) { 277 | if (value) 278 | base::operator()(value); 279 | else if (this->specs()->type == 'p') 280 | write_null_pointer(char_type()); 281 | else 282 | this->write("(null)"); 283 | return this->out(); 284 | } 285 | 286 | /** Formats a null-terminated wide C string. */ 287 | iterator operator()(const wchar_t* value) { 288 | if (value) 289 | base::operator()(value); 290 | else if (this->specs()->type == 'p') 291 | write_null_pointer(char_type()); 292 | else 293 | this->write(L"(null)"); 294 | return this->out(); 295 | } 296 | 297 | iterator operator()(basic_string_view value) { 298 | return base::operator()(value); 299 | } 300 | 301 | iterator operator()(monostate value) { return base::operator()(value); } 302 | 303 | /** Formats a pointer. */ 304 | iterator operator()(const void* value) { 305 | if (value) return base::operator()(value); 306 | this->specs()->type = 0; 307 | write_null_pointer(char_type()); 308 | return this->out(); 309 | } 310 | 311 | /** Formats an argument of a custom (user-defined) type. */ 312 | iterator operator()(typename basic_format_arg::handle handle) { 313 | handle.format(context_.parse_context(), context_); 314 | return this->out(); 315 | } 316 | }; 317 | 318 | template struct printf_formatter { 319 | printf_formatter() = delete; 320 | 321 | template 322 | auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 323 | return ctx.begin(); 324 | } 325 | 326 | template 327 | auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) { 328 | detail::format_value(detail::get_container(ctx.out()), value); 329 | return ctx.out(); 330 | } 331 | }; 332 | 333 | /** 334 | This template formats data and writes the output through an output iterator. 335 | */ 336 | template class basic_printf_context { 337 | public: 338 | /** The character type for the output. */ 339 | using char_type = Char; 340 | using iterator = OutputIt; 341 | using format_arg = basic_format_arg; 342 | using parse_context_type = basic_printf_parse_context; 343 | template using formatter_type = printf_formatter; 344 | 345 | private: 346 | using format_specs = basic_format_specs; 347 | 348 | OutputIt out_; 349 | basic_format_args args_; 350 | parse_context_type parse_ctx_; 351 | 352 | static void parse_flags(format_specs& specs, const Char*& it, 353 | const Char* end); 354 | 355 | // Returns the argument with specified index or, if arg_index is -1, the next 356 | // argument. 357 | format_arg get_arg(int arg_index = -1); 358 | 359 | // Parses argument index, flags and width and returns the argument index. 360 | int parse_header(const Char*& it, const Char* end, format_specs& specs); 361 | 362 | public: 363 | /** 364 | \rst 365 | Constructs a ``printf_context`` object. References to the arguments are 366 | stored in the context object so make sure they have appropriate lifetimes. 367 | \endrst 368 | */ 369 | basic_printf_context(OutputIt out, basic_string_view format_str, 370 | basic_format_args args) 371 | : out_(out), args_(args), parse_ctx_(format_str) {} 372 | 373 | OutputIt out() { return out_; } 374 | void advance_to(OutputIt it) { out_ = it; } 375 | 376 | detail::locale_ref locale() { return {}; } 377 | 378 | format_arg arg(int id) const { return args_.get(id); } 379 | 380 | parse_context_type& parse_context() { return parse_ctx_; } 381 | 382 | FMT_CONSTEXPR void on_error(const char* message) { 383 | parse_ctx_.on_error(message); 384 | } 385 | 386 | /** Formats stored arguments and writes the output to the range. */ 387 | template > 388 | OutputIt format(); 389 | }; 390 | 391 | template 392 | void basic_printf_context::parse_flags(format_specs& specs, 393 | const Char*& it, 394 | const Char* end) { 395 | for (; it != end; ++it) { 396 | switch (*it) { 397 | case '-': 398 | specs.align = align::left; 399 | break; 400 | case '+': 401 | specs.sign = sign::plus; 402 | break; 403 | case '0': 404 | specs.fill[0] = '0'; 405 | break; 406 | case ' ': 407 | if (specs.sign != sign::plus) { 408 | specs.sign = sign::space; 409 | } 410 | break; 411 | case '#': 412 | specs.alt = true; 413 | break; 414 | default: 415 | return; 416 | } 417 | } 418 | } 419 | 420 | template 421 | typename basic_printf_context::format_arg 422 | basic_printf_context::get_arg(int arg_index) { 423 | if (arg_index < 0) 424 | arg_index = parse_ctx_.next_arg_id(); 425 | else 426 | parse_ctx_.check_arg_id(--arg_index); 427 | return detail::get_arg(*this, arg_index); 428 | } 429 | 430 | template 431 | int basic_printf_context::parse_header(const Char*& it, 432 | const Char* end, 433 | format_specs& specs) { 434 | int arg_index = -1; 435 | char_type c = *it; 436 | if (c >= '0' && c <= '9') { 437 | // Parse an argument index (if followed by '$') or a width possibly 438 | // preceded with '0' flag(s). 439 | detail::error_handler eh; 440 | int value = parse_nonnegative_int(it, end, eh); 441 | if (it != end && *it == '$') { // value is an argument index 442 | ++it; 443 | arg_index = value; 444 | } else { 445 | if (c == '0') specs.fill[0] = '0'; 446 | if (value != 0) { 447 | // Nonzero value means that we parsed width and don't need to 448 | // parse it or flags again, so return now. 449 | specs.width = value; 450 | return arg_index; 451 | } 452 | } 453 | } 454 | parse_flags(specs, it, end); 455 | // Parse width. 456 | if (it != end) { 457 | if (*it >= '0' && *it <= '9') { 458 | detail::error_handler eh; 459 | specs.width = parse_nonnegative_int(it, end, eh); 460 | } else if (*it == '*') { 461 | ++it; 462 | specs.width = static_cast(visit_format_arg( 463 | detail::printf_width_handler(specs), get_arg())); 464 | } 465 | } 466 | return arg_index; 467 | } 468 | 469 | template 470 | template 471 | OutputIt basic_printf_context::format() { 472 | auto out = this->out(); 473 | const Char* start = parse_ctx_.begin(); 474 | const Char* end = parse_ctx_.end(); 475 | auto it = start; 476 | while (it != end) { 477 | char_type c = *it++; 478 | if (c != '%') continue; 479 | if (it != end && *it == c) { 480 | out = std::copy(start, it, out); 481 | start = ++it; 482 | continue; 483 | } 484 | out = std::copy(start, it - 1, out); 485 | 486 | format_specs specs; 487 | specs.align = align::right; 488 | 489 | // Parse argument index, flags and width. 490 | int arg_index = parse_header(it, end, specs); 491 | if (arg_index == 0) on_error("argument not found"); 492 | 493 | // Parse precision. 494 | if (it != end && *it == '.') { 495 | ++it; 496 | c = it != end ? *it : 0; 497 | if ('0' <= c && c <= '9') { 498 | detail::error_handler eh; 499 | specs.precision = parse_nonnegative_int(it, end, eh); 500 | } else if (c == '*') { 501 | ++it; 502 | specs.precision = static_cast( 503 | visit_format_arg(detail::printf_precision_handler(), get_arg())); 504 | } else { 505 | specs.precision = 0; 506 | } 507 | } 508 | 509 | format_arg arg = get_arg(arg_index); 510 | // For d, i, o, u, x, and X conversion specifiers, if a precision is 511 | // specified, the '0' flag is ignored 512 | if (specs.precision >= 0 && arg.is_integral()) 513 | specs.fill[0] = 514 | ' '; // Ignore '0' flag for non-numeric types or if '-' present. 515 | if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { 516 | auto str = visit_format_arg(detail::get_cstring(), arg); 517 | auto str_end = str + specs.precision; 518 | auto nul = std::find(str, str_end, Char()); 519 | arg = detail::make_arg(basic_string_view( 520 | str, 521 | detail::to_unsigned(nul != str_end ? nul - str : specs.precision))); 522 | } 523 | if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) 524 | specs.alt = false; 525 | if (specs.fill[0] == '0') { 526 | if (arg.is_arithmetic() && specs.align != align::left) 527 | specs.align = align::numeric; 528 | else 529 | specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' 530 | // flag is also present. 531 | } 532 | 533 | // Parse length and convert the argument to the required type. 534 | c = it != end ? *it++ : 0; 535 | char_type t = it != end ? *it : 0; 536 | using detail::convert_arg; 537 | switch (c) { 538 | case 'h': 539 | if (t == 'h') { 540 | ++it; 541 | t = it != end ? *it : 0; 542 | convert_arg(arg, t); 543 | } else { 544 | convert_arg(arg, t); 545 | } 546 | break; 547 | case 'l': 548 | if (t == 'l') { 549 | ++it; 550 | t = it != end ? *it : 0; 551 | convert_arg(arg, t); 552 | } else { 553 | convert_arg(arg, t); 554 | } 555 | break; 556 | case 'j': 557 | convert_arg(arg, t); 558 | break; 559 | case 'z': 560 | convert_arg(arg, t); 561 | break; 562 | case 't': 563 | convert_arg(arg, t); 564 | break; 565 | case 'L': 566 | // printf produces garbage when 'L' is omitted for long double, no 567 | // need to do the same. 568 | break; 569 | default: 570 | --it; 571 | convert_arg(arg, c); 572 | } 573 | 574 | // Parse type. 575 | if (it == end) FMT_THROW(format_error("invalid format string")); 576 | specs.type = static_cast(*it++); 577 | if (arg.is_integral()) { 578 | // Normalize type. 579 | switch (specs.type) { 580 | case 'i': 581 | case 'u': 582 | specs.type = 'd'; 583 | break; 584 | case 'c': 585 | visit_format_arg(detail::char_converter(arg), 586 | arg); 587 | break; 588 | } 589 | } 590 | 591 | start = it; 592 | 593 | // Format argument. 594 | out = visit_format_arg(ArgFormatter(out, specs, *this), arg); 595 | } 596 | return std::copy(start, it, out); 597 | } 598 | 599 | template 600 | using basic_printf_context_t = 601 | basic_printf_context, Char>; 602 | 603 | using printf_context = basic_printf_context_t; 604 | using wprintf_context = basic_printf_context_t; 605 | 606 | using printf_args = basic_format_args; 607 | using wprintf_args = basic_format_args; 608 | 609 | /** 610 | \rst 611 | Constructs an `~fmt::format_arg_store` object that contains references to 612 | arguments and can be implicitly converted to `~fmt::printf_args`. 613 | \endrst 614 | */ 615 | template 616 | inline format_arg_store make_printf_args( 617 | const Args&... args) { 618 | return {args...}; 619 | } 620 | 621 | /** 622 | \rst 623 | Constructs an `~fmt::format_arg_store` object that contains references to 624 | arguments and can be implicitly converted to `~fmt::wprintf_args`. 625 | \endrst 626 | */ 627 | template 628 | inline format_arg_store make_wprintf_args( 629 | const Args&... args) { 630 | return {args...}; 631 | } 632 | 633 | template > 634 | inline std::basic_string vsprintf( 635 | const S& format, 636 | basic_format_args>> args) { 637 | basic_memory_buffer buffer; 638 | vprintf(buffer, to_string_view(format), args); 639 | return to_string(buffer); 640 | } 641 | 642 | /** 643 | \rst 644 | Formats arguments and returns the result as a string. 645 | 646 | **Example**:: 647 | 648 | std::string message = fmt::sprintf("The answer is %d", 42); 649 | \endrst 650 | */ 651 | template ::value, char_t>> 653 | inline std::basic_string sprintf(const S& format, const Args&... args) { 654 | using context = basic_printf_context_t; 655 | return vsprintf(to_string_view(format), make_format_args(args...)); 656 | } 657 | 658 | template > 659 | inline int vfprintf( 660 | std::FILE* f, const S& format, 661 | basic_format_args>> args) { 662 | basic_memory_buffer buffer; 663 | vprintf(buffer, to_string_view(format), args); 664 | size_t size = buffer.size(); 665 | return std::fwrite(buffer.data(), sizeof(Char), size, f) < size 666 | ? -1 667 | : static_cast(size); 668 | } 669 | 670 | /** 671 | \rst 672 | Prints formatted data to the file *f*. 673 | 674 | **Example**:: 675 | 676 | fmt::fprintf(stderr, "Don't %s!", "panic"); 677 | \endrst 678 | */ 679 | template ::value, char_t>> 681 | inline int fprintf(std::FILE* f, const S& format, const Args&... args) { 682 | using context = basic_printf_context_t; 683 | return vfprintf(f, to_string_view(format), 684 | make_format_args(args...)); 685 | } 686 | 687 | template > 688 | inline int vprintf( 689 | const S& format, 690 | basic_format_args>> args) { 691 | return vfprintf(stdout, to_string_view(format), args); 692 | } 693 | 694 | /** 695 | \rst 696 | Prints formatted data to ``stdout``. 697 | 698 | **Example**:: 699 | 700 | fmt::printf("Elapsed time: %.2f seconds", 1.23); 701 | \endrst 702 | */ 703 | template ::value)> 705 | inline int printf(const S& format_str, const Args&... args) { 706 | using context = basic_printf_context_t>; 707 | return vprintf(to_string_view(format_str), 708 | make_format_args(args...)); 709 | } 710 | 711 | template > 712 | inline int vfprintf( 713 | std::basic_ostream& os, const S& format, 714 | basic_format_args>> args) { 715 | basic_memory_buffer buffer; 716 | vprintf(buffer, to_string_view(format), args); 717 | detail::write_buffer(os, buffer); 718 | return static_cast(buffer.size()); 719 | } 720 | 721 | /** Formats arguments and writes the output to the range. */ 722 | template > 725 | typename ArgFormatter::iterator vprintf( 726 | detail::buffer& out, basic_string_view format_str, 727 | basic_format_args> args) { 728 | typename ArgFormatter::iterator iter(out); 729 | Context(iter, format_str, args).template format(); 730 | return iter; 731 | } 732 | 733 | /** 734 | \rst 735 | Prints formatted data to the stream *os*. 736 | 737 | **Example**:: 738 | 739 | fmt::fprintf(cerr, "Don't %s!", "panic"); 740 | \endrst 741 | */ 742 | template > 743 | inline int fprintf(std::basic_ostream& os, const S& format_str, 744 | const Args&... args) { 745 | using context = basic_printf_context_t; 746 | return vfprintf(os, to_string_view(format_str), 747 | make_format_args(args...)); 748 | } 749 | FMT_END_NAMESPACE 750 | 751 | #endif // FMT_PRINTF_H_ 752 | -------------------------------------------------------------------------------- /ext/fmt-7.1.3/include/fmt/compile.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental format string compilation 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich and fmt contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_COMPILE_H_ 9 | #define FMT_COMPILE_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | namespace detail { 17 | 18 | // A compile-time string which is compiled into fast formatting code. 19 | class compiled_string {}; 20 | 21 | template 22 | struct is_compiled_string : std::is_base_of {}; 23 | 24 | /** 25 | \rst 26 | Converts a string literal *s* into a format string that will be parsed at 27 | compile time and converted into efficient formatting code. Requires C++17 28 | ``constexpr if`` compiler support. 29 | 30 | **Example**:: 31 | 32 | // Converts 42 into std::string using the most efficient method and no 33 | // runtime format string processing. 34 | std::string s = fmt::format(FMT_COMPILE("{}"), 42); 35 | \endrst 36 | */ 37 | #define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) 38 | 39 | template 40 | const T& first(const T& value, const Tail&...) { 41 | return value; 42 | } 43 | 44 | // Part of a compiled format string. It can be either literal text or a 45 | // replacement field. 46 | template struct format_part { 47 | enum class kind { arg_index, arg_name, text, replacement }; 48 | 49 | struct replacement { 50 | arg_ref arg_id; 51 | dynamic_format_specs specs; 52 | }; 53 | 54 | kind part_kind; 55 | union value { 56 | int arg_index; 57 | basic_string_view str; 58 | replacement repl; 59 | 60 | FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} 61 | FMT_CONSTEXPR value(basic_string_view s) : str(s) {} 62 | FMT_CONSTEXPR value(replacement r) : repl(r) {} 63 | } val; 64 | // Position past the end of the argument id. 65 | const Char* arg_id_end = nullptr; 66 | 67 | FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) 68 | : part_kind(k), val(v) {} 69 | 70 | static FMT_CONSTEXPR format_part make_arg_index(int index) { 71 | return format_part(kind::arg_index, index); 72 | } 73 | static FMT_CONSTEXPR format_part make_arg_name(basic_string_view name) { 74 | return format_part(kind::arg_name, name); 75 | } 76 | static FMT_CONSTEXPR format_part make_text(basic_string_view text) { 77 | return format_part(kind::text, text); 78 | } 79 | static FMT_CONSTEXPR format_part make_replacement(replacement repl) { 80 | return format_part(kind::replacement, repl); 81 | } 82 | }; 83 | 84 | template struct part_counter { 85 | unsigned num_parts = 0; 86 | 87 | FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { 88 | if (begin != end) ++num_parts; 89 | } 90 | 91 | FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; } 92 | FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; } 93 | FMT_CONSTEXPR int on_arg_id(basic_string_view) { 94 | return ++num_parts, 0; 95 | } 96 | 97 | FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} 98 | 99 | FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, 100 | const Char* end) { 101 | // Find the matching brace. 102 | unsigned brace_counter = 0; 103 | for (; begin != end; ++begin) { 104 | if (*begin == '{') { 105 | ++brace_counter; 106 | } else if (*begin == '}') { 107 | if (brace_counter == 0u) break; 108 | --brace_counter; 109 | } 110 | } 111 | return begin; 112 | } 113 | 114 | FMT_CONSTEXPR void on_error(const char*) {} 115 | }; 116 | 117 | // Counts the number of parts in a format string. 118 | template 119 | FMT_CONSTEXPR unsigned count_parts(basic_string_view format_str) { 120 | part_counter counter; 121 | parse_format_string(format_str, counter); 122 | return counter.num_parts; 123 | } 124 | 125 | template 126 | class format_string_compiler : public error_handler { 127 | private: 128 | using part = format_part; 129 | 130 | PartHandler handler_; 131 | part part_; 132 | basic_string_view format_str_; 133 | basic_format_parse_context parse_context_; 134 | 135 | public: 136 | FMT_CONSTEXPR format_string_compiler(basic_string_view format_str, 137 | PartHandler handler) 138 | : handler_(handler), 139 | format_str_(format_str), 140 | parse_context_(format_str) {} 141 | 142 | FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { 143 | if (begin != end) 144 | handler_(part::make_text({begin, to_unsigned(end - begin)})); 145 | } 146 | 147 | FMT_CONSTEXPR int on_arg_id() { 148 | part_ = part::make_arg_index(parse_context_.next_arg_id()); 149 | return 0; 150 | } 151 | 152 | FMT_CONSTEXPR int on_arg_id(int id) { 153 | parse_context_.check_arg_id(id); 154 | part_ = part::make_arg_index(id); 155 | return 0; 156 | } 157 | 158 | FMT_CONSTEXPR int on_arg_id(basic_string_view id) { 159 | part_ = part::make_arg_name(id); 160 | return 0; 161 | } 162 | 163 | FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) { 164 | part_.arg_id_end = ptr; 165 | handler_(part_); 166 | } 167 | 168 | FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, 169 | const Char* end) { 170 | auto repl = typename part::replacement(); 171 | dynamic_specs_handler> handler( 172 | repl.specs, parse_context_); 173 | auto it = parse_format_specs(begin, end, handler); 174 | if (*it != '}') on_error("missing '}' in format string"); 175 | repl.arg_id = part_.part_kind == part::kind::arg_index 176 | ? arg_ref(part_.val.arg_index) 177 | : arg_ref(part_.val.str); 178 | auto part = part::make_replacement(repl); 179 | part.arg_id_end = begin; 180 | handler_(part); 181 | return it; 182 | } 183 | }; 184 | 185 | // Compiles a format string and invokes handler(part) for each parsed part. 186 | template 187 | FMT_CONSTEXPR void compile_format_string(basic_string_view format_str, 188 | PartHandler handler) { 189 | parse_format_string( 190 | format_str, 191 | format_string_compiler(format_str, handler)); 192 | } 193 | 194 | template 195 | void format_arg( 196 | basic_format_parse_context& parse_ctx, 197 | Context& ctx, Id arg_id) { 198 | ctx.advance_to(visit_format_arg( 199 | arg_formatter(ctx, &parse_ctx), 200 | ctx.arg(arg_id))); 201 | } 202 | 203 | // vformat_to is defined in a subnamespace to prevent ADL. 204 | namespace cf { 205 | template 206 | auto vformat_to(OutputIt out, CompiledFormat& cf, 207 | basic_format_args args) -> typename Context::iterator { 208 | using char_type = typename Context::char_type; 209 | basic_format_parse_context parse_ctx( 210 | to_string_view(cf.format_str_)); 211 | Context ctx(out, args); 212 | 213 | const auto& parts = cf.parts(); 214 | for (auto part_it = std::begin(parts); part_it != std::end(parts); 215 | ++part_it) { 216 | const auto& part = *part_it; 217 | const auto& value = part.val; 218 | 219 | using format_part_t = format_part; 220 | switch (part.part_kind) { 221 | case format_part_t::kind::text: { 222 | const auto text = value.str; 223 | auto output = ctx.out(); 224 | auto&& it = reserve(output, text.size()); 225 | it = std::copy_n(text.begin(), text.size(), it); 226 | ctx.advance_to(output); 227 | break; 228 | } 229 | 230 | case format_part_t::kind::arg_index: 231 | advance_to(parse_ctx, part.arg_id_end); 232 | detail::format_arg(parse_ctx, ctx, value.arg_index); 233 | break; 234 | 235 | case format_part_t::kind::arg_name: 236 | advance_to(parse_ctx, part.arg_id_end); 237 | detail::format_arg(parse_ctx, ctx, value.str); 238 | break; 239 | 240 | case format_part_t::kind::replacement: { 241 | const auto& arg_id_value = value.repl.arg_id.val; 242 | const auto arg = value.repl.arg_id.kind == arg_id_kind::index 243 | ? ctx.arg(arg_id_value.index) 244 | : ctx.arg(arg_id_value.name); 245 | 246 | auto specs = value.repl.specs; 247 | 248 | handle_dynamic_spec(specs.width, specs.width_ref, ctx); 249 | handle_dynamic_spec(specs.precision, 250 | specs.precision_ref, ctx); 251 | 252 | error_handler h; 253 | numeric_specs_checker checker(h, arg.type()); 254 | if (specs.align == align::numeric) checker.require_numeric_argument(); 255 | if (specs.sign != sign::none) checker.check_sign(); 256 | if (specs.alt) checker.require_numeric_argument(); 257 | if (specs.precision >= 0) checker.check_precision(); 258 | 259 | advance_to(parse_ctx, part.arg_id_end); 260 | ctx.advance_to( 261 | visit_format_arg(arg_formatter( 262 | ctx, nullptr, &specs), 263 | arg)); 264 | break; 265 | } 266 | } 267 | } 268 | return ctx.out(); 269 | } 270 | } // namespace cf 271 | 272 | struct basic_compiled_format {}; 273 | 274 | template 275 | struct compiled_format_base : basic_compiled_format { 276 | using char_type = char_t; 277 | using parts_container = std::vector>; 278 | 279 | parts_container compiled_parts; 280 | 281 | explicit compiled_format_base(basic_string_view format_str) { 282 | compile_format_string(format_str, 283 | [this](const format_part& part) { 284 | compiled_parts.push_back(part); 285 | }); 286 | } 287 | 288 | const parts_container& parts() const { return compiled_parts; } 289 | }; 290 | 291 | template struct format_part_array { 292 | format_part data[N] = {}; 293 | FMT_CONSTEXPR format_part_array() = default; 294 | }; 295 | 296 | template 297 | FMT_CONSTEXPR format_part_array compile_to_parts( 298 | basic_string_view format_str) { 299 | format_part_array parts; 300 | unsigned counter = 0; 301 | // This is not a lambda for compatibility with older compilers. 302 | struct { 303 | format_part* parts; 304 | unsigned* counter; 305 | FMT_CONSTEXPR void operator()(const format_part& part) { 306 | parts[(*counter)++] = part; 307 | } 308 | } collector{parts.data, &counter}; 309 | compile_format_string(format_str, collector); 310 | if (counter < N) { 311 | parts.data[counter] = 312 | format_part::make_text(basic_string_view()); 313 | } 314 | return parts; 315 | } 316 | 317 | template constexpr const T& constexpr_max(const T& a, const T& b) { 318 | return (a < b) ? b : a; 319 | } 320 | 321 | template 322 | struct compiled_format_base::value>> 323 | : basic_compiled_format { 324 | using char_type = char_t; 325 | 326 | FMT_CONSTEXPR explicit compiled_format_base(basic_string_view) {} 327 | 328 | // Workaround for old compilers. Format string compilation will not be 329 | // performed there anyway. 330 | #if FMT_USE_CONSTEXPR 331 | static FMT_CONSTEXPR_DECL const unsigned num_format_parts = 332 | constexpr_max(count_parts(to_string_view(S())), 1u); 333 | #else 334 | static const unsigned num_format_parts = 1; 335 | #endif 336 | 337 | using parts_container = format_part[num_format_parts]; 338 | 339 | const parts_container& parts() const { 340 | static FMT_CONSTEXPR_DECL const auto compiled_parts = 341 | compile_to_parts( 342 | detail::to_string_view(S())); 343 | return compiled_parts.data; 344 | } 345 | }; 346 | 347 | template 348 | class compiled_format : private compiled_format_base { 349 | public: 350 | using typename compiled_format_base::char_type; 351 | 352 | private: 353 | basic_string_view format_str_; 354 | 355 | template 356 | friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf, 357 | basic_format_args args) -> 358 | typename Context::iterator; 359 | 360 | public: 361 | compiled_format() = delete; 362 | explicit constexpr compiled_format(basic_string_view format_str) 363 | : compiled_format_base(format_str), format_str_(format_str) {} 364 | }; 365 | 366 | #ifdef __cpp_if_constexpr 367 | template struct type_list {}; 368 | 369 | // Returns a reference to the argument at index N from [first, rest...]. 370 | template 371 | constexpr const auto& get([[maybe_unused]] const T& first, 372 | [[maybe_unused]] const Args&... rest) { 373 | static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); 374 | if constexpr (N == 0) 375 | return first; 376 | else 377 | return get(rest...); 378 | } 379 | 380 | template struct get_type_impl; 381 | 382 | template struct get_type_impl> { 383 | using type = remove_cvref_t(std::declval()...))>; 384 | }; 385 | 386 | template 387 | using get_type = typename get_type_impl::type; 388 | 389 | template struct is_compiled_format : std::false_type {}; 390 | 391 | template struct text { 392 | basic_string_view data; 393 | using char_type = Char; 394 | 395 | template 396 | OutputIt format(OutputIt out, const Args&...) const { 397 | return write(out, data); 398 | } 399 | }; 400 | 401 | template 402 | struct is_compiled_format> : std::true_type {}; 403 | 404 | template 405 | constexpr text make_text(basic_string_view s, size_t pos, 406 | size_t size) { 407 | return {{&s[pos], size}}; 408 | } 409 | 410 | template struct code_unit { 411 | Char value; 412 | using char_type = Char; 413 | 414 | template 415 | OutputIt format(OutputIt out, const Args&...) const { 416 | return write(out, value); 417 | } 418 | }; 419 | 420 | template 421 | struct is_compiled_format> : std::true_type {}; 422 | 423 | // A replacement field that refers to argument N. 424 | template struct field { 425 | using char_type = Char; 426 | 427 | template 428 | OutputIt format(OutputIt out, const Args&... args) const { 429 | // This ensures that the argument type is convertile to `const T&`. 430 | const T& arg = get(args...); 431 | return write(out, arg); 432 | } 433 | }; 434 | 435 | template 436 | struct is_compiled_format> : std::true_type {}; 437 | 438 | // A replacement field that refers to argument N and has format specifiers. 439 | template struct spec_field { 440 | using char_type = Char; 441 | mutable formatter fmt; 442 | 443 | template 444 | OutputIt format(OutputIt out, const Args&... args) const { 445 | // This ensures that the argument type is convertile to `const T&`. 446 | const T& arg = get(args...); 447 | const auto& vargs = 448 | make_format_args>(args...); 449 | basic_format_context ctx(out, vargs); 450 | return fmt.format(arg, ctx); 451 | } 452 | }; 453 | 454 | template 455 | struct is_compiled_format> : std::true_type {}; 456 | 457 | template struct concat { 458 | L lhs; 459 | R rhs; 460 | using char_type = typename L::char_type; 461 | 462 | template 463 | OutputIt format(OutputIt out, const Args&... args) const { 464 | out = lhs.format(out, args...); 465 | return rhs.format(out, args...); 466 | } 467 | }; 468 | 469 | template 470 | struct is_compiled_format> : std::true_type {}; 471 | 472 | template 473 | constexpr concat make_concat(L lhs, R rhs) { 474 | return {lhs, rhs}; 475 | } 476 | 477 | struct unknown_format {}; 478 | 479 | template 480 | constexpr size_t parse_text(basic_string_view str, size_t pos) { 481 | for (size_t size = str.size(); pos != size; ++pos) { 482 | if (str[pos] == '{' || str[pos] == '}') break; 483 | } 484 | return pos; 485 | } 486 | 487 | template 488 | constexpr auto compile_format_string(S format_str); 489 | 490 | template 491 | constexpr auto parse_tail(T head, S format_str) { 492 | if constexpr (POS != 493 | basic_string_view(format_str).size()) { 494 | constexpr auto tail = compile_format_string(format_str); 495 | if constexpr (std::is_same, 496 | unknown_format>()) 497 | return tail; 498 | else 499 | return make_concat(head, tail); 500 | } else { 501 | return head; 502 | } 503 | } 504 | 505 | template struct parse_specs_result { 506 | formatter fmt; 507 | size_t end; 508 | int next_arg_id; 509 | }; 510 | 511 | template 512 | constexpr parse_specs_result parse_specs(basic_string_view str, 513 | size_t pos, int arg_id) { 514 | str.remove_prefix(pos); 515 | auto ctx = basic_format_parse_context(str, {}, arg_id + 1); 516 | auto f = formatter(); 517 | auto end = f.parse(ctx); 518 | return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()}; 519 | } 520 | 521 | // Compiles a non-empty format string and returns the compiled representation 522 | // or unknown_format() on unrecognized input. 523 | template 524 | constexpr auto compile_format_string(S format_str) { 525 | using char_type = typename S::char_type; 526 | constexpr basic_string_view str = format_str; 527 | if constexpr (str[POS] == '{') { 528 | if (POS + 1 == str.size()) 529 | throw format_error("unmatched '{' in format string"); 530 | if constexpr (str[POS + 1] == '{') { 531 | return parse_tail(make_text(str, POS, 1), format_str); 532 | } else if constexpr (str[POS + 1] == '}') { 533 | using type = get_type; 534 | return parse_tail(field(), 535 | format_str); 536 | } else if constexpr (str[POS + 1] == ':') { 537 | using type = get_type; 538 | constexpr auto result = parse_specs(str, POS + 2, ID); 539 | return parse_tail( 540 | spec_field{result.fmt}, format_str); 541 | } else { 542 | return unknown_format(); 543 | } 544 | } else if constexpr (str[POS] == '}') { 545 | if (POS + 1 == str.size()) 546 | throw format_error("unmatched '}' in format string"); 547 | return parse_tail(make_text(str, POS, 1), format_str); 548 | } else { 549 | constexpr auto end = parse_text(str, POS + 1); 550 | if constexpr (end - POS > 1) { 551 | return parse_tail(make_text(str, POS, end - POS), 552 | format_str); 553 | } else { 554 | return parse_tail(code_unit{str[POS]}, 555 | format_str); 556 | } 557 | } 558 | } 559 | 560 | template ::value || 562 | detail::is_compiled_string::value)> 563 | constexpr auto compile(S format_str) { 564 | constexpr basic_string_view str = format_str; 565 | if constexpr (str.size() == 0) { 566 | return detail::make_text(str, 0, 0); 567 | } else { 568 | constexpr auto result = 569 | detail::compile_format_string, 0, 0>( 570 | format_str); 571 | if constexpr (std::is_same, 572 | detail::unknown_format>()) { 573 | return detail::compiled_format(to_string_view(format_str)); 574 | } else { 575 | return result; 576 | } 577 | } 578 | } 579 | #else 580 | template ::value)> 582 | constexpr auto compile(S format_str) -> detail::compiled_format { 583 | return detail::compiled_format(to_string_view(format_str)); 584 | } 585 | #endif // __cpp_if_constexpr 586 | 587 | // Compiles the format string which must be a string literal. 588 | template 589 | auto compile(const Char (&format_str)[N]) 590 | -> detail::compiled_format { 591 | return detail::compiled_format( 592 | basic_string_view(format_str, N - 1)); 593 | } 594 | } // namespace detail 595 | 596 | // DEPRECATED! use FMT_COMPILE instead. 597 | template 598 | FMT_DEPRECATED auto compile(const Args&... args) 599 | -> decltype(detail::compile(args...)) { 600 | return detail::compile(args...); 601 | } 602 | 603 | #if FMT_USE_CONSTEXPR 604 | # ifdef __cpp_if_constexpr 605 | 606 | template ::value)> 609 | FMT_INLINE std::basic_string format(const CompiledFormat& cf, 610 | const Args&... args) { 611 | basic_memory_buffer buffer; 612 | cf.format(detail::buffer_appender(buffer), args...); 613 | return to_string(buffer); 614 | } 615 | 616 | template ::value)> 618 | OutputIt format_to(OutputIt out, const CompiledFormat& cf, 619 | const Args&... args) { 620 | return cf.format(out, args...); 621 | } 622 | # endif // __cpp_if_constexpr 623 | #endif // FMT_USE_CONSTEXPR 624 | 625 | template ::value)> 629 | std::basic_string format(const CompiledFormat& cf, const Args&... args) { 630 | basic_memory_buffer buffer; 631 | using context = buffer_context; 632 | detail::cf::vformat_to(detail::buffer_appender(buffer), cf, 633 | make_format_args(args...)); 634 | return to_string(buffer); 635 | } 636 | 637 | template ::value)> 639 | FMT_INLINE std::basic_string format(const S&, 640 | Args&&... args) { 641 | #ifdef __cpp_if_constexpr 642 | if constexpr (std::is_same::value) { 643 | constexpr basic_string_view str = S(); 644 | if (str.size() == 2 && str[0] == '{' && str[1] == '}') 645 | return fmt::to_string(detail::first(args...)); 646 | } 647 | #endif 648 | constexpr auto compiled = detail::compile(S()); 649 | return format(compiled, std::forward(args)...); 650 | } 651 | 652 | template ::value)> 655 | OutputIt format_to(OutputIt out, const CompiledFormat& cf, 656 | const Args&... args) { 657 | using char_type = typename CompiledFormat::char_type; 658 | using context = format_context_t; 659 | return detail::cf::vformat_to(out, cf, 660 | make_format_args(args...)); 661 | } 662 | 663 | template ::value)> 665 | OutputIt format_to(OutputIt out, const S&, const Args&... args) { 666 | constexpr auto compiled = detail::compile(S()); 667 | return format_to(out, compiled, args...); 668 | } 669 | 670 | template 671 | auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf, 672 | const Args&... args) -> 673 | typename std::enable_if< 674 | detail::is_output_iterator::value && 676 | std::is_base_of::value, 678 | format_to_n_result>::type { 679 | auto it = 680 | format_to(detail::truncating_iterator(out, n), cf, args...); 681 | return {it.base(), it.count()}; 682 | } 683 | 684 | template ::value)> 686 | format_to_n_result format_to_n(OutputIt out, size_t n, const S&, 687 | const Args&... args) { 688 | constexpr auto compiled = detail::compile(S()); 689 | auto it = format_to(detail::truncating_iterator(out, n), compiled, 690 | args...); 691 | return {it.base(), it.count()}; 692 | } 693 | 694 | template 695 | size_t formatted_size(const CompiledFormat& cf, const Args&... args) { 696 | return format_to(detail::counting_iterator(), cf, args...).count(); 697 | } 698 | 699 | FMT_END_NAMESPACE 700 | 701 | #endif // FMT_COMPILE_H_ 702 | -------------------------------------------------------------------------------- /ext/fmt-7.1.3/include/fmt/chrono.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - chrono support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_CHRONO_H_ 9 | #define FMT_CHRONO_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "format.h" 17 | #include "locale.h" 18 | 19 | FMT_BEGIN_NAMESPACE 20 | 21 | // Enable safe chrono durations, unless explicitly disabled. 22 | #ifndef FMT_SAFE_DURATION_CAST 23 | # define FMT_SAFE_DURATION_CAST 1 24 | #endif 25 | #if FMT_SAFE_DURATION_CAST 26 | 27 | // For conversion between std::chrono::durations without undefined 28 | // behaviour or erroneous results. 29 | // This is a stripped down version of duration_cast, for inclusion in fmt. 30 | // See https://github.com/pauldreik/safe_duration_cast 31 | // 32 | // Copyright Paul Dreik 2019 33 | namespace safe_duration_cast { 34 | 35 | template ::value && 37 | std::numeric_limits::is_signed == 38 | std::numeric_limits::is_signed)> 39 | FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { 40 | ec = 0; 41 | using F = std::numeric_limits; 42 | using T = std::numeric_limits; 43 | static_assert(F::is_integer, "From must be integral"); 44 | static_assert(T::is_integer, "To must be integral"); 45 | 46 | // A and B are both signed, or both unsigned. 47 | if (F::digits <= T::digits) { 48 | // From fits in To without any problem. 49 | } else { 50 | // From does not always fit in To, resort to a dynamic check. 51 | if (from < (T::min)() || from > (T::max)()) { 52 | // outside range. 53 | ec = 1; 54 | return {}; 55 | } 56 | } 57 | return static_cast(from); 58 | } 59 | 60 | /** 61 | * converts From to To, without loss. If the dynamic value of from 62 | * can't be converted to To without loss, ec is set. 63 | */ 64 | template ::value && 66 | std::numeric_limits::is_signed != 67 | std::numeric_limits::is_signed)> 68 | FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { 69 | ec = 0; 70 | using F = std::numeric_limits; 71 | using T = std::numeric_limits; 72 | static_assert(F::is_integer, "From must be integral"); 73 | static_assert(T::is_integer, "To must be integral"); 74 | 75 | if (detail::const_check(F::is_signed && !T::is_signed)) { 76 | // From may be negative, not allowed! 77 | if (fmt::detail::is_negative(from)) { 78 | ec = 1; 79 | return {}; 80 | } 81 | // From is positive. Can it always fit in To? 82 | if (F::digits > T::digits && 83 | from > static_cast(detail::max_value())) { 84 | ec = 1; 85 | return {}; 86 | } 87 | } 88 | 89 | if (!F::is_signed && T::is_signed && F::digits >= T::digits && 90 | from > static_cast(detail::max_value())) { 91 | ec = 1; 92 | return {}; 93 | } 94 | return static_cast(from); // Lossless conversion. 95 | } 96 | 97 | template ::value)> 99 | FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { 100 | ec = 0; 101 | return from; 102 | } // function 103 | 104 | // clang-format off 105 | /** 106 | * converts From to To if possible, otherwise ec is set. 107 | * 108 | * input | output 109 | * ---------------------------------|--------------- 110 | * NaN | NaN 111 | * Inf | Inf 112 | * normal, fits in output | converted (possibly lossy) 113 | * normal, does not fit in output | ec is set 114 | * subnormal | best effort 115 | * -Inf | -Inf 116 | */ 117 | // clang-format on 118 | template ::value)> 120 | FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { 121 | ec = 0; 122 | using T = std::numeric_limits; 123 | static_assert(std::is_floating_point::value, "From must be floating"); 124 | static_assert(std::is_floating_point::value, "To must be floating"); 125 | 126 | // catch the only happy case 127 | if (std::isfinite(from)) { 128 | if (from >= T::lowest() && from <= (T::max)()) { 129 | return static_cast(from); 130 | } 131 | // not within range. 132 | ec = 1; 133 | return {}; 134 | } 135 | 136 | // nan and inf will be preserved 137 | return static_cast(from); 138 | } // function 139 | 140 | template ::value)> 142 | FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { 143 | ec = 0; 144 | static_assert(std::is_floating_point::value, "From must be floating"); 145 | return from; 146 | } 147 | 148 | /** 149 | * safe duration cast between integral durations 150 | */ 151 | template ::value), 153 | FMT_ENABLE_IF(std::is_integral::value)> 154 | To safe_duration_cast(std::chrono::duration from, 155 | int& ec) { 156 | using From = std::chrono::duration; 157 | ec = 0; 158 | // the basic idea is that we need to convert from count() in the from type 159 | // to count() in the To type, by multiplying it with this: 160 | struct Factor 161 | : std::ratio_divide {}; 162 | 163 | static_assert(Factor::num > 0, "num must be positive"); 164 | static_assert(Factor::den > 0, "den must be positive"); 165 | 166 | // the conversion is like this: multiply from.count() with Factor::num 167 | // /Factor::den and convert it to To::rep, all this without 168 | // overflow/underflow. let's start by finding a suitable type that can hold 169 | // both To, From and Factor::num 170 | using IntermediateRep = 171 | typename std::common_type::type; 173 | 174 | // safe conversion to IntermediateRep 175 | IntermediateRep count = 176 | lossless_integral_conversion(from.count(), ec); 177 | if (ec) return {}; 178 | // multiply with Factor::num without overflow or underflow 179 | if (detail::const_check(Factor::num != 1)) { 180 | const auto max1 = detail::max_value() / Factor::num; 181 | if (count > max1) { 182 | ec = 1; 183 | return {}; 184 | } 185 | const auto min1 = 186 | (std::numeric_limits::min)() / Factor::num; 187 | if (count < min1) { 188 | ec = 1; 189 | return {}; 190 | } 191 | count *= Factor::num; 192 | } 193 | 194 | if (detail::const_check(Factor::den != 1)) count /= Factor::den; 195 | auto tocount = lossless_integral_conversion(count, ec); 196 | return ec ? To() : To(tocount); 197 | } 198 | 199 | /** 200 | * safe duration_cast between floating point durations 201 | */ 202 | template ::value), 204 | FMT_ENABLE_IF(std::is_floating_point::value)> 205 | To safe_duration_cast(std::chrono::duration from, 206 | int& ec) { 207 | using From = std::chrono::duration; 208 | ec = 0; 209 | if (std::isnan(from.count())) { 210 | // nan in, gives nan out. easy. 211 | return To{std::numeric_limits::quiet_NaN()}; 212 | } 213 | // maybe we should also check if from is denormal, and decide what to do about 214 | // it. 215 | 216 | // +-inf should be preserved. 217 | if (std::isinf(from.count())) { 218 | return To{from.count()}; 219 | } 220 | 221 | // the basic idea is that we need to convert from count() in the from type 222 | // to count() in the To type, by multiplying it with this: 223 | struct Factor 224 | : std::ratio_divide {}; 225 | 226 | static_assert(Factor::num > 0, "num must be positive"); 227 | static_assert(Factor::den > 0, "den must be positive"); 228 | 229 | // the conversion is like this: multiply from.count() with Factor::num 230 | // /Factor::den and convert it to To::rep, all this without 231 | // overflow/underflow. let's start by finding a suitable type that can hold 232 | // both To, From and Factor::num 233 | using IntermediateRep = 234 | typename std::common_type::type; 236 | 237 | // force conversion of From::rep -> IntermediateRep to be safe, 238 | // even if it will never happen be narrowing in this context. 239 | IntermediateRep count = 240 | safe_float_conversion(from.count(), ec); 241 | if (ec) { 242 | return {}; 243 | } 244 | 245 | // multiply with Factor::num without overflow or underflow 246 | if (Factor::num != 1) { 247 | constexpr auto max1 = detail::max_value() / 248 | static_cast(Factor::num); 249 | if (count > max1) { 250 | ec = 1; 251 | return {}; 252 | } 253 | constexpr auto min1 = std::numeric_limits::lowest() / 254 | static_cast(Factor::num); 255 | if (count < min1) { 256 | ec = 1; 257 | return {}; 258 | } 259 | count *= static_cast(Factor::num); 260 | } 261 | 262 | // this can't go wrong, right? den>0 is checked earlier. 263 | if (Factor::den != 1) { 264 | using common_t = typename std::common_type::type; 265 | count /= static_cast(Factor::den); 266 | } 267 | 268 | // convert to the to type, safely 269 | using ToRep = typename To::rep; 270 | 271 | const ToRep tocount = safe_float_conversion(count, ec); 272 | if (ec) { 273 | return {}; 274 | } 275 | return To{tocount}; 276 | } 277 | } // namespace safe_duration_cast 278 | #endif 279 | 280 | // Prevents expansion of a preceding token as a function-style macro. 281 | // Usage: f FMT_NOMACRO() 282 | #define FMT_NOMACRO 283 | 284 | namespace detail { 285 | inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } 286 | inline null<> localtime_s(...) { return null<>(); } 287 | inline null<> gmtime_r(...) { return null<>(); } 288 | inline null<> gmtime_s(...) { return null<>(); } 289 | } // namespace detail 290 | 291 | // Thread-safe replacement for std::localtime 292 | inline std::tm localtime(std::time_t time) { 293 | struct dispatcher { 294 | std::time_t time_; 295 | std::tm tm_; 296 | 297 | dispatcher(std::time_t t) : time_(t) {} 298 | 299 | bool run() { 300 | using namespace fmt::detail; 301 | return handle(localtime_r(&time_, &tm_)); 302 | } 303 | 304 | bool handle(std::tm* tm) { return tm != nullptr; } 305 | 306 | bool handle(detail::null<>) { 307 | using namespace fmt::detail; 308 | return fallback(localtime_s(&tm_, &time_)); 309 | } 310 | 311 | bool fallback(int res) { return res == 0; } 312 | 313 | #if !FMT_MSC_VER 314 | bool fallback(detail::null<>) { 315 | using namespace fmt::detail; 316 | std::tm* tm = std::localtime(&time_); 317 | if (tm) tm_ = *tm; 318 | return tm != nullptr; 319 | } 320 | #endif 321 | }; 322 | dispatcher lt(time); 323 | // Too big time values may be unsupported. 324 | if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); 325 | return lt.tm_; 326 | } 327 | 328 | inline std::tm localtime( 329 | std::chrono::time_point time_point) { 330 | return localtime(std::chrono::system_clock::to_time_t(time_point)); 331 | } 332 | 333 | // Thread-safe replacement for std::gmtime 334 | inline std::tm gmtime(std::time_t time) { 335 | struct dispatcher { 336 | std::time_t time_; 337 | std::tm tm_; 338 | 339 | dispatcher(std::time_t t) : time_(t) {} 340 | 341 | bool run() { 342 | using namespace fmt::detail; 343 | return handle(gmtime_r(&time_, &tm_)); 344 | } 345 | 346 | bool handle(std::tm* tm) { return tm != nullptr; } 347 | 348 | bool handle(detail::null<>) { 349 | using namespace fmt::detail; 350 | return fallback(gmtime_s(&tm_, &time_)); 351 | } 352 | 353 | bool fallback(int res) { return res == 0; } 354 | 355 | #if !FMT_MSC_VER 356 | bool fallback(detail::null<>) { 357 | std::tm* tm = std::gmtime(&time_); 358 | if (tm) tm_ = *tm; 359 | return tm != nullptr; 360 | } 361 | #endif 362 | }; 363 | dispatcher gt(time); 364 | // Too big time values may be unsupported. 365 | if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); 366 | return gt.tm_; 367 | } 368 | 369 | inline std::tm gmtime( 370 | std::chrono::time_point time_point) { 371 | return gmtime(std::chrono::system_clock::to_time_t(time_point)); 372 | } 373 | 374 | namespace detail { 375 | inline size_t strftime(char* str, size_t count, const char* format, 376 | const std::tm* time) { 377 | return std::strftime(str, count, format, time); 378 | } 379 | 380 | inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format, 381 | const std::tm* time) { 382 | return std::wcsftime(str, count, format, time); 383 | } 384 | } // namespace detail 385 | 386 | template 387 | struct formatter, Char> 388 | : formatter { 389 | template 390 | auto format(std::chrono::time_point val, 391 | FormatContext& ctx) -> decltype(ctx.out()) { 392 | std::tm time = localtime(val); 393 | return formatter::format(time, ctx); 394 | } 395 | }; 396 | 397 | template struct formatter { 398 | template 399 | auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 400 | auto it = ctx.begin(); 401 | if (it != ctx.end() && *it == ':') ++it; 402 | auto end = it; 403 | while (end != ctx.end() && *end != '}') ++end; 404 | tm_format.reserve(detail::to_unsigned(end - it + 1)); 405 | tm_format.append(it, end); 406 | tm_format.push_back('\0'); 407 | return end; 408 | } 409 | 410 | template 411 | auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { 412 | basic_memory_buffer buf; 413 | size_t start = buf.size(); 414 | for (;;) { 415 | size_t size = buf.capacity() - start; 416 | size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm); 417 | if (count != 0) { 418 | buf.resize(start + count); 419 | break; 420 | } 421 | if (size >= tm_format.size() * 256) { 422 | // If the buffer is 256 times larger than the format string, assume 423 | // that `strftime` gives an empty result. There doesn't seem to be a 424 | // better way to distinguish the two cases: 425 | // https://github.com/fmtlib/fmt/issues/367 426 | break; 427 | } 428 | const size_t MIN_GROWTH = 10; 429 | buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); 430 | } 431 | return std::copy(buf.begin(), buf.end(), ctx.out()); 432 | } 433 | 434 | basic_memory_buffer tm_format; 435 | }; 436 | 437 | namespace detail { 438 | template FMT_CONSTEXPR const char* get_units() { 439 | return nullptr; 440 | } 441 | template <> FMT_CONSTEXPR const char* get_units() { return "as"; } 442 | template <> FMT_CONSTEXPR const char* get_units() { return "fs"; } 443 | template <> FMT_CONSTEXPR const char* get_units() { return "ps"; } 444 | template <> FMT_CONSTEXPR const char* get_units() { return "ns"; } 445 | template <> FMT_CONSTEXPR const char* get_units() { return "µs"; } 446 | template <> FMT_CONSTEXPR const char* get_units() { return "ms"; } 447 | template <> FMT_CONSTEXPR const char* get_units() { return "cs"; } 448 | template <> FMT_CONSTEXPR const char* get_units() { return "ds"; } 449 | template <> FMT_CONSTEXPR const char* get_units>() { return "s"; } 450 | template <> FMT_CONSTEXPR const char* get_units() { return "das"; } 451 | template <> FMT_CONSTEXPR const char* get_units() { return "hs"; } 452 | template <> FMT_CONSTEXPR const char* get_units() { return "ks"; } 453 | template <> FMT_CONSTEXPR const char* get_units() { return "Ms"; } 454 | template <> FMT_CONSTEXPR const char* get_units() { return "Gs"; } 455 | template <> FMT_CONSTEXPR const char* get_units() { return "Ts"; } 456 | template <> FMT_CONSTEXPR const char* get_units() { return "Ps"; } 457 | template <> FMT_CONSTEXPR const char* get_units() { return "Es"; } 458 | template <> FMT_CONSTEXPR const char* get_units>() { 459 | return "m"; 460 | } 461 | template <> FMT_CONSTEXPR const char* get_units>() { 462 | return "h"; 463 | } 464 | 465 | enum class numeric_system { 466 | standard, 467 | // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. 468 | alternative 469 | }; 470 | 471 | // Parses a put_time-like format string and invokes handler actions. 472 | template 473 | FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, 474 | const Char* end, 475 | Handler&& handler) { 476 | auto ptr = begin; 477 | while (ptr != end) { 478 | auto c = *ptr; 479 | if (c == '}') break; 480 | if (c != '%') { 481 | ++ptr; 482 | continue; 483 | } 484 | if (begin != ptr) handler.on_text(begin, ptr); 485 | ++ptr; // consume '%' 486 | if (ptr == end) FMT_THROW(format_error("invalid format")); 487 | c = *ptr++; 488 | switch (c) { 489 | case '%': 490 | handler.on_text(ptr - 1, ptr); 491 | break; 492 | case 'n': { 493 | const Char newline[] = {'\n'}; 494 | handler.on_text(newline, newline + 1); 495 | break; 496 | } 497 | case 't': { 498 | const Char tab[] = {'\t'}; 499 | handler.on_text(tab, tab + 1); 500 | break; 501 | } 502 | // Day of the week: 503 | case 'a': 504 | handler.on_abbr_weekday(); 505 | break; 506 | case 'A': 507 | handler.on_full_weekday(); 508 | break; 509 | case 'w': 510 | handler.on_dec0_weekday(numeric_system::standard); 511 | break; 512 | case 'u': 513 | handler.on_dec1_weekday(numeric_system::standard); 514 | break; 515 | // Month: 516 | case 'b': 517 | handler.on_abbr_month(); 518 | break; 519 | case 'B': 520 | handler.on_full_month(); 521 | break; 522 | // Hour, minute, second: 523 | case 'H': 524 | handler.on_24_hour(numeric_system::standard); 525 | break; 526 | case 'I': 527 | handler.on_12_hour(numeric_system::standard); 528 | break; 529 | case 'M': 530 | handler.on_minute(numeric_system::standard); 531 | break; 532 | case 'S': 533 | handler.on_second(numeric_system::standard); 534 | break; 535 | // Other: 536 | case 'c': 537 | handler.on_datetime(numeric_system::standard); 538 | break; 539 | case 'x': 540 | handler.on_loc_date(numeric_system::standard); 541 | break; 542 | case 'X': 543 | handler.on_loc_time(numeric_system::standard); 544 | break; 545 | case 'D': 546 | handler.on_us_date(); 547 | break; 548 | case 'F': 549 | handler.on_iso_date(); 550 | break; 551 | case 'r': 552 | handler.on_12_hour_time(); 553 | break; 554 | case 'R': 555 | handler.on_24_hour_time(); 556 | break; 557 | case 'T': 558 | handler.on_iso_time(); 559 | break; 560 | case 'p': 561 | handler.on_am_pm(); 562 | break; 563 | case 'Q': 564 | handler.on_duration_value(); 565 | break; 566 | case 'q': 567 | handler.on_duration_unit(); 568 | break; 569 | case 'z': 570 | handler.on_utc_offset(); 571 | break; 572 | case 'Z': 573 | handler.on_tz_name(); 574 | break; 575 | // Alternative representation: 576 | case 'E': { 577 | if (ptr == end) FMT_THROW(format_error("invalid format")); 578 | c = *ptr++; 579 | switch (c) { 580 | case 'c': 581 | handler.on_datetime(numeric_system::alternative); 582 | break; 583 | case 'x': 584 | handler.on_loc_date(numeric_system::alternative); 585 | break; 586 | case 'X': 587 | handler.on_loc_time(numeric_system::alternative); 588 | break; 589 | default: 590 | FMT_THROW(format_error("invalid format")); 591 | } 592 | break; 593 | } 594 | case 'O': 595 | if (ptr == end) FMT_THROW(format_error("invalid format")); 596 | c = *ptr++; 597 | switch (c) { 598 | case 'w': 599 | handler.on_dec0_weekday(numeric_system::alternative); 600 | break; 601 | case 'u': 602 | handler.on_dec1_weekday(numeric_system::alternative); 603 | break; 604 | case 'H': 605 | handler.on_24_hour(numeric_system::alternative); 606 | break; 607 | case 'I': 608 | handler.on_12_hour(numeric_system::alternative); 609 | break; 610 | case 'M': 611 | handler.on_minute(numeric_system::alternative); 612 | break; 613 | case 'S': 614 | handler.on_second(numeric_system::alternative); 615 | break; 616 | default: 617 | FMT_THROW(format_error("invalid format")); 618 | } 619 | break; 620 | default: 621 | FMT_THROW(format_error("invalid format")); 622 | } 623 | begin = ptr; 624 | } 625 | if (begin != ptr) handler.on_text(begin, ptr); 626 | return ptr; 627 | } 628 | 629 | struct chrono_format_checker { 630 | FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); } 631 | 632 | template void on_text(const Char*, const Char*) {} 633 | FMT_NORETURN void on_abbr_weekday() { report_no_date(); } 634 | FMT_NORETURN void on_full_weekday() { report_no_date(); } 635 | FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); } 636 | FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); } 637 | FMT_NORETURN void on_abbr_month() { report_no_date(); } 638 | FMT_NORETURN void on_full_month() { report_no_date(); } 639 | void on_24_hour(numeric_system) {} 640 | void on_12_hour(numeric_system) {} 641 | void on_minute(numeric_system) {} 642 | void on_second(numeric_system) {} 643 | FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); } 644 | FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); } 645 | FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); } 646 | FMT_NORETURN void on_us_date() { report_no_date(); } 647 | FMT_NORETURN void on_iso_date() { report_no_date(); } 648 | void on_12_hour_time() {} 649 | void on_24_hour_time() {} 650 | void on_iso_time() {} 651 | void on_am_pm() {} 652 | void on_duration_value() {} 653 | void on_duration_unit() {} 654 | FMT_NORETURN void on_utc_offset() { report_no_date(); } 655 | FMT_NORETURN void on_tz_name() { report_no_date(); } 656 | }; 657 | 658 | template ::value)> 659 | inline bool isnan(T) { 660 | return false; 661 | } 662 | template ::value)> 663 | inline bool isnan(T value) { 664 | return std::isnan(value); 665 | } 666 | 667 | template ::value)> 668 | inline bool isfinite(T) { 669 | return true; 670 | } 671 | template ::value)> 672 | inline bool isfinite(T value) { 673 | return std::isfinite(value); 674 | } 675 | 676 | // Converts value to int and checks that it's in the range [0, upper). 677 | template ::value)> 678 | inline int to_nonnegative_int(T value, int upper) { 679 | FMT_ASSERT(value >= 0 && value <= upper, "invalid value"); 680 | (void)upper; 681 | return static_cast(value); 682 | } 683 | template ::value)> 684 | inline int to_nonnegative_int(T value, int upper) { 685 | FMT_ASSERT( 686 | std::isnan(value) || (value >= 0 && value <= static_cast(upper)), 687 | "invalid value"); 688 | (void)upper; 689 | return static_cast(value); 690 | } 691 | 692 | template ::value)> 693 | inline T mod(T x, int y) { 694 | return x % static_cast(y); 695 | } 696 | template ::value)> 697 | inline T mod(T x, int y) { 698 | return std::fmod(x, static_cast(y)); 699 | } 700 | 701 | // If T is an integral type, maps T to its unsigned counterpart, otherwise 702 | // leaves it unchanged (unlike std::make_unsigned). 703 | template ::value> 704 | struct make_unsigned_or_unchanged { 705 | using type = T; 706 | }; 707 | 708 | template struct make_unsigned_or_unchanged { 709 | using type = typename std::make_unsigned::type; 710 | }; 711 | 712 | #if FMT_SAFE_DURATION_CAST 713 | // throwing version of safe_duration_cast 714 | template 715 | To fmt_safe_duration_cast(std::chrono::duration from) { 716 | int ec; 717 | To to = safe_duration_cast::safe_duration_cast(from, ec); 718 | if (ec) FMT_THROW(format_error("cannot format duration")); 719 | return to; 720 | } 721 | #endif 722 | 723 | template ::value)> 725 | inline std::chrono::duration get_milliseconds( 726 | std::chrono::duration d) { 727 | // this may overflow and/or the result may not fit in the 728 | // target type. 729 | #if FMT_SAFE_DURATION_CAST 730 | using CommonSecondsType = 731 | typename std::common_type::type; 732 | const auto d_as_common = fmt_safe_duration_cast(d); 733 | const auto d_as_whole_seconds = 734 | fmt_safe_duration_cast(d_as_common); 735 | // this conversion should be nonproblematic 736 | const auto diff = d_as_common - d_as_whole_seconds; 737 | const auto ms = 738 | fmt_safe_duration_cast>(diff); 739 | return ms; 740 | #else 741 | auto s = std::chrono::duration_cast(d); 742 | return std::chrono::duration_cast(d - s); 743 | #endif 744 | } 745 | 746 | template ::value)> 748 | inline std::chrono::duration get_milliseconds( 749 | std::chrono::duration d) { 750 | using common_type = typename std::common_type::type; 751 | auto ms = mod(d.count() * static_cast(Period::num) / 752 | static_cast(Period::den) * 1000, 753 | 1000); 754 | return std::chrono::duration(static_cast(ms)); 755 | } 756 | 757 | template 758 | OutputIt format_duration_value(OutputIt out, Rep val, int precision) { 759 | const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; 760 | if (precision >= 0) return format_to(out, pr_f, val, precision); 761 | const Char fp_f[] = {'{', ':', 'g', '}', 0}; 762 | const Char format[] = {'{', '}', 0}; 763 | return format_to(out, std::is_floating_point::value ? fp_f : format, 764 | val); 765 | } 766 | template 767 | OutputIt copy_unit(string_view unit, OutputIt out, Char) { 768 | return std::copy(unit.begin(), unit.end(), out); 769 | } 770 | 771 | template 772 | OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { 773 | // This works when wchar_t is UTF-32 because units only contain characters 774 | // that have the same representation in UTF-16 and UTF-32. 775 | utf8_to_utf16 u(unit); 776 | return std::copy(u.c_str(), u.c_str() + u.size(), out); 777 | } 778 | 779 | template 780 | OutputIt format_duration_unit(OutputIt out) { 781 | if (const char* unit = get_units()) 782 | return copy_unit(string_view(unit), out, Char()); 783 | const Char num_f[] = {'[', '{', '}', ']', 's', 0}; 784 | if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num); 785 | const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; 786 | return format_to(out, num_def_f, Period::num, Period::den); 787 | } 788 | 789 | template 791 | struct chrono_formatter { 792 | FormatContext& context; 793 | OutputIt out; 794 | int precision; 795 | // rep is unsigned to avoid overflow. 796 | using rep = 797 | conditional_t::value && sizeof(Rep) < sizeof(int), 798 | unsigned, typename make_unsigned_or_unchanged::type>; 799 | rep val; 800 | using seconds = std::chrono::duration; 801 | seconds s; 802 | using milliseconds = std::chrono::duration; 803 | bool negative; 804 | 805 | using char_type = typename FormatContext::char_type; 806 | 807 | explicit chrono_formatter(FormatContext& ctx, OutputIt o, 808 | std::chrono::duration d) 809 | : context(ctx), 810 | out(o), 811 | val(static_cast(d.count())), 812 | negative(false) { 813 | if (d.count() < 0) { 814 | val = 0 - val; 815 | negative = true; 816 | } 817 | 818 | // this may overflow and/or the result may not fit in the 819 | // target type. 820 | #if FMT_SAFE_DURATION_CAST 821 | // might need checked conversion (rep!=Rep) 822 | auto tmpval = std::chrono::duration(val); 823 | s = fmt_safe_duration_cast(tmpval); 824 | #else 825 | s = std::chrono::duration_cast( 826 | std::chrono::duration(val)); 827 | #endif 828 | } 829 | 830 | // returns true if nan or inf, writes to out. 831 | bool handle_nan_inf() { 832 | if (isfinite(val)) { 833 | return false; 834 | } 835 | if (isnan(val)) { 836 | write_nan(); 837 | return true; 838 | } 839 | // must be +-inf 840 | if (val > 0) { 841 | write_pinf(); 842 | } else { 843 | write_ninf(); 844 | } 845 | return true; 846 | } 847 | 848 | Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } 849 | 850 | Rep hour12() const { 851 | Rep hour = static_cast(mod((s.count() / 3600), 12)); 852 | return hour <= 0 ? 12 : hour; 853 | } 854 | 855 | Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } 856 | Rep second() const { return static_cast(mod(s.count(), 60)); } 857 | 858 | std::tm time() const { 859 | auto time = std::tm(); 860 | time.tm_hour = to_nonnegative_int(hour(), 24); 861 | time.tm_min = to_nonnegative_int(minute(), 60); 862 | time.tm_sec = to_nonnegative_int(second(), 60); 863 | return time; 864 | } 865 | 866 | void write_sign() { 867 | if (negative) { 868 | *out++ = '-'; 869 | negative = false; 870 | } 871 | } 872 | 873 | void write(Rep value, int width) { 874 | write_sign(); 875 | if (isnan(value)) return write_nan(); 876 | uint32_or_64_or_128_t n = 877 | to_unsigned(to_nonnegative_int(value, max_value())); 878 | int num_digits = detail::count_digits(n); 879 | if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); 880 | out = format_decimal(out, n, num_digits).end; 881 | } 882 | 883 | void write_nan() { std::copy_n("nan", 3, out); } 884 | void write_pinf() { std::copy_n("inf", 3, out); } 885 | void write_ninf() { std::copy_n("-inf", 4, out); } 886 | 887 | void format_localized(const tm& time, char format, char modifier = 0) { 888 | if (isnan(val)) return write_nan(); 889 | auto locale = context.locale().template get(); 890 | auto& facet = std::use_facet>(locale); 891 | std::basic_ostringstream os; 892 | os.imbue(locale); 893 | facet.put(os, os, ' ', &time, format, modifier); 894 | auto str = os.str(); 895 | std::copy(str.begin(), str.end(), out); 896 | } 897 | 898 | void on_text(const char_type* begin, const char_type* end) { 899 | std::copy(begin, end, out); 900 | } 901 | 902 | // These are not implemented because durations don't have date information. 903 | void on_abbr_weekday() {} 904 | void on_full_weekday() {} 905 | void on_dec0_weekday(numeric_system) {} 906 | void on_dec1_weekday(numeric_system) {} 907 | void on_abbr_month() {} 908 | void on_full_month() {} 909 | void on_datetime(numeric_system) {} 910 | void on_loc_date(numeric_system) {} 911 | void on_loc_time(numeric_system) {} 912 | void on_us_date() {} 913 | void on_iso_date() {} 914 | void on_utc_offset() {} 915 | void on_tz_name() {} 916 | 917 | void on_24_hour(numeric_system ns) { 918 | if (handle_nan_inf()) return; 919 | 920 | if (ns == numeric_system::standard) return write(hour(), 2); 921 | auto time = tm(); 922 | time.tm_hour = to_nonnegative_int(hour(), 24); 923 | format_localized(time, 'H', 'O'); 924 | } 925 | 926 | void on_12_hour(numeric_system ns) { 927 | if (handle_nan_inf()) return; 928 | 929 | if (ns == numeric_system::standard) return write(hour12(), 2); 930 | auto time = tm(); 931 | time.tm_hour = to_nonnegative_int(hour12(), 12); 932 | format_localized(time, 'I', 'O'); 933 | } 934 | 935 | void on_minute(numeric_system ns) { 936 | if (handle_nan_inf()) return; 937 | 938 | if (ns == numeric_system::standard) return write(minute(), 2); 939 | auto time = tm(); 940 | time.tm_min = to_nonnegative_int(minute(), 60); 941 | format_localized(time, 'M', 'O'); 942 | } 943 | 944 | void on_second(numeric_system ns) { 945 | if (handle_nan_inf()) return; 946 | 947 | if (ns == numeric_system::standard) { 948 | write(second(), 2); 949 | #if FMT_SAFE_DURATION_CAST 950 | // convert rep->Rep 951 | using duration_rep = std::chrono::duration; 952 | using duration_Rep = std::chrono::duration; 953 | auto tmpval = fmt_safe_duration_cast(duration_rep{val}); 954 | #else 955 | auto tmpval = std::chrono::duration(val); 956 | #endif 957 | auto ms = get_milliseconds(tmpval); 958 | if (ms != std::chrono::milliseconds(0)) { 959 | *out++ = '.'; 960 | write(ms.count(), 3); 961 | } 962 | return; 963 | } 964 | auto time = tm(); 965 | time.tm_sec = to_nonnegative_int(second(), 60); 966 | format_localized(time, 'S', 'O'); 967 | } 968 | 969 | void on_12_hour_time() { 970 | if (handle_nan_inf()) return; 971 | format_localized(time(), 'r'); 972 | } 973 | 974 | void on_24_hour_time() { 975 | if (handle_nan_inf()) { 976 | *out++ = ':'; 977 | handle_nan_inf(); 978 | return; 979 | } 980 | 981 | write(hour(), 2); 982 | *out++ = ':'; 983 | write(minute(), 2); 984 | } 985 | 986 | void on_iso_time() { 987 | on_24_hour_time(); 988 | *out++ = ':'; 989 | if (handle_nan_inf()) return; 990 | write(second(), 2); 991 | } 992 | 993 | void on_am_pm() { 994 | if (handle_nan_inf()) return; 995 | format_localized(time(), 'p'); 996 | } 997 | 998 | void on_duration_value() { 999 | if (handle_nan_inf()) return; 1000 | write_sign(); 1001 | out = format_duration_value(out, val, precision); 1002 | } 1003 | 1004 | void on_duration_unit() { 1005 | out = format_duration_unit(out); 1006 | } 1007 | }; 1008 | } // namespace detail 1009 | 1010 | template 1011 | struct formatter, Char> { 1012 | private: 1013 | basic_format_specs specs; 1014 | int precision; 1015 | using arg_ref_type = detail::arg_ref; 1016 | arg_ref_type width_ref; 1017 | arg_ref_type precision_ref; 1018 | mutable basic_string_view format_str; 1019 | using duration = std::chrono::duration; 1020 | 1021 | struct spec_handler { 1022 | formatter& f; 1023 | basic_format_parse_context& context; 1024 | basic_string_view format_str; 1025 | 1026 | template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { 1027 | context.check_arg_id(arg_id); 1028 | return arg_ref_type(arg_id); 1029 | } 1030 | 1031 | FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { 1032 | context.check_arg_id(arg_id); 1033 | return arg_ref_type(arg_id); 1034 | } 1035 | 1036 | FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) { 1037 | return arg_ref_type(context.next_arg_id()); 1038 | } 1039 | 1040 | void on_error(const char* msg) { FMT_THROW(format_error(msg)); } 1041 | void on_fill(basic_string_view fill) { f.specs.fill = fill; } 1042 | void on_align(align_t align) { f.specs.align = align; } 1043 | void on_width(int width) { f.specs.width = width; } 1044 | void on_precision(int _precision) { f.precision = _precision; } 1045 | void end_precision() {} 1046 | 1047 | template void on_dynamic_width(Id arg_id) { 1048 | f.width_ref = make_arg_ref(arg_id); 1049 | } 1050 | 1051 | template void on_dynamic_precision(Id arg_id) { 1052 | f.precision_ref = make_arg_ref(arg_id); 1053 | } 1054 | }; 1055 | 1056 | using iterator = typename basic_format_parse_context::iterator; 1057 | struct parse_range { 1058 | iterator begin; 1059 | iterator end; 1060 | }; 1061 | 1062 | FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { 1063 | auto begin = ctx.begin(), end = ctx.end(); 1064 | if (begin == end || *begin == '}') return {begin, begin}; 1065 | spec_handler handler{*this, ctx, format_str}; 1066 | begin = detail::parse_align(begin, end, handler); 1067 | if (begin == end) return {begin, begin}; 1068 | begin = detail::parse_width(begin, end, handler); 1069 | if (begin == end) return {begin, begin}; 1070 | if (*begin == '.') { 1071 | if (std::is_floating_point::value) 1072 | begin = detail::parse_precision(begin, end, handler); 1073 | else 1074 | handler.on_error("precision not allowed for this argument type"); 1075 | } 1076 | end = parse_chrono_format(begin, end, detail::chrono_format_checker()); 1077 | return {begin, end}; 1078 | } 1079 | 1080 | public: 1081 | formatter() : precision(-1) {} 1082 | 1083 | FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) 1084 | -> decltype(ctx.begin()) { 1085 | auto range = do_parse(ctx); 1086 | format_str = basic_string_view( 1087 | &*range.begin, detail::to_unsigned(range.end - range.begin)); 1088 | return range.end; 1089 | } 1090 | 1091 | template 1092 | auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) { 1093 | auto begin = format_str.begin(), end = format_str.end(); 1094 | // As a possible future optimization, we could avoid extra copying if width 1095 | // is not specified. 1096 | basic_memory_buffer buf; 1097 | auto out = std::back_inserter(buf); 1098 | detail::handle_dynamic_spec(specs.width, width_ref, 1099 | ctx); 1100 | detail::handle_dynamic_spec(precision, 1101 | precision_ref, ctx); 1102 | if (begin == end || *begin == '}') { 1103 | out = detail::format_duration_value(out, d.count(), precision); 1104 | detail::format_duration_unit(out); 1105 | } else { 1106 | detail::chrono_formatter f( 1107 | ctx, out, d); 1108 | f.precision = precision; 1109 | parse_chrono_format(begin, end, f); 1110 | } 1111 | return detail::write( 1112 | ctx.out(), basic_string_view(buf.data(), buf.size()), specs); 1113 | } 1114 | }; 1115 | 1116 | FMT_END_NAMESPACE 1117 | 1118 | #endif // FMT_CHRONO_H_ 1119 | --------------------------------------------------------------------------------