From fd26658558ed4714aa81c7605b36ca75814067ee Mon Sep 17 00:00:00 2001 From: translators Date: Thu, 18 May 2023 18:41:14 +0000 Subject: [PATCH 01/58] Update: Translations from eints russian: 2 changes by Ln-Wolf --- src/lang/russian.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/russian.txt b/src/lang/russian.txt index f509ab640c..2585fa1741 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -1187,12 +1187,12 @@ STR_GAME_OPTIONS_GUI_SCALE_4X :4x STR_GAME_OPTIONS_GUI_SCALE_5X :5x STR_GAME_OPTIONS_PARTICIPATE_SURVEY_FRAME :{BLACK}Автоматический сбор данных -STR_GAME_OPTIONS_PARTICIPATE_SURVEY :{BLACK}Разрешить автоматический сбор данных +STR_GAME_OPTIONS_PARTICIPATE_SURVEY :{BLACK}Разрешить сбор и отправку данных STR_GAME_OPTIONS_PARTICIPATE_SURVEY_TOOLTIP :{BLACK}Разрешить автоматический сбор данных о ваших настройках игры и используемых модулях. OpenTTD отправит эти данные при закрытии игры. STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK :{BLACK}О сборе данных и приватности STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK_TOOLTIP :{BLACK}Открыть ссылку с информацией об автоматическом сборе данных STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW :{BLACK}Просмотр собранных данных -STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :{BLACK}Показать собранные в текущем сеансе данные +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :{BLACK}Показать данные, собранные в текущем сеансе и подготовленные к отправке STR_GAME_OPTIONS_GRAPHICS :{BLACK}Настройки графики From c51a7f629e2d3336522027e03e7e0538c8b61c84 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 18 May 2023 12:19:51 +0100 Subject: [PATCH 02/58] Codechange: GRFParameterInfo no longer needs deep-copy constructor. --- src/newgrf_config.cpp | 20 -------------------- src/newgrf_config.h | 1 - 2 files changed, 21 deletions(-) diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index ed134c1741..3e07f518ed 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -208,26 +208,6 @@ GRFParameterInfo::GRFParameterInfo(uint nr) : complete_labels(false) {} -/** - * Create a new GRFParameterInfo object that is a deep copy of an existing - * parameter info object. - * @param info The GRFParameterInfo object to make a copy of. - */ -GRFParameterInfo::GRFParameterInfo(GRFParameterInfo &info) : - name(info.name), - desc(info.desc), - type(info.type), - min_value(info.min_value), - max_value(info.max_value), - def_value(info.def_value), - param_nr(info.param_nr), - first_bit(info.first_bit), - num_bit(info.num_bit), - value_names(info.value_names), - complete_labels(info.complete_labels) -{ -} - /** * Get the value of this user-changeable parameter from the given config. * @param config The GRFConfig to get the value from. diff --git a/src/newgrf_config.h b/src/newgrf_config.h index 3d345e6d95..be1060d6f6 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -132,7 +132,6 @@ enum GRFParameterType { /** Information about one grf parameter. */ struct GRFParameterInfo { GRFParameterInfo(uint nr); - GRFParameterInfo(GRFParameterInfo &info); GRFTextList name; ///< The name of this parameter GRFTextList desc; ///< The description of this parameter GRFParameterType type; ///< The type of this parameter From f14479d27d7c5a0079df90f051a95fe503d36d88 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 18 May 2023 12:19:51 +0100 Subject: [PATCH 03/58] Codechange: Use std::optional for GRF Parameter list. --- src/newgrf.cpp | 6 +-- src/newgrf_config.cpp | 28 +++----------- src/newgrf_config.h | 3 +- src/newgrf_gui.cpp | 90 +++++++++++++++++++++++++------------------ 4 files changed, 62 insertions(+), 65 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index b93caedb31..2286e34a45 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -8419,10 +8419,10 @@ static bool HandleParameterInfo(ByteReader *buf) if (id >= _cur.grfconfig->param_info.size()) { _cur.grfconfig->param_info.resize(id + 1); } - if (_cur.grfconfig->param_info[id] == nullptr) { - _cur.grfconfig->param_info[id] = new GRFParameterInfo(id); + if (!_cur.grfconfig->param_info[id].has_value()) { + _cur.grfconfig->param_info[id] = GRFParameterInfo(id); } - _cur_parameter = _cur.grfconfig->param_info[id]; + _cur_parameter = &_cur.grfconfig->param_info[id].value(); /* Read all parameter-data and process each node. */ if (!HandleNodes(buf, _tags_parameters)) return false; type = buf->ReadByte(); diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index 3e07f518ed..256de23f9c 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -58,24 +58,12 @@ GRFConfig::GRFConfig(const GRFConfig &config) : num_params(config.num_params), num_valid_params(config.num_valid_params), palette(config.palette), + param_info(config.param_info), has_param_defaults(config.has_param_defaults) { MemCpyT(this->original_md5sum, config.original_md5sum, lengthof(this->original_md5sum)); MemCpyT(this->param, config.param, lengthof(this->param)); if (config.error != nullptr) this->error = std::make_unique(*config.error); - for (uint i = 0; i < config.param_info.size(); i++) { - if (config.param_info[i] == nullptr) { - this->param_info.push_back(nullptr); - } else { - this->param_info.push_back(new GRFParameterInfo(*config.param_info[i])); - } - } -} - -/** Cleanup a GRFConfig object. */ -GRFConfig::~GRFConfig() -{ - for (uint i = 0; i < this->param_info.size(); i++) delete this->param_info[i]; } /** @@ -127,7 +115,7 @@ void GRFConfig::SetParameterDefaults() if (!this->has_param_defaults) return; for (uint i = 0; i < this->param_info.size(); i++) { - if (this->param_info[i] == nullptr) continue; + if (!this->param_info[i]) continue; this->param_info[i]->SetValue(this, this->param_info[i]->def_value); } } @@ -153,8 +141,8 @@ void GRFConfig::SetSuitablePalette() */ void GRFConfig::FinalizeParameterInfo() { - for (GRFParameterInfo *info : this->param_info) { - if (info == nullptr) continue; + for (auto &info : this->param_info) { + if (!info.has_value()) continue; info->Finalize(); } } @@ -527,14 +515,8 @@ compatible_grf: c->version = f->version; c->min_loadable_version = f->min_loadable_version; c->num_valid_params = f->num_valid_params; + c->param_info = f->param_info; c->has_param_defaults = f->has_param_defaults; - for (uint i = 0; i < f->param_info.size(); i++) { - if (f->param_info[i] == nullptr) { - c->param_info.push_back(nullptr); - } else { - c->param_info.push_back(new GRFParameterInfo(*f->param_info[i])); - } - } } } } diff --git a/src/newgrf_config.h b/src/newgrf_config.h index be1060d6f6..4f471c5c98 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -153,7 +153,6 @@ struct GRFParameterInfo { struct GRFConfig : ZeroedMemoryAllocator { GRFConfig(const std::string &filename = std::string{}); GRFConfig(const GRFConfig &config); - ~GRFConfig(); /* Remove the copy assignment, as the default implementation will not do the right thing. */ GRFConfig &operator=(GRFConfig &rhs) = delete; @@ -175,7 +174,7 @@ struct GRFConfig : ZeroedMemoryAllocator { uint8 num_params; ///< Number of used parameters uint8 num_valid_params; ///< NOSAVE: Number of valid parameters (action 0x14) uint8 palette; ///< GRFPalette, bitset - std::vector param_info; ///< NOSAVE: extra information about the parameters + std::vector> param_info; ///< NOSAVE: extra information about the parameters bool has_param_defaults; ///< NOSAVE: did this newgrf specify any defaults for it's parameters struct GRFConfig *next; ///< NOSAVE: Next item in the linked list diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index b11c390970..7fc105100a 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -187,10 +187,31 @@ struct NewGRFParametersWindow : public Window { * @param nr The param number that should be changed. * @return GRFParameterInfo with dummy information about the given parameter. */ - static GRFParameterInfo *GetDummyParameterInfo(uint nr) + static GRFParameterInfo &GetDummyParameterInfo(uint nr) { dummy_parameter_info.param_nr = nr; - return &dummy_parameter_info; + return dummy_parameter_info; + } + + /** + * Test if GRF Parameter Info exists for a given parameter index. + * @param nr The param number that should be tested. + * @return True iff the parameter info exists. + */ + bool HasParameterInfo(uint nr) const + { + return nr < this->grf_config->param_info.size() && this->grf_config->param_info[nr].has_value(); + } + + /** + * Get GRF Parameter Info exists for a given parameter index. + * If the parameter info does not exist, a dummy parameter-info is returned instead. + * @param nr The param number that should be got. + * @return Reference to the GRFParameterInfo. + */ + GRFParameterInfo &GetParameterInfo(uint nr) const + { + return this->HasParameterInfo(nr) ? this->grf_config->param_info[nr].value() : GetDummyParameterInfo(nr); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override @@ -223,9 +244,8 @@ struct NewGRFParametersWindow : public Window { case WID_NP_DESCRIPTION: /* Minimum size of 4 lines. The 500 is the default size of the window. */ Dimension suggestion = {500U - WidgetDimensions::scaled.frametext.Horizontal(), (uint)FONT_HEIGHT_NORMAL * 4 + WidgetDimensions::scaled.frametext.Vertical()}; - for (uint i = 0; i < this->grf_config->param_info.size(); i++) { - const GRFParameterInfo *par_info = this->grf_config->param_info[i]; - if (par_info == nullptr) continue; + for (const auto &par_info : this->grf_config->param_info) { + if (!par_info.has_value()) continue; const char *desc = GetGRFStringFromGRFText(par_info->desc); if (desc == nullptr) continue; Dimension d = GetStringMultiLineBoundingBox(desc, suggestion); @@ -249,9 +269,9 @@ struct NewGRFParametersWindow : public Window { void DrawWidget(const Rect &r, int widget) const override { if (widget == WID_NP_DESCRIPTION) { - const GRFParameterInfo *par_info = (this->clicked_row < this->grf_config->param_info.size()) ? this->grf_config->param_info[this->clicked_row] : nullptr; - if (par_info == nullptr) return; - const char *desc = GetGRFStringFromGRFText(par_info->desc); + if (!this->HasParameterInfo(this->clicked_row)) return; + const GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row); + const char *desc = GetGRFStringFromGRFText(par_info.desc); if (desc == nullptr) return; DrawStringMultiLine(r.Shrink(WidgetDimensions::scaled.framerect), desc, TC_BLACK); return; @@ -267,24 +287,23 @@ struct NewGRFParametersWindow : public Window { int button_y_offset = (this->line_height - SETTING_BUTTON_HEIGHT) / 2; int text_y_offset = (this->line_height - FONT_HEIGHT_NORMAL) / 2; for (uint i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < this->vscroll->GetCount(); i++) { - GRFParameterInfo *par_info = (i < this->grf_config->param_info.size()) ? this->grf_config->param_info[i] : nullptr; - if (par_info == nullptr) par_info = GetDummyParameterInfo(i); - uint32 current_value = par_info->GetValue(this->grf_config); + GRFParameterInfo &par_info = this->GetParameterInfo(i); + uint32 current_value = par_info.GetValue(this->grf_config); bool selected = (i == this->clicked_row); - if (par_info->type == PTYPE_BOOL) { + if (par_info.type == PTYPE_BOOL) { DrawBoolButton(buttons_left, ir.top + button_y_offset, current_value != 0, this->editable); - SetDParam(2, par_info->GetValue(this->grf_config) == 0 ? STR_CONFIG_SETTING_OFF : STR_CONFIG_SETTING_ON); - } else if (par_info->type == PTYPE_UINT_ENUM) { - if (par_info->complete_labels) { + SetDParam(2, par_info.GetValue(this->grf_config) == 0 ? STR_CONFIG_SETTING_OFF : STR_CONFIG_SETTING_ON); + } else if (par_info.type == PTYPE_UINT_ENUM) { + if (par_info.complete_labels) { DrawDropDownButton(buttons_left, ir.top + button_y_offset, COLOUR_YELLOW, this->clicked_row == i && this->clicked_dropdown, this->editable); } else { - DrawArrowButtons(buttons_left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, this->editable && current_value > par_info->min_value, this->editable && current_value < par_info->max_value); + DrawArrowButtons(buttons_left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, this->editable && current_value > par_info.min_value, this->editable && current_value < par_info.max_value); } SetDParam(2, STR_JUST_INT); SetDParam(3, current_value); - auto it = par_info->value_names.find(current_value); - if (it != par_info->value_names.end()) { + auto it = par_info.value_names.find(current_value); + if (it != par_info.value_names.end()) { const char *label = GetGRFStringFromGRFText(it->second); if (label != nullptr) { SetDParam(2, STR_JUST_RAW_STRING); @@ -293,7 +312,7 @@ struct NewGRFParametersWindow : public Window { } } - const char *name = GetGRFStringFromGRFText(par_info->name); + const char *name = GetGRFStringFromGRFText(par_info.name); if (name != nullptr) { SetDParam(0, STR_JUST_RAW_STRING); SetDParamStr(1, name); @@ -354,12 +373,11 @@ struct NewGRFParametersWindow : public Window { int x = pt.x - r.left; if (_current_text_dir == TD_RTL) x = r.Width() - 1 - x; - GRFParameterInfo *par_info = *it; - if (par_info == nullptr) par_info = GetDummyParameterInfo(num); + GRFParameterInfo &par_info = it->has_value() ? it->value() : GetDummyParameterInfo(num); /* One of the arrows is clicked */ - uint32 old_val = par_info->GetValue(this->grf_config); - if (par_info->type != PTYPE_BOOL && IsInsideMM(x, 0, SETTING_BUTTON_WIDTH) && par_info->complete_labels) { + uint32 old_val = par_info.GetValue(this->grf_config); + if (par_info.type != PTYPE_BOOL && IsInsideMM(x, 0, SETTING_BUTTON_WIDTH) && par_info.complete_labels) { if (this->clicked_dropdown) { /* unclick the dropdown */ HideDropDownMenu(this); @@ -380,8 +398,8 @@ struct NewGRFParametersWindow : public Window { this->closing_dropdown = false; DropDownList list; - for (uint32 i = par_info->min_value; i <= par_info->max_value; i++) { - list.emplace_back(new DropDownListCharStringItem(GetGRFStringFromGRFText(par_info->value_names.find(i)->second), i, false)); + for (uint32 i = par_info.min_value; i <= par_info.max_value; i++) { + list.emplace_back(new DropDownListCharStringItem(GetGRFStringFromGRFText(par_info.value_names.find(i)->second), i, false)); } ShowDropDownListAt(this, std::move(list), old_val, -1, wi_rect, COLOUR_ORANGE); @@ -389,26 +407,26 @@ struct NewGRFParametersWindow : public Window { } } else if (IsInsideMM(x, 0, SETTING_BUTTON_WIDTH)) { uint32 val = old_val; - if (par_info->type == PTYPE_BOOL) { + if (par_info.type == PTYPE_BOOL) { val = !val; } else { if (x >= SETTING_BUTTON_WIDTH / 2) { /* Increase button clicked */ - if (val < par_info->max_value) val++; + if (val < par_info.max_value) val++; this->clicked_increase = true; } else { /* Decrease button clicked */ - if (val > par_info->min_value) val--; + if (val > par_info.min_value) val--; this->clicked_increase = false; } } if (val != old_val) { - par_info->SetValue(this->grf_config, val); + par_info.SetValue(this->grf_config, val); this->clicked_button = num; this->unclick_timeout.Reset(); } - } else if (par_info->type == PTYPE_UINT_ENUM && !par_info->complete_labels && click_count >= 2) { + } else if (par_info.type == PTYPE_UINT_ENUM && !par_info.complete_labels && click_count >= 2) { /* Display a query box so users can enter a custom value. */ SetDParam(0, old_val); ShowQueryString(STR_JUST_INT, STR_CONFIG_SETTING_QUERY_CAPTION, 10, this, CS_NUMERAL, QSF_NONE); @@ -434,19 +452,17 @@ struct NewGRFParametersWindow : public Window { { if (StrEmpty(str)) return; int32 value = atoi(str); - GRFParameterInfo *par_info = ((uint)this->clicked_row < this->grf_config->param_info.size()) ? this->grf_config->param_info[this->clicked_row] : nullptr; - if (par_info == nullptr) par_info = GetDummyParameterInfo(this->clicked_row); - uint32 val = Clamp(value, par_info->min_value, par_info->max_value); - par_info->SetValue(this->grf_config, val); + GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row); + uint32 val = Clamp(value, par_info.min_value, par_info.max_value); + par_info.SetValue(this->grf_config, val); this->SetDirty(); } void OnDropdownSelect(int widget, int index) override { assert(this->clicked_dropdown); - GRFParameterInfo *par_info = ((uint)this->clicked_row < this->grf_config->param_info.size()) ? this->grf_config->param_info[this->clicked_row] : nullptr; - if (par_info == nullptr) par_info = GetDummyParameterInfo(this->clicked_row); - par_info->SetValue(this->grf_config, index); + GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row); + par_info.SetValue(this->grf_config, index); this->SetDirty(); } From 7934418133aa924577d352e306af79da357c505f Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 18 May 2023 17:33:04 +0100 Subject: [PATCH 04/58] Codechange: Remove comment alignment from GRFConfig. --- src/newgrf_config.h | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/newgrf_config.h b/src/newgrf_config.h index 4f471c5c98..44435427e6 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -157,27 +157,27 @@ struct GRFConfig : ZeroedMemoryAllocator { /* Remove the copy assignment, as the default implementation will not do the right thing. */ GRFConfig &operator=(GRFConfig &rhs) = delete; - GRFIdentifier ident; ///< grfid and md5sum to uniquely identify newgrfs - uint8 original_md5sum[16]; ///< MD5 checksum of original file if only a 'compatible' file was loaded - std::string filename; ///< Filename - either with or without full path - GRFTextWrapper name; ///< NOSAVE: GRF name (Action 0x08) - GRFTextWrapper info; ///< NOSAVE: GRF info (author, copyright, ...) (Action 0x08) - GRFTextWrapper url; ///< NOSAVE: URL belonging to this GRF. - std::unique_ptr error; ///< NOSAVE: Error/Warning during GRF loading (Action 0x0B) - - uint32 version; ///< NOSAVE: Version a NewGRF can set so only the newest NewGRF is shown - uint32 min_loadable_version; ///< NOSAVE: Minimum compatible version a NewGRF can define - uint8 flags; ///< NOSAVE: GCF_Flags, bitset - GRFStatus status; ///< NOSAVE: GRFStatus, enum - uint32 grf_bugs; ///< NOSAVE: bugs in this GRF in this run, @see enum GRFBugs - uint32 param[0x80]; ///< GRF parameters - uint8 num_params; ///< Number of used parameters - uint8 num_valid_params; ///< NOSAVE: Number of valid parameters (action 0x14) - uint8 palette; ///< GRFPalette, bitset + GRFIdentifier ident; ///< grfid and md5sum to uniquely identify newgrfs + uint8 original_md5sum[16]; ///< MD5 checksum of original file if only a 'compatible' file was loaded + std::string filename; ///< Filename - either with or without full path + GRFTextWrapper name; ///< NOSAVE: GRF name (Action 0x08) + GRFTextWrapper info; ///< NOSAVE: GRF info (author, copyright, ...) (Action 0x08) + GRFTextWrapper url; ///< NOSAVE: URL belonging to this GRF. + std::unique_ptr error; ///< NOSAVE: Error/Warning during GRF loading (Action 0x0B) + + uint32 version; ///< NOSAVE: Version a NewGRF can set so only the newest NewGRF is shown + uint32 min_loadable_version; ///< NOSAVE: Minimum compatible version a NewGRF can define + uint8 flags; ///< NOSAVE: GCF_Flags, bitset + GRFStatus status; ///< NOSAVE: GRFStatus, enum + uint32 grf_bugs; ///< NOSAVE: bugs in this GRF in this run, @see enum GRFBugs + uint32 param[0x80]; ///< GRF parameters + uint8 num_params; ///< Number of used parameters + uint8 num_valid_params; ///< NOSAVE: Number of valid parameters (action 0x14) + uint8 palette; ///< GRFPalette, bitset std::vector> param_info; ///< NOSAVE: extra information about the parameters - bool has_param_defaults; ///< NOSAVE: did this newgrf specify any defaults for it's parameters + bool has_param_defaults; ///< NOSAVE: did this newgrf specify any defaults for it's parameters - struct GRFConfig *next; ///< NOSAVE: Next item in the linked list + struct GRFConfig *next; ///< NOSAVE: Next item in the linked list void CopyParams(const GRFConfig &src); From d9a04ba4461ce8c903aabd6a55b1ba422125641a Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 18 May 2023 22:38:56 +0200 Subject: [PATCH 05/58] Codechange: make the MD5 hash/digest/checksum variables a std::array --- src/3rdparty/md5/md5.cpp | 2 +- src/3rdparty/md5/md5.h | 21 ++++++++++++++++++++- src/base_media_base.h | 3 ++- src/base_media_func.h | 11 ++++------- src/fios.cpp | 7 +++---- src/gamelog.cpp | 14 +++++++------- src/gfxinit.cpp | 4 ++-- src/misc.cpp | 2 +- src/network/core/game_info.cpp | 8 +++----- src/network/core/tcp_content.cpp | 2 +- src/network/core/tcp_content_type.h | 4 +++- src/network/network.cpp | 8 ++------ src/network/network_client.cpp | 4 ++-- src/network/network_content.cpp | 13 ++++++------- src/network/network_gamelist.cpp | 2 +- src/newgrf_config.cpp | 12 ++++++------ src/newgrf_config.h | 16 +++++++--------- src/newgrf_gui.cpp | 10 +++++----- src/script/script_scanner.cpp | 15 ++++++--------- src/settings.cpp | 9 +++++---- src/string.cpp | 4 ++-- src/string_func.h | 3 ++- 22 files changed, 91 insertions(+), 83 deletions(-) diff --git a/src/3rdparty/md5/md5.cpp b/src/3rdparty/md5/md5.cpp index d8e5eeb515..a0c1093b0c 100644 --- a/src/3rdparty/md5/md5.cpp +++ b/src/3rdparty/md5/md5.cpp @@ -297,7 +297,7 @@ void Md5::Append(const void *data, const size_t nbytes) if (left) memcpy(this->buf, p, left); } -void Md5::Finish(uint8 digest[16]) +void Md5::Finish(MD5Hash &digest) { static const uint8 pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/src/3rdparty/md5/md5.h b/src/3rdparty/md5/md5.h index 049c001d8d..199dab6bb8 100644 --- a/src/3rdparty/md5/md5.h +++ b/src/3rdparty/md5/md5.h @@ -53,6 +53,25 @@ #ifndef MD5_INCLUDED #define MD5_INCLUDED +/** The number of bytes in a MD5 hash. */ +static const size_t MD5_HASH_BYTES = 16; + +/** Container for storing a MD5 hash/checksum/digest. */ +using MD5Hash = std::array; + +/** + * Exclusively-or one hash into another hash. + * @param lhs The hash to exclusively-or into. + * @param rhs The hash to exclusively-or with. + * @return Reference to \c lhs hash. + */ +inline MD5Hash &operator^=(MD5Hash &lhs, const MD5Hash &rhs) +{ + for (size_t i = 0; i < lhs.size(); i++) lhs[i] ^= rhs[i]; + return lhs; +} + + struct Md5 { private: uint32 count[2]; ///< message length in bits, lsw first @@ -64,7 +83,7 @@ private: public: Md5(); void Append(const void *data, const size_t nbytes); - void Finish(uint8 digest[16]); + void Finish(MD5Hash &digest); }; #endif /* MD5_INCLUDED */ diff --git a/src/base_media_base.h b/src/base_media_base.h index 1a2c64422e..9131cbae58 100644 --- a/src/base_media_base.h +++ b/src/base_media_base.h @@ -14,6 +14,7 @@ #include "gfx_type.h" #include "textfile_type.h" #include "textfile_gui.h" +#include "3rdparty/md5/md5.h" #include /* Forward declare these; can't do 'struct X' in functions as older GCCs barf on that */ @@ -31,7 +32,7 @@ struct MD5File { }; std::string filename; ///< filename - uint8 hash[16]; ///< md5 sum of the file + MD5Hash hash; ///< md5 sum of the file std::string missing_warning; ///< warning when this file is missing ChecksumResult check_result; ///< cached result of md5 check diff --git a/src/base_media_func.h b/src/base_media_func.h index 2dc66022b7..102f0a412b 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -99,7 +99,7 @@ bool BaseSet::FillSetDetails(IniFile *ini, const return false; } const char *c = item->value->c_str(); - for (uint i = 0; i < sizeof(file->hash) * 2; i++, c++) { + for (size_t i = 0; i < file->hash.size() * 2; i++, c++) { uint j; if ('0' <= *c && *c <= '9') { j = *c - '0'; @@ -285,14 +285,11 @@ template const char *TryGetBaseSetFile(const ContentInfo *ci, if (s->shortname != ci->unique_id) continue; if (!md5sum) return s->files[0].filename.c_str(); - byte md5[16]; - memset(md5, 0, sizeof(md5)); + MD5Hash md5; for (uint i = 0; i < Tbase_set::NUM_FILES; i++) { - for (uint j = 0; j < sizeof(md5); j++) { - md5[j] ^= s->files[i].hash[j]; - } + md5 ^= s->files[i].hash; } - if (memcmp(md5, ci->md5sum, sizeof(md5)) == 0) return s->files[0].filename.c_str(); + if (md5 == ci->md5sum) return s->files[0].filename.c_str(); } return nullptr; } diff --git a/src/fios.cpp b/src/fios.cpp index 6cf83341ec..f5444fdb0b 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -629,13 +629,12 @@ const char *FiosGetScreenshotDir() /** Basic data to distinguish a scenario. Used in the server list window */ struct ScenarioIdentifier { uint32 scenid; ///< ID for the scenario (generated by content). - uint8 md5sum[16]; ///< MD5 checksum of file. + MD5Hash md5sum; ///< MD5 checksum of file. std::string filename; ///< filename of the file. bool operator == (const ScenarioIdentifier &other) const { - return this->scenid == other.scenid && - memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0; + return this->scenid == other.scenid && this->md5sum == other.md5sum; } bool operator != (const ScenarioIdentifier &other) const @@ -714,7 +713,7 @@ const char *FindScenario(const ContentInfo *ci, bool md5sum) _scanner.Scan(false); for (ScenarioIdentifier &id : _scanner) { - if (md5sum ? (memcmp(id.md5sum, ci->md5sum, sizeof(id.md5sum)) == 0) + if (md5sum ? (id.md5sum == ci->md5sum) : (id.scenid == ci->unique_id)) { return id.filename.c_str(); } diff --git a/src/gamelog.cpp b/src/gamelog.cpp index cba76e0a61..30e65d543a 100644 --- a/src/gamelog.cpp +++ b/src/gamelog.cpp @@ -106,10 +106,10 @@ void Gamelog::Reset() * @param md5sum array of md5sum to print, if known * @param gc GrfConfig, if known */ -static void AddGrfInfo(std::back_insert_iterator &output_iterator, uint32_t grfid, const uint8_t *md5sum, const GRFConfig *gc) +static void AddGrfInfo(std::back_insert_iterator &output_iterator, uint32_t grfid, const MD5Hash *md5sum, const GRFConfig *gc) { if (md5sum != nullptr) { - fmt::format_to(output_iterator, "GRF ID {:08X}, checksum {}", BSWAP32(grfid), MD5SumToString(md5sum)); + fmt::format_to(output_iterator, "GRF ID {:08X}, checksum {}", BSWAP32(grfid), MD5SumToString(*md5sum)); } else { fmt::format_to(output_iterator, "GRF ID {:08X}", BSWAP32(grfid)); } @@ -230,9 +230,9 @@ void Gamelog::Print(std::function proc) /* virtual */ void LoggedChangeGRFAdd::FormatTo(std::back_insert_iterator &output_iterator, GrfIDMapping &grf_names, GamelogActionType action_type) { /* A NewGRF got added to the game, either at the start of the game (never an issue), or later on when it could be an issue. */ - const GRFConfig *gc = FindGRFConfig(this->grfid, FGCM_EXACT, this->md5sum); + const GRFConfig *gc = FindGRFConfig(this->grfid, FGCM_EXACT, &this->md5sum); fmt::format_to(output_iterator, "Added NewGRF: "); - AddGrfInfo(output_iterator, this->grfid, this->md5sum, gc); + AddGrfInfo(output_iterator, this->grfid, &this->md5sum, gc); auto gm = grf_names.find(this->grfid); if (gm != grf_names.end() && !gm->second.was_missing) fmt::format_to(output_iterator, ". Gamelog inconsistency: GrfID was already added!"); grf_names[this->grfid] = gc; @@ -259,9 +259,9 @@ void Gamelog::Print(std::function proc) /* virtual */ void LoggedChangeGRFChanged::FormatTo(std::back_insert_iterator &output_iterator, GrfIDMapping &grf_names, GamelogActionType action_type) { /* Another version of the same NewGRF got loaded. */ - const GRFConfig *gc = FindGRFConfig(this->grfid, FGCM_EXACT, this->md5sum); + const GRFConfig *gc = FindGRFConfig(this->grfid, FGCM_EXACT, &this->md5sum); fmt::format_to(output_iterator, "Compatible NewGRF loaded: "); - AddGrfInfo(output_iterator, this->grfid, this->md5sum, gc); + AddGrfInfo(output_iterator, this->grfid, &this->md5sum, gc); if (grf_names.count(this->grfid) == 0) fmt::format_to(output_iterator, ". Gamelog inconsistency: GrfID was never added!"); grf_names[this->grfid] = gc; } @@ -654,7 +654,7 @@ void Gamelog::GRFUpdate(const GRFConfig *oldc, const GRFConfig *newc) this->GRFMove(nl[n++]->ident.grfid, -(int)oi); } } else { - if (memcmp(og->ident.md5sum, ng->ident.md5sum, sizeof(og->ident.md5sum)) != 0) { + if (og->ident.md5sum != ng->ident.md5sum) { /* md5sum changed, probably loading 'compatible' GRF */ this->GRFCompatible(&nl[n]->ident); } diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index cc2f381539..550d6cf49a 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -415,7 +415,7 @@ MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir, size_t max_size) Md5 checksum; uint8 buffer[1024]; - uint8 digest[16]; + MD5Hash digest; size_t len; while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) { @@ -426,7 +426,7 @@ MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir, size_t max_size) FioFCloseFile(f); checksum.Finish(digest); - return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH; + return this->hash == digest ? CR_MATCH : CR_MISMATCH; } /** Names corresponding to the GraphicsFileType */ diff --git a/src/misc.cpp b/src/misc.cpp index 5169a6dfc5..d2d645f059 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -77,7 +77,7 @@ std::string GenerateUid(std::string_view subject) std::string coding_string = fmt::format("{}{}{}", InteractiveRandom(), current_time, subject); Md5 checksum; - uint8 digest[16]; + MD5Hash digest; checksum.Append(coding_string.c_str(), coding_string.length()); checksum.Finish(digest); diff --git a/src/network/core/game_info.cpp b/src/network/core/game_info.cpp index 0c4ce31672..97071d4cc1 100644 --- a/src/network/core/game_info.cpp +++ b/src/network/core/game_info.cpp @@ -164,7 +164,7 @@ const NetworkServerGameInfo *GetCurrentNetworkServerGameInfo() static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config, std::string name) { /* Find the matching GRF file */ - const GRFConfig *f = FindGRFConfig(config->ident.grfid, FGCM_EXACT, config->ident.md5sum); + const GRFConfig *f = FindGRFConfig(config->ident.grfid, FGCM_EXACT, &config->ident.md5sum); if (f == nullptr) { AddGRFTextToList(config->name, name.empty() ? GetString(STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN) : name); config->status = GCS_NOT_FOUND; @@ -362,9 +362,8 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info, const GameInfo */ void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf) { - uint j; p->Send_uint32(grf->grfid); - for (j = 0; j < sizeof(grf->md5sum); j++) { + for (size_t j = 0; j < grf->md5sum.size(); j++) { p->Send_uint8(grf->md5sum[j]); } } @@ -376,9 +375,8 @@ void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf) */ void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf) { - uint j; grf->grfid = p->Recv_uint32(); - for (j = 0; j < sizeof(grf->md5sum); j++) { + for (size_t j = 0; j < grf->md5sum.size(); j++) { grf->md5sum[j] = p->Recv_uint8(); } } diff --git a/src/network/core/tcp_content.cpp b/src/network/core/tcp_content.cpp index 913a8b9bdc..350ae9fbb1 100644 --- a/src/network/core/tcp_content.cpp +++ b/src/network/core/tcp_content.cpp @@ -70,7 +70,7 @@ std::optional ContentInfo::GetTextfile(TextfileType type) const tmp = Game::GetScannerLibrary()->FindMainScript(this, true); break; case CONTENT_TYPE_NEWGRF: { - const GRFConfig *gc = FindGRFConfig(BSWAP32(this->unique_id), FGCM_EXACT, this->md5sum); + const GRFConfig *gc = FindGRFConfig(BSWAP32(this->unique_id), FGCM_EXACT, &this->md5sum); tmp = gc != nullptr ? gc->filename.c_str() : nullptr; break; } diff --git a/src/network/core/tcp_content_type.h b/src/network/core/tcp_content_type.h index b74271ea92..03a5ba8cc6 100644 --- a/src/network/core/tcp_content_type.h +++ b/src/network/core/tcp_content_type.h @@ -12,6 +12,8 @@ #ifndef NETWORK_CORE_TCP_CONTENT_TYPE_H #define NETWORK_CORE_TCP_CONTENT_TYPE_H +#include "../../3rdparty/md5/md5.h" + /** The values in the enum are important; they are used as database 'keys' */ enum ContentType { CONTENT_TYPE_BEGIN = 1, ///< Helper to mark the begin of the types @@ -67,7 +69,7 @@ struct ContentInfo { std::string url; ///< URL related to the content std::string description; ///< Description of the content uint32 unique_id = 0; ///< Unique ID; either GRF ID or shortname - byte md5sum[16] = {0}; ///< The MD5 checksum + MD5Hash md5sum; ///< The MD5 checksum std::vector dependencies; ///< The dependencies (unique server side ids) StringList tags; ///< Tags associated with the content State state = State::UNSELECTED; ///< Whether the content info is selected (for download) diff --git a/src/network/network.cpp b/src/network/network.cpp index b12b5b440c..2b86cec4fc 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -190,18 +190,14 @@ std::string GenerateCompanyPasswordHash(const std::string &password, const std:: } Md5 checksum; - uint8 digest[16]; + MD5Hash digest; /* Generate the MD5 hash */ std::string salted_password_string = salted_password.str(); checksum.Append(salted_password_string.data(), salted_password_string.size()); checksum.Finish(digest); - std::ostringstream hashed_password; - hashed_password << std::hex << std::setfill('0'); - for (int di = 0; di < 16; di++) hashed_password << std::setw(2) << (int)digest[di]; // Cast needed, otherwise interpreted as character to add - - return hashed_password.str(); + return MD5SumToString(digest); } /** diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 6ffecebe1e..8ba9f30d06 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -322,7 +322,7 @@ std::string _network_server_name; NetworkJoinInfo _network_join; /** Make sure the server ID length is the same as a md5 hash. */ -static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1); +static_assert(NETWORK_SERVER_ID_LENGTH == MD5_HASH_BYTES * 2 + 1); /*********** * Sending functions @@ -663,7 +663,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(P DeserializeGRFIdentifier(p, &c); /* Check whether we know this GRF */ - const GRFConfig *f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum); + const GRFConfig *f = FindGRFConfig(c.grfid, FGCM_EXACT, &c.md5sum); if (f == nullptr) { /* We do not know this GRF, bail out of initialization */ Debug(grf, 0, "NewGRF {:08X} not found; checksum {}", BSWAP32(c.grfid), MD5SumToString(c.md5sum)); diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index 6ac2cbbd10..c79bb1c012 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -37,7 +37,7 @@ ClientNetworkContentSocketHandler _network_content_client; /** Wrapper function for the HasProc */ static bool HasGRFConfig(const ContentInfo *ci, bool md5sum) { - return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? FGCM_EXACT : FGCM_ANY, md5sum ? ci->md5sum : nullptr) != nullptr; + return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? FGCM_EXACT : FGCM_ANY, md5sum ? &ci->md5sum : nullptr) != nullptr; } /** @@ -62,7 +62,7 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p) ci->description = p->Recv_string(NETWORK_CONTENT_DESC_LENGTH, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE); ci->unique_id = p->Recv_uint32(); - for (uint j = 0; j < sizeof(ci->md5sum); j++) { + for (size_t j = 0; j < ci->md5sum.size(); j++) { ci->md5sum[j] = p->Recv_uint8(); } @@ -144,8 +144,7 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p) /* Do we already have a stub for this? */ for (ContentInfo *ici : this->infos) { - if (ici->type == ci->type && ici->unique_id == ci->unique_id && - memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) { + if (ici->type == ci->type && ici->unique_id == ci->unique_id && ci->md5sum == ici->md5sum) { /* Preserve the name if possible */ if (ci->name.empty()) ci->name = ici->name; if (ici->IsSelected()) ci->state = ici->state; @@ -267,7 +266,7 @@ void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bo assert(cv->size() < 255); assert(cv->size() < (TCP_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / - (sizeof(uint8) + sizeof(uint32) + (send_md5sum ? /*sizeof(ContentInfo::md5sum)*/16 : 0))); + (sizeof(uint8) + sizeof(uint32) + (send_md5sum ? MD5_HASH_BYTES : 0))); Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID, TCP_MTU); p->Send_uint8((uint8)cv->size()); @@ -277,7 +276,7 @@ void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bo p->Send_uint32(ci->unique_id); if (!send_md5sum) continue; - for (uint j = 0; j < sizeof(ci->md5sum); j++) { + for (size_t j = 0; j < ci->md5sum.size(); j++) { p->Send_uint8(ci->md5sum[j]); } } @@ -288,7 +287,7 @@ void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bo bool found = false; for (ContentInfo *ci2 : this->infos) { if (ci->type == ci2->type && ci->unique_id == ci2->unique_id && - (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) { + (!send_md5sum || ci->md5sum == ci2->md5sum)) { found = true; break; } diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index 693b1fdc7c..3df2021830 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -124,7 +124,7 @@ void NetworkAfterNewGRFScan() for (GRFConfig *c = item->info.grfconfig; c != nullptr; c = c->next) { assert(HasBit(c->flags, GCF_COPY)); - const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, c->ident.md5sum); + const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, &c->ident.md5sum); if (f == nullptr) { /* Don't know the GRF (anymore), so mark game incompatible. */ c->status = GCS_NOT_FOUND; diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index 256de23f9c..f596e64f77 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -46,6 +46,7 @@ GRFConfig::GRFConfig(const std::string &filename) : GRFConfig::GRFConfig(const GRFConfig &config) : ZeroedMemoryAllocator(), ident(config.ident), + original_md5sum(config.original_md5sum), filename(config.filename), name(config.name), info(config.info), @@ -61,7 +62,6 @@ GRFConfig::GRFConfig(const GRFConfig &config) : param_info(config.param_info), has_param_defaults(config.has_param_defaults) { - MemCpyT(this->original_md5sum, config.original_md5sum, lengthof(this->original_md5sum)); MemCpyT(this->param, config.param, lengthof(this->param)); if (config.error != nullptr) this->error = std::make_unique(*config.error); } @@ -475,7 +475,7 @@ GRFListCompatibility IsGoodGRFConfigList(GRFConfig *grfconfig) GRFListCompatibility res = GLC_ALL_GOOD; for (GRFConfig *c = grfconfig; c != nullptr; c = c->next) { - const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, c->ident.md5sum); + const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, &c->ident.md5sum); if (f == nullptr || HasBit(f->flags, GCF_INVALID)) { /* If we have not found the exactly matching GRF try to find one with the * same grfid, as it most likely is compatible */ @@ -485,7 +485,7 @@ GRFListCompatibility IsGoodGRFConfigList(GRFConfig *grfconfig) if (!HasBit(c->flags, GCF_COMPATIBLE)) { /* Preserve original_md5sum after it has been assigned */ SetBit(c->flags, GCF_COMPATIBLE); - memcpy(c->original_md5sum, c->ident.md5sum, sizeof(c->original_md5sum)); + c->original_md5sum = c->ident.md5sum; } /* Non-found has precedence over compatibility load */ @@ -508,7 +508,7 @@ compatible_grf: * already a local one, so there is no need to replace it. */ if (!HasBit(c->flags, GCF_COPY)) { c->filename = f->filename; - memcpy(c->ident.md5sum, f->ident.md5sum, sizeof(c->ident.md5sum)); + c->ident.md5sum = f->ident.md5sum; c->name = f->name; c->info = f->name; c->error = nullptr; @@ -575,7 +575,7 @@ bool GRFFileScanner::AddFile(const std::string &filename, size_t basepath_length GRFConfig **pd, *d; bool stop = false; for (pd = &_all_grfs; (d = *pd) != nullptr; pd = &d->next) { - if (c->ident.grfid == d->ident.grfid && memcmp(c->ident.md5sum, d->ident.md5sum, sizeof(c->ident.md5sum)) == 0) added = false; + if (c->ident.grfid == d->ident.grfid && c->ident.md5sum == d->ident.md5sum) added = false; /* Because there can be multiple grfs with the same name, make sure we checked all grfs with the same name, * before inserting the entry. So insert a new grf at the end of all grfs with the same name, instead of * just after the first with the same name. Avoids doubles in the list. */ @@ -691,7 +691,7 @@ void ScanNewGRFFiles(NewGRFScanCallback *callback) * @param desired_version Requested version * @return The matching grf, if it exists in #_all_grfs, else \c nullptr. */ -const GRFConfig *FindGRFConfig(uint32 grfid, FindGRFConfigMode mode, const uint8 *md5sum, uint32 desired_version) +const GRFConfig *FindGRFConfig(uint32 grfid, FindGRFConfigMode mode, const MD5Hash *md5sum, uint32 desired_version) { assert((mode == FGCM_EXACT) != (md5sum == nullptr)); const GRFConfig *best = nullptr; diff --git a/src/newgrf_config.h b/src/newgrf_config.h index 44435427e6..bd8db85961 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -16,6 +16,7 @@ #include "fileio_type.h" #include "textfile_type.h" #include "newgrf_text.h" +#include "3rdparty/md5/md5.h" /** GRF config bit flags */ enum GCF_Flags { @@ -81,15 +82,12 @@ enum GRFPalette { /** Basic data to distinguish a GRF. Used in the server list window */ struct GRFIdentifier { uint32 grfid; ///< GRF ID (defined by Action 0x08) - uint8 md5sum[16]; ///< MD5 checksum of file to distinguish files with the same GRF ID (eg. newer version of GRF) + MD5Hash md5sum; ///< MD5 checksum of file to distinguish files with the same GRF ID (eg. newer version of GRF) GRFIdentifier() = default; GRFIdentifier(const GRFIdentifier &other) = default; GRFIdentifier(GRFIdentifier &&other) = default; - GRFIdentifier(uint32 grfid, const uint8 *md5sum) : grfid(grfid) - { - MemCpyT(this->md5sum, md5sum, lengthof(this->md5sum)); - } + GRFIdentifier(uint32 grfid, const MD5Hash &md5sum) : grfid(grfid), md5sum(md5sum) {} GRFIdentifier& operator =(const GRFIdentifier &other) = default; @@ -99,11 +97,11 @@ struct GRFIdentifier { * @param md5sum Expected md5sum, may be \c nullptr (in which case, do not check it). * @return the object has the provided grfid and md5sum. */ - inline bool HasGrfIdentifier(uint32 grfid, const uint8 *md5sum) const + inline bool HasGrfIdentifier(uint32 grfid, const MD5Hash *md5sum) const { if (this->grfid != grfid) return false; if (md5sum == nullptr) return true; - return memcmp(md5sum, this->md5sum, sizeof(this->md5sum)) == 0; + return *md5sum == this->md5sum; } }; @@ -158,7 +156,7 @@ struct GRFConfig : ZeroedMemoryAllocator { GRFConfig &operator=(GRFConfig &rhs) = delete; GRFIdentifier ident; ///< grfid and md5sum to uniquely identify newgrfs - uint8 original_md5sum[16]; ///< MD5 checksum of original file if only a 'compatible' file was loaded + MD5Hash original_md5sum; ///< MD5 checksum of original file if only a 'compatible' file was loaded std::string filename; ///< Filename - either with or without full path GRFTextWrapper name; ///< NOSAVE: GRF name (Action 0x08) GRFTextWrapper info; ///< NOSAVE: GRF info (author, copyright, ...) (Action 0x08) @@ -217,7 +215,7 @@ struct NewGRFScanCallback { size_t GRFGetSizeOfDataSection(FILE *f); void ScanNewGRFFiles(NewGRFScanCallback *callback); -const GRFConfig *FindGRFConfig(uint32 grfid, FindGRFConfigMode mode, const uint8 *md5sum = nullptr, uint32 desired_version = 0); +const GRFConfig *FindGRFConfig(uint32 grfid, FindGRFConfigMode mode, const MD5Hash *md5sum = nullptr, uint32 desired_version = 0); GRFConfig *GetGRFConfig(uint32 grfid, uint32 mask = 0xFFFFFFFF); GRFConfig **CopyGRFConfigList(GRFConfig **dst, const GRFConfig *src, bool init_only); void AppendStaticGRFConfigs(GRFConfig **dst); diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 7fc105100a..438413f6a5 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -1245,7 +1245,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { bool compatible = HasBit(c->flags, GCF_COMPATIBLE); if (c->status != GCS_NOT_FOUND && !compatible) continue; - const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, compatible ? c->original_md5sum : c->ident.md5sum); + const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, compatible ? &c->original_md5sum : &c->ident.md5sum); if (f == nullptr || HasBit(f->flags, GCF_INVALID)) continue; *l = new GRFConfig(*f); @@ -1452,7 +1452,7 @@ private: i = a->version - b->version; if (i != 0) return i < 0; - return memcmp(a->ident.md5sum, b->ident.md5sum, lengthof(b->ident.md5sum)) < 0; + return a->ident.md5sum < b->ident.md5sum; } /** Filter grfs by tags/name */ @@ -1473,7 +1473,7 @@ private: for (const GRFConfig *c = _all_grfs; c != nullptr; c = c->next) { bool found = false; - for (const GRFConfig *grf = this->actives; grf != nullptr && !found; grf = grf->next) found = grf->ident.HasGrfIdentifier(c->ident.grfid, c->ident.md5sum); + for (const GRFConfig *grf = this->actives; grf != nullptr && !found; grf = grf->next) found = grf->ident.HasGrfIdentifier(c->ident.grfid, &c->ident.md5sum); if (found) continue; if (_settings_client.gui.newgrf_show_old_versions) { @@ -1490,7 +1490,7 @@ private: * If we are the best version, then we definitely want to * show that NewGRF!. */ - if (best->version == 0 || best->ident.HasGrfIdentifier(c->ident.grfid, c->ident.md5sum)) { + if (best->version == 0 || best->ident.HasGrfIdentifier(c->ident.grfid, &c->ident.md5sum)) { this->avails.push_back(c); } } @@ -1575,7 +1575,7 @@ void ShowMissingContentWindow(const GRFConfig *list) ci->state = ContentInfo::DOES_NOT_EXIST; ci->name = c->GetName(); ci->unique_id = BSWAP32(c->ident.grfid); - memcpy(ci->md5sum, HasBit(c->flags, GCF_COMPATIBLE) ? c->original_md5sum : c->ident.md5sum, sizeof(ci->md5sum)); + ci->md5sum = HasBit(c->flags, GCF_COMPATIBLE) ? c->original_md5sum : c->ident.md5sum; cv.push_back(ci); } ShowNetworkContentListWindow(cv.size() == 0 ? nullptr : &cv, CONTENT_TYPE_NEWGRF); diff --git a/src/script/script_scanner.cpp b/src/script/script_scanner.cpp index 6192b4f00e..850ae245bf 100644 --- a/src/script/script_scanner.cpp +++ b/src/script/script_scanner.cpp @@ -154,18 +154,14 @@ std::string ScriptScanner::GetConsoleList(bool newest_only) const /** Helper for creating a MD5sum of all files within of a script. */ struct ScriptFileChecksumCreator : FileScanner { - byte md5sum[16]; ///< The final md5sum. + MD5Hash md5sum; ///< The final md5sum. Subdirectory dir; ///< The directory to look in. /** * Initialise the md5sum to be all zeroes, * so we can easily xor the data. */ - ScriptFileChecksumCreator(Subdirectory dir) - { - this->dir = dir; - memset(this->md5sum, 0, sizeof(this->md5sum)); - } + ScriptFileChecksumCreator(Subdirectory dir) : dir(dir) {} /* Add the file and calculate the md5 sum. */ virtual bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) @@ -173,7 +169,6 @@ struct ScriptFileChecksumCreator : FileScanner { Md5 checksum; uint8 buffer[1024]; size_t len, size; - byte tmp_md5sum[16]; /* Open the file ... */ FILE *f = FioFOpenFile(filename, "rb", this->dir, &size); @@ -184,12 +179,14 @@ struct ScriptFileChecksumCreator : FileScanner { size -= len; checksum.Append(buffer, len); } + + MD5Hash tmp_md5sum; checksum.Finish(tmp_md5sum); FioFCloseFile(f); /* ... and xor it to the overall md5sum. */ - for (uint i = 0; i < sizeof(md5sum); i++) this->md5sum[i] ^= tmp_md5sum[i]; + this->md5sum ^= tmp_md5sum; return true; } @@ -237,7 +234,7 @@ static bool IsSameScript(const ContentInfo *ci, bool md5sum, ScriptInfo *info, S checksum.Scan(".nut", path); } - return memcmp(ci->md5sum, checksum.md5sum, sizeof(ci->md5sum)) == 0; + return ci->md5sum == checksum.md5sum; } bool ScriptScanner::HasScript(const ContentInfo *ci, bool md5sum) diff --git a/src/settings.cpp b/src/settings.cpp index 158945a79a..b280a5204e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -969,7 +969,8 @@ static GRFConfig *GRFLoadConfig(IniFile &ini, const char *grpname, bool is_stati for (item = group->item; item != nullptr; item = item->next) { GRFConfig *c = nullptr; - uint8 grfid_buf[4], md5sum[16]; + uint8 grfid_buf[4]; + MD5Hash md5sum; const char *filename = item->name.c_str(); bool has_grfid = false; bool has_md5sum = false; @@ -978,12 +979,12 @@ static GRFConfig *GRFLoadConfig(IniFile &ini, const char *grpname, bool is_stati has_grfid = DecodeHexText(filename, grfid_buf, lengthof(grfid_buf)); if (has_grfid) { filename += 1 + 2 * lengthof(grfid_buf); - has_md5sum = DecodeHexText(filename, md5sum, lengthof(md5sum)); - if (has_md5sum) filename += 1 + 2 * lengthof(md5sum); + has_md5sum = DecodeHexText(filename, md5sum.data(), md5sum.size()); + if (has_md5sum) filename += 1 + 2 * md5sum.size(); uint32 grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24); if (has_md5sum) { - const GRFConfig *s = FindGRFConfig(grfid, FGCM_EXACT, md5sum); + const GRFConfig *s = FindGRFConfig(grfid, FGCM_EXACT, &md5sum); if (s != nullptr) c = new GRFConfig(*s); } if (c == nullptr && !FioCheckFileExists(filename, NEWGRF_DIR)) { diff --git a/src/string.cpp b/src/string.cpp index c129c6426f..2f8174eb4e 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -618,9 +618,9 @@ int CDECL seprintf(char *str, const char *last, const char *format, ...) * @param md5sum the md5sum itself * @return the string representation of the md5sum. */ -std::string MD5SumToString(const uint8 md5sum[16]) +std::string MD5SumToString(const MD5Hash &md5sum) { - return FormatArrayAsHex({md5sum, 16}); + return FormatArrayAsHex(md5sum); } diff --git a/src/string_func.h b/src/string_func.h index 08e1600b73..dd6a10341a 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -29,6 +29,7 @@ #include "core/bitmath_func.hpp" #include "core/span_type.hpp" #include "string_type.h" +#include "3rdparty/md5/md5.h" char *strecat(char *dst, const char *src, const char *last) NOACCESS(3); char *strecpy(char *dst, const char *src, const char *last) NOACCESS(3); @@ -90,7 +91,7 @@ static inline size_t ttd_strnlen(const char *str, size_t maxlen) return t - str; } -std::string MD5SumToString(const uint8 md5sum[16]); +std::string MD5SumToString(const MD5Hash &md5sum); bool IsValidChar(WChar key, CharSetFilter afilter); From acec34a0fe175f427a509df0987e4f2929d2c575 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 18 May 2023 22:41:42 +0200 Subject: [PATCH 06/58] Cleanup: remove MD5SumToString in lieu of FormatArrayAsHex --- src/console_cmds.cpp | 2 +- src/gamelog.cpp | 2 +- src/misc.cpp | 2 +- src/network/network.cpp | 2 +- src/network/network_client.cpp | 2 +- src/network/network_content_gui.cpp | 2 +- src/network/network_survey.cpp | 2 +- src/newgrf_config.cpp | 4 ++-- src/newgrf_gui.cpp | 2 +- src/openttd.cpp | 2 +- src/saveload/afterload.cpp | 4 ++-- src/screenshot.cpp | 2 +- src/settings.cpp | 2 +- src/string.cpp | 11 ----------- src/string_func.h | 3 --- 15 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 19c7ed0c8a..8519c0d217 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1899,7 +1899,7 @@ static void OutputContentState(const ContentInfo *const ci) static const char * const states[] = { "Not selected", "Selected", "Dep Selected", "Installed", "Unknown" }; static const TextColour state_to_colour[] = { CC_COMMAND, CC_INFO, CC_INFO, CC_WHITE, CC_ERROR }; - IConsolePrint(state_to_colour[ci->state], "{}, {}, {}, {}, {:08X}, {}", ci->id, types[ci->type - 1], states[ci->state], ci->name, ci->unique_id, MD5SumToString(ci->md5sum)); + IConsolePrint(state_to_colour[ci->state], "{}, {}, {}, {}, {:08X}, {}", ci->id, types[ci->type - 1], states[ci->state], ci->name, ci->unique_id, FormatArrayAsHex(ci->md5sum)); } DEF_CONSOLE_CMD(ConContent) diff --git a/src/gamelog.cpp b/src/gamelog.cpp index 30e65d543a..05ae5f4ff4 100644 --- a/src/gamelog.cpp +++ b/src/gamelog.cpp @@ -109,7 +109,7 @@ void Gamelog::Reset() static void AddGrfInfo(std::back_insert_iterator &output_iterator, uint32_t grfid, const MD5Hash *md5sum, const GRFConfig *gc) { if (md5sum != nullptr) { - fmt::format_to(output_iterator, "GRF ID {:08X}, checksum {}", BSWAP32(grfid), MD5SumToString(*md5sum)); + fmt::format_to(output_iterator, "GRF ID {:08X}, checksum {}", BSWAP32(grfid), FormatArrayAsHex(*md5sum)); } else { fmt::format_to(output_iterator, "GRF ID {:08X}", BSWAP32(grfid)); } diff --git a/src/misc.cpp b/src/misc.cpp index d2d645f059..a22abe3121 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -81,7 +81,7 @@ std::string GenerateUid(std::string_view subject) checksum.Append(coding_string.c_str(), coding_string.length()); checksum.Finish(digest); - return MD5SumToString(digest); + return FormatArrayAsHex(digest); } /** diff --git a/src/network/network.cpp b/src/network/network.cpp index 2b86cec4fc..561c1df7fd 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -197,7 +197,7 @@ std::string GenerateCompanyPasswordHash(const std::string &password, const std:: checksum.Append(salted_password_string.data(), salted_password_string.size()); checksum.Finish(digest); - return MD5SumToString(digest); + return FormatArrayAsHex(digest); } /** diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 8ba9f30d06..6c14acc5f1 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -666,7 +666,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(P const GRFConfig *f = FindGRFConfig(c.grfid, FGCM_EXACT, &c.md5sum); if (f == nullptr) { /* We do not know this GRF, bail out of initialization */ - Debug(grf, 0, "NewGRF {:08X} not found; checksum {}", BSWAP32(c.grfid), MD5SumToString(c.md5sum)); + Debug(grf, 0, "NewGRF {:08X} not found; checksum {}", BSWAP32(c.grfid), FormatArrayAsHex(c.md5sum)); ret = NETWORK_RECV_STATUS_NEWGRF_MISMATCH; } } diff --git a/src/network/network_content_gui.cpp b/src/network/network_content_gui.cpp index ff049d2d58..f82b807c62 100644 --- a/src/network/network_content_gui.cpp +++ b/src/network/network_content_gui.cpp @@ -365,7 +365,7 @@ class NetworkContentListWindow : public Window, ContentCallback { if (!first) url.push_back(','); first = false; - fmt::format_to(std::back_inserter(url), "{:08X}:{}", ci->unique_id, MD5SumToString(ci->md5sum)); + fmt::format_to(std::back_inserter(url), "{:08X}:{}", ci->unique_id, FormatArrayAsHex(ci->md5sum)); } } else { url += "do=searchtext&q="; diff --git a/src/network/network_survey.cpp b/src/network/network_survey.cpp index aa9aa84f11..3f21fdca71 100644 --- a/src/network/network_survey.cpp +++ b/src/network/network_survey.cpp @@ -268,7 +268,7 @@ static void SurveyGrfs(nlohmann::json &survey) auto grfid = fmt::format("{:08x}", BSWAP32(c->ident.grfid)); auto &grf = survey[grfid]; - grf["md5sum"] = MD5SumToString(c->ident.md5sum); + grf["md5sum"] = FormatArrayAsHex(c->ident.md5sum); grf["status"] = c->status; if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_UNSET) grf["palette"] = "unset"; diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index f596e64f77..235d49a73d 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -481,7 +481,7 @@ GRFListCompatibility IsGoodGRFConfigList(GRFConfig *grfconfig) * same grfid, as it most likely is compatible */ f = FindGRFConfig(c->ident.grfid, FGCM_COMPATIBLE, nullptr, c->version); if (f != nullptr) { - Debug(grf, 1, "NewGRF {:08X} ({}) not found; checksum {}. Compatibility mode on", BSWAP32(c->ident.grfid), c->filename, MD5SumToString(c->ident.md5sum)); + Debug(grf, 1, "NewGRF {:08X} ({}) not found; checksum {}. Compatibility mode on", BSWAP32(c->ident.grfid), c->filename, FormatArrayAsHex(c->ident.md5sum)); if (!HasBit(c->flags, GCF_COMPATIBLE)) { /* Preserve original_md5sum after it has been assigned */ SetBit(c->flags, GCF_COMPATIBLE); @@ -494,7 +494,7 @@ GRFListCompatibility IsGoodGRFConfigList(GRFConfig *grfconfig) } /* No compatible grf was found, mark it as disabled */ - Debug(grf, 0, "NewGRF {:08X} ({}) not found; checksum {}", BSWAP32(c->ident.grfid), c->filename, MD5SumToString(c->ident.md5sum)); + Debug(grf, 0, "NewGRF {:08X} ({}) not found; checksum {}", BSWAP32(c->ident.grfid), c->filename, FormatArrayAsHex(c->ident.md5sum)); c->status = GCS_NOT_FOUND; res = GLC_NOT_FOUND; diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 438413f6a5..da26d62f36 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -105,7 +105,7 @@ static void ShowNewGRFInfo(const GRFConfig *c, const Rect &r, bool show_params) } /* Prepare and draw MD5 sum */ - tmp = MD5SumToString(c->ident.md5sum); + tmp = FormatArrayAsHex(c->ident.md5sum); SetDParamStr(0, tmp); tr.top = DrawStringMultiLine(tr, STR_NEWGRF_SETTINGS_MD5SUM); diff --git a/src/openttd.cpp b/src/openttd.cpp index 168d82bcb9..6444d410b9 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -258,7 +258,7 @@ static void WriteSavegameInfo(const char *name) if (_load_check_data.HasNewGrfs()) { for (GRFConfig *c = _load_check_data.grfconfig; c != nullptr; c = c->next) { fmt::format_to(std::back_inserter(message), "{:08X} {} {}\n", c->ident.grfid, - MD5SumToString(HasBit(c->flags, GCF_COMPATIBLE) ? c->original_md5sum : c->ident.md5sum), c->filename); + FormatArrayAsHex(HasBit(c->flags, GCF_COMPATIBLE) ? c->original_md5sum : c->ident.md5sum), c->filename); } } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 15213ca7d0..7cdd8dbb0b 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -389,11 +389,11 @@ static void CDECL HandleSavegameLoadCrash(int signum) if (HasBit(c->flags, GCF_COMPATIBLE)) { const GRFIdentifier *replaced = _gamelog.GetOverriddenIdentifier(c); fmt::format_to(std::back_inserter(message), "NewGRF {:08X} (checksum {}) not found.\n Loaded NewGRF \"{}\" (checksum {}) with same GRF ID instead.\n", - BSWAP32(c->ident.grfid), MD5SumToString(c->original_md5sum), c->filename, MD5SumToString(replaced->md5sum)); + BSWAP32(c->ident.grfid), FormatArrayAsHex(c->original_md5sum), c->filename, FormatArrayAsHex(replaced->md5sum)); } if (c->status == GCS_NOT_FOUND) { fmt::format_to(std::back_inserter(message), "NewGRF {:08X} ({}) not found; checksum {}.\n", - BSWAP32(c->ident.grfid), c->filename, MD5SumToString(c->ident.md5sum)); + BSWAP32(c->ident.grfid), c->filename, FormatArrayAsHex(c->ident.md5sum)); } } } else { diff --git a/src/screenshot.cpp b/src/screenshot.cpp index 2f3a8257cb..8d853c52dc 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -320,7 +320,7 @@ static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *user fmt::format_to(std::back_inserter(message), "Graphics set: {} ({})\n", BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet()->version); message += "NewGRFs:\n"; for (const GRFConfig *c = _game_mode == GM_MENU ? nullptr : _grfconfig; c != nullptr; c = c->next) { - fmt::format_to(std::back_inserter(message), "{:08X} {} {}\n", BSWAP32(c->ident.grfid), MD5SumToString(c->ident.md5sum), c->filename); + fmt::format_to(std::back_inserter(message), "{:08X} {} {}\n", BSWAP32(c->ident.grfid), FormatArrayAsHex(c->ident.md5sum), c->filename); } message += "\nCompanies:\n"; for (const Company *c : Company::Iterate()) { diff --git a/src/settings.cpp b/src/settings.cpp index b280a5204e..7843714889 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1140,7 +1140,7 @@ static void GRFSaveConfig(IniFile &ini, const char *grpname, const GRFConfig *li GRFBuildParamList(params, c, lastof(params)); std::string key = fmt::format("{:08X}|{}|{}", BSWAP32(c->ident.grfid), - MD5SumToString(c->ident.md5sum), c->filename); + FormatArrayAsHex(c->ident.md5sum), c->filename); group->GetItem(key, true)->SetValue(params); } } diff --git a/src/string.cpp b/src/string.cpp index 2f8174eb4e..f55ebfbe24 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -613,17 +613,6 @@ int CDECL seprintf(char *str, const char *last, const char *format, ...) } -/** - * Convert the md5sum to a hexadecimal string representation - * @param md5sum the md5sum itself - * @return the string representation of the md5sum. - */ -std::string MD5SumToString(const MD5Hash &md5sum) -{ - return FormatArrayAsHex(md5sum); -} - - /* UTF-8 handling routines */ diff --git a/src/string_func.h b/src/string_func.h index dd6a10341a..914cd064c3 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -29,7 +29,6 @@ #include "core/bitmath_func.hpp" #include "core/span_type.hpp" #include "string_type.h" -#include "3rdparty/md5/md5.h" char *strecat(char *dst, const char *src, const char *last) NOACCESS(3); char *strecpy(char *dst, const char *src, const char *last) NOACCESS(3); @@ -91,8 +90,6 @@ static inline size_t ttd_strnlen(const char *str, size_t maxlen) return t - str; } -std::string MD5SumToString(const MD5Hash &md5sum); - bool IsValidChar(WChar key, CharSetFilter afilter); size_t Utf8Decode(WChar *c, const char *s); From c23aae96a2c704016451e56b82a157ddaa6e8315 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 18 May 2023 19:56:09 +0100 Subject: [PATCH 07/58] Codechange: Use std::array instead of C array for automatic deep-copies. --- src/newgrf.cpp | 2 +- src/newgrf_config.cpp | 18 +----------------- src/newgrf_config.h | 6 +----- src/newgrf_gui.cpp | 4 ++-- 4 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 2286e34a45..49af8ccf41 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -7191,7 +7191,7 @@ static void GRFLoadError(ByteReader *buf) } /* Only two parameter numbers can be used in the string. */ - for (uint i = 0; i < lengthof(error->param_value) && buf->HasData(); i++) { + for (uint i = 0; i < error->param_value.size() && buf->HasData(); i++) { uint param_number = buf->ReadByte(); error->param_value[i] = _cur.grffile->GetParam(param_number); } diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index 235d49a73d..17b0ab6464 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -158,26 +158,10 @@ uint _missing_extra_graphics = 0; * @param severity The severity of this error. * @param message The actual error-string. */ -GRFError::GRFError(StringID severity, StringID message) : - message(message), - severity(severity), - param_value() +GRFError::GRFError(StringID severity, StringID message) : message(message), severity(severity) { } -/** - * Create a new GRFError that is a deep copy of an existing error message. - * @param error The GRFError object to make a copy of. - */ -GRFError::GRFError(const GRFError &error) : - custom_message(error.custom_message), - data(error.data), - message(error.message), - severity(error.severity) -{ - memcpy(this->param_value, error.param_value, sizeof(this->param_value)); -} - /** * Create a new empty GRFParameterInfo object. * @param nr The newgrf parameter that is changed. diff --git a/src/newgrf_config.h b/src/newgrf_config.h index bd8db85961..ef71e7e8d1 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -108,16 +108,12 @@ struct GRFIdentifier { /** Information about why GRF had problems during initialisation */ struct GRFError { GRFError(StringID severity, StringID message = 0); - GRFError(const GRFError &error); - - /* Remove the copy assignment, as the default implementation will not do the right thing. */ - GRFError &operator=(GRFError &rhs) = delete; std::string custom_message; ///< Custom message (if present) std::string data; ///< Additional data for message and custom_message StringID message; ///< Default message StringID severity; ///< Info / Warning / Error / Fatal - uint32 param_value[2]; ///< Values of GRF parameters to show for message and custom_message + std::array param_value; ///< Values of GRF parameters to show for message and custom_message }; /** The possible types of a newgrf parameter. */ diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index da26d62f36..ba6ffa307d 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -55,7 +55,7 @@ void ShowNewGRFError() SetDParamStr(2, c->error->custom_message); SetDParamStr(3, c->filename); SetDParamStr(4, c->error->data); - for (uint i = 0; i < lengthof(c->error->param_value); i++) { + for (uint i = 0; i < c->error->param_value.size(); i++) { SetDParam(5 + i, c->error->param_value[i]); } if (c->error->severity == STR_NEWGRF_ERROR_MSG_FATAL) { @@ -75,7 +75,7 @@ static void ShowNewGRFInfo(const GRFConfig *c, const Rect &r, bool show_params) SetDParamStr(0, c->error->custom_message); // is skipped by built-in messages SetDParamStr(1, c->filename); SetDParamStr(2, c->error->data); - for (uint i = 0; i < lengthof(c->error->param_value); i++) { + for (uint i = 0; i < c->error->param_value.size(); i++) { SetDParam(3 + i, c->error->param_value[i]); } GetString(message, c->error->message != STR_NULL ? c->error->message : STR_JUST_RAW_STRING, lastof(message)); From 6b87fe6540d1c1a4798ee2169d4606765396adf2 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 18 May 2023 19:56:24 +0100 Subject: [PATCH 08/58] Codechange: Use std::array for GRF(File|Config) parameters. This simplifies comparison, copying and assignment operations. --- src/gamelog.cpp | 2 +- src/newgrf.cpp | 11 +++-------- src/newgrf.h | 6 +++--- src/newgrf_config.cpp | 8 ++++---- src/newgrf_config.h | 2 +- src/newgrf_gui.cpp | 4 ++-- src/settings.cpp | 8 ++++---- 7 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/gamelog.cpp b/src/gamelog.cpp index 05ae5f4ff4..461e0ab3a5 100644 --- a/src/gamelog.cpp +++ b/src/gamelog.cpp @@ -659,7 +659,7 @@ void Gamelog::GRFUpdate(const GRFConfig *oldc, const GRFConfig *newc) this->GRFCompatible(&nl[n]->ident); } - if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) { + if (og->num_params != ng->num_params || og->param == ng->param) { this->GRFParameters(ol[o]->ident.grfid); } diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 49af8ccf41..ba49f32c92 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -8090,7 +8090,7 @@ static bool ChangeGRFNumUsedParams(size_t len, ByteReader *buf) GrfMsg(2, "StaticGRFInfo: expected only 1 byte for 'INFO'->'NPAR' but got {}, ignoring this field", len); buf->Skip(len); } else { - _cur.grfconfig->num_valid_params = std::min(buf->ReadByte(), lengthof(_cur.grfconfig->param)); + _cur.grfconfig->num_valid_params = std::min(buf->ReadByte(), ClampTo(_cur.grfconfig->param.size())); } return true; } @@ -8239,7 +8239,7 @@ static bool ChangeGRFParamMask(size_t len, ByteReader *buf) buf->Skip(len); } else { byte param_nr = buf->ReadByte(); - if (param_nr >= lengthof(_cur.grfconfig->param)) { + if (param_nr >= _cur.grfconfig->param.size()) { GrfMsg(2, "StaticGRFInfo: invalid parameter number in 'INFO'->'PARA'->'MASK', param {}, ignoring this field", param_nr); buf->Skip(len - 1); } else { @@ -8926,13 +8926,8 @@ GRFFile::GRFFile(const GRFConfig *config) /* Copy the initial parameter list * 'Uninitialised' parameters are zeroed as that is their default value when dynamically creating them. */ - static_assert(lengthof(this->param) == lengthof(config->param) && lengthof(this->param) == 0x80); - - assert(config->num_params <= lengthof(config->param)); + this->param = config->param; this->param_end = config->num_params; - if (this->param_end > 0) { - MemCpyT(this->param, config->param, this->param_end); - } } GRFFile::~GRFFile() diff --git a/src/newgrf.h b/src/newgrf.h index af7af801d0..50f2897d5b 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -121,7 +121,7 @@ struct GRFFile : ZeroedMemoryAllocator { std::vector> airtspec; std::vector> roadstops; - uint32 param[0x80]; + std::array param; uint param_end; ///< one more than the highest set parameter std::vector labels; ///< List of labels @@ -154,9 +154,9 @@ struct GRFFile : ZeroedMemoryAllocator { /** Get GRF Parameter with range checking */ uint32 GetParam(uint number) const { - /* Note: We implicitly test for number < lengthof(this->param) and return 0 for invalid parameters. + /* Note: We implicitly test for number < this->param.size() and return 0 for invalid parameters. * In fact this is the more important test, as param is zeroed anyway. */ - assert(this->param_end <= lengthof(this->param)); + assert(this->param_end <= this->param.size()); return (number < this->param_end) ? this->param[number] : 0; } }; diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index 17b0ab6464..f0e6d7a482 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -35,7 +35,7 @@ * @param filename Set the filename of this GRFConfig to filename. */ GRFConfig::GRFConfig(const std::string &filename) : - filename(filename), num_valid_params(lengthof(param)) + filename(filename), num_valid_params(ClampTo(GRFConfig::param.size())) { } @@ -56,13 +56,13 @@ GRFConfig::GRFConfig(const GRFConfig &config) : flags(config.flags & ~(1 << GCF_COPY)), status(config.status), grf_bugs(config.grf_bugs), + param(config.param), num_params(config.num_params), num_valid_params(config.num_valid_params), palette(config.palette), param_info(config.param_info), has_param_defaults(config.has_param_defaults) { - MemCpyT(this->param, config.param, lengthof(this->param)); if (config.error != nullptr) this->error = std::make_unique(*config.error); } @@ -74,7 +74,7 @@ void GRFConfig::CopyParams(const GRFConfig &src) { this->num_params = src.num_params; this->num_valid_params = src.num_valid_params; - MemCpyT(this->param, src.param, lengthof(this->param)); + this->param = src.param; } /** @@ -110,7 +110,7 @@ const char *GRFConfig::GetURL() const void GRFConfig::SetParameterDefaults() { this->num_params = 0; - MemSetT(this->param, 0, lengthof(this->param)); + this->param = {}; if (!this->has_param_defaults) return; diff --git a/src/newgrf_config.h b/src/newgrf_config.h index ef71e7e8d1..3273d001bf 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -164,7 +164,7 @@ struct GRFConfig : ZeroedMemoryAllocator { uint8 flags; ///< NOSAVE: GCF_Flags, bitset GRFStatus status; ///< NOSAVE: GRFStatus, enum uint32 grf_bugs; ///< NOSAVE: bugs in this GRF in this run, @see enum GRFBugs - uint32 param[0x80]; ///< GRF parameters + std::array param; ///< GRF parameters uint8 num_params; ///< Number of used parameters uint8 num_valid_params; ///< NOSAVE: Number of valid parameters (action 0x14) uint8 palette; ///< GRFPalette, bitset diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index ba6ffa307d..e73dbd90e6 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -169,7 +169,7 @@ struct NewGRFParametersWindow : public Window { clicked_row(UINT_MAX), editable(editable) { - this->action14present = (c->num_valid_params != lengthof(c->param) || c->param_info.size() != 0); + this->action14present = (c->num_valid_params != c->param.size() || c->param_info.size() != 0); this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_NP_SCROLLBAR); @@ -225,7 +225,7 @@ struct NewGRFParametersWindow : public Window { } case WID_NP_NUMPAR: { - SetDParamMaxValue(0, lengthof(this->grf_config->param)); + SetDParamMaxValue(0, this->grf_config->param.size()); Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); d.width += padding.width; d.height += padding.height; diff --git a/src/settings.cpp b/src/settings.cpp index 7843714889..5e5cfe1178 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -227,9 +227,9 @@ static size_t LookupManyOfMany(const std::vector &many, const char * @return returns the number of items found, or -1 on an error */ template -static int ParseIntList(const char *p, T *items, int maxitems) +static int ParseIntList(const char *p, T *items, size_t maxitems) { - int n = 0; // number of items read so far + size_t n = 0; // number of items read so far bool comma = false; // do we accept comma? while (*p != '\0') { @@ -262,7 +262,7 @@ static int ParseIntList(const char *p, T *items, int maxitems) * We have read comma when (n != 0) and comma is not allowed */ if (n != 0 && !comma) return -1; - return n; + return ClampTo(n); } /** @@ -996,7 +996,7 @@ static GRFConfig *GRFLoadConfig(IniFile &ini, const char *grpname, bool is_stati /* Parse parameters */ if (item->value.has_value() && !item->value->empty()) { - int count = ParseIntList(item->value->c_str(), c->param, lengthof(c->param)); + int count = ParseIntList(item->value->c_str(), c->param.data(), c->param.size()); if (count < 0) { SetDParamStr(0, filename); ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL); From cdb184f53b79e1018c1eb5779d2e133123d63f7f Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 19 May 2023 18:39:30 +0000 Subject: [PATCH 09/58] Update: Translations from eints catalan: 14 changes by J0anJosep dutch: 14 changes by Afoklala --- src/lang/catalan.txt | 14 ++++++++++++++ src/lang/dutch.txt | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 13d9464528..f19db67f29 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -1042,6 +1042,13 @@ STR_GAME_OPTIONS_GUI_SCALE_3X :x3 STR_GAME_OPTIONS_GUI_SCALE_4X :x4 STR_GAME_OPTIONS_GUI_SCALE_5X :x5 +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_FRAME :{BLACK}Enquesta automatitzada +STR_GAME_OPTIONS_PARTICIPATE_SURVEY :{BLACK}Participa a l'enquesta automatitzada +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_TOOLTIP :{BLACK}Quan s'activa, l'OpenTTD transmetrà dades a l'enquesta quan es surti d'una partida. +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK :{BLACK}Sobre l'enquesta i la privacitat +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK_TOOLTIP :{BLACK}Obre una pàgina amb més informació sobre l'enquesta automatitzada. +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW :{BLACK}Vista prèvia del resultat de l'enquesta +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :{BLACK}Mostra el resultat de l'enquesta automatitzada de la partida actual. STR_GAME_OPTIONS_GRAPHICS :{BLACK}Gràfics @@ -2404,6 +2411,12 @@ STR_NETWORK_ASK_RELAY_NO :{BLACK}No STR_NETWORK_ASK_RELAY_YES_ONCE :{BLACK}Sí, aquest una vegada. STR_NETWORK_ASK_RELAY_YES_ALWAYS :{BLACK}Sí, no ho preguntis més. +STR_NETWORK_ASK_SURVEY_CAPTION :Voleu participar a l'enquesta automatitzada? +STR_NETWORK_ASK_SURVEY_TEXT :Voleu participar en l'enquesta automatitzada?{}L'OpenTTD transmetrà dades sobre l'enquesta quan sortiu d'una partida.{}Podeu canviar-ho en qualsevol moment des de les "Opcions de la partida". +STR_NETWORK_ASK_SURVEY_PREVIEW :Vista prèvia del resultat de l'enquesta +STR_NETWORK_ASK_SURVEY_LINK :Sobre l'enquesta i la privacitat +STR_NETWORK_ASK_SURVEY_NO :No +STR_NETWORK_ASK_SURVEY_YES :Sí STR_NETWORK_SPECTATORS :Espectadors @@ -4661,6 +4674,7 @@ STR_TEXTFILE_VIEW_LICENCE :{BLACK}Llicènc STR_TEXTFILE_README_CAPTION :{WHITE}Llegeix-me del {STRING} de {STRING} STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}Registre de canvis del {STRING} de {STRING} STR_TEXTFILE_LICENCE_CAPTION :{WHITE}Llicència del {STRING} de {STRING} +STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Vista prèvia del resultat de l'enquesta # Vehicle loading indicators diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index fdde13e670..d14dcc2737 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -1041,6 +1041,13 @@ STR_GAME_OPTIONS_GUI_SCALE_3X :3x STR_GAME_OPTIONS_GUI_SCALE_4X :4x STR_GAME_OPTIONS_GUI_SCALE_5X :5x +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_FRAME :{BLACK}Geautomatiseerd onderzoek +STR_GAME_OPTIONS_PARTICIPATE_SURVEY :{BLACK}Deelnemen aan geautomatiseerd onderzoek +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_TOOLTIP :{BLACK}Als dit is ingeschakeld, verzendt OpenTTD een onderzoek wanneer je het spel afsluit +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK :{BLACK}Onderzoek en privacy +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK_TOOLTIP :{BLACK}Hiermee open je een browser met meer informatie over het geautomatiseerde onderzoek. +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW :{BLACK}Voorbeeld van resultaten van onderzoek +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :{BLACK}Geef de resultaten weer van het onderzoek van het huidige spel STR_GAME_OPTIONS_GRAPHICS :{BLACK}Weergave @@ -2403,6 +2410,12 @@ STR_NETWORK_ASK_RELAY_NO :{BLACK}Nee STR_NETWORK_ASK_RELAY_YES_ONCE :{BLACK}Ja, deze keer STR_NETWORK_ASK_RELAY_YES_ALWAYS :{BLACK}Ja, en vraag dit niet opnieuw +STR_NETWORK_ASK_SURVEY_CAPTION :Deelnemen aan geautomatiseerd onderzoek? +STR_NETWORK_ASK_SURVEY_TEXT :Wil je meedoen aan het geautomatiseerde onderzoek?{}OpenTTD verzendt een onderzoek wanneer je het spel afsluit.{}Je kunt dit altijd wijzigen bij 'Spelopties'. +STR_NETWORK_ASK_SURVEY_PREVIEW :Voorbeeld van resultaten van onderzoek +STR_NETWORK_ASK_SURVEY_LINK :Onderzoek en privacy +STR_NETWORK_ASK_SURVEY_NO :Nee +STR_NETWORK_ASK_SURVEY_YES :Ja STR_NETWORK_SPECTATORS :Toeschouwers @@ -4660,6 +4673,7 @@ STR_TEXTFILE_VIEW_LICENCE :{BLACK}Licentie STR_TEXTFILE_README_CAPTION :{WHITE}{STRING} leesmij van {STRING} STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}{STRING} wijzigingen van {STRING} STR_TEXTFILE_LICENCE_CAPTION :{WHITE}{STRING} licentie van {STRING} +STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Voorbeeld van resultaten van onderzoek # Vehicle loading indicators From 0cbbfd3eaeb84448d0f6ad5aec43caa3fdc67190 Mon Sep 17 00:00:00 2001 From: PeterN Date: Fri, 19 May 2023 22:19:00 +0100 Subject: [PATCH 10/58] Codechange: Use display list instead of exclude list for file window. (#10845) This simplifies retrieving the correct data for each row when data is filtered. The background FileList is left intact so that savegame data does not have to be rescanned when the filter is changed, and sorting still remains the task of the background FileList. --- src/fios_gui.cpp | 89 +++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 50 deletions(-) diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index 7d0e8fe556..b23d20b3fa 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -280,7 +280,7 @@ private: StringFilter string_filter; ///< Filter for available games. QueryString filter_editbox; ///< Filter editbox; - std::vector fios_items_shown; ///< Map of the filtered out fios items + std::vector display_list; ///< Filtered display list static void SaveGameConfirmationCallback(Window *w, bool confirmed) { @@ -443,14 +443,8 @@ public: Rect tr = r.Shrink(WidgetDimensions::scaled.inset).WithHeight(this->resize.step_height); uint scroll_pos = this->vscroll->GetPosition(); - for (uint row = 0; row < this->fios_items.size() && tr.top < br.bottom; row++) { - if (!this->fios_items_shown[row]) { - /* The current item is filtered out : we do not show it */ - scroll_pos++; - continue; - } - if (row < scroll_pos) continue; - const FiosItem *item = &this->fios_items[row]; + for (auto it = this->display_list.begin() + scroll_pos; it != this->display_list.end() && tr.top < br.bottom; ++it) { + const FiosItem *item = *it; if (item == this->selected) { GfxFillRect(br.left, tr.top, br.right, tr.bottom, PC_DARK_BLUE); @@ -654,16 +648,11 @@ public: break; case WID_SL_DRIVES_DIRECTORIES_LIST: { // Click the listbox - int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SL_DRIVES_DIRECTORIES_LIST, WidgetDimensions::scaled.inset.top); - if (y == INT_MAX) return; + auto it = this->vscroll->GetScrolledItemFromWidget(this->display_list, pt.y, this, WID_SL_DRIVES_DIRECTORIES_LIST, WidgetDimensions::scaled.inset.top); + if (it == this->display_list.end()) return; /* Get the corresponding non-filtered out item from the list */ - int i = 0; - while (i <= y) { - if (!this->fios_items_shown[i]) y++; - i++; - } - const FiosItem *file = &this->fios_items[y]; + const FiosItem *file = *it; if (FiosBrowseTo(file)) { /* Changed directory, need refresh. */ @@ -731,16 +720,11 @@ public: void OnMouseOver(Point pt, int widget) override { if (widget == WID_SL_DRIVES_DIRECTORIES_LIST) { - int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SL_DRIVES_DIRECTORIES_LIST, WidgetDimensions::scaled.inset.top); - if (y == INT_MAX) return; + auto it = this->vscroll->GetScrolledItemFromWidget(this->display_list, pt.y, this, WID_SL_DRIVES_DIRECTORIES_LIST, WidgetDimensions::scaled.inset.top); + if (it == this->display_list.end()) return; /* Get the corresponding non-filtered out item from the list */ - int i = 0; - while (i <= y) { - if (!this->fios_items_shown[i]) y++; - i++; - } - const FiosItem *file = &this->fios_items[y]; + const FiosItem *file = *it; if (file != this->highlighted) { this->highlighted = file; @@ -802,6 +786,35 @@ public: this->vscroll->SetCapacityFromWidget(this, WID_SL_DRIVES_DIRECTORIES_LIST); } + void BuildDisplayList() + { + /* Filter changes */ + this->display_list.clear(); + this->display_list.reserve(this->fios_items.size()); + + if (this->string_filter.IsEmpty()) { + /* We don't filter anything out if the filter editbox is empty */ + for (auto &it : this->fios_items) { + this->display_list.push_back(&it); + } + } else { + for (auto &it : this->fios_items) { + this->string_filter.ResetState(); + this->string_filter.AddLine(it.title); + /* We set the vector to show this fios element as filtered depending on the result of the filter */ + if (this->string_filter.GetState()) { + this->display_list.push_back(&it); + } else if (&it == this->selected) { + /* The selected element has been filtered out */ + this->selected = nullptr; + this->OnInvalidateData(SLIWD_SELECTION_CHANGES); + } + } + } + + this->vscroll->SetCount(this->display_list.size()); + } + /** * Some data on this window has become invalid. * @param data Information about the changed data. @@ -818,7 +831,6 @@ public: _fios_path_changed = true; this->fios_items.BuildFileList(this->abstract_filetype, this->fop); - this->vscroll->SetCount(this->fios_items.size()); this->selected = nullptr; _load_check_data.Clear(); @@ -857,30 +869,7 @@ public: break; case SLIWD_FILTER_CHANGES: - /* Filter changes */ - this->fios_items_shown.resize(this->fios_items.size()); - uint items_shown_count = 0; ///< The number of items shown in the list - /* We pass through every fios item */ - for (uint i = 0; i < this->fios_items.size(); i++) { - if (this->string_filter.IsEmpty()) { - /* We don't filter anything out if the filter editbox is empty */ - this->fios_items_shown[i] = true; - items_shown_count++; - } else { - this->string_filter.ResetState(); - this->string_filter.AddLine(this->fios_items[i].title.c_str()); - /* We set the vector to show this fios element as filtered depending on the result of the filter */ - this->fios_items_shown[i] = this->string_filter.GetState(); - if (this->fios_items_shown[i]) items_shown_count++; - - if (&(this->fios_items[i]) == this->selected && !this->fios_items_shown[i]) { - /* The selected element has been filtered out */ - this->selected = nullptr; - this->OnInvalidateData(SLIWD_SELECTION_CHANGES); - } - } - } - this->vscroll->SetCount(items_shown_count); + this->BuildDisplayList(); break; } } From 8d2a0a7da4a9e38c29680abdd29be2b05ac19061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 20 May 2023 16:43:22 +0200 Subject: [PATCH 11/58] Fix #10846: [Squirrel] Ensure sqvector size does not overflow (#10848) --- src/3rdparty/squirrel/squirrel/squtils.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/3rdparty/squirrel/squirrel/squtils.h b/src/3rdparty/squirrel/squirrel/squtils.h index d7d260dba4..2c7a343638 100644 --- a/src/3rdparty/squirrel/squirrel/squtils.h +++ b/src/3rdparty/squirrel/squirrel/squtils.h @@ -2,6 +2,9 @@ #ifndef _SQUTILS_H_ #define _SQUTILS_H_ +#include "../../fmt/format.h" +#include "../../../script/script_fatalerror.hpp" + void *sq_vm_malloc(SQUnsignedInteger size); void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size); void sq_vm_free(void *p,SQUnsignedInteger size); @@ -102,6 +105,10 @@ private: void _realloc(SQUnsignedInteger newsize) { newsize = (newsize > 0)?newsize:4; + if (newsize > SIZE_MAX / sizeof(T)) { + std::string msg = fmt::format("cannot resize to {}", newsize); + throw Script_FatalError(msg); + } _vals = (T*)SQ_REALLOC(_vals, _allocated * sizeof(T), newsize * sizeof(T)); _allocated = (size_t)newsize; } From 07860e67e26af5a7acfd86de6d6431695db9b173 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Fri, 19 May 2023 23:22:30 +0200 Subject: [PATCH 12/58] Codechange: use fmt::format_to to format the help message --- src/ai/ai.hpp | 4 ++-- src/ai/ai_core.cpp | 8 ++++---- src/base_media_base.h | 2 +- src/base_media_func.h | 22 +++++++++------------- src/blitter/factory.hpp | 10 ++++------ src/console_cmds.cpp | 31 +++++++++++++++---------------- src/debug.cpp | 24 ++++++++++-------------- src/debug.h | 2 +- src/driver.cpp | 14 +++++--------- src/driver.h | 2 +- src/game/game.hpp | 4 ++-- src/game/game_core.cpp | 8 ++++---- src/openttd.cpp | 35 ++++++++++++++++------------------- src/script/script_scanner.cpp | 11 ++++------- src/script/script_scanner.hpp | 4 +++- 15 files changed, 81 insertions(+), 100 deletions(-) diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp index ae19439754..b005f9c990 100644 --- a/src/ai/ai.hpp +++ b/src/ai/ai.hpp @@ -112,9 +112,9 @@ public: static void Save(CompanyID company); /** Wrapper function for AIScanner::GetAIConsoleList */ - static std::string GetConsoleList(bool newest_only = false); + static void GetConsoleList(std::back_insert_iterator &output_iterator, bool newest_only); /** Wrapper function for AIScanner::GetAIConsoleLibraryList */ - static std::string GetConsoleLibraryList(); + static void GetConsoleLibraryList(std::back_insert_iterator &output_iterator); /** Wrapper function for AIScanner::GetAIInfoList */ static const ScriptInfoList *GetInfoList(); /** Wrapper function for AIScanner::GetUniqueAIInfoList */ diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp index 0660dfe0f5..1251b652af 100644 --- a/src/ai/ai_core.cpp +++ b/src/ai/ai_core.cpp @@ -289,14 +289,14 @@ } } -/* static */ std::string AI::GetConsoleList(bool newest_only) +/* static */ void AI::GetConsoleList(std::back_insert_iterator &output_iterator, bool newest_only) { - return AI::scanner_info->GetConsoleList(newest_only); + AI::scanner_info->GetConsoleList(output_iterator, newest_only); } -/* static */ std::string AI::GetConsoleLibraryList() +/* static */ void AI::GetConsoleLibraryList(std::back_insert_iterator &output_iterator) { - return AI::scanner_library->GetConsoleList(true); + AI::scanner_library->GetConsoleList(output_iterator, true); } /* static */ const ScriptInfoList *AI::GetInfoList() diff --git a/src/base_media_base.h b/src/base_media_base.h index 9131cbae58..7c36002946 100644 --- a/src/base_media_base.h +++ b/src/base_media_base.h @@ -192,7 +192,7 @@ public: static Tbase_set *GetAvailableSets(); static bool SetSet(const std::string &name); - static char *GetSetsList(char *p, const char *last); + static void GetSetsList(std::back_insert_iterator &output_iterator); static int GetNumSets(); static int GetIndexOfUsedSet(); static const Tbase_set *GetSet(int index); diff --git a/src/base_media_func.h b/src/base_media_func.h index 102f0a412b..b5ffe7dc8a 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -248,31 +248,27 @@ template /** * Returns a list with the sets. - * @param p where to print to - * @param last the last character to print to - * @return the last printed character + * @param output_iterator The iterator to write the string to. */ template -/* static */ char *BaseMedia::GetSetsList(char *p, const char *last) +/* static */ void BaseMedia::GetSetsList(std::back_insert_iterator &output_iterator) { - p += seprintf(p, last, "List of " SET_TYPE " sets:\n"); + fmt::format_to(output_iterator, "List of " SET_TYPE " sets:\n"); for (const Tbase_set *s = BaseMedia::available_sets; s != nullptr; s = s->next) { - p += seprintf(p, last, "%18s: %s", s->name.c_str(), s->GetDescription({})); + fmt::format_to(output_iterator, "{:>18}: {}", s->name, s->GetDescription({})); int invalid = s->GetNumInvalid(); if (invalid != 0) { int missing = s->GetNumMissing(); if (missing == 0) { - p += seprintf(p, last, " (%i corrupt file%s)\n", invalid, invalid == 1 ? "" : "s"); + fmt::format_to(output_iterator, " ({} corrupt file{})\n", invalid, invalid == 1 ? "" : "s"); } else { - p += seprintf(p, last, " (unusable: %i missing file%s)\n", missing, missing == 1 ? "" : "s"); + fmt::format_to(output_iterator, " (unusable: {} missing file{})\n", missing, missing == 1 ? "" : "s"); } } else { - p += seprintf(p, last, "\n"); + fmt::format_to(output_iterator, "\n"); } } - p += seprintf(p, last, "\n"); - - return p; + fmt::format_to(output_iterator, "\n"); } #include "network/core/tcp_content_type.h" @@ -378,7 +374,7 @@ template template bool repl_type::AddFile(const std::string &filename, size_t pathlength, const std::string &tar_filename); \ template bool repl_type::HasSet(const struct ContentInfo *ci, bool md5sum); \ template bool repl_type::SetSet(const std::string &name); \ - template char *repl_type::GetSetsList(char *p, const char *last); \ + template void repl_type::GetSetsList(std::back_insert_iterator &output_iterator); \ template int repl_type::GetNumSets(); \ template int repl_type::GetIndexOfUsedSet(); \ template const set_type *repl_type::GetSet(int index); \ diff --git a/src/blitter/factory.hpp b/src/blitter/factory.hpp index 77911fc31c..3def36d0ad 100644 --- a/src/blitter/factory.hpp +++ b/src/blitter/factory.hpp @@ -146,16 +146,14 @@ public: * @param last The last element of the buffer. * @return p The location till where we filled the buffer. */ - static char *GetBlittersInfo(char *p, const char *last) + static void GetBlittersInfo(std::back_insert_iterator &output_iterator) { - p += seprintf(p, last, "List of blitters:\n"); + fmt::format_to(output_iterator, "List of blitters:\n"); for (auto &it : GetBlitters()) { BlitterFactory *b = it.second; - p += seprintf(p, last, "%18s: %s\n", b->name.c_str(), b->GetDescription().c_str()); + fmt::format_to(output_iterator, "{:>18}: {}\n", b->name, b->GetDescription()); } - p += seprintf(p, last, "\n"); - - return p; + fmt::format_to(output_iterator, "\n"); } /** diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 8519c0d217..0980ffcfce 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1180,6 +1180,17 @@ static void PrintLineByLine(const std::string &full_string) } } +template +bool PrintList(F list_function, Args... args) +{ + std::string output_str; + auto inserter = std::back_inserter(output_str); + list_function(inserter, args...); + PrintLineByLine(output_str); + + return true; +} + DEF_CONSOLE_CMD(ConListAILibs) { if (argc == 0) { @@ -1187,10 +1198,7 @@ DEF_CONSOLE_CMD(ConListAILibs) return true; } - const std::string output_str = AI::GetConsoleLibraryList(); - PrintLineByLine(output_str); - - return true; + return PrintList(AI::GetConsoleLibraryList); } DEF_CONSOLE_CMD(ConListAI) @@ -1200,10 +1208,7 @@ DEF_CONSOLE_CMD(ConListAI) return true; } - const std::string output_str = AI::GetConsoleList(); - PrintLineByLine(output_str); - - return true; + return PrintList(AI::GetConsoleList, false); } DEF_CONSOLE_CMD(ConListGameLibs) @@ -1213,10 +1218,7 @@ DEF_CONSOLE_CMD(ConListGameLibs) return true; } - const std::string output_str = Game::GetConsoleLibraryList(); - PrintLineByLine(output_str); - - return true; + return PrintList(Game::GetConsoleLibraryList); } DEF_CONSOLE_CMD(ConListGame) @@ -1226,10 +1228,7 @@ DEF_CONSOLE_CMD(ConListGame) return true; } - const std::string output_str = Game::GetConsoleList(); - PrintLineByLine(output_str); - - return true; + return PrintList(Game::GetConsoleList, false); } DEF_CONSOLE_CMD(ConStartAI) diff --git a/src/debug.cpp b/src/debug.cpp index c82d75236e..b088af9aaa 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -85,27 +85,23 @@ struct DebugLevel { /** * Dump the available debug facility names in the help text. - * @param buf Start address for storing the output. - * @param last Last valid address for storing the output. - * @return Next free position in the output. + * @param output_iterator The iterator to write the string to. */ -char *DumpDebugFacilityNames(char *buf, char *last) +void DumpDebugFacilityNames(std::back_insert_iterator &output_iterator) { - size_t length = 0; + bool written = false; for (const DebugLevel *i = debug_level; i != endof(debug_level); ++i) { - if (length == 0) { - buf = strecpy(buf, "List of debug facility names:\n", last); + if (!written) { + fmt::format_to(output_iterator, "List of debug facility names:\n"); } else { - buf = strecpy(buf, ", ", last); - length += 2; + fmt::format_to(output_iterator, ", "); } - buf = strecpy(buf, i->name, last); - length += strlen(i->name); + fmt::format_to(output_iterator, i->name); + written = true; } - if (length > 0) { - buf = strecpy(buf, "\n\n", last); + if (written) { + fmt::format_to(output_iterator, "\n\n"); } - return buf; } /** diff --git a/src/debug.h b/src/debug.h index 125c5403c5..bb939ccc4c 100644 --- a/src/debug.h +++ b/src/debug.h @@ -56,7 +56,7 @@ extern int _debug_console_level; extern int _debug_random_level; #endif -char *DumpDebugFacilityNames(char *buf, char *last); +void DumpDebugFacilityNames(std::back_insert_iterator &output_iterator); void SetDebugString(const char *s, void (*error_func)(const std::string &)); const char *GetDebugString(); diff --git a/src/driver.cpp b/src/driver.cpp index eefcbbcb12..10a20599b1 100644 --- a/src/driver.cpp +++ b/src/driver.cpp @@ -179,28 +179,24 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t /** * Build a human readable list of available drivers, grouped by type. - * @param p The buffer to write to. - * @param last The last element in the buffer. - * @return The end of the written buffer. + * @param output_iterator The iterator to write the string to. */ -char *DriverFactoryBase::GetDriversInfo(char *p, const char *last) +void DriverFactoryBase::GetDriversInfo(std::back_insert_iterator &output_iterator) { for (Driver::Type type = Driver::DT_BEGIN; type != Driver::DT_END; type++) { - p += seprintf(p, last, "List of %s drivers:\n", GetDriverTypeName(type)); + fmt::format_to(output_iterator, "List of {} drivers:\n", GetDriverTypeName(type)); for (int priority = 10; priority >= 0; priority--) { for (auto &it : GetDrivers()) { DriverFactoryBase *d = it.second; if (d->type != type) continue; if (d->priority != priority) continue; - p += seprintf(p, last, "%18s: %s\n", d->name, d->GetDescription()); + fmt::format_to(output_iterator, "{:>18}: {}\n", d->name, d->GetDescription()); } } - p += seprintf(p, last, "\n"); + fmt::format_to(output_iterator, "\n"); } - - return p; } /** diff --git a/src/driver.h b/src/driver.h index 91287fbdb2..80b05b41bd 100644 --- a/src/driver.h +++ b/src/driver.h @@ -127,7 +127,7 @@ public: } static void SelectDriver(const std::string &name, Driver::Type type); - static char *GetDriversInfo(char *p, const char *last); + static void GetDriversInfo(std::back_insert_iterator &output_iterator); /** * Get a nice description of the driver-class. diff --git a/src/game/game.hpp b/src/game/game.hpp index 3c8fcdad92..c3664cd696 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -83,9 +83,9 @@ public: static void Save(); /** Wrapper function for GameScanner::GetConsoleList */ - static std::string GetConsoleList(bool newest_only = false); + static void GetConsoleList(std::back_insert_iterator &output_iterator, bool newest_only); /** Wrapper function for GameScanner::GetConsoleLibraryList */ - static std::string GetConsoleLibraryList(); + static void GetConsoleLibraryList(std::back_insert_iterator &output_iterator); /** Wrapper function for GameScanner::GetInfoList */ static const ScriptInfoList *GetInfoList(); /** Wrapper function for GameScanner::GetUniqueInfoList */ diff --git a/src/game/game_core.cpp b/src/game/game_core.cpp index c67446f7cc..865aa2a8fc 100644 --- a/src/game/game_core.cpp +++ b/src/game/game_core.cpp @@ -219,14 +219,14 @@ } } -/* static */ std::string Game::GetConsoleList(bool newest_only) +/* static */ void Game::GetConsoleList(std::back_insert_iterator &output_iterator, bool newest_only) { - return Game::scanner_info->GetConsoleList(newest_only); + Game::scanner_info->GetConsoleList(output_iterator, newest_only); } -/* static */ std::string Game::GetConsoleLibraryList() +/* static */ void Game::GetConsoleLibraryList(std::back_insert_iterator &output_iterator) { - return Game::scanner_library->GetConsoleList(true); + Game::scanner_library->GetConsoleList(output_iterator, true); } /* static */ const ScriptInfoList *Game::GetInfoList() diff --git a/src/openttd.cpp b/src/openttd.cpp index 6444d410b9..61c0e1da9d 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -155,11 +155,12 @@ void FatalErrorI(const std::string &str) */ static void ShowHelp() { - char buf[8192]; - char *p = buf; + std::string str; + str.reserve(8192); - p += seprintf(p, lastof(buf), "OpenTTD %s\n", _openttd_revision); - p = strecpy(p, + std::back_insert_iterator output_iterator = std::back_inserter(str); + fmt::format_to(output_iterator, "OpenTTD {}\n", _openttd_revision); + str += "\n" "\n" "Command line options:\n" @@ -191,46 +192,42 @@ static void ShowHelp() " -q savegame = Write some information about the savegame and exit\n" " -Q = Don't scan for/load NewGRF files on startup\n" " -QQ = Disable NewGRF scanning/loading entirely\n" - "\n", - lastof(buf) - ); + "\n"; /* List the graphics packs */ - p = BaseGraphics::GetSetsList(p, lastof(buf)); + BaseGraphics::GetSetsList(output_iterator); /* List the sounds packs */ - p = BaseSounds::GetSetsList(p, lastof(buf)); + BaseSounds::GetSetsList(output_iterator); /* List the music packs */ - p = BaseMusic::GetSetsList(p, lastof(buf)); + BaseMusic::GetSetsList(output_iterator); /* List the drivers */ - p = DriverFactoryBase::GetDriversInfo(p, lastof(buf)); + DriverFactoryBase::GetDriversInfo(output_iterator); /* List the blitters */ - p = BlitterFactory::GetBlittersInfo(p, lastof(buf)); + BlitterFactory::GetBlittersInfo(output_iterator); /* List the debug facilities. */ - p = DumpDebugFacilityNames(p, lastof(buf)); + DumpDebugFacilityNames(output_iterator); /* We need to initialize the AI, so it finds the AIs */ AI::Initialize(); - const std::string ai_list = AI::GetConsoleList(true); - p = strecpy(p, ai_list.c_str(), lastof(buf)); + AI::GetConsoleList(output_iterator, true); AI::Uninitialize(true); /* We need to initialize the GameScript, so it finds the GSs */ Game::Initialize(); - const std::string game_list = Game::GetConsoleList(true); - p = strecpy(p, game_list.c_str(), lastof(buf)); + Game::GetConsoleList(output_iterator, true); Game::Uninitialize(true); /* ShowInfo put output to stderr, but version information should go * to stdout; this is the only exception */ #if !defined(_WIN32) - printf("%s\n", buf); + printf("%s\n", str.c_str()); #else - ShowInfoI(buf); + ShowInfoI(str); #endif } diff --git a/src/script/script_scanner.cpp b/src/script/script_scanner.cpp index 850ae245bf..bb5bd413fc 100644 --- a/src/script/script_scanner.cpp +++ b/src/script/script_scanner.cpp @@ -138,18 +138,15 @@ void ScriptScanner::RegisterScript(ScriptInfo *info) } } -std::string ScriptScanner::GetConsoleList(bool newest_only) const +void ScriptScanner::GetConsoleList(std::back_insert_iterator &output_iterator, bool newest_only) const { - std::string p; - p += fmt::format("List of {}:\n", this->GetScannerName()); + fmt::format_to(output_iterator, "List of {}:\n", this->GetScannerName()); const ScriptInfoList &list = newest_only ? this->info_single_list : this->info_list; for (const auto &item : list) { ScriptInfo *i = item.second; - p += fmt::format("{:>10} (v{:d}): {}\n", i->GetName(), i->GetVersion(), i->GetDescription()); + fmt::format_to(output_iterator, "{:>10} (v{:d}): {}\n", i->GetName(), i->GetVersion(), i->GetDescription()); } - p += "\n"; - - return p; + fmt::format_to(output_iterator, "\n"); } /** Helper for creating a MD5sum of all files within of a script. */ diff --git a/src/script/script_scanner.hpp b/src/script/script_scanner.hpp index 9b0ad6bf31..1f5dca0485 100644 --- a/src/script/script_scanner.hpp +++ b/src/script/script_scanner.hpp @@ -55,8 +55,10 @@ public: /** * Get the list of registered scripts to print on the console. + * @param output_iterator The iterator to write the output to. + * @param newest_only Whether to only show the newest scripts. */ - std::string GetConsoleList(bool newest_only) const; + void GetConsoleList(std::back_insert_iterator &output_iterator, bool newest_only) const; /** * Check whether we have a script with the exact characteristics as ci. From 80d8c01814a6561ec23659d382cf92e3b642f2fd Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 18 May 2023 11:02:36 +0200 Subject: [PATCH 13/58] Codechange: replace std::vector + duplicate preventing include with std::set --- src/hotkeys.cpp | 8 ++++---- src/hotkeys.h | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp index 2e8e56a3f3..7e62cc5481 100644 --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -213,9 +213,9 @@ static std::string KeycodeToString(uint16 keycode) std::string SaveKeycodes(const Hotkey *hotkey) { std::string str; - for (uint i = 0; i < hotkey->keycodes.size(); i++) { - if (i > 0) str += ","; - str += KeycodeToString(hotkey->keycodes[i]); + for (auto keycode : hotkey->keycodes) { + if (!str.empty()) str += ","; + str += KeycodeToString(keycode); } return str; } @@ -257,7 +257,7 @@ Hotkey::Hotkey(const uint16 *default_keycodes, const char *name, int num) : */ void Hotkey::AddKeycode(uint16 keycode) { - include(this->keycodes, keycode); + this->keycodes.insert(keycode); } HotkeyList::HotkeyList(const char *ini_group, Hotkey *items, GlobalHotkeyHandlerFunc global_hotkey_handler) : diff --git a/src/hotkeys.h b/src/hotkeys.h index 59fec34570..781feb2185 100644 --- a/src/hotkeys.h +++ b/src/hotkeys.h @@ -10,7 +10,6 @@ #ifndef HOTKEYS_H #define HOTKEYS_H -#include "core/smallvec_type.hpp" #include "gfx_type.h" #include "window_type.h" #include "string_type.h" @@ -27,7 +26,7 @@ struct Hotkey { const char *name; int num; - std::vector keycodes; + std::set keycodes; }; #define HOTKEY_LIST_END Hotkey((uint16)0, nullptr, -1) From 3323402aaa0d02a87f32a9469ee4e2fdb579bbaa Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 18 May 2023 11:20:35 +0200 Subject: [PATCH 14/58] Codechange: rename smallvec_type to container_func and use only when needed --- src/animated_tile.cpp | 2 +- src/core/CMakeLists.txt | 2 +- .../{smallvec_type.hpp => container_func.hpp} | 37 +++++++++---------- src/core/pool_type.hpp | 1 - src/core/smallstack_type.hpp | 1 - src/economy.cpp | 1 + src/error_gui.cpp | 1 + src/game/game_text.hpp | 2 - src/gfx.cpp | 1 + src/gfx_layout.cpp | 1 + src/group_gui.cpp | 1 + src/language.h | 1 - src/music/midifile.cpp | 1 + src/music/midifile.hpp | 1 - src/music/win32_m.cpp | 1 + src/music_gui.cpp | 1 + src/network/network_content.h | 1 + src/newgrf.cpp | 1 + src/newgrf.h | 2 +- src/newgrf_commons.h | 1 - src/newgrf_debug.h | 1 - src/newgrf_engine.cpp | 1 + src/newgrf_railtype.cpp | 1 + src/newgrf_roadtype.cpp | 1 + src/newgrf_text.h | 1 - src/rail_cmd.cpp | 1 + src/road_cmd.cpp | 1 + src/saveload/animated_tile_sl.cpp | 1 - src/saveload/oldloader_sl.cpp | 1 - src/settings_func.h | 1 - src/settingsgen/settingsgen.cpp | 2 +- src/sortlist_type.h | 2 +- src/station_cmd.cpp | 1 + src/strgen/strgen.cpp | 1 + src/strgen/strgen_base.cpp | 1 + src/stringfilter_type.h | 1 - src/subsidy.cpp | 1 + src/texteff.cpp | 1 - src/vehicle.cpp | 1 + src/vehicle_base.h | 1 + src/vehicle_gui.cpp | 1 + src/vehicle_gui_base.h | 1 - src/vehiclelist.h | 1 - src/viewport_sprite_sorter.h | 1 - src/widgets/dropdown_type.h | 1 - src/window_gui.h | 1 - 46 files changed, 45 insertions(+), 43 deletions(-) rename src/core/{smallvec_type.hpp => container_func.hpp} (51%) diff --git a/src/animated_tile.cpp b/src/animated_tile.cpp index e31d1b8e03..0b5401564c 100644 --- a/src/animated_tile.cpp +++ b/src/animated_tile.cpp @@ -8,7 +8,7 @@ /** @file animated_tile.cpp Everything related to animated tiles. */ #include "stdafx.h" -#include "core/smallvec_type.hpp" +#include "core/container_func.hpp" #include "tile_cmd.h" #include "viewport_func.h" #include "framerate_type.h" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b0576299a2..afe1112316 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -23,7 +23,7 @@ add_files( random_func.cpp random_func.hpp smallstack_type.hpp - smallvec_type.hpp + container_func.hpp span_type.hpp strong_typedef_type.hpp ) diff --git a/src/core/smallvec_type.hpp b/src/core/container_func.hpp similarity index 51% rename from src/core/smallvec_type.hpp rename to src/core/container_func.hpp index 086e2b531f..5a713649aa 100644 --- a/src/core/smallvec_type.hpp +++ b/src/core/container_func.hpp @@ -5,46 +5,45 @@ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ -/** @file smallvec_type.hpp Simple vector class that allows allocating an item without the need to copy this->data needlessly. */ +/** @file container_func.hpp Some simple functions to help with accessing containers. */ -#ifndef SMALLVEC_TYPE_HPP -#define SMALLVEC_TYPE_HPP - -#include "mem_func.hpp" +#ifndef CONTAINER_FUNC_HPP +#define CONTAINER_FUNC_HPP /** - * Helper function to append an item to a vector if it is not already contained - * Consider using std::set, std::unordered_set or std::flat_set in new code + * Helper function to append an item to a container if it is not already contained. + * The container must have a \c emplace_back function. + * Consider using std::set, std::unordered_set or std::flat_set in new code. * - * @param vec A reference to the vector to be extended + * @param container A reference to the container to be extended * @param item Reference to the item to be copy-constructed if not found * * @return Whether the item was already present */ -template -inline bool include(std::vector& vec, const T &item) +template +inline bool include(Container &container, typename Container::const_reference &item) { - const bool is_member = std::find(vec.begin(), vec.end(), item) != vec.end(); - if (!is_member) vec.emplace_back(item); + const bool is_member = std::find(container.begin(), container.end(), item) != container.end(); + if (!is_member) container.emplace_back(item); return is_member; } /** * Helper function to get the index of an item - * Consider using std::set, std::unordered_set or std::flat_set in new code + * Consider using std::set, std::unordered_set or std::flat_set in new code. * - * @param vec A reference to the vector to be extended + * @param container A reference to the container to be searched. * @param item Reference to the item to be search for * * @return Index of element if found, otherwise -1 */ -template -int find_index(std::vector const& vec, T const& item) +template +int find_index(Container const &container, typename Container::const_reference item) { - auto const it = std::find(vec.begin(), vec.end(), item); - if (it != vec.end()) return it - vec.begin(); + auto const it = std::find(container.begin(), container.end(), item); + if (it != container.end()) return std::distance(container.begin(), it); return -1; } -#endif /* SMALLVEC_TYPE_HPP */ +#endif /* CONTAINER_FUNC_HPP */ diff --git a/src/core/pool_type.hpp b/src/core/pool_type.hpp index 20e8f8b29f..d325327521 100644 --- a/src/core/pool_type.hpp +++ b/src/core/pool_type.hpp @@ -10,7 +10,6 @@ #ifndef POOL_TYPE_HPP #define POOL_TYPE_HPP -#include "smallvec_type.hpp" #include "enum_type.hpp" /** Various types of a pool. */ diff --git a/src/core/smallstack_type.hpp b/src/core/smallstack_type.hpp index c05454b8ae..503d027bf3 100644 --- a/src/core/smallstack_type.hpp +++ b/src/core/smallstack_type.hpp @@ -10,7 +10,6 @@ #ifndef SMALLSTACK_TYPE_HPP #define SMALLSTACK_TYPE_HPP -#include "smallvec_type.hpp" #include /** diff --git a/src/economy.cpp b/src/economy.cpp index 644d6e023c..b4bc993f79 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -41,6 +41,7 @@ #include "economy_base.h" #include "core/pool_func.hpp" #include "core/backup_type.hpp" +#include "core/container_func.hpp" #include "cargo_type.h" #include "water.h" #include "game/game.hpp" diff --git a/src/error_gui.cpp b/src/error_gui.cpp index 117a9db8ee..ad0758fc24 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -8,6 +8,7 @@ /** @file error_gui.cpp GUI related to errors. */ #include "stdafx.h" +#include "core/mem_func.hpp" #include "landscape.h" #include "newgrf_text.h" #include "error.h" diff --git a/src/game/game_text.hpp b/src/game/game_text.hpp index 0f4c49698d..619d094e6c 100644 --- a/src/game/game_text.hpp +++ b/src/game/game_text.hpp @@ -10,8 +10,6 @@ #ifndef GAME_TEXT_HPP #define GAME_TEXT_HPP -#include "../core/smallvec_type.hpp" - struct StringParam { enum ParamType { RAW_STRING, diff --git a/src/gfx.cpp b/src/gfx.cpp index f9d587e258..d2f826366e 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -22,6 +22,7 @@ #include "newgrf_debug.h" #include "thread.h" #include "core/backup_type.hpp" +#include "core/container_func.hpp" #include "viewport_func.h" #include "table/palettes.h" diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp index 73420b0add..9de917a80e 100644 --- a/src/gfx_layout.cpp +++ b/src/gfx_layout.cpp @@ -8,6 +8,7 @@ /** @file gfx_layout.cpp Handling of laying out text. */ #include "stdafx.h" +#include "core/math_func.hpp" #include "gfx_layout.h" #include "string_func.h" #include "debug.h" diff --git a/src/group_gui.cpp b/src/group_gui.cpp index 9f72b22235..d2ddf10b0e 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -22,6 +22,7 @@ #include "tilehighlight_func.h" #include "vehicle_gui_base.h" #include "core/geometry_func.hpp" +#include "core/container_func.hpp" #include "company_base.h" #include "company_gui.h" #include "gui.h" diff --git a/src/language.h b/src/language.h index 9d2499068b..0966ccb197 100644 --- a/src/language.h +++ b/src/language.h @@ -10,7 +10,6 @@ #ifndef LANGUAGE_H #define LANGUAGE_H -#include "core/smallvec_type.hpp" #ifdef WITH_ICU_I18N #include #endif /* WITH_ICU_I18N */ diff --git a/src/music/midifile.cpp b/src/music/midifile.cpp index 894de4f072..4598569a01 100644 --- a/src/music/midifile.cpp +++ b/src/music/midifile.cpp @@ -12,6 +12,7 @@ #include "../fileio_type.h" #include "../string_func.h" #include "../core/endian_func.hpp" +#include "../core/mem_func.hpp" #include "../base_media_base.h" #include "midi.h" diff --git a/src/music/midifile.hpp b/src/music/midifile.hpp index 254fda9af0..16e6678e27 100644 --- a/src/music/midifile.hpp +++ b/src/music/midifile.hpp @@ -11,7 +11,6 @@ #define MUSIC_MIDIFILE_HPP #include "../stdafx.h" -#include "../core/smallvec_type.hpp" #include "midi.h" struct MusicSongInfo; diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp index 8ff8a72ac9..0f2ee1652a 100644 --- a/src/music/win32_m.cpp +++ b/src/music/win32_m.cpp @@ -17,6 +17,7 @@ #include "midifile.hpp" #include "midi.h" #include "../base_media_base.h" +#include "../core/mem_func.hpp" #include #include "../safeguards.h" diff --git a/src/music_gui.cpp b/src/music_gui.cpp index 1bf66211f1..63f8e50242 100644 --- a/src/music_gui.cpp +++ b/src/music_gui.cpp @@ -18,6 +18,7 @@ #include "gfx_func.h" #include "zoom_func.h" #include "core/random_func.hpp" +#include "core/mem_func.hpp" #include "error.h" #include "core/geometry_func.hpp" #include "string_func.h" diff --git a/src/network/network_content.h b/src/network/network_content.h index a11d29b991..3172564a7c 100644 --- a/src/network/network_content.h +++ b/src/network/network_content.h @@ -13,6 +13,7 @@ #include "core/tcp_content.h" #include "core/http.h" #include +#include "../core/container_func.hpp" /** Vector with content info */ typedef std::vector ContentVector; diff --git a/src/newgrf.cpp b/src/newgrf.cpp index ba49f32c92..ec1257223f 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -9,6 +9,7 @@ #include "stdafx.h" +#include "core/container_func.hpp" #include "debug.h" #include "fileio_func.h" #include "engine_func.h" diff --git a/src/newgrf.h b/src/newgrf.h index 50f2897d5b..af7bb4b85c 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -16,7 +16,7 @@ #include "fileio_type.h" #include "core/bitmath_func.hpp" #include "core/alloc_type.hpp" -#include "core/smallvec_type.hpp" +#include "core/mem_func.hpp" /** * List of different canal 'features'. diff --git a/src/newgrf_commons.h b/src/newgrf_commons.h index 4a4df50707..c83653ccf0 100644 --- a/src/newgrf_commons.h +++ b/src/newgrf_commons.h @@ -15,7 +15,6 @@ #include "sprite.h" #include "core/alloc_type.hpp" -#include "core/smallvec_type.hpp" #include "command_type.h" #include "direction_type.h" #include "company_type.h" diff --git a/src/newgrf_debug.h b/src/newgrf_debug.h index 34530dc759..56a9c60117 100644 --- a/src/newgrf_debug.h +++ b/src/newgrf_debug.h @@ -11,7 +11,6 @@ #define NEWGRF_DEBUG_H #include "newgrf.h" -#include "core/smallvec_type.hpp" #include "tile_type.h" #include "vehicle_type.h" diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index b276cb1385..1719057539 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -17,6 +17,7 @@ #include "timer/timer_game_calendar.h" #include "vehicle_func.h" #include "core/random_func.hpp" +#include "core/container_func.hpp" #include "aircraft.h" #include "station_base.h" #include "company_base.h" diff --git a/src/newgrf_railtype.cpp b/src/newgrf_railtype.cpp index 848d51f230..7309df603b 100644 --- a/src/newgrf_railtype.cpp +++ b/src/newgrf_railtype.cpp @@ -8,6 +8,7 @@ /** @file newgrf_railtype.cpp NewGRF handling of rail types. */ #include "stdafx.h" +#include "core/container_func.hpp" #include "debug.h" #include "newgrf_railtype.h" #include "timer/timer_game_calendar.h" diff --git a/src/newgrf_roadtype.cpp b/src/newgrf_roadtype.cpp index ecd1ce54e5..c51963d171 100644 --- a/src/newgrf_roadtype.cpp +++ b/src/newgrf_roadtype.cpp @@ -8,6 +8,7 @@ /** @file newgrf_roadtype.cpp NewGRF handling of road types. */ #include "stdafx.h" +#include "core/container_func.hpp" #include "debug.h" #include "newgrf_roadtype.h" #include "timer/timer_game_calendar.h" diff --git a/src/newgrf_text.h b/src/newgrf_text.h index 82b27a57eb..03d4822052 100644 --- a/src/newgrf_text.h +++ b/src/newgrf_text.h @@ -12,7 +12,6 @@ #include "string_type.h" #include "strings_type.h" -#include "core/smallvec_type.hpp" #include "table/control_codes.h" #include diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index a2c97af8b7..1f44022211 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -26,6 +26,7 @@ #include "pbs.h" #include "company_base.h" #include "core/backup_type.hpp" +#include "core/container_func.hpp" #include "timer/timer_game_calendar.h" #include "strings_func.h" #include "company_gui.h" diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 6766a299f4..0f112dc0a7 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -30,6 +30,7 @@ #include "town.h" #include "company_base.h" #include "core/random_func.hpp" +#include "core/container_func.hpp" #include "newgrf_debug.h" #include "newgrf_railtype.h" #include "newgrf_roadtype.h" diff --git a/src/saveload/animated_tile_sl.cpp b/src/saveload/animated_tile_sl.cpp index a36366125a..950cc9c20c 100644 --- a/src/saveload/animated_tile_sl.cpp +++ b/src/saveload/animated_tile_sl.cpp @@ -13,7 +13,6 @@ #include "compat/animated_tile_sl_compat.h" #include "../tile_type.h" -#include "../core/smallvec_type.hpp" #include "../safeguards.h" diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index 55a03cd846..56705daf87 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -27,7 +27,6 @@ #include "../engine_func.h" #include "../company_base.h" #include "../disaster_vehicle.h" -#include "../core/smallvec_type.hpp" #include "../timer/timer.h" #include "../timer/timer_game_tick.h" #include "../timer/timer_game_calendar.h" diff --git a/src/settings_func.h b/src/settings_func.h index 9f56b6de14..198bab3e7d 100644 --- a/src/settings_func.h +++ b/src/settings_func.h @@ -10,7 +10,6 @@ #ifndef SETTINGS_FUNC_H #define SETTINGS_FUNC_H -#include "core/smallvec_type.hpp" #include "company_type.h" #include "string_type.h" diff --git a/src/settingsgen/settingsgen.cpp b/src/settingsgen/settingsgen.cpp index 8952f9ee77..603278859d 100644 --- a/src/settingsgen/settingsgen.cpp +++ b/src/settingsgen/settingsgen.cpp @@ -12,7 +12,7 @@ #include "../strings_type.h" #include "../misc/getoptdata.h" #include "../ini_type.h" -#include "../core/smallvec_type.hpp" +#include "../core/mem_func.hpp" #include "../error_func.h" #if !defined(_WIN32) || defined(__CYGWIN__) diff --git a/src/sortlist_type.h b/src/sortlist_type.h index 9c14d382bd..21cbca16fa 100644 --- a/src/sortlist_type.h +++ b/src/sortlist_type.h @@ -12,7 +12,7 @@ #include "core/enum_type.hpp" #include "core/bitmath_func.hpp" -#include "core/smallvec_type.hpp" +#include "core/mem_func.hpp" #include "date_type.h" /** Flags of the sort list. */ diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 20b2143f93..d09710dcbc 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -45,6 +45,7 @@ #include "pbs.h" #include "debug.h" #include "core/random_func.hpp" +#include "core/container_func.hpp" #include "company_base.h" #include "table/airporttile_ids.h" #include "newgrf_airporttiles.h" diff --git a/src/strgen/strgen.cpp b/src/strgen/strgen.cpp index bd32b4f352..e4f4f55ed7 100644 --- a/src/strgen/strgen.cpp +++ b/src/strgen/strgen.cpp @@ -9,6 +9,7 @@ #include "../stdafx.h" #include "../core/endian_func.hpp" +#include "../core/mem_func.hpp" #include "../error_func.h" #include "../string_func.h" #include "../strings_type.h" diff --git a/src/strgen/strgen_base.cpp b/src/strgen/strgen_base.cpp index 4f293dc41c..4f190f47e2 100644 --- a/src/strgen/strgen_base.cpp +++ b/src/strgen/strgen_base.cpp @@ -10,6 +10,7 @@ #include "../stdafx.h" #include "../core/alloc_func.hpp" #include "../core/endian_func.hpp" +#include "../core/mem_func.hpp" #include "../error_func.h" #include "../string_func.h" #include "../table/control_codes.h" diff --git a/src/stringfilter_type.h b/src/stringfilter_type.h index d71a1115d7..5b24587cb5 100644 --- a/src/stringfilter_type.h +++ b/src/stringfilter_type.h @@ -10,7 +10,6 @@ #ifndef STRINGFILTER_TYPE_H #define STRINGFILTER_TYPE_H -#include "core/smallvec_type.hpp" #include "strings_type.h" /** diff --git a/src/subsidy.cpp b/src/subsidy.cpp index 21649c9dd5..88879592eb 100644 --- a/src/subsidy.cpp +++ b/src/subsidy.cpp @@ -20,6 +20,7 @@ #include "subsidy_func.h" #include "core/pool_func.hpp" #include "core/random_func.hpp" +#include "core/container_func.hpp" #include "game/game.hpp" #include "command_func.h" #include "string_func.h" diff --git a/src/texteff.cpp b/src/texteff.cpp index 3235b8720d..6695d29944 100644 --- a/src/texteff.cpp +++ b/src/texteff.cpp @@ -11,7 +11,6 @@ #include "texteff.hpp" #include "transparency.h" #include "strings_func.h" -#include "core/smallvec_type.hpp" #include "viewport_func.h" #include "settings_type.h" #include "command_type.h" diff --git a/src/vehicle.cpp b/src/vehicle.cpp index b61764e261..89880910ff 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -38,6 +38,7 @@ #include "roadstop_base.h" #include "core/random_func.hpp" #include "core/backup_type.hpp" +#include "core/container_func.hpp" #include "order_backup.h" #include "sound_func.h" #include "effectvehicle_func.h" diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 8aa5fc0873..f725784d0c 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -23,6 +23,7 @@ #include "network/network.h" #include "saveload/saveload.h" #include "timer/timer_game_calendar.h" +#include "core/mem_func.hpp" const uint TILE_AXIAL_DISTANCE = 192; // Logical length of the tile in any DiagDirection used in vehicle movement. const uint TILE_CORNER_DISTANCE = 128; // Logical length of the tile corner crossing in any non-diagonal direction used in vehicle movement. diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 77782f070d..345771a84d 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -31,6 +31,7 @@ #include "articulated_vehicles.h" #include "spritecache.h" #include "core/geometry_func.hpp" +#include "core/container_func.hpp" #include "company_base.h" #include "engine_func.h" #include "station_base.h" diff --git a/src/vehicle_gui_base.h b/src/vehicle_gui_base.h index 8e6c272448..86c722bfd6 100644 --- a/src/vehicle_gui_base.h +++ b/src/vehicle_gui_base.h @@ -10,7 +10,6 @@ #ifndef VEHICLE_GUI_BASE_H #define VEHICLE_GUI_BASE_H -#include "core/smallvec_type.hpp" #include "cargo_type.h" #include "timer/timer_game_calendar.h" #include "economy_type.h" diff --git a/src/vehiclelist.h b/src/vehiclelist.h index 6f6e5cb5fe..df9649d5e3 100644 --- a/src/vehiclelist.h +++ b/src/vehiclelist.h @@ -10,7 +10,6 @@ #ifndef VEHICLELIST_H #define VEHICLELIST_H -#include "core/smallvec_type.hpp" #include "vehicle_type.h" #include "company_type.h" #include "tile_type.h" diff --git a/src/viewport_sprite_sorter.h b/src/viewport_sprite_sorter.h index 2a91f48a4f..0528fc39a9 100644 --- a/src/viewport_sprite_sorter.h +++ b/src/viewport_sprite_sorter.h @@ -8,7 +8,6 @@ /** @file viewport_sprite_sorter.h Types related to sprite sorting. */ #include "stdafx.h" -#include "core/smallvec_type.hpp" #include "gfx_type.h" #ifndef VIEWPORT_SPRITE_SORTER_H diff --git a/src/widgets/dropdown_type.h b/src/widgets/dropdown_type.h index 0d8951ef7b..56ed0061b5 100644 --- a/src/widgets/dropdown_type.h +++ b/src/widgets/dropdown_type.h @@ -12,7 +12,6 @@ #include "../window_type.h" #include "../gfx_func.h" -#include "../core/smallvec_type.hpp" #include "table/strings.h" /** diff --git a/src/window_gui.h b/src/window_gui.h index 953282bf7b..3ef708ab4d 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -16,7 +16,6 @@ #include "company_type.h" #include "tile_type.h" #include "widget_type.h" -#include "core/smallvec_type.hpp" #include "string_type.h" /** From 98dffc31577d89351d7b7e3b00dea6f68ed9ceb7 Mon Sep 17 00:00:00 2001 From: translators Date: Sat, 20 May 2023 18:39:31 +0000 Subject: [PATCH 15/58] Update: Translations from eints catalan: 12 changes by J0anJosep esperanto: 678 changes by legoscia --- src/lang/catalan.txt | 24 +- src/lang/esperanto.txt | 744 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 690 insertions(+), 78 deletions(-) diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index f19db67f29..b44eab9b91 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -1042,13 +1042,13 @@ STR_GAME_OPTIONS_GUI_SCALE_3X :x3 STR_GAME_OPTIONS_GUI_SCALE_4X :x4 STR_GAME_OPTIONS_GUI_SCALE_5X :x5 -STR_GAME_OPTIONS_PARTICIPATE_SURVEY_FRAME :{BLACK}Enquesta automatitzada -STR_GAME_OPTIONS_PARTICIPATE_SURVEY :{BLACK}Participa a l'enquesta automatitzada -STR_GAME_OPTIONS_PARTICIPATE_SURVEY_TOOLTIP :{BLACK}Quan s'activa, l'OpenTTD transmetrà dades a l'enquesta quan es surti d'una partida. -STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK :{BLACK}Sobre l'enquesta i la privacitat -STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK_TOOLTIP :{BLACK}Obre una pàgina amb més informació sobre l'enquesta automatitzada. -STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW :{BLACK}Vista prèvia del resultat de l'enquesta -STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :{BLACK}Mostra el resultat de l'enquesta automatitzada de la partida actual. +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_FRAME :{BLACK}Sondeig automatitzat +STR_GAME_OPTIONS_PARTICIPATE_SURVEY :{BLACK}Participa al sondeig automatitzat +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_TOOLTIP :{BLACK}Quan s'activa, l'OpenTTD transmetrà dades sobre l'ús del joc del sondeig quan es surti d'una partida. +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK :{BLACK}Sobre el sondeig i la privacitat +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK_TOOLTIP :{BLACK}Obre una pàgina amb més informació sobre el sondeig automatitzat. +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW :{BLACK}Vista prèvia del resultat del sondeig +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :{BLACK}Mostra el resultat del sondeig automatitzat de la partida actual. STR_GAME_OPTIONS_GRAPHICS :{BLACK}Gràfics @@ -2411,10 +2411,10 @@ STR_NETWORK_ASK_RELAY_NO :{BLACK}No STR_NETWORK_ASK_RELAY_YES_ONCE :{BLACK}Sí, aquest una vegada. STR_NETWORK_ASK_RELAY_YES_ALWAYS :{BLACK}Sí, no ho preguntis més. -STR_NETWORK_ASK_SURVEY_CAPTION :Voleu participar a l'enquesta automatitzada? -STR_NETWORK_ASK_SURVEY_TEXT :Voleu participar en l'enquesta automatitzada?{}L'OpenTTD transmetrà dades sobre l'enquesta quan sortiu d'una partida.{}Podeu canviar-ho en qualsevol moment des de les "Opcions de la partida". -STR_NETWORK_ASK_SURVEY_PREVIEW :Vista prèvia del resultat de l'enquesta -STR_NETWORK_ASK_SURVEY_LINK :Sobre l'enquesta i la privacitat +STR_NETWORK_ASK_SURVEY_CAPTION :Voleu participar al sondeig automatitzat? +STR_NETWORK_ASK_SURVEY_TEXT :Voleu participar en el sondeig automatitzat?{}L'OpenTTD transmetrà dades sobre el soondeig quan sortiu d'una partida.{}Podeu canviar-ho en qualsevol moment des de les "Opcions de la partida". +STR_NETWORK_ASK_SURVEY_PREVIEW :Vista prèvia del resultat del sondeig +STR_NETWORK_ASK_SURVEY_LINK :Sobre el sondeig i la privacitat STR_NETWORK_ASK_SURVEY_NO :No STR_NETWORK_ASK_SURVEY_YES :Sí @@ -4674,7 +4674,7 @@ STR_TEXTFILE_VIEW_LICENCE :{BLACK}Llicènc STR_TEXTFILE_README_CAPTION :{WHITE}Llegeix-me del {STRING} de {STRING} STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}Registre de canvis del {STRING} de {STRING} STR_TEXTFILE_LICENCE_CAPTION :{WHITE}Llicència del {STRING} de {STRING} -STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Vista prèvia del resultat de l'enquesta +STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Vista prèvia del resultat del sondeig # Vehicle loading indicators diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index afc72d1e3c..ffd1ad46d6 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -27,103 +27,174 @@ STR_JUST_NOTHING :Nenio # Plural cargo name STR_CARGO_PLURAL_NOTHING : STR_CARGO_PLURAL_PASSENGERS :Pasaĝeroj +STR_CARGO_PLURAL_PASSENGERS.n :Pasaĝerojn STR_CARGO_PLURAL_COAL :Karbo +STR_CARGO_PLURAL_COAL.n :Karbon STR_CARGO_PLURAL_MAIL :Poŝto +STR_CARGO_PLURAL_MAIL.n :Poŝton STR_CARGO_PLURAL_OIL :Oleo +STR_CARGO_PLURAL_OIL.n :Oleon STR_CARGO_PLURAL_LIVESTOCK :Brutoj +STR_CARGO_PLURAL_LIVESTOCK.n :Brutojn STR_CARGO_PLURAL_GOODS :Varoj +STR_CARGO_PLURAL_GOODS.n :Varojn STR_CARGO_PLURAL_GRAIN :Greno +STR_CARGO_PLURAL_GRAIN.n :Grenon STR_CARGO_PLURAL_WOOD :Ligno +STR_CARGO_PLURAL_WOOD.n :Lignon STR_CARGO_PLURAL_IRON_ORE :Fera Erco +STR_CARGO_PLURAL_IRON_ORE.n :Feran Ercon STR_CARGO_PLURAL_STEEL :Ŝtalo +STR_CARGO_PLURAL_STEEL.n :Ŝtalon STR_CARGO_PLURAL_VALUABLES :Valoraĵoj +STR_CARGO_PLURAL_VALUABLES.n :Valoraĵojn STR_CARGO_PLURAL_COPPER_ORE :Kupra Erco +STR_CARGO_PLURAL_COPPER_ORE.n :Kupran Ercon STR_CARGO_PLURAL_MAIZE :Maizo +STR_CARGO_PLURAL_MAIZE.n :Maizon STR_CARGO_PLURAL_FRUIT :Fruktoj +STR_CARGO_PLURAL_FRUIT.n :Fruktojn STR_CARGO_PLURAL_DIAMONDS :Diamantoj +STR_CARGO_PLURAL_DIAMONDS.n :Diamantojn STR_CARGO_PLURAL_FOOD :Manĝaĵoj +STR_CARGO_PLURAL_FOOD.n :Manĝaĵojn STR_CARGO_PLURAL_PAPER :Papero +STR_CARGO_PLURAL_PAPER.n :Paperon STR_CARGO_PLURAL_GOLD :Oro +STR_CARGO_PLURAL_GOLD.n :Oron STR_CARGO_PLURAL_WATER :Akvo +STR_CARGO_PLURAL_WATER.n :Akvon STR_CARGO_PLURAL_WHEAT :Tritiko +STR_CARGO_PLURAL_WHEAT.n :Tritikon STR_CARGO_PLURAL_RUBBER :Kaŭĉuko +STR_CARGO_PLURAL_RUBBER.n :Kaŭĉukon STR_CARGO_PLURAL_SUGAR :Sukero +STR_CARGO_PLURAL_SUGAR.n :Sukeron STR_CARGO_PLURAL_TOYS :Ludiloj +STR_CARGO_PLURAL_TOYS.n :Ludilojn STR_CARGO_PLURAL_SWEETS :Dolĉaĵoj +STR_CARGO_PLURAL_SWEETS.n :Dolĉaĵojn STR_CARGO_PLURAL_COLA :Kolao +STR_CARGO_PLURAL_COLA.n :Kolaon STR_CARGO_PLURAL_CANDYFLOSS :Sukerŝpinaĵoj +STR_CARGO_PLURAL_CANDYFLOSS.n :Sukerŝpinaĵojn STR_CARGO_PLURAL_BUBBLES :Vezikoj -STR_CARGO_PLURAL_TOFFEE :Tofeoj +STR_CARGO_PLURAL_BUBBLES.n :Vezikojn +STR_CARGO_PLURAL_TOFFEE :Karameloj +STR_CARGO_PLURAL_TOFFEE.n :Karamelojn STR_CARGO_PLURAL_BATTERIES :Baterioj -STR_CARGO_PLURAL_PLASTIC :Plastiko +STR_CARGO_PLURAL_BATTERIES.n :Bateriojn +STR_CARGO_PLURAL_PLASTIC :Plasto +STR_CARGO_PLURAL_PLASTIC.n :Plaston STR_CARGO_PLURAL_FIZZY_DRINKS :Amuzaj Trinkaĵoj # Singular cargo name STR_CARGO_SINGULAR_NOTHING : STR_CARGO_SINGULAR_PASSENGER :Pasaĝero +STR_CARGO_SINGULAR_PASSENGER.n :Pasaĝeron STR_CARGO_SINGULAR_COAL :Karbo +STR_CARGO_SINGULAR_COAL.n :Karbon STR_CARGO_SINGULAR_MAIL :Poŝto +STR_CARGO_SINGULAR_MAIL.n :Poŝton STR_CARGO_SINGULAR_OIL :Oleo +STR_CARGO_SINGULAR_OIL.n :Oleon STR_CARGO_SINGULAR_LIVESTOCK :Bruto +STR_CARGO_SINGULAR_LIVESTOCK.n :Bruton STR_CARGO_SINGULAR_GOODS :Varo +STR_CARGO_SINGULAR_GOODS.n :Varon STR_CARGO_SINGULAR_GRAIN :Greno +STR_CARGO_SINGULAR_GRAIN.n :Grenon STR_CARGO_SINGULAR_WOOD :Ligno +STR_CARGO_SINGULAR_WOOD.n :Lignon STR_CARGO_SINGULAR_IRON_ORE :Fera Erco +STR_CARGO_SINGULAR_IRON_ORE.n :Feran Ercon STR_CARGO_SINGULAR_STEEL :Ŝtalo +STR_CARGO_SINGULAR_STEEL.n :Ŝtalon STR_CARGO_SINGULAR_VALUABLES :Valoraĵoj +STR_CARGO_SINGULAR_VALUABLES.n :Valoraĵojn STR_CARGO_SINGULAR_COPPER_ORE :Kupra Erco +STR_CARGO_SINGULAR_COPPER_ORE.n :Kupran Ercon STR_CARGO_SINGULAR_MAIZE :Maizo +STR_CARGO_SINGULAR_MAIZE.n :Maizon STR_CARGO_SINGULAR_FRUIT :Frukto +STR_CARGO_SINGULAR_FRUIT.n :Frukton STR_CARGO_SINGULAR_DIAMOND :Diamanto +STR_CARGO_SINGULAR_DIAMOND.n :Diamanton STR_CARGO_SINGULAR_FOOD :Manĝaĵo +STR_CARGO_SINGULAR_FOOD.n :Manĝaĵon STR_CARGO_SINGULAR_PAPER :Papero +STR_CARGO_SINGULAR_PAPER.n :Paperon STR_CARGO_SINGULAR_GOLD :Oro +STR_CARGO_SINGULAR_GOLD.n :Oron STR_CARGO_SINGULAR_WATER :Akvo +STR_CARGO_SINGULAR_WATER.n :Akvon STR_CARGO_SINGULAR_WHEAT :Tritiko +STR_CARGO_SINGULAR_WHEAT.n :Tritikon STR_CARGO_SINGULAR_RUBBER :Kaŭĉuko +STR_CARGO_SINGULAR_RUBBER.n :Kaŭĉukon STR_CARGO_SINGULAR_SUGAR :Sukero +STR_CARGO_SINGULAR_SUGAR.n :Sukeron STR_CARGO_SINGULAR_TOY :Ludilo +STR_CARGO_SINGULAR_TOY.n :Ludilon STR_CARGO_SINGULAR_SWEETS :Dolĉaĵo +STR_CARGO_SINGULAR_SWEETS.n :Dolĉaĵon STR_CARGO_SINGULAR_COLA :Kolao +STR_CARGO_SINGULAR_COLA.n :Kolaon STR_CARGO_SINGULAR_CANDYFLOSS :Sukerŝpinaĵo +STR_CARGO_SINGULAR_CANDYFLOSS.n :Sukerŝpinaĵon STR_CARGO_SINGULAR_BUBBLE :Veziko -STR_CARGO_SINGULAR_TOFFEE :Tofeo +STR_CARGO_SINGULAR_BUBBLE.n :Vezikon +STR_CARGO_SINGULAR_TOFFEE :Karamelo +STR_CARGO_SINGULAR_TOFFEE.n :Karamelon STR_CARGO_SINGULAR_BATTERY :Baterio -STR_CARGO_SINGULAR_PLASTIC :Plastiko +STR_CARGO_SINGULAR_BATTERY.n :Baterion +STR_CARGO_SINGULAR_PLASTIC :Plasto +STR_CARGO_SINGULAR_PLASTIC.n :Plaston STR_CARGO_SINGULAR_FIZZY_DRINK :Amuza Trinkaĵo # Quantity of cargo STR_QUANTITY_NOTHING : STR_QUANTITY_PASSENGERS :{COMMA}{NBSP}pasaĝero{P "" j} +STR_QUANTITY_PASSENGERS.n :{COMMA}{NBSP}pasaĝero{P "" j}n STR_QUANTITY_COAL :{WEIGHT_LONG} da karbo STR_QUANTITY_MAIL :{COMMA}{NBSP}sako{P "" j} da poŝto +STR_QUANTITY_MAIL.n :{COMMA}{NBSP}sako{P "" j}n da poŝto STR_QUANTITY_OIL :{VOLUME_LONG} da oleo STR_QUANTITY_LIVESTOCK :{COMMA}{NBSP}bruto{P "" j} +STR_QUANTITY_LIVESTOCK.n :{COMMA}{NBSP}bruto{P "" j}n STR_QUANTITY_GOODS :{COMMA}{NBSP}kesto{P "" j} da varoj +STR_QUANTITY_GOODS.n :{COMMA}{NBSP}kesto{P "" j}n da varoj STR_QUANTITY_GRAIN :{WEIGHT_LONG} da greno STR_QUANTITY_WOOD :{WEIGHT_LONG} da ligno STR_QUANTITY_IRON_ORE :{WEIGHT_LONG} da fera erco STR_QUANTITY_STEEL :{WEIGHT_LONG} da ŝtalo STR_QUANTITY_VALUABLES :{COMMA}{NBSP}sako{P "" j} da valoraĵoj +STR_QUANTITY_VALUABLES.n :{COMMA}{NBSP}sako{P "" j}n da valoraĵoj STR_QUANTITY_COPPER_ORE :{WEIGHT_LONG} da kupra erco STR_QUANTITY_MAIZE :{WEIGHT_LONG} da maizo STR_QUANTITY_FRUIT :{WEIGHT_LONG} da fruktoj STR_QUANTITY_DIAMONDS :{COMMA}{NBSP}sako{P "" j} da diamantoj +STR_QUANTITY_DIAMONDS.n :{COMMA}{NBSP}sako{P "" j}n da diamantoj STR_QUANTITY_FOOD :{WEIGHT_LONG} da manĝaĵoj STR_QUANTITY_PAPER :{WEIGHT_LONG} da papero STR_QUANTITY_GOLD :{COMMA}{NBSP}sako{P "" j} da oro +STR_QUANTITY_GOLD.n :{COMMA}{NBSP}sako{P "" j}n da oro STR_QUANTITY_WATER :{VOLUME_LONG} da akvo STR_QUANTITY_WHEAT :{WEIGHT_LONG} da tritiko STR_QUANTITY_RUBBER :{VOLUME_LONG} da kaŭĉuko STR_QUANTITY_SUGAR :{WEIGHT_LONG} da sukero STR_QUANTITY_TOYS :{COMMA}{NBSP}ludilo{P "" j} +STR_QUANTITY_TOYS.n :{COMMA}{NBSP}ludilo{P "" j}n STR_QUANTITY_SWEETS :{COMMA}{NBSP}sako{P "" j} da dolĉaĵoj +STR_QUANTITY_SWEETS.n :{COMMA}{NBSP}sako{P "" j}n da dolĉaĵoj STR_QUANTITY_COLA :{VOLUME_LONG} da kolao STR_QUANTITY_CANDYFLOSS :{WEIGHT_LONG} da sukerŝpinaĵo STR_QUANTITY_BUBBLES :{COMMA} veziko{P "" j} -STR_QUANTITY_TOFFEE :{WEIGHT_LONG} da tofeo +STR_QUANTITY_BUBBLES.n :{COMMA} veziko{P "" j}n +STR_QUANTITY_TOFFEE :{WEIGHT_LONG} da karamelo STR_QUANTITY_BATTERIES :{COMMA} baterio{P "" j} -STR_QUANTITY_PLASTIC :{VOLUME_LONG} da plastiko +STR_QUANTITY_BATTERIES.n :{COMMA} baterio{P "" j}n +STR_QUANTITY_PLASTIC :{VOLUME_LONG} da plasto STR_QUANTITY_FIZZY_DRINKS :{COMMA} amuza{P "" j} trinkaĵo{P "" j} STR_QUANTITY_N_A :N/A @@ -165,12 +236,19 @@ STR_ABBREV_ALL :ĈIU # 'Mode' of transport for cargoes STR_PASSENGERS :{COMMA}{NBSP}pasaĝero{P "" j} +STR_PASSENGERS.n :{COMMA}{NBSP}pasaĝero{P "" j}n STR_BAGS :{COMMA}{NBSP}sako{P "" j} +STR_BAGS.n :{COMMA}{NBSP}sako{P "" j}n STR_TONS :{COMMA}{NBSP}tuno{P "" j} +STR_TONS.n :{COMMA}{NBSP}tuno{P "" j}n STR_LITERS :{COMMA}{NBSP}litro{P "" j} +STR_LITERS.n :{COMMA}{NBSP}litro{P "" j}n STR_ITEMS :{COMMA}{NBSP}peco{P "" j} +STR_ITEMS.n :{COMMA}{NBSP}peco{P "" j}n STR_CRATES :{COMMA}{NBSP}kesto{P "" j} +STR_CRATES.n :{COMMA}{NBSP}kesto{P "" j}n +STR_COLOUR_DEFAULT :Defaŭlta ###length 17 STR_COLOUR_DARK_BLUE :Malhelblua STR_COLOUR_PALE_GREEN :Palverda @@ -188,31 +266,45 @@ STR_COLOUR_ORANGE :Oranĝa STR_COLOUR_BROWN :Bruna STR_COLOUR_GREY :Griza STR_COLOUR_WHITE :Blanka +STR_COLOUR_RANDOM :Hazarda # Units used in OpenTTD STR_UNITS_VELOCITY_IMPERIAL :{DECIMAL}{NBSP}mph STR_UNITS_VELOCITY_METRIC :{DECIMAL}{NBSP}km/h STR_UNITS_VELOCITY_SI :{DECIMAL}{NBSP}m/s +STR_UNITS_VELOCITY_GAMEUNITS :{DECIMAL}{NBSP}kaheloj tage +STR_UNITS_VELOCITY_GAMEUNITS.n :{DECIMAL}{NBSP}kahelojn tage +STR_UNITS_VELOCITY_KNOTS :{DECIMAL}{NBSP}knotoj STR_UNITS_POWER_IMPERIAL :{DECIMAL}{NBSP}ĉp STR_UNITS_POWER_METRIC :{DECIMAL}{NBSP}ĉp STR_UNITS_POWER_SI :{DECIMAL}{NBSP}kW +STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_IMPERIAL :{DECIMAL}{NBSP}ĉp/t +STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_SI :{DECIMAL}{NBSP}ĉp/Mg +STR_UNITS_POWER_METRIC_TO_WEIGHT_METRIC :{DECIMAL}{NBSP}ĉp/t +STR_UNITS_POWER_METRIC_TO_WEIGHT_SI :{DECIMAL}{NBSP}ĉp/Mg +STR_UNITS_POWER_SI_TO_WEIGHT_METRIC :{DECIMAL}{NBSP}kW/t +STR_UNITS_POWER_SI_TO_WEIGHT_SI :{DECIMAL}{NBSP}W/kg STR_UNITS_WEIGHT_SHORT_IMPERIAL :{DECIMAL}{NBSP}t STR_UNITS_WEIGHT_SHORT_METRIC :{DECIMAL}{NBSP}t STR_UNITS_WEIGHT_SHORT_SI :{DECIMAL}{NBSP}kg STR_UNITS_WEIGHT_LONG_IMPERIAL :{DECIMAL}{NBSP}tuno{P "" j} +STR_UNITS_WEIGHT_LONG_IMPERIAL.n :{DECIMAL}{NBSP}tuno{P "" j}n STR_UNITS_WEIGHT_LONG_METRIC :{DECIMAL}{NBSP}tuno{P "" j} +STR_UNITS_WEIGHT_LONG_METRIC.n :{DECIMAL}{NBSP}tuno{P "" j}n STR_UNITS_WEIGHT_LONG_SI :{DECIMAL}{NBSP}kg STR_UNITS_VOLUME_SHORT_IMPERIAL :{DECIMAL}{NBSP}gal STR_UNITS_VOLUME_SHORT_METRIC :{DECIMAL}{NBSP}l STR_UNITS_VOLUME_SHORT_SI :{DECIMAL}{NBSP}m³ -STR_UNITS_VOLUME_LONG_IMPERIAL :{DECIMAL}{NBSP}galono{P "" j} +STR_UNITS_VOLUME_LONG_IMPERIAL :{DECIMAL}{NBSP}galjono{P "" j} +STR_UNITS_VOLUME_LONG_IMPERIAL.n :{DECIMAL}{NBSP}galjono{P "" j}n STR_UNITS_VOLUME_LONG_METRIC :{DECIMAL}{NBSP}litro{P "" j} +STR_UNITS_VOLUME_LONG_METRIC.n :{DECIMAL}{NBSP}litro{P "" j}n STR_UNITS_VOLUME_LONG_SI :{DECIMAL}{NBSP}m³ STR_UNITS_FORCE_IMPERIAL :{DECIMAL}{NBSP}lbf @@ -233,10 +325,13 @@ STR_TOOLTIP_SORT_ORDER :{BLACK}Elektu v STR_TOOLTIP_SORT_CRITERIA :{BLACK}Elektu ordigaj kondiĉoj STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Selektu kriteriojn por filtrado STR_BUTTON_SORT_BY :{BLACK}Ordigu laŭ +STR_BUTTON_CATCHMENT :{BLACK}Servata areo +STR_TOOLTIP_CATCHMENT :{BLACK}Ŝaltu montradon de servata areo STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Fermu fenestron STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Fenestra titolo - tiru por movi la fenestron STR_TOOLTIP_SHADE :{BLACK}Minimumigi fenestron - montri nur la titolbreton +STR_TOOLTIP_DEFSIZE :{BLACK}Aligrandigu la fenestron al la defaŭlta grandeco. Stir+Klaki konservas la nunan grandecon kiel defaŭltan STR_TOOLTIP_STICKY :{BLACK}Marku ke ne fermiĝu la fenestro per la 'ĉiujn fenestrojn fermu'-klavo. Konservu kiel defaŭlta stato per Ctrl-klavo + alklaki. STR_TOOLTIP_RESIZE :{BLACK}Klaku kaj tiru por reformi la fenestron STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW :{BLACK}Ŝaltu inter granda kaj malgranda fenestro @@ -246,12 +341,15 @@ STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC :{BLACK}Detruu k # Show engines button ###length VEHICLE_TYPES -STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN :{BLACK}Montru kaŝita -STR_SHOW_HIDDEN_ENGINES_VEHICLE_ROAD_VEHICLE :{BLACK}Montru kaŝita -STR_SHOW_HIDDEN_ENGINES_VEHICLE_SHIP :{BLACK}Montru kaŝita -STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT :{BLACK}Montru kaŝita +STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN :{BLACK}Montru kaŝitajn +STR_SHOW_HIDDEN_ENGINES_VEHICLE_ROAD_VEHICLE :{BLACK}Montru kaŝitajn +STR_SHOW_HIDDEN_ENGINES_VEHICLE_SHIP :{BLACK}Montru kaŝitajn +STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT :{BLACK}Montru kaŝitajn ###length VEHICLE_TYPES +STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP :{BLACK}Post aktivigo de tiu ĉi butono, kaŝitaj trajnveturiloj ankaŭ estos montrataj +STR_SHOW_HIDDEN_ENGINES_VEHICLE_ROAD_VEHICLE_TOOLTIP :{BLACK}Post aktivigo de tiu ĉi butono, kaŝitaj stratveturiloj ankaŭ estos montrataj +STR_SHOW_HIDDEN_ENGINES_VEHICLE_SHIP_TOOLTIP :{BLACK}Post aktivigo de tiu ĉi butono, kaŝitaj ŝipoj ankaŭ estos montrataj # Query window STR_BUTTON_DEFAULT :{BLACK}Normo @@ -304,10 +402,12 @@ STR_SORT_BY_POWER_VS_RUNNING_COST :Potenco/Irkosto STR_SORT_BY_CARGO_CAPACITY :Ŝarĝkapablo STR_SORT_BY_RANGE :Atingopovo STR_SORT_BY_POPULATION :Enloĝantaro +STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR :Averaĝa profito ĉi-jare # Group by options for vehicle list # Order button in shared orders vehicle list +STR_GOTO_ORDER_VIEW :{BLACK}Itinero # Tooltips for the main toolbar ###length 31 @@ -463,6 +563,7 @@ STR_TOOLBAR_SOUND_MUSIC :Sono/muziko ###length 3 STR_NEWS_MENU_LAST_MESSAGE_NEWS_REPORT :Lasta mesaĝo/novaĵo STR_NEWS_MENU_MESSAGE_HISTORY_MENU :Mesaĝa Historio +STR_NEWS_MENU_DELETE_ALL_MESSAGES :Forigu ĉiujn mesaĝojn # About menu ###length 10 @@ -535,7 +636,7 @@ STR_MONTH_ABBREV_APR :Apr STR_MONTH_ABBREV_MAY :Maj STR_MONTH_ABBREV_JUN :Jun STR_MONTH_ABBREV_JUL :Jul -STR_MONTH_ABBREV_AUG :Aug +STR_MONTH_ABBREV_AUG :Aŭg STR_MONTH_ABBREV_SEP :Sep STR_MONTH_ABBREV_OCT :Okt STR_MONTH_ABBREV_NOV :Nov @@ -663,6 +764,7 @@ STR_MUSIC_TOOLTIP_TOGGLE_PROGRAM_SHUFFLE :{BLACK}Miksu/ne STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION :{BLACK}Montru muziker-elektan fenestron # Playlist window +STR_PLAYLIST_MUSIC_SELECTION_SETNAME :{WHITE}Muzikprogramo - '{STRING}' STR_PLAYLIST_TRACK_NAME :{TINY_FONT}{LTBLUE}{ZEROFILL_NUM} "{STRING}" STR_PLAYLIST_TRACK_INDEX :{TINY_FONT}{BLACK}Numera Indekso STR_PLAYLIST_PROGRAM :{TINY_FONT}{BLACK}Programo - '{STRING}' @@ -675,7 +777,7 @@ STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK :{BLACK}Alklaku STR_HIGHSCORE_TOP_COMPANIES_WHO_REACHED :{BIG_FONT}{BLACK}Superkompanioj kiuj atingis {NUM} STR_HIGHSCORE_TOP_COMPANIES_NETWORK_GAME :{BIG_FONT}{BLACK}Kompania tabelo en {NUM} STR_HIGHSCORE_POSITION :{BIG_FONT}{BLACK}{COMMA}. -STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN :Kompaniisto +STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN :Komercisto STR_HIGHSCORE_PERFORMANCE_TITLE_ENTREPRENEUR :Entreprenisto STR_HIGHSCORE_PERFORMANCE_TITLE_INDUSTRIALIST :Industriisto STR_HIGHSCORE_PERFORMANCE_TITLE_CAPITALIST :Kapitalisto @@ -751,6 +853,7 @@ STR_SMALLMAP_TOOLTIP_ENABLE_ALL_INDUSTRIES :{BLACK}Montri STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT :{BLACK}Ŝaltu montro de altecmapo STR_SMALLMAP_TOOLTIP_DISABLE_ALL_COMPANIES :{BLACK}Montru ne kompanian posedaĵon en la mapo STR_SMALLMAP_TOOLTIP_ENABLE_ALL_COMPANIES :{BLACK}Montru ĉiujn kompanian posedaĵon en la mapo +STR_SMALLMAP_TOOLTIP_ENABLE_ALL_CARGOS :{BLACK}Montru ĉiujn ŝarĝojn sur la mapo # Status bar messages STR_STATUSBAR_TOOLTIP_SHOW_LAST_NEWS :{BLACK}Montru lastan mesaĝon aŭ novaĵon @@ -802,12 +905,13 @@ STR_NEWS_MERGER_TAKEOVER_TITLE :{BIG_FONT}{BLAC STR_PRESIDENT_NAME_MANAGER :{BLACK}{PRESIDENT_NAME}{}(Manaĝanto) STR_NEWS_NEW_TOWN :{BLACK}{BIG_FONT}{STRING} financis konstruon de nova urbo {TOWN}! +STR_NEWS_NEW_TOWN_UNSPONSORED :{BLACK}{BIG_FONT}Nova urbo nomata {TOWN} estas konstruita! STR_NEWS_INDUSTRY_CONSTRUCTION :{BIG_FONT}{BLACK}Nova {STRING} konstruiĝas ĉe {TOWN}! STR_NEWS_INDUSTRY_PLANTED :{BIG_FONT}{BLACK}Nova {STRING} plantiĝas ĉe {TOWN}! STR_NEWS_INDUSTRY_CLOSURE_GENERAL :{BIG_FONT}{BLACK}{STRING} anoncas tre baldaŭan fermiĝon! -STR_NEWS_INDUSTRY_CLOSURE_SUPPLY_PROBLEMS :{BIG_FONT}{BLACK}Liverproblemoj devigas al {STRING} anonci tre baldaŭan fermiĝon! +STR_NEWS_INDUSTRY_CLOSURE_SUPPLY_PROBLEMS :{BIG_FONT}{BLACK}Pro liverproblemoj {STRING} devas anonci tre baldaŭan fermiĝon! STR_NEWS_INDUSTRY_CLOSURE_LACK_OF_TREES :{BIG_FONT}{BLACK}Pro manko da proksimaj arboj {STRING} devas anonci tre baldaŭan fermiĝon! STR_NEWS_EURO_INTRODUCTION :{BIG_FONT}{BLACK}Eŭropa Mona Unio!{}{}De nun uziĝos la Eŭro kiel monunuo por ĉiutagaj montransskriboj en via lando! @@ -835,6 +939,7 @@ STR_NEWS_VEHICLE_HAS_TOO_FEW_ORDERS :{WHITE}{VEHICLE STR_NEWS_VEHICLE_HAS_VOID_ORDER :{WHITE}{VEHICLE} havas malplenan ordonon STR_NEWS_VEHICLE_HAS_DUPLICATE_ENTRY :{WHITE}{VEHICLE} havas duoblajn ordonojn STR_NEWS_VEHICLE_HAS_INVALID_ENTRY :{WHITE}Ordonoj de {VEHICLE} enhavas nevalidan stacion +STR_NEWS_PLANE_USES_TOO_SHORT_RUNWAY :{WHITE}{VEHICLE} havas en sia itinero flughavenon kies kurejo estas tro mallonga STR_NEWS_VEHICLE_IS_GETTING_OLD :{WHITE}{VEHICLE} malnoviĝis STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD :{WHITE}{VEHICLE} tre malnoviĝis @@ -842,6 +947,7 @@ STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND :{WHITE}{VEHICLE STR_NEWS_TRAIN_IS_STUCK :{WHITE}{VEHICLE} ne povas trovi padon por daŭrigi. STR_NEWS_VEHICLE_IS_LOST :{WHITE}{VEHICLE} estas perdita STR_NEWS_VEHICLE_IS_UNPROFITABLE :{WHITE}{VEHICLE} pasintjare gajnis {CURRENCY_LONG} +STR_NEWS_AIRCRAFT_DEST_TOO_FAR :{WHITE}{VEHICLE} ne povas iri ĝis la sekva celpunkto ĉar ĝi estas ekster la atingebla distanco STR_NEWS_ORDER_REFIT_FAILED :{WHITE}{VEHICLE} haltis ĉar komanda transformo fiaskis STR_NEWS_VEHICLE_AUTORENEW_FAILED :{WHITE}Aŭtorenovigo fiaskis ĉe {VEHICLE}{}{STRING} @@ -856,9 +962,14 @@ STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_OR_CARGO :{WHITE}{STATION STR_NEWS_STATION_NOW_ACCEPTS_CARGO :{WHITE}{STATION} nun akceptas {STRING.n} STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO :{WHITE}{STATION} nun akceptas {STRING.n} kaj {STRING.n} -STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIG_FONT}{BLACK}Subvencia oferto ne plu validas:{}{}{STRING} de {STRING} al {STRING} ne estos subvenciata. +STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIG_FONT}{BLACK}Subvencia oferto ne plu validas:{}{}Transportado de {STRING} de {STRING} al {STRING} ne estos subvenciata. STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE :{BIG_FONT}{BLACK}Subvencio retiriĝis:{}{}Servo de {STRING} de {STRING} al {STRING} ne plu estas subvenciata. +STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Subvencia oferto:{}{}Kiu unue transportos {STRING.n} de {STRING} al {STRING} ricevos {NUM}-jaran subvencion de la lokaj estroj! ###length 4 +STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIG_FONT}{BLACK}{STRING} ricevis subvencion!{}{}Transportado de {STRING} de {STRING} al {STRING} pagiĝos je 50% pli dum la sekva{P 4 "" j} {4:NUM} jaro{P 4 "" j}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLACK}{STRING} ricevis subvencion!{}{}Transportado de {STRING} de {STRING} al {STRING} pagiĝos duoble dum la sekva{P 4 "" j} {4:NUM} jaro{P 4 "" j}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}{STRING} ricevis subvencion!{}{}Transportado de {STRING} de {STRING} al {STRING} pagiĝos trioble dum la sekva{P 4 "" j} {4:NUM} jaro{P 4 "" j}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}{STRING} ricevis subvencion!{}{}Transportado de {STRING} de {STRING} al {STRING} pagiĝos kvaroble dum la sekva{P 4 "" j} {4:NUM} jaro{P 4 "" j}! STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}Trafika ĥaoso en {TOWN}!{}{}Vojrekonstruada programo komencita de {STRING} alportas 6-monatan mizeron al vojuzantoj! STR_NEWS_EXCLUSIVE_RIGHTS_TITLE :{BIG_FONT}{BLACK}Transporta monopolo! @@ -873,15 +984,26 @@ STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Gluu la # Game options window STR_GAME_OPTIONS_CAPTION :{WHITE}Ludaj Opcioj +STR_GAME_OPTIONS_TAB_GENERAL :Ĝenerale +STR_GAME_OPTIONS_TAB_GRAPHICS :Grafiko +STR_GAME_OPTIONS_TAB_SOUND :Sono +STR_GAME_OPTIONS_TAB_SOUND_TT :{BLACK}Elektu agordojn pri sono kaj muziko +STR_GAME_OPTIONS_VOLUME :Laŭteco +STR_GAME_OPTIONS_MUSIC_VOLUME :Muziko +STR_GAME_OPTIONS_VOLUME_0 :0% +STR_GAME_OPTIONS_VOLUME_25 :25% +STR_GAME_OPTIONS_VOLUME_50 :50% +STR_GAME_OPTIONS_VOLUME_75 :75% +STR_GAME_OPTIONS_VOLUME_100 :100% STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Monunuoj STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Elekto de monunuoj ###length 42 STR_GAME_OPTIONS_CURRENCY_GBP :Brita Pundo (GBP) -STR_GAME_OPTIONS_CURRENCY_USD :Amerika Dolaro (USD) +STR_GAME_OPTIONS_CURRENCY_USD :Usona Dolaro (USD) STR_GAME_OPTIONS_CURRENCY_EUR :Eŭro (EUR) STR_GAME_OPTIONS_CURRENCY_JPY :Enoj (JPY) STR_GAME_OPTIONS_CURRENCY_ATS :Aŭstriaj Ŝilingoj (ATS) @@ -908,9 +1030,11 @@ STR_GAME_OPTIONS_CURRENCY_TRY :Turkaj Liroj (T STR_GAME_OPTIONS_CURRENCY_SKK :Slovakaj Korunoj (SKK) STR_GAME_OPTIONS_CURRENCY_BRL :Brazilaj Realoj (BRL) STR_GAME_OPTIONS_CURRENCY_EEK :Estona Krono (EEK) +STR_GAME_OPTIONS_CURRENCY_ZAR :Sudafrika rando (ZAR) STR_GAME_OPTIONS_CURRENCY_CUSTOM :Alia... STR_GAME_OPTIONS_CURRENCY_GEL :Kartvela Lario (GEL) STR_GAME_OPTIONS_CURRENCY_IRR :Irana Rialo (IRR) +STR_GAME_OPTIONS_CURRENCY_CNY :Ĉina juano (CNY) STR_GAME_OPTIONS_AUTOSAVE_FRAME :{BLACK}Aŭtomata konservado STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_TOOLTIP :{BLACK}Elektu intervalon inter aŭtomataj konservadoj de la ludo @@ -918,9 +1042,13 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_TOOLTIP :{BLACK}Elektu i # Autosave dropdown ###length 5 STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_OFF :Malaktiva +STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_30_MINUTES :Ĉiujn 30 minutojn +STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_60_MINUTES :Ĉiujn 60 minutojn +STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_120_MINUTES :Ĉiujn 120 minutojn STR_GAME_OPTIONS_LANGUAGE :{BLACK}Lingvo STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Elektu uzotan interfacan lingvon +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% kompleta) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Plena ekrano STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Ŝaltu ĉi tiun kvadrateton por plenekrane ludi OpenTTD @@ -934,9 +1062,18 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :alia +STR_GAME_OPTIONS_GUI_SCALE_1X :1x +STR_GAME_OPTIONS_GUI_SCALE_2X :2x +STR_GAME_OPTIONS_GUI_SCALE_3X :3x +STR_GAME_OPTIONS_GUI_SCALE_4X :4x +STR_GAME_OPTIONS_GUI_SCALE_5X :5x +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_TOOLTIP :{BLACK}Kiam tiu ĉi agordo estas aktiva, OpenTTD sendos sondaĵon je forlaso de ludo +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :{BLACK}Montru la sondaĵrezulton de la kuranta ludo +STR_GAME_OPTIONS_GRAPHICS :{BLACK}Grafiko +STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}Elektu ekranan aktualigoftecon STR_GAME_OPTIONS_BASE_GRF :{BLACK}Baza grafikaro STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Selektu la uzendan bazgrafikaron @@ -948,16 +1085,19 @@ STR_GAME_OPTIONS_BASE_SFX_TOOLTIP :{BLACK}Elektu l STR_GAME_OPTIONS_BASE_SFX_DESCRIPTION_TOOLTIP :{BLACK}Aldonaj informoj pri la baza sonaro STR_GAME_OPTIONS_BASE_MUSIC :{BLACK}Baza muzikaro. -STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP :{BLACK}Elekti baza muzikaron por uzi. +STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP :{BLACK}Elekti bazan muzikaron por uzi. STR_GAME_OPTIONS_BASE_MUSIC_STATUS :{RED}{NUM} difektita{P "" j} dosiero{P "" j} STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP :{BLACK}Pluaj informoj pri la baza muzikaro. +STR_ERROR_RESOLUTION_LIST_FAILED :{WHITE}Ne eblis akiri liston de subtenataj distingivoj STR_ERROR_FULLSCREEN_FAILED :{WHITE}Plenekrana moduso fiaskis # Custom currency window STR_CURRENCY_WINDOW :{WHITE}Alia monunuo STR_CURRENCY_EXCHANGE_RATE :{LTBLUE}Interŝanĝa valoro: {ORANGE}{CURRENCY_LONG} = £ {COMMA} +STR_CURRENCY_DECREASE_EXCHANGE_RATE_TOOLTIP :{BLACK}Malpliigu la kvanton de via valuto kiu egalas al unu pundo (£) +STR_CURRENCY_INCREASE_EXCHANGE_RATE_TOOLTIP :{BLACK}Pliigu la kvanton de via valuto kiu egalas al unu pundo (£) STR_CURRENCY_SEPARATOR :{LTBLUE}Dividilo: {ORANGE}{STRING} STR_CURRENCY_SET_CUSTOM_CURRENCY_SEPARATOR_TOOLTIP :{BLACK}Difini apartigilon por via valuto @@ -1037,11 +1177,12 @@ STR_TERRAIN_TYPE_VERY_FLAT :Tre Ebene STR_TERRAIN_TYPE_FLAT :Ebene STR_TERRAIN_TYPE_HILLY :Montete STR_TERRAIN_TYPE_MOUNTAINOUS :Monte +STR_TERRAIN_TYPE_ALPINIST :Alpeca ###length 4 STR_CITY_APPROVAL_TOLERANT :Tolerante STR_CITY_APPROVAL_HOSTILE :Gastige -STR_CITY_APPROVAL_PERMISSIVE :Permeseme +STR_CITY_APPROVAL_PERMISSIVE :Permeseme (ne efikas al la agoj de la kompanio) STR_WARNING_NO_SUITABLE_AI :{WHITE}Neniu taŭga AI estas disponebla...{}Kelkaj AI-oj estas elŝuteblaj per la sistemo 'Enreta Enhavo'. @@ -1049,9 +1190,14 @@ STR_WARNING_NO_SUITABLE_AI :{WHITE}Neniu ta STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Agordoj STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtroteksto: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Etendu ĉio +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Restarigu ĉiujn valorojn STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(neniu ekspliko disponeble) +STR_CONFIG_SETTING_VALUE :{PUSH_COLOUR}{ORANGE}{STRING}{POP_COLOUR} STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Defaŭlta valoro: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Tipo de agordo: {ORANGE}{STRING} +STR_CONFIG_SETTING_TYPE_GAME_INGAME :Luda agordo (konservata en dosiero; tuŝas nur tiun ĉi ludon) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Atentu! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Tiu ĉi ago reagordos ĉiujn agordojn al la defaŭltaj valoroj.{}Ĉu vi certas ke vi volas daŭrigi? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategorio: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Tipo: @@ -1061,6 +1207,7 @@ STR_CONFIG_SETTING_RESTRICT_ALL :Altnivelaj agor STR_CONFIG_SETTING_RESTRICT_CHANGED_AGAINST_DEFAULT :agordoj kiuj malsimilas al la originala STR_CONFIG_SETTING_RESTRICT_CHANGED_AGAINST_NEW :Agordoj kiu malsimilas al via novajuldaj agordoj +STR_CONFIG_SETTING_TYPE_DROPDOWN_HELPTEXT :{BLACK}Limigas la suban liston al certaj tipoj de agordoj STR_CONFIG_SETTING_TYPE_DROPDOWN_ALL :Ĉiuj agordoj STR_CONFIG_SETTING_TYPE_DROPDOWN_GAME_MENU :Ludo agordojn (stokite en savoj; nur tuŝas novajn ludojn) STR_CONFIG_SETTING_TYPE_DROPDOWN_GAME_INGAME :Ludo agordojn (stokite en savo; tuŝas nur kuranta ludo) @@ -1100,30 +1247,39 @@ STR_CONFIG_SETTING_RUNNING_COSTS :Irkostoj: {STRI STR_CONFIG_SETTING_CONSTRUCTION_SPEED :Konstrurapido: {STRING} STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS :Veturilpaneoj: {STRING} +STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS_HELPTEXT :Regu kiom ofte povas panei maladekvate prizorgataj veturiloj STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER_HELPTEXT :Ŝanĝu kio oni pagas por subvenciataj rilatoj +STR_CONFIG_SETTING_SUBSIDY_DURATION :Daŭro de subvencio: {STRING} ###setting-zero-is-special +STR_CONFIG_SETTING_SUBSIDY_DURATION_DISABLED :Neniuj subvencioj STR_CONFIG_SETTING_CONSTRUCTION_COSTS :Kostoj de konstruado: {STRING} STR_CONFIG_SETTING_CONSTRUCTION_COSTS_HELPTEXT :Ŝanĝu nivelon de konstruaj kaj aĉetaj kostoj STR_CONFIG_SETTING_RECESSIONS :Recesioj: {STRING} +STR_CONFIG_SETTING_TRAIN_REVERSING :Malpermesu al trajnoj turniĝi en stacioj: {STRING} STR_CONFIG_SETTING_DISASTERS :Katastrofoj: {STRING} +STR_CONFIG_SETTING_CITY_APPROVAL :Sinteno de lokaj estraroj: {STRING} ###setting-zero-is-special +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_AUTO :(aŭtomata) +STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}Ne eblas ŝanĝi la mapan alteclimon al tiu ĉi valoro. Almenaŭ unu monto sur la mapo estas pli alta STR_CONFIG_SETTING_AUTOSLOPE :Permesu terecigon sub konstruaĵoj, trakoj, ktp.: {STRING} +STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :Permesu terenŝangadon sub konstruaĵoj kaj trakoj sen forigi ilin STR_CONFIG_SETTING_CATCHMENT :Permesu pli realgrandaj kaptoregionoj: {STRING} STR_CONFIG_SETTING_EXTRADYNAMITE :Permesu forigon de pli da urbaj stratoj, pontoj ktp: {STRING} +STR_CONFIG_SETTING_TRAIN_LENGTH_HELPTEXT :Agordu la maksimuman longecon de trajnoj STR_CONFIG_SETTING_TILE_LENGTH :{COMMA} kvadrato{P 0 "" j} STR_CONFIG_SETTING_SMOKE_AMOUNT :Kvanto da lokomotivo fumo/fajreroj: {STRING} @@ -1132,8 +1288,11 @@ STR_CONFIG_SETTING_TRAIN_ACCELERATION_MODEL :Modelo de trajn STR_CONFIG_SETTING_ROAD_VEHICLE_ACCELERATION_MODEL :Modelo de stratveturilakcelado: {STRING} +STR_CONFIG_SETTING_TRAIN_SLOPE_STEEPNESS :Deklivkruteco por trajnoj: {STRING} +STR_CONFIG_SETTING_TRAIN_SLOPE_STEEPNESS_HELPTEXT :Kruteco de dekliva kahelo por trajno. Kun pli alta valoro estas pli malfacile supreniri altaĵon STR_CONFIG_SETTING_PERCENTAGE :{COMMA}% +STR_CONFIG_SETTING_ROAD_VEHICLE_SLOPE_STEEPNESS_HELPTEXT :Kruteco de dekliva kahelo por stratveturilo. Kun pli alta valoro estas pli malfacile supreniri altaĵon STR_CONFIG_SETTING_FORBID_90_DEG :Vagonaroj kaj ŝipoj ne ort-turniĝu: {STRING} @@ -1141,8 +1300,11 @@ STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS :Permesu ligi st STR_CONFIG_SETTING_INFLATION :Inflacio: {STRING} +STR_CONFIG_SETTING_MAX_BRIDGE_LENGTH :Maksimuma longeco de pontoj: {STRING} +STR_CONFIG_SETTING_MAX_BRIDGE_HEIGHT_HELPTEXT :Maksimuma alteco por konstrui pontojn +STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH_HELPTEXT :Maksimuma longeco por konstrui tunelojn STR_CONFIG_SETTING_RAW_INDUSTRY_CONSTRUCTION_METHOD :Permana ĉefa industri-konstrumaniero: {STRING} ###length 3 @@ -1150,8 +1312,11 @@ STR_CONFIG_SETTING_RAW_INDUSTRY_CONSTRUCTION_METHOD_NONE :neniu STR_CONFIG_SETTING_RAW_INDUSTRY_CONSTRUCTION_METHOD_NORMAL :kiel aliaj industrioj STR_CONFIG_SETTING_RAW_INDUSTRY_CONSTRUCTION_METHOD_PROSPECTING :esploradon +STR_CONFIG_SETTING_INDUSTRY_PLATFORM :Ebena areo ĉirkaŭ fabrikoj: {STRING} +STR_CONFIG_SETTING_INDUSTRY_PLATFORM_HELPTEXT :Kvanto de ebena spaco ĉirkaŭ fabriko. Tio certigas ke restos spaco ĉirkaŭ fabriko por konstrui fervojojn ktp STR_CONFIG_SETTING_MULTIPINDTOWN :Permesu pliajn similajn industriojn en la sama urbo: {STRING} +STR_CONFIG_SETTING_MULTIPINDTOWN_HELPTEXT :Normale, urbo ne volas pli ol unu fabrikon de ĉiu tipo. Kun tiu ĉi agordo, ĝi permesos plurajn fabrikojn de la sama tipo en la sama urbo STR_CONFIG_SETTING_SIGNALSIDE :Montru signaloj: {STRING} ###length 3 @@ -1159,6 +1324,7 @@ STR_CONFIG_SETTING_SIGNALSIDE_LEFT :Maldekstre STR_CONFIG_SETTING_SIGNALSIDE_RIGHT :Dekstre STR_CONFIG_SETTING_SHOWFINANCES :Montru financan fenestron fine de la jaro: {STRING} +STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :Kiam tiu ĉi agordo estas aktiva, la financa fenesto aperas je la fino de ĉiu jaro por ebligi facilan inspektadon de la financa stato de la kompanio STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT :Novaj ordonoj estas 'sen-halte' per defaŭlto: {STRING} @@ -1175,8 +1341,10 @@ STR_CONFIG_SETTING_AUTOSCROLL_MAIN_VIEWPORT :Ĉefa vidujo STR_CONFIG_SETTING_AUTOSCROLL_EVERY_VIEWPORT :Ĉiu vidujo STR_CONFIG_SETTING_BRIBE :Permesu subaĉeti la lokajn estrojn: {STRING} +STR_CONFIG_SETTING_BRIBE_HELPTEXT :Permesu al kompanioj provi subaĉeti lokajn estrojn. Se inspektoro rimarkas la subaĉeton, la kompanio dum ses monatoj ne povos agi en la urbo STR_CONFIG_SETTING_ALLOW_EXCLUSIVE :Permesu aĉeti ekskluzivajn transportrajtojn: {STRING} +STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT :Se kompanio aĉetas ekskluzivajn transportrajtojn en urbo, stacioj de konkurantoj (pasaĝeraj kaj kargaj) ne ricevos ŝarĝojn dum tuta jaro STR_CONFIG_SETTING_ALLOW_FUND_ROAD :Permesi fondusante lokan stratan rekonstruo: {STRING} @@ -1185,6 +1353,7 @@ STR_CONFIG_SETTING_ALLOW_GIVE_MONEY :Permesu sendi m STR_CONFIG_SETTING_FREIGHT_TRAINS :Pezpliigo pro ŝarĝo por imiti pezajn trajnojn: {STRING} +STR_CONFIG_SETTING_PLANE_SPEED :Aviadila rapidecfaktoro: {STRING} STR_CONFIG_SETTING_PLANE_SPEED_VALUE :1 / {COMMA} STR_CONFIG_SETTING_PLANE_CRASHES :Nombro da aviadilokraŝoj: {STRING} @@ -1194,16 +1363,22 @@ STR_CONFIG_SETTING_PLANE_CRASHES_REDUCED :reduktita STR_CONFIG_SETTING_PLANE_CRASHES_NORMAL :normala STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD :Permesu trairajn bushaltejojn sur urboposedataj stratoj: {STRING} +STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :Permesu konstrui trairajn strathaltejojn sur stratoj posedataj de urboj STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD :Permesu trairajn strathaltejojn sur stratoj posedataj de konkurantoj: {STRING} +STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Permesu konstrui trairajn strathaltejojn sur stratoj posedataj de konkurantoj STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Ne eblas ŝanĝi ĉi tiujn agordon dum veturiloj ĉeestas +STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :Kiam tiu ĉi agordo estas aktiva, infrastrukturo kaŭzas bontenadajn kostojn. La kosto kreskas super-proporcie laŭ la grandeco de la transportreto, kaj tial pli efikas al grandaj kompanioj ol al malgrandaj +STR_CONFIG_SETTING_COMPANY_STARTING_COLOUR_HELPTEXT :Elektu komencan koloron por la kompanio STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS :Flughavenoj neniam antikviĝas: {STRING} STR_CONFIG_SETTING_WARN_LOST_VEHICLE :Avertu se veturilo estas perdita: {STRING} +STR_CONFIG_SETTING_WARN_LOST_VEHICLE_HELPTEXT :Montru mesaĝojn pri veturiloj kiuj ne povas trovi vojon al sia celpunkto STR_CONFIG_SETTING_ORDER_REVIEW :Revui veturilajn ordonojn: {STRING} +STR_CONFIG_SETTING_ORDER_REVIEW_HELPTEXT :Kiam tiu ĉi agordo estas aktiva, ordonoj de veturiloj estos regule kontrolataj, kaj iuj evidentaj problemoj estos anoncataj per novaĵmesaĝo post detektiĝo ###length 3 STR_CONFIG_SETTING_ORDER_REVIEW_OFF :ne STR_CONFIG_SETTING_ORDER_REVIEW_EXDEPOT :jes, escepte de haltigitaj veturiloj @@ -1212,34 +1387,50 @@ STR_CONFIG_SETTING_ORDER_REVIEW_ON :de ĉiuj veturi STR_CONFIG_SETTING_WARN_INCOME_LESS :Avertu se gajno de veturilo negativas: {STRING} STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES :Veturiloj neniam eluziĝas: {STRING} +STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_HELPTEXT :Se tiu ĉi agordo estas aktiva, ĉiuj modeloj de veturiloj restos aĉeteblaj por ĉiam post ekhaveblo STR_CONFIG_SETTING_AUTORENEW_VEHICLE :Aŭtomate anstataŭu veturilon se malnoviĝas: {STRING} +STR_CONFIG_SETTING_AUTORENEW_VEHICLE_HELPTEXT :Se tiu ĉi agordo estas aktiva, veturilo kiu alproksimiĝas al la fino de sia vivdaŭro estos aŭtomate anstataŭigita kiam la renovigkondiĉoj estas plenumitaj +STR_CONFIG_SETTING_AUTORENEW_MONTHS_HELPTEXT :Relativa aĝo je kiu veturilo estu konsiderata por aŭtomata anstataŭigo ###length 2 STR_CONFIG_SETTING_AUTORENEW_MONTHS_VALUE_BEFORE :{COMMA} monato{P 0 "" j} antaŭe STR_CONFIG_SETTING_AUTORENEW_MONTHS_VALUE_AFTER :{COMMA} monato{P 0 "" j} post STR_CONFIG_SETTING_AUTORENEW_MONEY :Minimuma mono por anstataŭado: {STRING} +STR_CONFIG_SETTING_ERRMSG_DURATION :Daŭro de erarmesaĝo: {STRING} +STR_CONFIG_SETTING_ERRMSG_DURATION_VALUE :{COMMA} sekundo{P 0 "" j} +STR_CONFIG_SETTING_ERRMSG_DURATION_VALUE.n :{COMMA} sekundo{P 0 "" j}n +STR_CONFIG_SETTING_HOVER_DELAY :Montri ŝpruchelpilojn: {STRING} ###setting-zero-is-special STR_CONFIG_SETTING_POPULATION_IN_LABEL :Montru enloĝantaron en urbnomindikilo: {STRING} +STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Larĝeco de la linio en la diagramoj. Mallarĝa linio estas pli precize legebla, larĝa linio estas pli facile videbla kaj koloroj pli facile distingeblas. +STR_CONFIG_SETTING_SHOW_NEWGRF_NAME :Montru la nomon de la NewGRF in la veturilkonstrua fenestro: {STRING} +STR_CONFIG_SETTING_LANDSCAPE :Landaspekto: {STRING} STR_CONFIG_SETTING_LAND_GENERATOR :Landgenerilo: {STRING} ###length 2 STR_CONFIG_SETTING_LAND_GENERATOR_ORIGINAL :Originale STR_CONFIG_SETTING_LAND_GENERATOR_TERRA_GENESIS :TerraGenesis +STR_CONFIG_SETTING_TERRAIN_TYPE :Terentipo: {STRING} +STR_CONFIG_SETTING_INDUSTRY_DENSITY :Denseco de fabrikoj: {STRING} +STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :Agordu kiom da fabrikoj estu generitaj, kaj kiu nivelo estu tenata dum la ludo +STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Maksimuma distanco de la maprando por olea industrio: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Neĝregiona alto: {STRING} +STR_CONFIG_SETTING_SNOW_COVERAGE :Neĝa kovro: {STRING} +STR_CONFIG_SETTING_DESERT_COVERAGE_HELPTEXT :Regu la proksimuman kvanton de dezerto en la tropika terentipo. Dezertoj ankaŭ influas generadon de fabrikoj. Nur uzata dum mapgenerado STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :Nereguleco de tereno: {STRING} ###length 4 @@ -1249,6 +1440,7 @@ STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_ROUGH :Neregule STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_ROUGH :Malregule +STR_CONFIG_SETTING_RIVER_AMOUNT :Nombro de riveroj: {STRING} STR_CONFIG_SETTING_TREE_PLACER :Arbometa algoritmo: {STRING} ###length 3 @@ -1256,6 +1448,7 @@ STR_CONFIG_SETTING_TREE_PLACER_NONE :Neniu STR_CONFIG_SETTING_TREE_PLACER_ORIGINAL :Originale STR_CONFIG_SETTING_TREE_PLACER_IMPROVED :Plibonigita +STR_CONFIG_SETTING_ROAD_SIDE :Stratveturiloj: {STRING} ###length 2 STR_CONFIG_SETTING_ROAD_SIDE_LEFT :Veturu maldekstre @@ -1268,22 +1461,27 @@ STR_CONFIG_SETTING_HEIGHTMAP_ROTATION_CLOCKWISE :Dekstrume STR_CONFIG_SETTING_SE_FLAT_WORLD_HEIGHT :Kiom alta fariĝos scenara mapo: {STRING} ###length 2 -STR_CONFIG_SETTING_EDGES_NOT_EMPTY :{WHITE}Unu ay pli da xeloj en norda parto ne malplenas +STR_CONFIG_SETTING_EDGES_NOT_EMPTY :{WHITE}Almenaŭ unu kahelo ĉe la norda rando ne estas malplena STR_CONFIG_SETTING_EDGES_NOT_WATER :{WHITE}Almenaŭ unu kahelo ĉe unu de la eĝoj ne estas akvo +STR_CONFIG_SETTING_STATION_SPREAD_HELPTEXT :Maksimuma area sur kiu povas etendiĝi unu stacio. Atentu ke altaj valoroj malrapidigos la ludon STR_CONFIG_SETTING_SERVICEATHELIPAD :Aŭtomate prizorgu helikopterojn sur helikopterejoj: {STRING} STR_CONFIG_SETTING_LINK_TERRAFORM_TOOLBAR :Ligu landaspektan breton al konstruadaj bretoj: {STRING} +STR_CONFIG_SETTING_LINK_TERRAFORM_TOOLBAR_HELPTEXT :Kiam malfermiĝas ilbreto por iu transporttipo, ankaŭ malfermu la landaspektan ilbreton +STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_HELPTEXT :Terena koloro en la mapeto ###length 3 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_GREEN :Verda STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_DARK_GREEN :Malhele verda STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :Viola ###length 4 +STR_CONFIG_SETTING_LINKGRAPH_COLOURS_GREEN_TO_RED :Verda al ruĝa (originalo) ###length 4 +STR_CONFIG_SETTING_SCROLLMODE_RMB_LOCKED :Movu la mapon per la dekstra musbutono, la pozicio de la muskursoro estas ŝlosita STR_CONFIG_SETTING_SMOOTH_SCROLLING :Glata rulumado de vidujo: {STRING} @@ -1298,8 +1496,10 @@ STR_CONFIG_SETTING_LIVERIES_ALL :Ĉiuj kompanioj STR_CONFIG_SETTING_PREFER_TEAMCHAT :Preferu teambabiladon per : {STRING} STR_CONFIG_SETTING_SCROLLWHEEL_MULTIPLIER :Mapskrolrada rapido: {STRING} +STR_CONFIG_SETTING_SCROLLWHEEL_MULTIPLIER_HELPTEXT :Agordu la sentemon de musrada rulumado STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING :Funkcio de skrol-rado: {STRING} +STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING_HELPTEXT :Ebligu rulumadon per du-dimensiaj musradoj ###length 3 STR_CONFIG_SETTING_SCROLLWHEEL_ZOOM :Zomi mapon STR_CONFIG_SETTING_SCROLLWHEEL_SCROLL :Skroli mapon @@ -1312,7 +1512,10 @@ STR_CONFIG_SETTING_OSK_ACTIVATION_DOUBLE_CLICK :Duobla klako STR_CONFIG_SETTING_OSK_ACTIVATION_SINGLE_CLICK_FOCUS :Unuobla klako (kiam enfokusigita) STR_CONFIG_SETTING_OSK_ACTIVATION_SINGLE_CLICK :Unuobla klako (tuj) +STR_CONFIG_SETTING_USE_RELAY_SERVICE :Uzu relajsan servon: {STRING} ###length 3 +STR_CONFIG_SETTING_USE_RELAY_SERVICE_NEVER :Neniam +STR_CONFIG_SETTING_USE_RELAY_SERVICE_ALLOW :Permesu STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU :Imitado de dekstra klako: {STRING} ###length 3 @@ -1320,7 +1523,10 @@ STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_COMMAND :Komando+Klako STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_CONTROL :Ctrl+Klako STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_OFF :Ne +STR_CONFIG_SETTING_RIGHT_MOUSE_WND_CLOSE_HELPTEXT :Fermas fenestron per dekstra klako ene de ĝi. Malebligas ŝpruchelpilon je dekstra klako! +STR_CONFIG_SETTING_AUTOSAVE :Aŭtomata konservado: {STRING} +STR_CONFIG_SETTING_AUTOSAVE_HELPTEXT :Elektu intervalon inter aŭtomataj konservadoj de la ludo STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES :Uzu la {STRING} datformon por nomoj de konservludoj. ###length 3 @@ -1331,15 +1537,17 @@ STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES_ISO :ISO (2008-12-31 STR_CONFIG_SETTING_PAUSE_ON_NEW_GAME :Aŭtomate paŭzu startante novan ludon: {STRING} STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL :Permesu dum paŭza: {STRING} +STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_HELPTEXT :Elektu kiaj agoj fareblas dum la ludo estas paŭzigita ###length 4 STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_NO_ACTIONS :neniu agoj STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_NON_CONSTRUCTION :ĉiuj ne-konstruado agoj -STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_NON_LANDSCAPING :ĉiuj sed pejzaĝo ŝanĝanta agoj +STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_NON_LANDSCAPING :Ĉiuj krom agoj kiuj ŝanĝas landaspekton STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_ACTIONS :ĉiuj agoj STR_CONFIG_SETTING_ADVANCED_VEHICLE_LISTS :Uzu grupoj en veturilara listo: {STRING} STR_CONFIG_SETTING_LOADING_INDICATORS :Uzu ŝarg-indikilojn: {STRING} +STR_CONFIG_SETTING_LOADING_INDICATORS_HELPTEXT :Elektu ĉu ŝarĝ-indikiloj estas montrataj sub ŝarĝataj aŭ malŝarĝataj veturiloj STR_CONFIG_SETTING_TIMETABLE_IN_TICKS :Montru horaron laŭ tikoj anstataŭ laŭ tagoj: {STRING} @@ -1354,16 +1562,23 @@ STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_LAST :Laste havebla STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_MOST_USED :Pli uzata STR_CONFIG_SETTING_SHOW_TRACK_RESERVATION :Montru rezervitajn trakojn: {STRING} +STR_CONFIG_SETTING_SHOW_TRACK_RESERVATION_HELPTEXT :Donu alian koloron al rezervitaj trakoj por helpi je problemoj pri trajnoj kiuj rifuzas eniri voj-bazitajn blokojn STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :Lasu aktivaj la konstruilojn post uzado: {STRING} +STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Lasu aktivaj la ilojn por konstrui pontojn, tunelojn ktp post uzo +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT :Maksimuma rapideco dum rapidpluigo: {STRING} ###setting-zero-is-special +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :Senlime (tiel rapide kiel ebligas via komputilo) +STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :Ludu sonon je resumaj novaĵmesaĝoj STR_CONFIG_SETTING_SOUND_NEWS :Ĵurnalo: {STRING} +STR_CONFIG_SETTING_SOUND_NEWS_HELPTEXT :Ludu sonon je montro de gazetoj STR_CONFIG_SETTING_SOUND_NEW_YEAR :Fine de la jaro: {STRING} +STR_CONFIG_SETTING_SOUND_NEW_YEAR_HELPTEXT :Ludu sonon je la fino de jaro resumantan la rendimenton de la kompanio dum la jaro kompare al la antaŭa jaro STR_CONFIG_SETTING_SOUND_CONFIRM :Konstruado: {STRING} @@ -1378,18 +1593,23 @@ STR_CONFIG_SETTING_SOUND_AMBIENT :Ĉirkaŭaĵo: { STR_CONFIG_SETTING_SOUND_AMBIENT_HELPTEXT :Ludu ĉirkaŭaĵa sonefektoj de pejzaĝo, industrioj kaj urboj STR_CONFIG_SETTING_MAX_TRAINS :Trajna maksimumo por ĉiu kompanio: {STRING} +STR_CONFIG_SETTING_MAX_TRAINS_HELPTEXT :Maksimuma nombro de trajnoj kiun kompanio povas havi STR_CONFIG_SETTING_MAX_ROAD_VEHICLES :Stratveturila maksimumo por ĉiu kompanio: {STRING} STR_CONFIG_SETTING_MAX_AIRCRAFT :Aviadila maksimumo por ĉiu kompanio: {STRING} STR_CONFIG_SETTING_MAX_SHIPS :Ŝipa maksimumo por ĉiu kompanio: {STRING} +STR_CONFIG_SETTING_MAX_SHIPS_HELPTEXT :Maksimuma nombro de ŝipoj kiun kompanio povas havi STR_CONFIG_SETTING_AI_BUILDS_TRAINS :Malebligu trajnojn por la komputilo: {STRING} +STR_CONFIG_SETTING_AI_BUILDS_TRAINS_HELPTEXT :Se tiu ĉi agordo estas aktiva, komputila ludanto ne povas konstrui trajnojn STR_CONFIG_SETTING_AI_BUILDS_ROAD_VEHICLES :Malebligu stratveturilojn por la komputilo: {STRING} +STR_CONFIG_SETTING_AI_BUILDS_ROAD_VEHICLES_HELPTEXT :Se tiu ĉi agordo estas aktiva, komputila ludanto ne povas konstrui stratveturilojn STR_CONFIG_SETTING_AI_BUILDS_AIRCRAFT :Malebligu aviadilojn por la komputilo: {STRING} +STR_CONFIG_SETTING_AI_BUILDS_AIRCRAFT_HELPTEXT :Se tiu ĉi agordo estas aktiva, komputila ludanto ne povas konstrui aviadilojn STR_CONFIG_SETTING_AI_BUILDS_SHIPS :Malebligu ŝipojn por la komputilo: {STRING} @@ -1400,10 +1620,15 @@ STR_CONFIG_SETTING_AI_PROFILE_MEDIUM :Mezfacila STR_CONFIG_SETTING_AI_PROFILE_HARD :Malfacila STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :Permesu ArtefaritajnIntelektojn en pluropaj ludoj: {STRING} +STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :Permesu ke Artefarit-Inteligentaj (AI) komputilaj ludantoj partoprenu en pluropaj ludoj +STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_HELPTEXT :Kiom da memoro unuopa skripto rajtas konsumi antaŭ ol ĝi estos devige haltigita. Povas esti necese altigi tiun ĉi agordon por grandaj mapoj. STR_CONFIG_SETTING_SERVINT_ISPERCENT :Prizorgintervala procentaĵo: {STRING} +STR_CONFIG_SETTING_SERVINT_ISPERCENT_HELPTEXT :Kiam tiu ĉi agordo estas aktiva, veturiloj klopodas iri al priservado kiam ilia fidindeco estas je certa procentaĵo malpli alta ol la maksimuma fidindeco.{}{}Ekzemple, se la maksimuma fidindeco de veturilo estas 90% kaj la priservintervalo estas 20%, la veturilo klopodos iri al priservado kiam ĝi atingas fidindecon de 72%. +STR_CONFIG_SETTING_SERVINT_TRAINS :Defaŭlta priservintervalo for trajnoj: {STRING} +STR_CONFIG_SETTING_SERVINT_SHIPS :Defaŭlta priservintervalo por ŝipoj: {STRING} ###setting-zero-is-special STR_CONFIG_SETTING_NOSERVICE :Malŝaltu prizorgadon se rompiĝoj ne okazas: {STRING} @@ -1414,57 +1639,77 @@ STR_CONFIG_SETTING_WAGONSPEEDLIMITS_HELPTEXT :Kiam ebligita, STR_CONFIG_SETTING_DISABLE_ELRAILS :Malŝaltu elektrajn relojn: {STRING} STR_CONFIG_SETTING_NEWS_ARRIVAL_FIRST_VEHICLE_OWN :Unua veturilo atingas propran stacion: {STRING} +STR_CONFIG_SETTING_NEWS_ARRIVAL_FIRST_VEHICLE_OWN_HELPTEXT :Montru gazetan anoncon kiam alvenas la unua veturilo ĉe nova stacio de la ludanto STR_CONFIG_SETTING_NEWS_ARRIVAL_FIRST_VEHICLE_OTHER :Unua veturilo atingas konkurantan stacion: {STRING} +STR_CONFIG_SETTING_NEWS_ARRIVAL_FIRST_VEHICLE_OTHER_HELPTEXT :Montru gazetan anoncon kiam alvenas la unua veturilo ĉe nova stacio de konkuranto STR_CONFIG_SETTING_NEWS_ACCIDENTS_DISASTERS :Akcidentoj / katastrofoj: {STRING} +STR_CONFIG_SETTING_NEWS_ACCIDENT_OTHER :Akcidentoj de veturiloj de konkurantoj: {STRING} +STR_CONFIG_SETTING_NEWS_ACCIDENT_OTHER_HELPTEXT :Montru gazetajn anoncojn pri kraŝintaj veturiloj de konkurantoj STR_CONFIG_SETTING_NEWS_COMPANY_INFORMATION :Kompaniaj informoj: {STRING} STR_CONFIG_SETTING_NEWS_INDUSTRY_OPEN :Malfermiĝo de industrioj: {STRING} STR_CONFIG_SETTING_NEWS_INDUSTRY_CLOSE :Fermiĝo de industrioj: {STRING} +STR_CONFIG_SETTING_NEWS_INDUSTRY_CLOSE_HELPTEXT :Montru gazetan anoncon kiam fabriko fermiĝas STR_CONFIG_SETTING_NEWS_ECONOMY_CHANGES :Ekonomiaj ŝanĝoj: {STRING} -STR_CONFIG_SETTING_NEWS_INDUSTRY_CHANGES_COMPANY :Profuktadŝanĝiĝoj de industrioj servitaj de la kompanio: {STRING} +STR_CONFIG_SETTING_NEWS_INDUSTRY_CHANGES_COMPANY :Produktadŝanĝiĝoj de industrioj servataj de la kompanio: {STRING} +STR_CONFIG_SETTING_NEWS_INDUSTRY_CHANGES_COMPANY_HELPTEXT :Montru gazetan anoncon kiam ŝanĝiĝas la produktadkvanto de fabrikoj servataj de la kompanio -STR_CONFIG_SETTING_NEWS_INDUSTRY_CHANGES_OTHER :Profuktadŝanĝiĝoj de industrioj servitaj de konkuranto(j): {STRING} +STR_CONFIG_SETTING_NEWS_INDUSTRY_CHANGES_OTHER :Produktadŝanĝiĝoj de industrioj servataj de konkuranto(j): {STRING} STR_CONFIG_SETTING_NEWS_INDUSTRY_CHANGES_UNSERVED :Aliaj industriaj produktadŝanĝiĝoj: {STRING} +STR_CONFIG_SETTING_NEWS_INDUSTRY_CHANGES_UNSERVED_HELPTEXT :Montru gazetan anoncon kiam ŝanĝiĝas la produktadkvanto de fabrikoj ne servataj de la kompanio nek de konkurantoj STR_CONFIG_SETTING_NEWS_ADVICE :Konsilo / informoj pri kompania veturilaro: {STRING} STR_CONFIG_SETTING_NEWS_NEW_VEHICLES :Novaj veturiloj: {STRING} +STR_CONFIG_SETTING_NEWS_NEW_VEHICLES_HELPTEXT :Montru gazetan anoncon kiam nova tipo de veturilo ekhaveblas STR_CONFIG_SETTING_NEWS_CHANGES_ACCEPTANCE :Ŝarĝakceptado ŝanĝiĝas: {STRING} STR_CONFIG_SETTING_NEWS_SUBSIDIES :Subvencioj: {STRING} STR_CONFIG_SETTING_NEWS_GENERAL_INFORMATION :Ĝeneralaj informoj: {STRING} +STR_CONFIG_SETTING_NEWS_GENERAL_INFORMATION_HELPTEXT :Montru gazetajn anoncojn pri ĝeneralaj eventoj, ekzemple aĉeto de ekskluzivaj rajtoj aŭ monprovizo por vojrekonstruado ###length 3 STR_CONFIG_SETTING_NEWS_MESSAGES_OFF :Ne STR_CONFIG_SETTING_NEWS_MESSAGES_SUMMARY :Resume STR_CONFIG_SETTING_NEWS_MESSAGES_FULL :Plene STR_CONFIG_SETTING_COLOURED_NEWS_YEAR :Koloraj novaĵoj aperas en: {STRING} +STR_CONFIG_SETTING_COLOURED_NEWS_YEAR_HELPTEXT :Jaro ekde kiu gazetaj anoncoj estas koloraj. Antaŭ tiu ĉi jaro, ili estas unukolore nigra-blankaj STR_CONFIG_SETTING_STARTING_YEAR :Komenca jaro: {STRING} +STR_CONFIG_SETTING_ENDING_YEAR :Poentkalkula finjaro: {STRING} +STR_CONFIG_SETTING_ENDING_YEAR_HELPTEXT :La jaro en kiu la ludo finiĝas rilate al poentkalkulado. Je la fino de tiu ĉi jaro, la poentaro de la kompanio estas registrita kaj la altpoentara listo aperas, sed la ludantoj povas daŭrigi ludi post tio.{}Se tiu ĉi jaro estas antaŭ la ludkomenca jaro, la altpoentara listo neniam aperas. +STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM} ###setting-zero-is-special +STR_CONFIG_SETTING_ENDING_YEAR_ZERO :Neniam +STR_CONFIG_SETTING_ECONOMY_TYPE_HELPTEXT :En glata ekonomio, produktadkvantoj ŝanĝiĝas pli ofte, kaj je malpli multe. En frostigita ekonomio, ne ŝanĝiĝas produktadkvantoj kaj ne fermiĝas fabrikoj. Tiu ĉi agordo povas esti senefika se iu NewGRF provizas tipojn de fabrikoj. ###length 3 +STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_VALUE :{COMMA} kahelo{P 0 "" j} STR_CONFIG_SETTING_SEMAPHORE_BUILD_BEFORE_DATE :Aŭtomate konstruu semaforojn antaŭ: {STRING} +STR_CONFIG_SETTING_SEMAPHORE_BUILD_BEFORE_DATE_HELPTEXT :Elektu la jaron ekde kiam uziĝos elektraj semaforoj por fervojaj trakoj. Antaŭ tiuj ĉi jaro uziĝos ne-elektraj semaforoj (kiuj havas ekzakte la saman funkcion, sed alian aspekton) STR_CONFIG_SETTING_CYCLE_SIGNAL_TYPES :Iteraciu tra signaltipoj: {STRING} ###length 2 -STR_CONFIG_SETTING_CYCLE_SIGNAL_PBS :Pad-signaloj sole -STR_CONFIG_SETTING_CYCLE_SIGNAL_ALL :ĉiom +STR_CONFIG_SETTING_CYCLE_SIGNAL_PBS :Nur vojrezervaj semaforoj +STR_CONFIG_SETTING_CYCLE_SIGNAL_ALL :ĉiuj videblaj +STR_CONFIG_SETTING_SIGNAL_GUI_MODE :Montru semaforajn tipojn: {STRING} +STR_CONFIG_SETTING_SIGNAL_GUI_MODE_HELPTEXT :Elektu kiuj tipoj de semaforoj montriĝas en la semafora ilbreto ###length 2 +STR_CONFIG_SETTING_SIGNAL_GUI_MODE_PATH :Nur vojrezervaj semaforoj STR_CONFIG_SETTING_TOWN_LAYOUT :Strataranĝo por novaj urboj: {STRING} ###length 5 @@ -1475,9 +1720,11 @@ STR_CONFIG_SETTING_TOWN_LAYOUT_3X3_GRID :kvadrataro de 3 STR_CONFIG_SETTING_TOWN_LAYOUT_RANDOM :hazarda STR_CONFIG_SETTING_ALLOW_TOWN_ROADS :Permesu ke urboj konstruu stratojn: {STRING} -STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS :Urboj estas permesa konstrui traknivelajn pasejojn: {STRING} +STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS :Urboj rajtas konstrui traknivelajn pasejojn: {STRING} +STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS_HELPTEXT :Kiam tiu ĉi agordo estas aktiva, urboj rajtas konstrui traknivelajn pasejojn STR_CONFIG_SETTING_NOISE_LEVEL :Permesu urbe kontrolatan laŭtecon por flughavenoj: {STRING} +STR_CONFIG_SETTING_NOISE_LEVEL_HELPTEXT :Kiam tiu ĉi agordo estas malaktiva, povas esti nur du flughavenoj en ĉiu urbo. Kiam tiu ĉi agordo estas aktiva, la nombron de flughavenoj en urbo limigas la bruo-toleremo de la urbo, kiu dependas de la loĝantaro kaj de la grandeco kaj distanco de la flughaveno STR_CONFIG_SETTING_TOWN_FOUNDING :Konstruanta urbojn en ludo: {STRING} ###length 3 @@ -1486,21 +1733,39 @@ STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED :permesate STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT :permesate, kutimo urbomapo ###length 2 +STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :Lineara STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT :En ludo metanta de arbojn: {STRING} ###length 4 +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_ALL :Kresku kaj disvastiĝu ĉie +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_GROWTH_NO_SPREAD :Ne kresku, ne disvastiĝu {RED}(malfunkciigas lignejojn) STR_CONFIG_SETTING_TOOLBAR_POS :Loko de ĉefa ilbreto: {STRING} +STR_CONFIG_SETTING_TOOLBAR_POS_HELPTEXT :Horizontala pozicio de la ĉefa ilobreto je la supro de la ekrano STR_CONFIG_SETTING_STATUSBAR_POS :Loko de kondiĉbaro: {STRING} +STR_CONFIG_SETTING_STATUSBAR_POS_HELPTEXT :Horizontala pozicio de la stata breto je la subo de la ekrano +STR_CONFIG_SETTING_SNAP_RADIUS_VALUE :{COMMA} rastrumero{P 0 "" j} ###setting-zero-is-special +STR_CONFIG_SETTING_SNAP_RADIUS_DISABLED :Malaktiva STR_CONFIG_SETTING_SOFT_LIMIT :Fenestra mollimo (ne-glueca): {STRING} +STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} ###setting-zero-is-special +STR_CONFIG_SETTING_ZOOM_MAX :Maksimuma elzom-nivelo: {STRING} +STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT :La maksumuma elzom-nivelo por vidujoj. Pli altaj elzom-niveloj povas kaŭzi malrapidiĝon ###length 6 +STR_CONFIG_SETTING_ZOOM_LVL_MIN :4x +STR_CONFIG_SETTING_ZOOM_LVL_NORMAL :Normala +STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X :2x +STR_CONFIG_SETTING_ZOOM_LVL_OUT_4X :4x +STR_CONFIG_SETTING_ZOOM_LVL_OUT_8X :8x ###length 3 +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_IN_2X :2x +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_NORMAL :1x STR_CONFIG_SETTING_TOWN_GROWTH :Urba kreskorapido: {STRING} +STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT :Urba kreskorapido ###length 5 STR_CONFIG_SETTING_TOWN_GROWTH_NONE :Neniom STR_CONFIG_SETTING_TOWN_GROWTH_SLOW :Malrapide @@ -1511,28 +1776,46 @@ STR_CONFIG_SETTING_TOWN_GROWTH_VERY_FAST :Tre rapide ###setting-zero-is-special STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :Komenca urbgrandeca multobligo: {STRING} +STR_CONFIG_SETTING_LINKGRAPH_RECALC_INTERVAL :Ĝisdatigu la distribuan grafeon {P 0:2 "ĉiun" "ĉiujn"} {STRING}{NBSP}{P 0:2 "sekundon" "sekundojn"} +STR_CONFIG_SETTING_DISTRIBUTION_MAIL :Distribua reĝimo por poŝto: {STRING} ###length 3 +STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :nesimetria +STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :simetria ###length 5 +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :Angla-usona sistemo (mejloj hore) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :Ludunuoj (kaheloj tage) +STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_HELPTEXT :Kiam ajn montriĝas povumo de veturilo en la uzantinterfaco, montru ĝin en la indikitaj unuoj ###length 3 +STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_IMPERIAL :Angla-usona sistemo (ĉp) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_METRIC :Metriko (hp) +STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT :Pezunuoj: {STRING} +STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT_HELPTEXT :Kiam ajn montriĝas pezo en la uzantinterfaco, montru ĝin en la indikitaj unuoj ###length 3 +STR_CONFIG_SETTING_LOCALISATION_UNITS_VOLUME :Volumenaj unuoj: {STRING} +STR_CONFIG_SETTING_LOCALISATION_UNITS_VOLUME_HELPTEXT :Kiam ajn montriĝas volumeno en la uzantinterfaco, montru ĝin en la indikitaj unuoj ###length 3 +STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE_HELPTEXT :Kiam ajn montriĝas trakcia forto en la uzantinterfaco, montru ĝin en la indikitaj unuoj ###length 3 +STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT :Altecunuoj: {STRING} ###length 3 +STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_IMPERIAL :Angla-usona sistemo (futoj) +STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_METRIC :Metra (m) STR_CONFIG_SETTING_SOUND :{ORANGE}Sonefektoj STR_CONFIG_SETTING_INTERFACE :{ORANGE}Interfaco STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION :{ORANGE}Konstruado +STR_CONFIG_SETTING_COMPANY :{ORANGE}Kompanio +STR_CONFIG_SETTING_ACCOUNTING :{ORANGE}Librotenado STR_CONFIG_SETTING_VEHICLES :{ORANGE}Veturiloj STR_CONFIG_SETTING_VEHICLES_ROUTING :{ORANGE}Navigado STR_CONFIG_SETTING_ACCIDENTS :{ORANGE}Katastrofoj / akcidentoj @@ -1540,13 +1823,16 @@ STR_CONFIG_SETTING_GENWORLD :{ORANGE}Mondgen STR_CONFIG_SETTING_ENVIRONMENT :{ORANGE}Medio STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :{ORANGE}Urboj STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :{ORANGE}Industrioj +STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :{ORANGE}Ŝarĝodistribuado STR_CONFIG_SETTING_AI :{ORANGE}Konkurantoj STR_CONFIG_SETTING_AI_NPC :{ORANGE}Komputil-ludantoj +STR_CONFIG_SETTING_NETWORK :{ORANGE}Reto STR_CONFIG_SETTING_PATHFINDER_FOR_TRAINS :Padtrovilo por trajnoj: {STRING} STR_CONFIG_SETTING_PATHFINDER_FOR_ROAD_VEHICLES :Padtrovilo por stratveturiloj: {STRING} STR_CONFIG_SETTING_PATHFINDER_FOR_SHIPS :Padtrovilo por ŝipoj: {STRING} STR_CONFIG_SETTING_REVERSE_AT_SIGNALS :Aŭtomata dorsflankante ĉe signaloj: {STRING} +STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Permesu al trajno turniĝi ĉe semaforo, se ĝi jam atendis dum longa tempo ###length 2 STR_CONFIG_SETTING_PATHFINDER_NPF :NPF STR_CONFIG_SETTING_PATHFINDER_YAPF :YAPF {BLUE}(Rekomendite) @@ -1554,8 +1840,15 @@ STR_CONFIG_SETTING_PATHFINDER_YAPF :YAPF {BLUE}(Rek STR_CONFIG_SETTING_QUERY_CAPTION :{WHITE}Ŝanĝu agord-valoron # Config errors +STR_CONFIG_ERROR :{WHITE}Eraro en la agorda dosiero... +STR_CONFIG_ERROR_INVALID_VALUE :{WHITE}... malvalida valoro '{STRING}' por '{STRING}' +STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE :malkongrua kun tiu ĉi versio de OpenTTD +STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN :nekonata +STR_CONFIG_ERROR_INVALID_BASE_GRAPHICS_NOT_FOUND :{WHITE}... ignoras Bazan Grafikaron '{STRING}': ne trovebla +STR_CONFIG_ERROR_INVALID_BASE_SOUNDS_NOT_FOUND :{WHITE}... ignoras Bazan Sonaron '{STRING}': ne trovebla # Video initalization errors +STR_VIDEO_DRIVER_ERROR :{WHITE}Eraro pri videaj agordoj... # Intro window STR_INTRO_CAPTION :{WHITE}OpenTTD {REV} @@ -1572,6 +1865,7 @@ STR_INTRO_HIGHSCORE :{BLACK}Altpoent STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Agordoj STR_INTRO_NEWGRF_SETTINGS :{BLACK}Agordoj de NewGRF STR_INTRO_ONLINE_CONTENT :{BLACK}Provu Enretan Enhavon +STR_INTRO_GAMESCRIPT_SETTINGS :{BLACK}Ludoskriptaj agordoj STR_INTRO_QUIT :{BLACK}Forlasi STR_INTRO_TOOLTIP_NEW_GAME :{BLACK}Starti novan ludon. Stir-Klak por preterpasi mapan agordon. @@ -1597,6 +1891,7 @@ STR_INTRO_TRANSLATION :{BLACK}Ĉi tiu # Quit window STR_QUIT_CAPTION :{WHITE}Forlasi +STR_QUIT_ARE_YOU_SURE_YOU_WANT_TO_EXIT_OPENTTD :{YELLOW}Ĉu vi vere volas fermi OpenTTD? STR_QUIT_YES :{BLACK}Jes STR_QUIT_NO :{BLACK}Ne @@ -1608,11 +1903,14 @@ STR_ABANDON_SCENARIO_QUERY :{YELLOW}Ĉu vi # Cheat window STR_CHEATS :{WHITE}Filudaĵoj STR_CHEATS_TOOLTIP :{BLACK}La kvadratetoj indikas ĉu vi jam uzis la filudaĵon antaŭe. +STR_CHEATS_NOTE :{BLACK}Notu: ajna uzado de tiuj ĉi agordoj estos registritaj en la konservdosiero STR_CHEAT_MONEY :{LTBLUE}Altigu monkvanton per {CURRENCY_LONG} STR_CHEAT_CHANGE_COMPANY :{LTBLUE}Ludanta kiel kompanio: {ORANGE}{COMMA} STR_CHEAT_EXTRA_DYNAMITE :{LTBLUE}Magia forigilo (forviŝu industriojn, nemovebla objektoj): {ORANGE}{STRING} STR_CHEAT_CROSSINGTUNNELS :{LTBLUE}Tuneloj rajtas kruciĝi unu kun la alia: {ORANGE}{STRING} STR_CHEAT_NO_JETCRASH :{LTBLUE}Jetoj ne kraŝos (ofte) sur malgrandaj flughavenoj: {ORANGE} {STRING} +STR_CHEAT_EDIT_MAX_HL :{LTBLUE}Ŝanĝu la maksimuman mapaltecon: {ORANGE}{NUM} +STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT :{WHITE}Ŝanĝu la maksimuman altecon de montoj sur la mapo STR_CHEAT_CHANGE_DATE :{LTBLUE}Ŝanĝu daton: {ORANGE}{DATE_SHORT} STR_CHEAT_CHANGE_DATE_QUERY_CAPT :{WHITE}Ŝanĝu nunan jaron STR_CHEAT_SETUP_PROD :{LTBLUE}Ebligu ŝanĝi produktvalorojn: {ORANGE}{STRING} @@ -1713,6 +2011,7 @@ STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ŝanĝi # Matches ServerGameType ###length 3 +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Publika # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Pluraj ludantoj @@ -1743,11 +2042,14 @@ STR_NETWORK_SERVER_LIST_LANDSCAPE :{SILVER}Pejzaĝ STR_NETWORK_SERVER_LIST_MAP_SIZE :{SILVER}Mapgrandeco: {WHITE}{COMMA}x{COMMA} STR_NETWORK_SERVER_LIST_SERVER_VERSION :{SILVER}Versio de servilo: {WHITE}{STRING} STR_NETWORK_SERVER_LIST_SERVER_ADDRESS :{SILVER}Adreso de servilo: {WHITE}{STRING} +STR_NETWORK_SERVER_LIST_INVITE_CODE :{SILVER}Invitokodo: {WHITE}{STRING} STR_NETWORK_SERVER_LIST_START_DATE :{SILVER}Komenca dato: {WHITE}{DATE_SHORT} STR_NETWORK_SERVER_LIST_CURRENT_DATE :{SILVER}Nuna dato: {WHITE}{DATE_SHORT} +STR_NETWORK_SERVER_LIST_GAMESCRIPT :{SILVER}Ludoskripto: {WHITE}{STRING} (v{NUM}) STR_NETWORK_SERVER_LIST_PASSWORD :{SILVER}Protektata per pasvorto! STR_NETWORK_SERVER_LIST_SERVER_OFFLINE :{SILVER}SERVILO NE SURRETAS STR_NETWORK_SERVER_LIST_SERVER_FULL :{SILVER}SERVILO PLENAS +STR_NETWORK_SERVER_LIST_SERVER_BANNED :{SILVER}LA SERVILO FORBARIS VIN STR_NETWORK_SERVER_LIST_VERSION_MISMATCH :{SILVER}VERSIOJ NE KONGRUAS STR_NETWORK_SERVER_LIST_GRF_MISMATCH :{SILVER}MISO PRI NEWGRF @@ -1755,12 +2057,15 @@ STR_NETWORK_SERVER_LIST_JOIN_GAME :{BLACK}Aliĝu a STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Refreŝigu servilon STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Refreŝigu servilajn informojn +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Serĉu publikajn servilojn en la Interreto +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Serĉu en la loka reto STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Aldonu servilon STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Aldonas servilon al la listo kiu ĉiam estos kontrolata pri kurantaj ludoj STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Startu servilon STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Startu propran servilon STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Tajpu vian nomon +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}Entajpu adreson de servilo aŭ invitokodon # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Komencu novan ludon por pluraj ludantoj @@ -1805,13 +2110,29 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Kompanio STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klientlisto # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Enretaj ludantoj +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servilo +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nomo +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE :{BLACK}Invitkodo +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Konektotipo +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Ĉu kaj kiel vian servilon povas atingi aliuloj +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Sendu mesaĝon al ĉiuj ludantoj de tiu ĉi kompanio +STR_NETWORK_CLIENT_LIST_SPECTATORS :Spektantoj +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Kreu novan kompanion kaj aliĝu al ĝi +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Tio ĉi estas vi # Matches ConnectionType ###length 5 +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TURN :{BLACK}Tra relajso +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Ĉu vi certas ke vi volas forbari la ludanton '{STRING}'? +STR_NETWORK_ASK_RELAY_YES_ONCE :{BLACK}Jes, ĉi-foje +STR_NETWORK_ASK_SURVEY_CAPTION :Partopreni aŭtomatan sondadon? +STR_NETWORK_ASK_SURVEY_LINK :Pri sondado kaj privateco +STR_NETWORK_ASK_SURVEY_YES :Jes STR_NETWORK_SPECTATORS :Spektantoj @@ -1840,6 +2161,7 @@ STR_NETWORK_CHAT_TO_COMPANY :[Teamo] Al {STR STR_NETWORK_CHAT_CLIENT :[Private] {STRING}: {WHITE}{STRING} STR_NETWORK_CHAT_TO_CLIENT :[Private] Al {STRING}: {WHITE}{STRING} STR_NETWORK_CHAT_ALL :[Ĉiuj] {STRING}: {WHITE}{STRING} +STR_NETWORK_CHAT_EXTERNAL :[{3:STRING}] {0:STRING}: {WHITE}{1:STRING} STR_NETWORK_CHAT_OSKTITLE :{BLACK}Tajpu tekston por retbabilado # Network messages @@ -1856,6 +2178,7 @@ STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Malĝust STR_NETWORK_ERROR_SERVER_FULL :{WHITE}La servilo plenas STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}Oni forbaris vin de la servilo STR_NETWORK_ERROR_KICKED :{WHITE}Oni forbatis vin el la ludo +STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}Kialo: {STRING} STR_NETWORK_ERROR_CHEATER :{WHITE}Vi ne rajtas filudi en ĉi tiu servilo STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}Vi estis sendanta tro da komandojn al la servilo @@ -1877,6 +2200,9 @@ STR_NETWORK_ERROR_CLIENT_KICKED :forbatite de se STR_NETWORK_ERROR_CLIENT_CHEATER :provis uzi filudaĵon STR_NETWORK_ERROR_CLIENT_SERVER_FULL :servilo plenas STR_NETWORK_ERROR_CLIENT_TOO_MANY_COMMANDS :estis sendanta tro da komandojn +STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :ne ricevis pasvorton ene de la tempolimo +STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :ĝenerala tempolimo +STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :elŝutado de mapo daŭris tro longe # Network related errors STR_NETWORK_SERVER_MESSAGE :*** {1:STRING} @@ -1899,9 +2225,11 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} i STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} ekis novan kompanion (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} ĉesis la ludon ({2:STRING}) STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ŝanĝis sian nomon al {STRING} +STR_NETWORK_MESSAGE_GIVE_MONEY :*** {0:STRING} donis {2:CURRENCY_LONG} al {1:STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}La servilo fermis la seancon STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}La servilo restartiĝas...{}Bonvolu atendi... +STR_NETWORK_ERROR_COORDINATOR_REUSE_OF_INVITE_CODE :{WHITE}Alia servilo kun la sama invitokodo registris sin. Ŝaltas al "loka" ludotipo. # Content downloading window STR_CONTENT_TITLE :{WHITE}Enhavo elŝutiĝas @@ -1918,7 +2246,10 @@ STR_CONTENT_UNSELECT_ALL_CAPTION_TOOLTIP :{BLACK}Marki ĉ STR_CONTENT_SEARCH_EXTERNAL :{BLACK}Serĉo eksteraj retejoj STR_CONTENT_SEARCH_EXTERNAL_TOOLTIP :{BLACK} Serĉu enhavo ne estas disponebla en OpenTTD enhavon servon en retejoj ne asocias al OpenTTD STR_CONTENT_SEARCH_EXTERNAL_DISCLAIMER_CAPTION :{WHITE}Vi forlase OpenTTD! +STR_CONTENT_SEARCH_EXTERNAL_DISCLAIMER :{WHITE}La uzokondiĉoj por elŝuti enhavon de eksteraj retejoj estas variaj.{}Vi devas kontroli ĉe la eksteraj retejoj por trovi informojn pri kiel instali la enhavon al OpenTTD.{}Ĉu vi volas daŭrigi? STR_CONTENT_FILTER_TITLE :{BLACK}Etikeda/noma filtro: +STR_CONTENT_OPEN_URL :{BLACK}Vizitu la retejon +STR_CONTENT_OPEN_URL_TOOLTIP :{BLACK}Vizitu la retejon de tiu ĉi enhavo STR_CONTENT_DOWNLOAD_CAPTION :{BLACK}Elŝuti STR_CONTENT_DOWNLOAD_CAPTION_TOOLTIP :{BLACK}Komencu elŝuti la elektitan enhavon. STR_CONTENT_TOTAL_DOWNLOAD_SIZE :{SILVER}Suma elŝuta grando: {WHITE}{BYTES} @@ -1951,7 +2282,7 @@ STR_CONTENT_TYPE_AI_LIBRARY :AI-biblioteko STR_CONTENT_TYPE_SCENARIO :Scenaro STR_CONTENT_TYPE_HEIGHTMAP :Altecmapo STR_CONTENT_TYPE_BASE_SOUNDS :Bazaj sonoj -STR_CONTENT_TYPE_BASE_MUSIC :Baza musiko +STR_CONTENT_TYPE_BASE_MUSIC :Baza muziko # Content downloading progress window STR_CONTENT_DOWNLOAD_TITLE :{WHITE}Enhavo elŝutiĝas... @@ -1971,6 +2302,8 @@ STR_MISSING_GRAPHICS_SET_MESSAGE :{BLACK}OpenTTD STR_MISSING_GRAPHICS_YES_DOWNLOAD :{BLACK}Jes, elŝuti la grafikojn STR_MISSING_GRAPHICS_NO_QUIT :{BLACK}Ne, fermi OpenTTD +STR_MISSING_GRAPHICS_ERROR_TITLE :{WHITE}Elŝutado malsukcesis +STR_MISSING_GRAPHICS_ERROR :{BLACK}Malsukcesis elŝutado de grafiko.{}Bonvolu elŝuti grafikon permane. # Transparency settings window STR_TRANSPARENCY_CAPTION :{WHITE}Ebloj por travidebleco @@ -1983,12 +2316,18 @@ STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Baskulig STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}Baskuligi travideblecon de aĵoj kiel lumturoj kaj antenoj. Ctrl+Klaku por ŝlosi STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}Baskuligi travideblecon de katenarion. Ctrl+Klaku por ŝlosi STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Baskuligi travideblecon de ŝarĝindikiloj. Ctrl+Klaku por ŝlosi +STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}Igu objektojn nevideblaj anstataŭ travideblaj # Linkgraph legend window +STR_LINKGRAPH_LEGEND_ALL :{BLACK}Ĉiuj # Linkgraph legend window and linkgraph legend in smallmap +STR_LINKGRAPH_LEGEND_UNUSED :{TINY_FONT}{BLACK}neuzata +STR_LINKGRAPH_LEGEND_OVERLOADED :{TINY_FONT}{BLACK}superŝutata # Linkgraph tooltip +STR_LINKGRAPH_STATS_TOOLTIP :{BLACK}{CARGO_LONG} transportenda ĉiumonate de {STATION} al {STATION} ({COMMA}% de la kapacito){STRING} +STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION :{}{CARGO_LONG} transportenda reen ({COMMA}% de la kapacito) # Base for station construction window(s) STR_STATION_BUILD_COVERAGE_AREA_TITLE :{BLACK}Ŝarĝregiona marko @@ -2052,16 +2391,27 @@ STR_STATION_BUILD_DRAG_DROP_TOOLTIP :{BLACK}Tiru por STR_STATION_BUILD_STATION_CLASS_TOOLTIP :{BLACK}Elekti stacidomklason por montri. STR_STATION_BUILD_STATION_TYPE_TOOLTIP :{BLACK}Elekti stacidomotipon por konstrui. -STR_STATION_CLASS_DFLT :Defaŭlta stacio +STR_STATION_CLASS_DFLT :Defaŭlta +STR_STATION_CLASS_DFLT_ROADSTOP :Defaŭlta vojhaltejo STR_STATION_CLASS_WAYP :Vojpunktoj # Signal window STR_BUILD_SIGNAL_CAPTION :{WHITE}Elekto de Signalo +STR_BUILD_SIGNAL_TOGGLE_ADVANCED_SIGNAL_TOOLTIP :{BLACK}Ŝaltu montradon de altnivelaj tipoj de semaforoj +STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TOOLTIP :{BLACK}Enirsemaforo (elektra){}Restas verda dum estas almenaŭ unu verda elirsemaforo de la sekva traksekcio. Alikaze ĝi estas ruĝa +STR_BUILD_SIGNAL_ELECTRIC_EXIT_TOOLTIP :{BLACK}Elira semaforo (elektra){}Kondutas same kiel bloka semaforo, sed estas bezonata por kaŭzi la ĝustan koloron ĉe eniraj kaj kombinaj antaŭsemaforoj +STR_BUILD_SIGNAL_ELECTRIC_COMBO_TOOLTIP :{BLACK}Kombina semaforo (elektra){}La kombina semaforo simple agas kaj kiel enira kaj kiel elira semaforo. Tio ebligas konstrui grandajn "arbojn" de antaŭsemaforoj +STR_BUILD_SIGNAL_ELECTRIC_PBS_TOOLTIP :{BLACK}Vojrezerva semaforo (elektra){}Vojrezerva semaforo ebligas ke pli ol unu trajno eniru signalan blokon samtempe, se la trajno povas rezervi vojon ĝis sekura haltopunkto. Normalan vojrezervan semaforon eblas trapasi de la malantaŭa flanko +STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TOOLTIP :{BLACK}Unudirekta vojrezerva semaforo (elektra){}Vojrezerva semaforo ebligas ke pli ol unu trajno eniru signalan blokon samtempe, se la trajno povas rezervi vojon ĝis sekura haltopunkto. Unudirektan vojrezervan semaforon ne eblas trapasi de la malantaŭa flanko # Bridge selection window STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Elektu Relan Ponton STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}Elektu Vojan Ponton STR_SELECT_BRIDGE_SELECTION_TOOLTIP :{BLACK}Pontelekto - klaku vian preferatan ponton por konstrui ĝin +STR_SELECT_BRIDGE_INFO_NAME :{GOLD}{STRING} +STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED :{GOLD}{STRING},{} {VELOCITY} +STR_SELECT_BRIDGE_INFO_NAME_COST :{GOLD}{0:STRING},{} {WHITE}{2:CURRENCY_LONG} +STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED_COST :{GOLD}{STRING},{} {VELOCITY} {WHITE}{CURRENCY_LONG} STR_BRIDGE_NAME_SUSPENSION_STEEL :Ŝtala pendanta STR_BRIDGE_NAME_GIRDER_STEEL :Ŝtala ebena STR_BRIDGE_NAME_CANTILEVER_STEEL :Ŝtala luksa @@ -2076,6 +2426,8 @@ STR_BRIDGE_TUBULAR_SILICON :Tuba, Silicia STR_ROAD_TOOLBAR_ROAD_CONSTRUCTION_CAPTION :{WHITE}Vojkonstruado STR_ROAD_TOOLBAR_TRAM_CONSTRUCTION_CAPTION :{WHITE}Konstruado de tramvojoj STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION :{BLACK}Konstruu vojeron. Ctrl baskuligas inter konstrui/mal-konstrui vojeron. La majuskliga klavo baskuligas inter konstrui/(kosto)taksi +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION :{BLACK}Konstruu tramvojan sekcion. Stir-klavo ŝaltas konstruadon/forigon por tramvojkonstruado. Majuskliga klavo ŝaltas konstruadon/montradon de kostotakso +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}Konstruu vojan sekcion per la Aŭtomata voj-reĝimo. Stir-klavo ŝaltas konstruadon/forigon por vojkonstruado. Majuskliga klavo ŝaltas konstruadon/montradon de kostotakso STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Konstrui stratveturilan garaĝon (por konstrui kaj prizorgi veturilojn). Baskulu inter konstrui/(kosto)taksi per maljuskliga klavo STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Konstrui tramveturilan garaĝon (por konstrui kaj prizorgi veturilojn). Baskulu inter konstrui/(kosto)taksi per maljuskliga klavo STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}Konstrui bushaltejon. Premu Ctrl-klavo por ligi haltejojn. Baskulu inter konstrui/(kosto)taksi per maljuskliga klavo @@ -2088,7 +2440,10 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_BRIDGE :{BLACK}Konstrui STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL :{BLACK}Konstruu vojtunelon. Baskulu inter konstrui/(kosto)taksi per maljuskliga klavo STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL :{BLACK}Konstrui tramtunelon. Baskulu inter konstrui/(kosto)taksi per maljuskliga klavo STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD :{BLACK}Ĉu konstrui ĉu forigi por vojkonstruado +STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM :{BLACK}Konvertu/Plibonigu la tipon de tramo. Majuskliga klavo ŝaltas konstruadon/montradon de kostotakso +STR_ROAD_NAME_ROAD :Vojo +STR_ROAD_NAME_TRAM :Tramvojo # Road depot construction window STR_BUILD_DEPOT_ROAD_ORIENTATION_CAPTION :{WHITE}Direkto de la stratveturila garaĝo @@ -2176,6 +2531,9 @@ STR_TREES_RANDOM_TYPE :{BLACK}Arboj de STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}Metu arbojn de hazardaj tipoj. Baskulu inter konstrui/(kosto)taksi per maljuskliga klavo STR_TREES_RANDOM_TREES_BUTTON :{BLACK}Hazardaj Arboj STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}Hazarde plantu arbojn en la tuta lando +STR_TREES_MODE_FOREST_LG_BUTTON :{BLACK}Arbaro +STR_TREES_MODE_FOREST_LG_BUTTON.n :{BLACK}Arbaron +STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}Plantu grandajn arbarojn per trenado super la pejzaĝo. # Land generation window (SE) STR_TERRAFORM_TOOLBAR_LAND_GENERATION_CAPTION :{WHITE}Landa Generado @@ -2226,8 +2584,9 @@ STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM :{BLACK}Hazarde # Fund new industry window STR_FUND_INDUSTRY_CAPTION :{WHITE}Fondu novan industrion STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}Elektu la taŭgan industrion de ĉi tiu listo -STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :Multaj hazardaj industrioj +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}Kreu hazardajn fabrikojn STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Kovru la mapon per hazarde metitajn industriojn +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Ĉu vi certas ke vi volas krei multajn hazardajn fabrikojn? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Kosto: {YELLOW}{CURRENCY_LONG} STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospektori STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Konstruu @@ -2235,7 +2594,7 @@ STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Fondi # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Ĉeno de industrio por {STRING} industrio -STR_INDUSTRY_CARGOES_CARGO_CAPTION :{WHITE}Ĉeno de industrio por {STRING} ŝarĝo +STR_INDUSTRY_CARGOES_CARGO_CAPTION :{WHITE}Ĉeno de industrio por ŝarĝo de {STRING} STR_INDUSTRY_CARGOES_PRODUCERS :{WHITE}Provizantaj industrioj STR_INDUSTRY_CARGOES_CUSTOMERS :{WHITE}Akceptantaj industrioj STR_INDUSTRY_CARGOES_HOUSES :{WHITE}Domoj @@ -2272,7 +2631,12 @@ STR_LAND_AREA_INFORMATION_AIRPORTTILE_NAME :{BLACK}Nomo de STR_LAND_AREA_INFORMATION_NEWGRF_NAME :{BLACK}NewGRF: {LTBLUE}{STRING} STR_LAND_AREA_INFORMATION_CARGO_ACCEPTED :{BLACK}Ŝarĝo akceptata: {LTBLUE} STR_LAND_AREA_INFORMATION_CARGO_EIGHTS :({COMMA}/8 {STRING}) +STR_LANG_AREA_INFORMATION_RAIL_TYPE :{BLACK}Tipo de relo: {LTBLUE}{STRING} +STR_LANG_AREA_INFORMATION_ROAD_TYPE :{BLACK}Vojtipo: {LTBLUE}{STRING} +STR_LANG_AREA_INFORMATION_TRAM_TYPE :{BLACK}Tramvoja tipo: {LTBLUE}{STRING} STR_LANG_AREA_INFORMATION_RAIL_SPEED_LIMIT :{BLACK}Rela rapideclimo: {LTBLUE}{VELOCITY} +STR_LANG_AREA_INFORMATION_ROAD_SPEED_LIMIT :{BLACK}Voja rapideclimo: {LTBLUE}{VELOCITY} +STR_LANG_AREA_INFORMATION_TRAM_SPEED_LIMIT :{BLACK}Trama rapideclimo: {LTBLUE}{VELOCITY} # Description of land area of different tiles STR_LAI_CLEAR_DESCRIPTION_ROCKS :Rokoj @@ -2283,6 +2647,29 @@ STR_LAI_CLEAR_DESCRIPTION_FIELDS :Kampoj STR_LAI_CLEAR_DESCRIPTION_SNOW_COVERED_LAND :Neĝkovrita lando STR_LAI_CLEAR_DESCRIPTION_DESERT :Dezerto +STR_LAI_RAIL_DESCRIPTION_TRACK :Fervoja trako +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS :Fervoja trako kun blokaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS :Fervoja trako kun antaŭsemaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS :Fervoja trako kun elirsemaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS :Fervoja trako kun kombinaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS :Fervoja trako kun vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS :Fervoja trako kun unudirektaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS :Fervoja trako kun blokaj kaj antaŭaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS :Fervoja trako kun blokaj kaj eliraj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS :Fervoja trako kun blokaj kaj kombinaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS :Fervoja trako kun blokaj kaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS :Fervoja trako kun blokaj kaj unudirektaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS :Fervoja trako kun antaŭaj kaj eliraj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS :Fervoja trako kun antaŭaj kaj kombinaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS :Fervoja trako kun antaŭsemaforoj kaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS :Fervoja trako kun antaŭaj kaj unudirektaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS :Fervoja trako kun eliraj kaj kombinaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS :Fervoja trako kun eliraj kaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS :Fervoja trako kun eliraj kaj unudirektaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS :Fervoja trako kun kombinaj kaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS :Fervoja trako kun kombinaj kaj unudirektaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS :Fervoja trako kun vojrezervaj kaj unudirektaj vojrezervaj semaforoj +STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT :Trajngaraĝo STR_LAI_ROAD_DESCRIPTION_ROAD :Strato STR_LAI_ROAD_DESCRIPTION_ROAD_WITH_STREETLIGHTS :Strato kun lampoj @@ -2349,10 +2736,21 @@ STR_ABOUT_VERSION :{BLACK}OpenTTD- STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD {COPYRIGHT}2002-{STRING} La teamo de OpenTTD # Framerate display window +STR_FRAMERATE_CAPTION :{WHITE}Bildrapido +STR_FRAMERATE_CAPTION_SMALL :{STRING}{WHITE} ({DECIMAL}x) +STR_FRAMERATE_DATA_POINTS :{BLACK}La datumoj baziĝas je {COMMA} mezuroj ###length 15 +STR_FRAMERATE_SOUND :{BLACK}Sonmiksado: +STR_FRAMERATE_AI :{BLACK} AI {NUM} {STRING} ###length 15 +STR_FRAMETIME_CAPTION_GAMELOOP :Luda iteracio +STR_FRAMETIME_CAPTION_GL_LINKGRAPH :Ligografea prokrasto +STR_FRAMETIME_CAPTION_VIDEO :Videa eligo +STR_FRAMETIME_CAPTION_SOUND :Sonmiksado +STR_FRAMETIME_CAPTION_GAMESCRIPT :Ludoskripto +STR_FRAMETIME_CAPTION_AI :AI {NUM} {STRING} # Save/load game/scenario @@ -2376,6 +2774,9 @@ STR_SAVELOAD_DETAIL_CAPTION :{BLACK}Detaloj STR_SAVELOAD_DETAIL_NOT_AVAILABLE :{BLACK}Ne disponeblas informoj STR_SAVELOAD_DETAIL_COMPANY_INDEX :{SILVER}{COMMA}: {WHITE}{STRING} STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF: {WHITE}{STRING} +STR_SAVELOAD_OVERWRITE_WARNING :{YELLOW}Ĉu vi certas ke vi volas anstataŭigi la ekzistantan dosieron? +STR_SAVELOAD_DIRECTORY :{STRING} (Dosierujo) +STR_SAVELOAD_PARENT_DIRECTORY :{STRING} (Supra dosierujo) STR_SAVELOAD_OSKTITLE :{BLACK}Entajpu ludkonservnomon @@ -2389,12 +2790,18 @@ STR_MAPGEN_TOWN_NAME_LABEL :{BLACK}Urbonomo STR_MAPGEN_TOWN_NAME_DROPDOWN_TOOLTIP :{BLACK}Elektu stilon de urbonomoj STR_MAPGEN_DATE :{BLACK}Dato: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}Kiom da industrioj: +STR_MAPGEN_HEIGHTMAP_HEIGHT_UP :{BLACK}Pliigu la maksimuman altecon de la plej alta pinto sur la mapo je unu +STR_MAPGEN_HEIGHTMAP_HEIGHT_DOWN :{BLACK}Malpliigu la maksimuman altecon de la plej alta pinto sur la mapo je unu +STR_MAPGEN_SNOW_COVERAGE :{BLACK}Neĝa kovro: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Terentipo: STR_MAPGEN_SEA_LEVEL :{BLACK}Marnivelo: STR_MAPGEN_QUANTITY_OF_RIVERS :{BLACK}Riveroj: STR_MAPGEN_SMOOTHNESS :{BLACK}Reguleco: STR_MAPGEN_VARIETY :{BLACK}Diverseca distribuo: STR_MAPGEN_GENERATE :{WHITE}Generu +STR_MAPGEN_NEWGRF_SETTINGS :{BLACK}Agordoj de NewGRF +STR_MAPGEN_NEWGRF_SETTINGS_TOOLTIP :{BLACK}Montru NewGRF-agordojn +STR_MAPGEN_GS_SETTINGS :{BLACK}Ludoskriptaj agordoj ###length 21 STR_MAPGEN_TOWN_NAME_ORIGINAL_ENGLISH :Angle (Originale) @@ -2463,6 +2870,7 @@ STR_GENERATION_OBJECT_GENERATION :{BLACK}Nemovebl STR_GENERATION_CLEARING_TILES :{BLACK}Generado de malglataj kaj rokaj regionoj STR_GENERATION_SETTINGUP_GAME :{BLACK}Agordante ludon STR_GENERATION_PREPARING_TILELOOP :{BLACK}Trakurante kvadratojn +STR_GENERATION_PREPARING_SCRIPT :{BLACK}Ruligas la skripton STR_GENERATION_PREPARING_GAME :{BLACK}Preparante ludon # NewGRF settings @@ -2487,9 +2895,11 @@ STR_NEWGRF_SETTINGS_MOVEUP :{BLACK}Supren STR_NEWGRF_SETTINGS_MOVEUP_TOOLTIP :{BLACK}Movu la elektitan NewGRF-dosieron supren en la listo STR_NEWGRF_SETTINGS_MOVEDOWN :{BLACK}Malsupren STR_NEWGRF_SETTINGS_MOVEDOWN_TOOLTIP :{BLACK}Movu la elektitan NewGRF-dosieron malsupren en la listo +STR_NEWGRF_SETTINGS_UPGRADE :{BLACK}Ĝisdatigu STR_NEWGRF_SETTINGS_FILE_TOOLTIP :{BLACK}Listo da instalitaj NewGRF-dosieroj STR_NEWGRF_SETTINGS_SET_PARAMETERS :{BLACK}Agordu parametrojn +STR_NEWGRF_SETTINGS_SHOW_PARAMETERS :{BLACK}Montru parametrojn STR_NEWGRF_SETTINGS_TOGGLE_PALETTE :{BLACK}Baskuligi paletron STR_NEWGRF_SETTINGS_APPLY_CHANGES :{BLACK}Apliku ŝanĝojn @@ -2502,6 +2912,7 @@ STR_NEWGRF_SETTINGS_VERSION :{BLACK}Versio: STR_NEWGRF_SETTINGS_MIN_VERSION :{BLACK}Min. kongrua versio: {SILVER}{NUM} STR_NEWGRF_SETTINGS_MD5SUM :{BLACK}MD5sum: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PALETTE :{BLACK}Paletro: {SILVER}{STRING} +STR_NEWGRF_SETTINGS_PALETTE_DEFAULT :Defaŭlta (D) STR_NEWGRF_SETTINGS_PARAMETER :{BLACK}Parametroj: {SILVER}{STRING} STR_NEWGRF_SETTINGS_NO_INFO :{BLACK}Ne haveblas informoj @@ -2510,7 +2921,9 @@ STR_NEWGRF_SETTINGS_DISABLED :{RED}Malaktiva STR_NEWGRF_SETTINGS_INCOMPATIBLE :{RED}Malkongrua ĉe ĉi tio versio de OpenTTD # NewGRF save preset window +STR_SAVE_PRESET_CAPTION :{WHITE}Konservu antaŭagordon STR_SAVE_PRESET_CANCEL :{BLACK}Nuligu +STR_SAVE_PRESET_CANCEL_TOOLTIP :{BLACK}Ne ŝanĝu la antaŭagordon STR_SAVE_PRESET_SAVE :{BLACK}Konservu # NewGRF parameters window @@ -2550,6 +2963,7 @@ STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{STRING STR_NEWGRF_ERROR_MSG_WARNING :{RED}Averto: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_ERROR :{RED}Eraro: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatalo: {SILVER}{STRING} +STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}La NewGRF "{STRING}" respondis per neriparebla eraro:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} ne funkcios kun la versio de TTDPatch menciata de OpenTTD. STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} estas por la {STRING} versio de TTD. STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} estas intencita por uzo kun {STRING} @@ -2559,8 +2973,11 @@ STR_NEWGRF_ERROR_LOAD_AFTER :{1:STRING} devi STR_NEWGRF_ERROR_OTTD_VERSION_NUMBER :{1:STRING} bezonas OpenTTD versio {STRING} aŭ pli bona STR_NEWGRF_ERROR_AFTER_TRANSLATED_FILE :la GRF-dosiero estas farita por traduki STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED :Tro da NewGRF-oj estas ŝarĝataj +STR_NEWGRF_ERROR_UNEXPECTED_SPRITE :Neatendita bildeto (bildeto {3:NUM}) STR_NEWGRF_ERROR_CORRUPT_SPRITE :{YELLOW}{STRING} enhavas rompitan bildeton. Ĉiuj rompitaj bildetoj aspektos kiel ruĝaj demandsignoj (?) +STR_NEWGRF_ERROR_MULTIPLE_ACTION_8 :Enhavas plurajn "Action 8"-erojn (bildeto {3:NUM}) STR_NEWGRF_ERROR_FORCEFULLY_DISABLED :{1:STRING} estis malaktivata per {STRING} +STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT :Malvalida/nekonata bildeta aranĝoformato (bildeto {3:NUM}) # NewGRF related 'general' warnings STR_NEWGRF_POPUP_CAUTION_CAPTION :{WHITE}Atentu! @@ -2573,6 +2990,7 @@ STR_NEWGRF_TOO_MANY_NEWGRFS :{WHITE}Ne eblas STR_NEWGRF_COMPATIBLE_LOAD_WARNING :{WHITE}Taŭga(j) GRF(oj) ŝarĝiĝis por mankaj dosieroj STR_NEWGRF_DISABLED_WARNING :{WHITE}Manka(j) GRF-dosiero(j) malŝaltiĝis STR_NEWGRF_UNPAUSE_WARNING_TITLE :{YELLOW}Manka(j) GRF-dosiero(j) +STR_NEWGRF_UNPAUSE_WARNING :{WHITE}Malpaŭzigo povas paneigi OpenTTD. Ne sendu erarraporton se sekve okazos paneo.{}Ĉu vi vere volas malpaŭzigi? # NewGRF status STR_NEWGRF_LIST_NONE :Neniu @@ -2582,6 +3000,7 @@ STR_NEWGRF_LIST_COMPATIBLE :{YELLOW}Trovis STR_NEWGRF_LIST_MISSING :{RED}Mankaj dosieroj # NewGRF 'it's broken' warnings +STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}Ĝi ŝanĝis la veturilan longecon de '{1:ENGINE}' dum ĝi ne estis en garaĝo STR_NEWGRF_BUGGY :{WHITE}NewGRF '{0:STRING}' donas malĝustan informon STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT :{WHITE}'Revoko' {1:HEX} revenis nekonatan/malvalidan rezulton {2:HEX} @@ -2617,16 +3036,24 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Enigu no STR_TOWN_DIRECTORY_CAPTION :{WHITE}Urboj STR_TOWN_DIRECTORY_NONE :{ORANGE}-Neniu - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Urbego){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Urbonomoj - klaku nomon por centri vidpukto ĉe la urbo. Ctrl+Klak por malfermi novan vidujon ĉe la urba loko STR_TOWN_POPULATION :{BLACK}Monda enloĝantaro: {COMMA} # Town view window STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} -STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Urbo) +STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Urbego) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Enloĝantoj: {ORANGE}{COMMA}{BLACK} Domoj: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} pasintmonate: {ORANGE}{COMMA}{BLACK} maksimume: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Kargo bezonata por kreskigi urbon: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} bezonatas STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} bezonatas en vintro +STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED_GENERAL :{ORANGE}{STRING}{GREEN} liverita +STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED :{ORANGE}{CARGO_TINY} / {CARGO_LONG}{RED} (ankoraŭ bezonata) +STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED :{ORANGE}{CARGO_TINY} / {CARGO_LONG}{GREEN} (liverita) +STR_TOWN_VIEW_TOWN_GROWS_EVERY :{BLACK}La urbo kreskas ĉiun {ORANGE}{COMMA}an{BLACK}{NBSP}tagon +STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED :{BLACK}La urbo kreskas ĉiun {ORANGE}{COMMA}an{BLACK}{NBSP}tagon (pagata) +STR_TOWN_VIEW_TOWN_GROW_STOPPED :{BLACK}La urbo {RED}ne{BLACK} kreskas STR_TOWN_VIEW_NOISE_IN_TOWN :{BLACK}Urba brulimo: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} STR_TOWN_VIEW_CENTER_TOOLTIP :{BLACK}Centri ĉefvidon ĉe la urbo. Stir+Klak por malfermi novan vidujon ĉe la urba loko. STR_TOWN_VIEW_LOCAL_AUTHORITY_BUTTON :{BLACK}Lokaj estroj @@ -2660,24 +3087,33 @@ STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :Aĉetu ekskluzi STR_LOCAL_AUTHORITY_ACTION_BRIBE :Subaĉetu la lokajn estrojn ###length 8 -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}Komencu malgrandan lokan reklamadon, por altiri pli da pasaĝeroj kaj ŝarĝoj al viaj transportservoj.{}Kosto: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}Komencu mezgrandan lokan reklamadon, por altiri pli da pasaĝeroj kaj ŝarĝoj al viaj transportservoj.{}Kosto: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}Komencu grandan lokan varbadkampanjon, por altiri pliajn pasaĝerojn kaj kargojn al viaj transportservoj.{} Kosto: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}Pagu rekonstruadon de la urba stratreto. Kaŭzos konsiderindan ĝenon al la trafiko por ĝis 6 monatoj.{} Kosto: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}Konstruu statuon honore al via kompanio.{}Kosto: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Pagu la konstruadon de novaj komercaj konstruaĵoj en la urbo.{} Kosto: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW}Aĉetu ekskluzivajn transportrajtojn en la urbo por 1 jaro. La urbo nur permesos pasaĝerojn kaj kargojn uzi la staciojn de via entrepreno.{}Cost: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW}Subaĉetu la lokajn estrojn por pliigi vian rendimenton, riskante grandan punon je kaptiĝo.{}Kosto: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{PUSH_COLOUR}{YELLOW}Komencu malgrandan lokan varbadkampanjon, por altiri pliajn pasaĝerojn kaj kargojn al viaj transportservoj.{}Portempe altigas staciajn pritaksojn en malgranda areo ĉirkaŭ la urbocentro.{}{POP_COLOUR}Kosto: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{PUSH_COLOUR}{YELLOW}Komencu mezgrandan lokan varbadkampanjon, por altiri pliajn pasaĝerojn kaj kargojn al viaj transportservoj.{}Portempe altigas staciajn pritaksojn en mezgranda areo ĉirkaŭ la urbocentro.{}{POP_COLOUR}Kosto: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{PUSH_COLOUR}{YELLOW}Komencu grandan lokan varbadkampanjon, por altiri pliajn pasaĝerojn kaj kargojn al viaj transportservoj.{}Portempe altigas staciajn pritaksojn en granda areo ĉirkaŭ la urbocentro.{}{POP_COLOUR}Kosto: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{PUSH_COLOUR}{YELLOW}Pagu rekonstruadon de la urba stratreto.{}Kaŭzos konsiderindan ĝenon al la trafiko por ĝis 6 monatoj.{}{POP_COLOUR}Kosto: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{PUSH_COLOUR}{YELLOW}Konstruu statuon honore al via kompanio.{}Porĉiame altigas staciajn pritaksojn en tiu ĉi urbo.{}{POP_COLOUR}Kosto: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{PUSH_COLOUR}{YELLOW}Pagu la konstruadon de novaj komercaj konstruaĵoj en la urbo.{}Portempe rapidigas la kreskadon de tiu ĉi urbo.{}{POP_COLOUR}Kosto: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{PUSH_COLOUR}{YELLOW}Aĉetu ekskluzivajn transportrajtojn en la urbo por 1 jaro.{}La urbo nur permesos pasaĝerojn kaj kargojn uzi la staciojn de via entrepreno.{}{POP_COLOUR}Kosto: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{PUSH_COLOUR}{YELLOW}Subaĉetu la lokajn estrojn por pliigi vian rendimenton, riskante grandan punon je kaptiĝo.{}{POP_COLOUR}Kosto: {CURRENCY_LONG} # Goal window +STR_GOALS_COMPANY_BUTTON :{BLACK}Kompanio +STR_GOALS_COMPANY_BUTTON.n :{BLACK}Kompanion STR_GOALS_TEXT :{ORANGE}{STRING} STR_GOALS_NONE :{ORANGE}- Nenia - # Goal question window +STR_GOAL_QUESTION_CAPTION_INFORMATION :{BLACK}Informoj +STR_GOAL_QUESTION_CAPTION_WARNING :{BLACK}Atentu +STR_GOAL_QUESTION_CAPTION_ERROR :{YELLOW}Eraro # Goal Question button list ###length 18 +STR_GOAL_QUESTION_BUTTON_CANCEL :Nuligi +STR_GOAL_QUESTION_BUTTON_PREVIOUS :Antaŭa +STR_GOAL_QUESTION_BUTTON_NEXT :Sekva STR_GOAL_QUESTION_BUTTON_CONTINUE :Daŭrigu +STR_GOAL_QUESTION_BUTTON_CLOSE :Fermu # Subsidies window STR_SUBSIDIES_CAPTION :{WHITE}Subvencioj @@ -2689,6 +3125,9 @@ STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Klaku servon por centri vidpunkto ĉe la industrio/urbo. Ctrl+Klak por malfermi novan vidujon ĉe la loko # Story book window +STR_STORY_BOOK_TITLE :{YELLOW}{STRING} +STR_STORY_BOOK_GENERIC_PAGE_ITEM :Paĝo {NUM} +STR_STORY_BOOK_PREV_PAGE :{BLACK}Antaŭa # Station list window STR_STATION_LIST_TOOLTIP :{BLACK}Stacionomoj - klaku nomon por centri vidpunkto ĉe la stacio. Ctrl+Klak por malfermi novan vidujon ĉe la stacioloko @@ -2713,8 +3152,20 @@ STR_STATION_VIEW_ACCEPTS_CARGO :{BLACK}Akcepti STR_STATION_VIEW_RATINGS_BUTTON :{BLACK}Takso STR_STATION_VIEW_RATINGS_TOOLTIP :{BLACK}Montru stacian takson +STR_STATION_VIEW_SUPPLY_RATINGS_TITLE :{BLACK}Ĉiumonata provizo kaj loka pritakso: +STR_STATION_VIEW_CARGO_SUPPLY_RATING :{WHITE}{STRING}: {YELLOW}{COMMA} / {STRING} ({COMMA}%) +STR_STATION_VIEW_GROUP :{BLACK}Grupigu laŭ +STR_STATION_VIEW_FROM :{YELLOW}{CARGO_SHORT} de {STATION} +STR_STATION_VIEW_TO :{YELLOW}{CARGO_SHORT} al {STATION} +STR_STATION_VIEW_FROM_ANY :{RED}{CARGO_SHORT} el nekonata stacio +STR_STATION_VIEW_GROUP_S_V_D :Ekirpunkto-Tra-Celpunkto +STR_STATION_VIEW_GROUP_S_D_V :Ekirpunkto-Celpunkto-Tra +STR_STATION_VIEW_GROUP_V_S_D :Tra-Ekirpunkto-Celpunkto +STR_STATION_VIEW_GROUP_V_D_S :Tra-Celpunkto-Ekirpunkto +STR_STATION_VIEW_GROUP_D_S_V :Celpunkto-Ekirpunkto-Tra +STR_STATION_VIEW_GROUP_D_V_S :Celpunkto-Tra-Ekirpunkto ###length 8 STR_CARGO_RATING_APPALLING :Plorige @@ -2736,6 +3187,8 @@ STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP :{BLACK}Montru STR_STATION_VIEW_RENAME_STATION_CAPTION :Alinomi stacion/ŝarĝejon +STR_STATION_VIEW_CLOSE_AIRPORT :{BLACK}Fermu la flughavenon +STR_STATION_VIEW_CLOSE_AIRPORT_TOOLTIP :{BLACK}Malpermesu al aviadiloj surteriĝi ĉe tiu ĉi flughaveno # Waypoint/buoy view window STR_WAYPOINT_VIEW_CAPTION :{WHITE}{WAYPOINT} @@ -2756,23 +3209,35 @@ STR_FINANCES_YEAR :{WHITE}{NUM} ###length 13 STR_FINANCES_SECTION_CONSTRUCTION :{GOLD}Konstruado STR_FINANCES_SECTION_NEW_VEHICLES :{GOLD}Novaj Veturiloj -STR_FINANCES_SECTION_TRAIN_RUNNING_COSTS :{GOLD}Trajnaj Irkostoj -STR_FINANCES_SECTION_ROAD_VEHICLE_RUNNING_COSTS :{GOLD}Stratveturilaj irkostoj -STR_FINANCES_SECTION_AIRCRAFT_RUNNING_COSTS :{GOLD}Aviadilaj Irkostoj -STR_FINANCES_SECTION_SHIP_RUNNING_COSTS :{GOLD}Ŝipaj Irkostoj +STR_FINANCES_SECTION_TRAIN_RUNNING_COSTS :{GOLD}Trajnoj +STR_FINANCES_SECTION_ROAD_VEHICLE_RUNNING_COSTS :{GOLD}Stratveturiloj +STR_FINANCES_SECTION_AIRCRAFT_RUNNING_COSTS :{GOLD}Aviadiloj +STR_FINANCES_SECTION_SHIP_RUNNING_COSTS :{GOLD}Ŝipoj +STR_FINANCES_SECTION_INFRASTRUCTURE :{GOLD}Infrastrukturo +STR_FINANCES_SECTION_TRAIN_REVENUE :{GOLD}Trajnoj +STR_FINANCES_SECTION_ROAD_VEHICLE_REVENUE :{GOLD}Stratveturiloj +STR_FINANCES_SECTION_AIRCRAFT_REVENUE :{GOLD}Aviadiloj +STR_FINANCES_SECTION_SHIP_REVENUE :{GOLD}Ŝipoj STR_FINANCES_SECTION_LOAN_INTEREST :{GOLD}Rento pri Prunto STR_FINANCES_SECTION_OTHER :{GOLD}Alia -STR_FINANCES_NEGATIVE_INCOME :{BLACK}-{CURRENCY_LONG} -STR_FINANCES_POSITIVE_INCOME :{BLACK}+{CURRENCY_LONG} +STR_FINANCES_TOTAL_CAPTION :{WHITE}Ensume +STR_FINANCES_NEGATIVE_INCOME :-{CURRENCY_LONG} +STR_FINANCES_ZERO_INCOME :{CURRENCY_LONG} +STR_FINANCES_POSITIVE_INCOME :+{CURRENCY_LONG} +STR_FINANCES_PROFIT :{WHITE}Profito STR_FINANCES_BANK_BALANCE_TITLE :{WHITE}Banka Balanco +STR_FINANCES_OWN_FUNDS_TITLE :{WHITE}Propra kapitalo STR_FINANCES_LOAN_TITLE :{WHITE}Prunto +STR_FINANCES_INTEREST_RATE :{WHITE}Interezo de prunto: {BLACK}{NUM}% STR_FINANCES_MAX_LOAN :{WHITE}Maksimuma prunto: {BLACK}{CURRENCY_LONG} STR_FINANCES_TOTAL_CURRENCY :{BLACK}{CURRENCY_LONG} +STR_FINANCES_BANK_BALANCE :{WHITE}{CURRENCY_LONG} STR_FINANCES_BORROW_BUTTON :{BLACK}Pruntu {CURRENCY_LONG} STR_FINANCES_BORROW_TOOLTIP :{BLACK}Grandigu prunton. Ctrl+Klak prunteprenas maksimume STR_FINANCES_REPAY_BUTTON :{BLACK}Repagu {CURRENCY_LONG} STR_FINANCES_REPAY_TOOLTIP :{BLACK}Repagu pruntparton. Ctrl+klak malpruntigas maksimume. +STR_FINANCES_INFRASTRUCTURE_BUTTON :{BLACK}Infrastrukturo # Company view STR_COMPANY_VIEW_CAPTION :{WHITE}{COMPANY} {BLACK}{COMPANY_NUM} @@ -2787,6 +3252,11 @@ STR_COMPANY_VIEW_AIRCRAFT :{WHITE}{COMMA} STR_COMPANY_VIEW_SHIPS :{WHITE}{COMMA} ŝipo{P "" j} STR_COMPANY_VIEW_VEHICLES_NONE :{WHITE}Neniu STR_COMPANY_VIEW_COMPANY_VALUE :{GOLD}Kompania valoro: {WHITE}{CURRENCY_LONG} +STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD :{WHITE}{COMMA} vojpeco{P "" j} +STR_COMPANY_VIEW_INFRASTRUCTURE_STATION :{WHITE}{COMMA} stacia{P "" j} kahelo{P "" j} +STR_COMPANY_VIEW_INFRASTRUCTURE_STATION.n :{WHITE}{COMMA} stacia{P "" j}n kahelo{P "" j}n +STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT :{WHITE}{COMMA} flughaveno{P "" j} +STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT.n :{WHITE}{COMMA} flughaveno{P "" j}n STR_COMPANY_VIEW_BUILD_HQ_BUTTON :{BLACK}Faru HQ STR_COMPANY_VIEW_BUILD_HQ_TOOLTIP :{BLACK}Konstrui kompanian administraciejon @@ -2794,6 +3264,9 @@ STR_COMPANY_VIEW_VIEW_HQ_BUTTON :{BLACK}Vidu HQ STR_COMPANY_VIEW_VIEW_HQ_TOOLTIP :{BLACK}Rigardi kompanian administraciejon STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}Relokigu HQ STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Rekonstruu kompanian ĉefkonstruaĵon aliloke por 1% de kompanivaloro. Tenu maljuskliga klavo + Klak por (kost)taksi sen rekonstrui ĉefkonstruaĵon +STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP :{BLACK}Vidu detalajn nombrojn de infrastruktureroj +STR_COMPANY_VIEW_GIVE_MONEY_BUTTON :{BLACK}Donu monon +STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP :{BLACK}Donu monon al tiu ĉi kompanio STR_COMPANY_VIEW_NEW_FACE_BUTTON :{BLACK}Nova Vizaĝo STR_COMPANY_VIEW_NEW_FACE_TOOLTIP :{BLACK}Elektu novan vizaĝon por la manaĝanto @@ -2806,16 +3279,33 @@ STR_COMPANY_VIEW_PRESIDENT_NAME_TOOLTIP :{BLACK}Ŝanĝu STR_COMPANY_VIEW_COMPANY_NAME_QUERY_CAPTION :Kompania Nomo STR_COMPANY_VIEW_PRESIDENT_S_NAME_QUERY_CAPTION :Nomo de la Manaĝanto +STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION :Entajpu la monkvanton kiun vi volas doni STR_BUY_COMPANY_MESSAGE :{WHITE}Ni serĉas transportkompanion por transpreni nian kompanion.{}{}Ĉu vi volas aĉeti {COMPANY} por {CURRENCY_LONG}? # Company infrastructure window +STR_COMPANY_INFRASTRUCTURE_VIEW_CAPTION :{WHITE}Infrastrukturo de {COMPANY} +STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT :{GOLD}Fervojpecoj: +STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS :{WHITE}Semaforoj +STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS.n :{WHITE}Semaforojn +STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT :{GOLD}Vojpecoj: +STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT :{GOLD}Tramvojpecoj: +STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT :{GOLD}Akvaj kaheloj: +STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS :{WHITE}Kanaloj +STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT :{GOLD}Stacioj: +STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS :{WHITE}Staciaj kaheloj +STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS :{WHITE}Flughavenoj +STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL :{WHITE}{CURRENCY_LONG} jare # Industry directory STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}Industrioj STR_INDUSTRY_DIRECTORY_NONE :{ORANGE}- Neniu - +STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_LONG}{STRING}{YELLOW} ({COMMA}% transportiĝis){BLACK} STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} +STR_INDUSTRY_DIRECTORY_ITEM_PROD3 :{ORANGE}{INDUSTRY} {STRING}, {STRING}, {STRING} STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}Industrionomoj - klaku nomon por centri vidon ĉe la industrio. Ctrl+Klak por malfermi novan vidujon ĉe la loko +STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER :{BLACK}Ŝarĝo akceptata: {SILVER}{STRING} +STR_INDUSTRY_DIRECTORY_FILTER_NONE :Neniu # Industry view STR_INDUSTRY_VIEW_CAPTION :{WHITE}{INDUSTRY} @@ -2824,7 +3314,12 @@ STR_INDUSTRY_VIEW_TRANSPORTED :{YELLOW}{CARGO_ STR_INDUSTRY_VIEW_LOCATION_TOOLTIP :{BLACK}Centri ĉefvidon ĉe la industrio. Stir+Klak por malfermi novan vidujon ĉe la industrioloko. STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}Produkta nivelo: {YELLOW}{COMMA}% +STR_INDUSTRY_VIEW_REQUIRES_N_CARGO :{BLACK}Bezonas: {YELLOW}{STRING.n}{STRING.n} +STR_INDUSTRY_VIEW_PRODUCES_N_CARGO :{BLACK}Produktas: {YELLOW}{STRING}{STRING.n} +STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION :, {STRING}{STRING} +STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION.n :, {STRING.n}{STRING.n} +STR_INDUSTRY_VIEW_REQUIRES :{BLACK}Bezonas: STR_CONFIG_GAME_PRODUCTION :{WHITE}Ŝanĝu produktadon (multoble de 8, ĝis 2040) STR_CONFIG_GAME_PRODUCTION_LEVEL :{WHITE}Ŝanĝu produktan nivelon (procento, limigo je 800%) @@ -2834,7 +3329,7 @@ STR_CONFIG_GAME_PRODUCTION_LEVEL :{WHITE}Ŝanĝu STR_VEHICLE_LIST_TRAIN_CAPTION :{WHITE}{STRING} - {COMMA} Trajno{P "" j} STR_VEHICLE_LIST_ROAD_VEHICLE_CAPTION :{WHITE}{STRING} - {COMMA} Stratveturilo{P "" j} STR_VEHICLE_LIST_SHIP_CAPTION :{WHITE}{STRING} - {COMMA} Ŝipo{P "" j} -STR_VEHICLE_LIST_AIRCRAFT_CAPTION :{WHITE}{STRING} - {COMMA} Aviadiloj +STR_VEHICLE_LIST_AIRCRAFT_CAPTION :{WHITE}{STRING} - {COMMA} Aviadilo{P "" j} ###length VEHICLE_TYPES STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP :{BLACK}Trajnoj - alklaku trajnon por pliaj informoj @@ -2853,6 +3348,8 @@ STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP :{BLACK}Sendu in STR_VEHICLE_LIST_REPLACE_VEHICLES :Anstataŭu veturilojn STR_VEHICLE_LIST_SEND_FOR_SERVICING :Sendu por Prizorgo STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR :{TINY_FONT}{BLACK}Gajno ĉi-jare: {CURRENCY_LONG} (pasintjare: {CURRENCY_LONG}) +STR_VEHICLE_LIST_CARGO :[{CARGO_LIST}] +STR_VEHICLE_LIST_NAME_AND_CARGO :{STRING} {STRING} STR_VEHICLE_LIST_SEND_TRAIN_TO_DEPOT :Sendu al Garaĝo STR_VEHICLE_LIST_SEND_ROAD_VEHICLE_TO_DEPOT :Sendu al Garaĝo @@ -2884,12 +3381,16 @@ STR_GROUP_CREATE_TOOLTIP :{BLACK}Klaku po STR_GROUP_DELETE_TOOLTIP :{BLACK}Viŝu la selektitan grupon STR_GROUP_RENAME_TOOLTIP :{BLACK}Alinomi la elektitan grupon +STR_GROUP_DELETE_QUERY_TEXT :{WHITE}Ĉu vi certas ke vi volas forigi tiun ĉi grupon kaj ajnajn subgrupojn? STR_GROUP_ADD_SHARED_VEHICLE :Aldonu kunhavajn veturilojn STR_GROUP_REMOVE_ALL_VEHICLES :Forigi ĉiujn veturilojn STR_GROUP_RENAME_CAPTION :{BLACK}Alinomi grupon +STR_GROUP_PROFIT_THIS_YEAR :Ĉi-jara profito: +STR_GROUP_PROFIT_LAST_YEAR :Pasintjara profito: +STR_GROUP_OCCUPANCY_VALUE :{NUM}% # Build vehicle window ###length 4 @@ -2899,10 +3400,12 @@ STR_BUY_VEHICLE_TRAIN_MONORAIL_CAPTION :Novaj Unurelaj STR_BUY_VEHICLE_TRAIN_MAGLEV_CAPTION :Novaj Maglevaj Veturiloj STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION :Novaj stratveturiloj +STR_BUY_VEHICLE_TRAM_VEHICLE_CAPTION :Novaj tramveturiloj # Vehicle availability ###length VEHICLE_TYPES STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :Novaj stratveturiloj +STR_BUY_VEHICLE_ROAD_VEHICLE_ALL_CAPTION :Novaj stratveturiloj STR_BUY_VEHICLE_SHIP_CAPTION :Novaj Ŝipoj STR_BUY_VEHICLE_AIRCRAFT_CAPTION :Novaj Aviadiloj @@ -2917,16 +3420,26 @@ STR_PURCHASE_INFO_REFITTABLE :(transformebla) STR_PURCHASE_INFO_DESIGNED_LIFE :{BLACK}Pripensite: {GOLD}{NUM}{BLACK} Vivlongo: {GOLD}{COMMA} jaro{P "" j} STR_PURCHASE_INFO_RELIABILITY :{BLACK}Pleja Fidebleco: {GOLD}{COMMA}% STR_PURCHASE_INFO_COST :{BLACK}Kosto: {GOLD}{CURRENCY_LONG} +STR_PURCHASE_INFO_COST_REFIT :{BLACK}Kosto: {GOLD}{CURRENCY_LONG}{BLACK} (Kosto de readapto: {GOLD}{CURRENCY_LONG}{BLACK}) STR_PURCHASE_INFO_WEIGHT_CWEIGHT :{BLACK}Pezo: {GOLD}{WEIGHT_SHORT} ({WEIGHT_SHORT}) STR_PURCHASE_INFO_COST_SPEED :{BLACK}Kosto: {GOLD}{CURRENCY_LONG}{BLACK} Rapido: {GOLD}{VELOCITY} +STR_PURCHASE_INFO_COST_REFIT_SPEED :{BLACK}Kosto: {GOLD}{CURRENCY_LONG}{BLACK} (Kosto de readapto: {GOLD}{CURRENCY_LONG}{BLACK}) Rapideco: {GOLD}{VELOCITY} STR_PURCHASE_INFO_AIRCRAFT_CAPACITY :{BLACK}Enhaveco: {GOLD}{CARGO_LONG}, {CARGO_LONG} STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT :{BLACK}Vagonfortoj: {GOLD}+{POWER}{BLACK} Weight: {GOLD}+{WEIGHT_SHORT} STR_PURCHASE_INFO_REFITTABLE_TO :{BLACK}Transformebla al: {GOLD}{STRING} STR_PURCHASE_INFO_ALL_TYPES :Ĉiaj ŝarĝoj +STR_PURCHASE_INFO_NONE :Neniu +STR_PURCHASE_INFO_NONE.n :Neniun +STR_PURCHASE_INFO_ENGINES_ONLY :Nur lokomotivoj STR_PURCHASE_INFO_ALL_BUT :Ĉio krom {CARGO_LIST} STR_PURCHASE_INFO_MAX_TE :{BLACK}Maksimuma Tiro: {GOLD}{FORCE} +STR_PURCHASE_INFO_AIRCRAFT_RANGE :{BLACK}Atingdistanco: {GOLD}{COMMA} kaheloj +STR_PURCHASE_INFO_AIRCRAFT_TYPE :{BLACK}Tipo de aviadilo: {GOLD}{STRING} ###length 3 +STR_CARGO_TYPE_FILTER_ALL :Ĉiaj ŝarĝoj +STR_CARGO_TYPE_FILTER_FREIGHT :Varoj +STR_CARGO_TYPE_FILTER_NONE :Neniaj ###length VEHICLE_TYPES STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP :{BLACK}Trajnveturila elektolisto - alklaku veturilon por informoj @@ -2941,6 +3454,10 @@ STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_BUTTON :{BLACK}Aĉeti STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_BUTTON :{BLACK}Aĉeti Aviadilon ###length VEHICLE_TYPES +STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Aĉetu kaj readaptu veturilon +STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Aĉetu kaj readaptu veturilon +STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Aĉetu kaj readaptu ŝipon +STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Aĉetu kaj readaptu la aviadilon ###length VEHICLE_TYPES STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}Aĉeti la emfazitan trajnveturilon. Montri taksitaj kostoj sen aĉeti per maljuskliga klavo + Klaki @@ -2966,6 +3483,7 @@ STR_BUY_VEHICLE_AIRCRAFT_RENAME_TOOLTIP :{BLACK}Alinomi STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON :{BLACK}Kaŝu STR_BUY_VEHICLE_ROAD_VEHICLE_HIDE_TOGGLE_BUTTON :{BLACK}Kaŝu STR_BUY_VEHICLE_SHIP_HIDE_TOGGLE_BUTTON :{BLACK}Kaŝu +STR_BUY_VEHICLE_AIRCRAFT_HIDE_TOGGLE_BUTTON :{BLACK}Kaŝu ###length VEHICLE_TYPES STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON :{BLACK}Montru @@ -2974,6 +3492,8 @@ STR_BUY_VEHICLE_SHIP_SHOW_TOGGLE_BUTTON :{BLACK}Montru STR_BUY_VEHICLE_AIRCRAFT_SHOW_TOGGLE_BUTTON :{BLACK}Montru ###length VEHICLE_TYPES +STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP :{BLACK}Ŝaltu kaŝadon/montradon de trajnveturila tipo +STR_BUY_VEHICLE_ROAD_VEHICLE_HIDE_SHOW_TOGGLE_TOOLTIP :{BLACK}Ŝaltu kaŝadon/montradon de stratveturila tipo ###length VEHICLE_TYPES STR_QUERY_RENAME_TRAIN_TYPE_CAPTION :{WHITE}Alinomi trajnan veturiltipon @@ -3072,10 +3592,14 @@ STR_ENGINE_PREVIEW_CAPTION :{WHITE}Mesaĝo STR_ENGINE_PREVIEW_MESSAGE :{GOLD}Nova {STRING} estas inventita - ĉu vi ŝatus jaron ekskluzive uzi la veturilon, por ke ni povu vidi ĝian funkciadon antaŭ ol ĝi estos plene havebla? STR_ENGINE_PREVIEW_RAILROAD_LOCOMOTIVE :fervoja lokomotivo +STR_ENGINE_PREVIEW_ELRAIL_LOCOMOTIVE :elektrofervoja lokomotivo +STR_ENGINE_PREVIEW_ELRAIL_LOCOMOTIVE.n :elektrofervojan lokomotivon STR_ENGINE_PREVIEW_MONORAIL_LOCOMOTIVE :unurellokomotivo STR_ENGINE_PREVIEW_MAGLEV_LOCOMOTIVE :magleva lokomotivo STR_ENGINE_PREVIEW_ROAD_VEHICLE :stratveturilo +STR_ENGINE_PREVIEW_TRAM_VEHICLE :tramvoja veturilo +STR_ENGINE_PREVIEW_TRAM_VEHICLE.n :tramvojan veturilon STR_ENGINE_PREVIEW_AIRCRAFT :aviadilo STR_ENGINE_PREVIEW_SHIP :ŝipo @@ -3083,6 +3607,10 @@ STR_ENGINE_PREVIEW_SHIP :ŝipo STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER :{BLACK}Kosto: {CURRENCY_LONG} Pezo: {WEIGHT_SHORT}{}Rapido: {VELOCITY} Forto: {POWER}{}Irkosto: po {CURRENCY_LONG} jare{}Kapablo: {CARGO_LONG} STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER_MAX_TE :{BLACK}Kosto: {CURRENCY_LONG} Pezo: {WEIGHT_SHORT}{}Rapido: {VELOCITY} Povo: {POWER} Maks. T.E.: {6:FORCE}{}Irkosto: {4:CURRENCY_LONG}/jaro{}Kapacito: {5:CARGO_LONG} STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAP_RUNCOST :{BLACK}Kosto: {CURRENCY_LONG} Maks. Rapido: {VELOCITY}{}Kapacito: {CARGO_LONG}{}Irkosto: {CURRENCY_LONG}/jaro +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_CAP_RUNCOST :{BLACK}Kosto: {CURRENCY_LONG} Maks. rapideco: {VELOCITY}{}Tipo de aviadilo: {STRING}{}Kapacito: {CARGO_LONG}, {CARGO_LONG}{}Irkosto: {CURRENCY_LONG} jare +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_RUNCOST :{BLACK}Kosto: {CURRENCY_LONG} Maks. rapideco: {VELOCITY}{}Tipo de aviadilo: {STRING}{}Kapacito: {CARGO_LONG}{}Irkosto: {CURRENCY_LONG} jare +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_CAP_RUNCOST :{BLACK}Kosto: {CURRENCY_LONG} Maks. rapideco: {VELOCITY}{}Tipo de aviadilo: {STRING} Atingdistanco: {COMMA} kaheloj{}Kapacito: {CARGO_LONG}, {CARGO_LONG}{}Irkosto: {CURRENCY_LONG} jare +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_RUNCOST :{BLACK}Kosto: {CURRENCY_LONG} Maks. rapideco: {VELOCITY}{}Tipo de aviadilo: {STRING} Atingdistanco: {COMMA} kaheloj{}Kapacito: {CARGO_LONG}{}Irkosto: {CURRENCY_LONG} jare # Autoreplace window STR_REPLACE_VEHICLES_WHITE :{WHITE}Anstataŭu {STRING.n} - {STRING} @@ -3098,6 +3626,8 @@ STR_REPLACE_HELP_LEFT_ARRAY :{BLACK}Elektu m STR_REPLACE_HELP_RIGHT_ARRAY :{BLACK}Elektu novan maŝinon por uzi anstataŭ la maldekstre elektitan STR_REPLACE_VEHICLES_START :{BLACK}Komencu Anstataŭi +STR_REPLACE_VEHICLES_NOW :Anstataŭigu ĉiujn veturilojn nun +STR_REPLACE_VEHICLES_WHEN_OLD :Anstataŭigu nur malnovajn veturilojn STR_REPLACE_HELP_START_BUTTON :{BLACK}Premu por komenci anstataŭi la maldekstre elektitan maŝinon per la dekstre elektitan STR_REPLACE_NOT_REPLACING :{BLACK}Ne anstataŭante STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED :{BLACK}Ne elektiĝis veturilo @@ -3107,9 +3637,13 @@ STR_REPLACE_HELP_STOP_BUTTON :{BLACK}Premu po STR_REPLACE_ENGINE_WAGON_SELECT_HELP :{BLACK}Ŝaltu inter maŝina kaj vagona anstataŭigo STR_REPLACE_ENGINES :Maŝinoj STR_REPLACE_WAGONS :Vagonoj +STR_REPLACE_ALL_RAILTYPE :Ĉiuj relveturiloj +STR_REPLACE_ALL_RAILTYPE.n :Ĉiujn relveturilojn +STR_REPLACE_ALL_ROADTYPE :Ĉiuj stratveturiloj ###length 2 STR_REPLACE_HELP_RAILTYPE :{BLACK}Elektu fervojtipon por kiu vi volas anstataŭigi maŝinojn +STR_REPLACE_HELP_ROADTYPE :{BLACK}Elektu la vojtipon por kiu vi volas anstataŭigi veturilojn ###next-name-looks-similar STR_REPLACE_HELP_REPLACE_INFO_TAB :{BLACK}Montras ontan maŝinecon de la maldekstra maŝino, se anstataŭote @@ -3118,13 +3652,20 @@ STR_REPLACE_ELRAIL_VEHICLES :Elektraj Relaj STR_REPLACE_MONORAIL_VEHICLES :Unurelaj Veturiloj STR_REPLACE_MAGLEV_VEHICLES :Maglevaj Veturiloj +STR_REPLACE_ROAD_VEHICLES :Stratveturiloj +STR_REPLACE_ROAD_VEHICLES.n :Stratveturilojn +STR_REPLACE_TRAM_VEHICLES :Tramvojaj veturiloj +STR_REPLACE_TRAM_VEHICLES.n :Tramvojajn veturilojn +STR_REPLACE_REMOVE_WAGON :{BLACK}Vagonforigo ({STRING}): {ORANGE}{STRING} STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}Ĉe aŭtomata anstataŭigo tenu saman longecon de la trajno per forigo de vagonoj (defronte), se per nova maŝino la trajno plilongiĝas # Vehicle view STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} ###length VEHICLE_TYPES +STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP :{BLACK}Centrigu ĉefvidon ĉe la pozicio de la trajno. Duobla klako igas la ĉefvidon sekvi la trajnon. Stir+Klak malfermas novan vidujon ĉe la pozicio de la trajno +STR_VEHICLE_VIEW_AIRCRAFT_CENTER_TOOLTIP :{BLACK}Centrigu ĉefvidon ĉe la pozicio de la aviadilo. Duobla klako igas la ĉefvidon sekvi la aviadilon. Stir+Klak malfermas novan vidujon ĉe la pozicio de la aviadilo ###length VEHICLE_TYPES STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}Sendi trajnon al garaĝo. Stir+Klak nur prizorgos @@ -3141,6 +3682,7 @@ STR_VEHICLE_VIEW_CLONE_AIRCRAFT_INFO :{BLACK}Aĉeti k STR_VEHICLE_VIEW_TRAIN_IGNORE_SIGNAL_TOOLTIP :{BLACK}Igu trajnon iri malgraŭ signalo STR_VEHICLE_VIEW_TRAIN_REVERSE_TOOLTIP :{BLACK}Inversigu trajnan direkton STR_VEHICLE_VIEW_ROAD_VEHICLE_REVERSE_TOOLTIP :{BLACK}Igu veturilon ĉirkaŭturni +STR_VEHICLE_VIEW_ORDER_LOCATION_TOOLTIP :{BLACK}Centrigu ĉefvidon ĉe la celpunkto de la ordono. Stir+Klak por malfermi novan vidujon ĉe la celpunkto de la ordono ###length VEHICLE_TYPES STR_VEHICLE_VIEW_TRAIN_REFIT_TOOLTIP :{BLACK}Transformi trajnon por porti alian ŝarĝtipon @@ -3161,6 +3703,9 @@ STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Montru STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Montru aviadilajn detalojn ###length VEHICLE_TYPES +STR_VEHICLE_VIEW_ROAD_VEHICLE_STATUS_START_STOP_TOOLTIP :{BLACK}Nuna ago de la veturilo - klaku por haltigi/ekigi la veturilon +STR_VEHICLE_VIEW_SHIP_STATE_STATUS_STOP_TOOLTIP :{BLACK}Nuna ago de la ŝipo - klaku por haltigi/ekigi la ŝipon +STR_VEHICLE_VIEW_AIRCRAFT_STATUS_START_STOP_TOOLTIP :{BLACK}Nuna ago de la aviadilo - klaku por haltigi/ekigi la aviadilon # Messages in the start stop button in the vehicle view STR_VEHICLE_STATUS_LOADING_UNLOADING :{LTBLUE}Ŝarĝante / Deŝarĝante @@ -3171,6 +3716,7 @@ STR_VEHICLE_STATUS_STOPPED :{RED}Haltis STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL :{RED}Haltiĝas, {VELOCITY} STR_VEHICLE_STATUS_TRAIN_NO_POWER :{RED}Senforte STR_VEHICLE_STATUS_TRAIN_STUCK :{ORANGE}Atendas haveblan padon +STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR :{ORANGE}La sekva celo estas tro for STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL :{LTBLUE}Irante al {STATION}, {VELOCITY} STR_VEHICLE_STATUS_NO_ORDERS_VEL :{LTBLUE}Neniu ordono, {VELOCITY} @@ -3178,6 +3724,10 @@ STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL :{LTBLUE}Irante STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_VEL :{ORANGE}Irante al {DEPOT}, {VELOCITY} STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_SERVICE_VEL :{LTBLUE}Prizorgo en {DEPOT}, {VELOCITY} +STR_VEHICLE_STATUS_CANNOT_REACH_STATION_VEL :{LTBLUE}Ne povas atingi {STATION}, {VELOCITY} +STR_VEHICLE_STATUS_CANNOT_REACH_WAYPOINT_VEL :{LTBLUE}Ne povas atingi {WAYPOINT}, {VELOCITY} +STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_VEL :{ORANGE}Ne povas atingi {DEPOT}, {VELOCITY} +STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_SERVICE_VEL :{LTBLUE}Ne povas atingi {DEPOT}, {VELOCITY} # Vehicle stopped/started animations ###length 2 @@ -3202,16 +3752,19 @@ STR_VEHICLE_INFO_AGE :{COMMA} jaro{P STR_VEHICLE_INFO_AGE_RED :{RED}{COMMA} jaro{P "" j} ({COMMA}) STR_VEHICLE_INFO_MAX_SPEED :{BLACK}Maks. rapido: {LTBLUE}{VELOCITY} +STR_VEHICLE_INFO_MAX_SPEED_TYPE :{BLACK}Maks. rapideco: {LTBLUE}{VELOCITY} {BLACK}Tipo de aviadilo: {LTBLUE}{STRING} +STR_VEHICLE_INFO_MAX_SPEED_TYPE_RANGE :{BLACK}Maks. rapideco: {LTBLUE}{VELOCITY} {BLACK}Tipo de aviadilo: {LTBLUE}{STRING} {BLACK}Atingdistanco: {LTBLUE}{COMMA} kaheloj STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}Pezo: {LTBLUE}{WEIGHT_SHORT} {BLACK}Povo: {LTBLUE}{POWER}{BLACK} Maks. rapido: {LTBLUE}{VELOCITY} STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}Pezo: {LTBLUE}{WEIGHT_SHORT} {BLACK}Povo: {LTBLUE}{POWER}{BLACK} Maks. rapido: {LTBLUE}{VELOCITY} {BLACK}Maks. T.E.: {LTBLUE}{FORCE} STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR :{BLACK}Profito ĉijare: {LTBLUE}{CURRENCY_LONG} (lastjare: {CURRENCY_LONG}) +STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_MIN_PERFORMANCE :{BLACK}Profito ĉi-jare: {LTBLUE}{CURRENCY_LONG} (pasintjare: {CURRENCY_LONG}) {BLACK}Min. rendimento: {LTBLUE}{POWER_TO_WEIGHT} STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS :{BLACK}Fidindo: {LTBLUE}{COMMA}% {BLACK}Paneoj ekde lasta servo: {LTBLUE}{COMMA} STR_VEHICLE_INFO_BUILT_VALUE :{LTBLUE}{ENGINE} {BLACK}Konstruita: {LTBLUE}{NUM}{BLACK} Valoro: {LTBLUE}{CURRENCY_LONG} STR_VEHICLE_INFO_NO_CAPACITY :{BLACK}Enhaveco: {LTBLUE}Nenion{STRING} -STR_VEHICLE_INFO_CAPACITY :{BLACK}Enhaveco: {LTBLUE}{CARGO_LONG}{3:STRING} -STR_VEHICLE_INFO_CAPACITY_MULT :{BLACK}Enhaveco: {LTBLUE}{CARGO_LONG}{3:STRING} (x{4:NUM}) +STR_VEHICLE_INFO_CAPACITY :{BLACK}Kapacito: {LTBLUE}{0:CARGO_LONG}{3:STRING} +STR_VEHICLE_INFO_CAPACITY_MULT :{BLACK}Kapacito: {LTBLUE}{0:CARGO_LONG}{3:STRING} (x{4:NUM}) STR_VEHICLE_INFO_CAPACITY_CAPACITY :{BLACK}Enhaveco: {LTBLUE}{CARGO_LONG}, {CARGO_LONG}{STRING} STR_VEHICLE_INFO_FEEDER_CARGO_VALUE :{BLACK}Transigaj Kreditoj: {LTBLUE}{CURRENCY_LONG} @@ -3389,6 +3942,7 @@ STR_ORDER_REFIT_STOP_ORDER :(Transformu al STR_ORDER_STOP_ORDER :(Haltu) STR_ORDER_GO_TO_STATION :{STRING} {STATION} {STRING} +STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION :{PUSH_COLOUR}{RED}(Ne eblas uzi la stacion){POP_COLOUR} {STRING} {STATION} {STRING} STR_ORDER_IMPLICIT :(Aŭtomata) @@ -3424,10 +3978,11 @@ STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY_REFIT :(Ne malŝarĝi STR_ORDER_AUTO_REFIT_ANY :haveblaj ŝarĝoj ###length 3 -STR_ORDER_STOP_LOCATION_NEAR_END :[near end] -STR_ORDER_STOP_LOCATION_MIDDLE :[middle] -STR_ORDER_STOP_LOCATION_FAR_END :[far end] +STR_ORDER_STOP_LOCATION_NEAR_END :[proksima fino] +STR_ORDER_STOP_LOCATION_MIDDLE :[mezo] +STR_ORDER_STOP_LOCATION_FAR_END :[fora fino] +STR_ORDER_OUT_OF_RANGE :{RED} (La sekva celpunkto estas ekster la atingebla distanco) STR_ORDER_CONDITIONAL_UNCONDITIONAL :Saltu al ordono {COMMA} STR_ORDER_CONDITIONAL_NUM :Saltu al ordono {COMMA} kiam {STRING} {STRING} {COMMA} @@ -3445,13 +4000,19 @@ STR_TIMETABLE_TOOLTIP :{BLACK}Horaro - STR_TIMETABLE_NO_TRAVEL :Neniu veturo STR_TIMETABLE_NOT_TIMETABLEABLE :Veturu (aŭtomata; horara ĝis sekvanta mana ordono) STR_TIMETABLE_TRAVEL_NOT_TIMETABLED :Veturo (ne horarita) -STR_TIMETABLE_TRAVEL_FOR :Veturu por {STRING} -STR_TIMETABLE_STAY_FOR :kaj restu por {STRING} -STR_TIMETABLE_AND_TRAVEL_FOR :kaj veturu por {STRING} +STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED :Veturu (sen horaro) je maksimume {2:VELOCITY} +STR_TIMETABLE_TRAVEL_FOR :Veturu dum {STRING} +STR_TIMETABLE_TRAVEL_FOR_SPEED :Veturu dum {STRING} je maksimume {VELOCITY} +STR_TIMETABLE_TRAVEL_FOR_ESTIMATED :Veturu (dum {STRING}, sen horaro) +STR_TIMETABLE_TRAVEL_FOR_SPEED_ESTIMATED :Veturu (dum {STRING}, sen horaro) je maksimume {VELOCITY} +STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED :(veturos dum {STRING}, sen horaro) +STR_TIMETABLE_STAY_FOR :kaj restu dum {STRING} +STR_TIMETABLE_AND_TRAVEL_FOR :kaj veturu dum {STRING} STR_TIMETABLE_DAYS :{COMMA}{NBSP}tago{P "" j} +STR_TIMETABLE_DAYS.n :{COMMA}{NBSP}tago{P "" j}n STR_TIMETABLE_TICKS :{COMMA}{NBSP}tiko{P "" j} -STR_TIMETABLE_TOTAL_TIME :{BLACK} {STRING} necesas por plenumigi ĉi tiun horaron. +STR_TIMETABLE_TOTAL_TIME :{BLACK}Plenumi ĉi tiun horaron postulos {STRING.n} STR_TIMETABLE_TOTAL_TIME_INCOMPLETE :{BLACK}Ĉi tio horaro postulos alemnaŭ {STRING} plenigi (ne ĉiu horarita) STR_TIMETABLE_STATUS_ON_TIME :{BLACK}Ĉi tiu veturilo nun iras akurate @@ -3466,7 +4027,11 @@ STR_TIMETABLE_CHANGE_TIME :{BLACK}Ŝanĝu STR_TIMETABLE_CLEAR_TIME :{BLACK}Vakigi tempon +STR_TIMETABLE_CHANGE_SPEED :{BLACK}Ŝanĝu rapideclimon +STR_TIMETABLE_CHANGE_SPEED_TOOLTIP :{BLACK}Ŝanĝu la maksimuman veturrapidecon de la markita ordono. Stir+Klak ŝanĝas la rapidecon por ĉiuj ordonoj +STR_TIMETABLE_CLEAR_SPEED :{BLACK}Forigu rapideclimon +STR_TIMETABLE_CLEAR_SPEED_TOOLTIP :{BLACK}Forigu la maksimuman rapidecon de la markita ordono. Stir+Klak forigas la rapideclimon por ĉiuj ordonoj. STR_TIMETABLE_RESET_LATENESS :{BLACK}Reigu malfruan kalkulumon STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}Reigu malfruan kalkulumon, tiel la veturilon estos akuratan @@ -3498,6 +4063,7 @@ STR_AI_DEBUG_SETTINGS :{BLACK}Agordoj STR_AI_DEBUG_SETTINGS_TOOLTIP :{BLACK}Ŝanĝi agordojn de la skripto STR_AI_DEBUG_RELOAD :{BLACK}Reŝarĝi AI-on STR_AI_DEBUG_RELOAD_TOOLTIP :{BLACK}Mortigi la AI-on, reŝarĝi la skripton, kaj restarti la AI-on +STR_AI_DEBUG_BREAK_STR_ON_OFF_TOOLTIP :{BLACK}Ŝaltu/malŝaltu haltadon kiam protokolero de AI kongruas kun la halta tekstoĉeno STR_AI_DEBUG_BREAK_ON_LABEL :{BLACK}Rompu sur: STR_AI_DEBUG_BREAK_STR_OSKTITLE :{BLACK}Rompu sur STR_AI_DEBUG_MATCH_CASE :{BLACK}Parigu kazon @@ -3510,6 +4076,7 @@ STR_AI_DEBUG_SELECT_AI_TOOLTIP :{BLACK}Vidu for STR_AI_CONFIG_HUMAN_PLAYER :Homa ludanto STR_AI_CONFIG_RANDOM_AI :Hazarda AI STR_AI_CONFIG_NONE :(neniu) +STR_AI_CONFIG_NAME_VERSION :{STRING} {YELLOW}v{NUM} STR_AI_CONFIG_MAX_COMPETITORS :{LTBLUE}Maksimumo da konkurantoj: {ORANGE}{COMMA} STR_AI_CONFIG_MOVE_UP :{BLACK}Movi supren @@ -3517,12 +4084,14 @@ STR_AI_CONFIG_MOVE_UP_TOOLTIP :{BLACK}Movi ele STR_AI_CONFIG_MOVE_DOWN :{BLACK}Movi suben STR_AI_CONFIG_MOVE_DOWN_TOOLTIP :{BLACK}Movi elektitan AI-on suben en la listo +STR_AI_CONFIG_GAMESCRIPT :{SILVER}Ludoskripto STR_AI_CONFIG_AI :{SILVER}AIj STR_AI_CONFIG_CONFIGURE :{BLACK}Agordi STR_AI_CONFIG_CONFIGURE_TOOLTIP :{BLACK}Agordi parametroj de la skripto # Available AIs window +STR_AI_LIST_CAPTION_GAMESCRIPT :Ludoskriptoj STR_AI_LIST_TOOLTIP :{BLACK}Klaki por elekti skripto STR_AI_LIST_AUTHOR :{LTBLUE}Aŭtoro: {ORANGE}{STRING} @@ -3534,9 +4103,12 @@ STR_AI_LIST_ACCEPT_TOOLTIP :{BLACK}Elekti e STR_AI_LIST_CANCEL :{BLACK}Nuligi STR_AI_LIST_CANCEL_TOOLTIP :{BLACK}Ne ŝanĝu AI-on +STR_SCREENSHOT_SCREENSHOT :{BLACK}Normala ekrankapto +STR_SCREENSHOT_WORLD_SCREENSHOT :{BLACK}Tutmapa ekrankapto # Script Parameters STR_AI_SETTINGS_CAPTION_AI :AI +STR_AI_SETTINGS_CAPTION_GAMESCRIPT :Ludoskripto STR_AI_SETTINGS_CLOSE :{BLACK}Fermi STR_AI_SETTINGS_RESET :{BLACK}Reŝargo STR_AI_SETTINGS_SETTING :{STRING}: {ORANGE}{STRING} @@ -3585,12 +4157,13 @@ STR_ERROR_UNABLE_TO_DELETE_FILE :{WHITE}Ne eblas STR_ERROR_GAME_LOAD_FAILED :{WHITE}Ludŝarĝado Fiaskis{}{STRING} STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR :Interna eraro: {STRING} STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME :Rompa konservludo - {STRING} -STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME :Konservludo estas fari kun pli novan version +STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME :Tiu ĉi ludo estis konservita per pli nova versio STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE :Ne legebla dosiero STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE :Ne enskribebla dosiero STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED :Datena rekteco kontrolo fiaskis +STR_GAME_SAVELOAD_ERROR_PATCHPACK :La konservdosiero estas farita per modifita versio STR_GAME_SAVELOAD_NOT_AVAILABLE : -STR_WARNING_LOADGAME_REMOVED_TRAMS :{WHITE}Ludon estis konservinta en versio sen subteno por tramveturilo. Ĉiujn tramveturilojn estas formovita. +STR_WARNING_LOADGAME_REMOVED_TRAMS :{WHITE}La ludo estis konservita en versio kiu ne subtenas tramojn. Ĉiuj tramoj estas forigitaj. # Map generation messages STR_ERROR_COULD_NOT_CREATE_TOWN :{WHITE}Mapkreado haltita...{}... mankas taŭgaj urbejoj @@ -3615,6 +4188,7 @@ STR_WARNING_FALLBACK_SOUNDSET :{WHITE}Sola bak # Screenshot related messages STR_WARNING_SCREENSHOT_SIZE_CAPTION :{WHITE}Grandega ekranfoto +STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY :{WHITE}La altecmapo estis sukcese konservita kiel '{STRING}'. La plej alta pinto estas {NUM} STR_MESSAGE_SCREENSHOT_SUCCESSFULLY :{WHITE}Ekranfoto sukcese konserviĝis kiel '{STRING}' STR_ERROR_SCREENSHOT_FAILED :{WHITE}Ekranfoto fiaskis! @@ -3656,6 +4230,7 @@ STR_ERROR_EXCAVATION_WOULD_DAMAGE :{WHITE}Elkavigo STR_ERROR_ALREADY_AT_SEA_LEVEL :{WHITE}Jam marnivele STR_ERROR_TOO_HIGH :{WHITE}Tro alte STR_ERROR_ALREADY_LEVELLED :{WHITE}... jam plata +STR_ERROR_BRIDGE_TOO_HIGH_AFTER_LOWER_LAND :{WHITE}Poste la ponto super ĝi estus tro alta. # Company related errors STR_ERROR_CAN_T_CHANGE_COMPANY_NAME :{WHITE}Ne povas ŝanĝi kompaninomon... @@ -3689,6 +4264,7 @@ STR_ERROR_TOO_MANY_INDUSTRIES :{WHITE}... tro STR_ERROR_CAN_T_GENERATE_INDUSTRIES :{WHITE}Ne povas generi industriojn... STR_ERROR_CAN_T_BUILD_HERE :{WHITE}Ne povas konstrui {STRING.n} ĉi tie... STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY :{WHITE}Industritipo ne konstrueblas ĉi tie... +STR_ERROR_CAN_T_PROSPECT_INDUSTRY :{WHITE}Ne eblas prospektori por fabriko... STR_ERROR_INDUSTRY_TOO_CLOSE :{WHITE}... tro proksime al alia industrio STR_ERROR_MUST_FOUND_TOWN_FIRST :{WHITE}... antaŭe konstruu urbon STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN :{WHITE}... nur unu permesata en ĉiu urbo @@ -3703,6 +4279,7 @@ STR_ERROR_FOREST_CAN_ONLY_BE_PLANTED :{WHITE}... arba STR_ERROR_CAN_ONLY_BE_BUILT_ABOVE_SNOW_LINE :{WHITE}... nur konstruebla super la neĝlinio STR_ERROR_CAN_ONLY_BE_BUILT_BELOW_SNOW_LINE :{WHITE}... nur konstruebla sub la neĝlinio +STR_ERROR_PROSPECTING_WAS_UNLUCKY :{WHITE}Malgraŭ la financado, prospektorado malsukcesis pro malbonŝanco; provu denove # Station construction related errors STR_ERROR_CAN_T_BUILD_RAILROAD_STATION :{WHITE}Ne povas konstrui stacidomon ĉi tie... @@ -3723,9 +4300,9 @@ STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK :{WHITE}Tro prok STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT :{WHITE}Tro proksime al alia flughaveno STR_ERROR_CAN_T_RENAME_STATION :{WHITE}Ne povas alinomi stacion... STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ĉi tiun straton posedas la urbo -STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... strato direktiĝas malĝuste -STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... traveturaj haltejoj ne povas havi anguloj -STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... traveturaj haltejoj ne povas havi kuniĝoj +STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... malĝusta stratdirekto +STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... traveturaj haltejoj ne povas havi angulojn +STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... traveturaj haltejoj ne povas havi vojkrucojn # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Ne povas forigi stacidomeron... @@ -3788,6 +4365,8 @@ STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Malĝusta gara STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT :{WHITE}{VEHICLE} tro longas post anstataŭado STR_ERROR_AUTOREPLACE_NOTHING_TO_DO :{WHITE}Neniuj aŭtoanstataŭo/renovigo reguloj aplikitaj. STR_ERROR_AUTOREPLACE_MONEY_LIMIT :(monlimo) +STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO :{WHITE}La nova veturilo ne povas porti {STRING.n} +STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT :{WHITE}La nova veturilo ne povas plenumi readapton en la {NUM}a ordono # Rail construction errors STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}Neebla trakkombino @@ -3795,7 +4374,8 @@ STR_ERROR_MUST_REMOVE_SIGNALS_FIRST :{WHITE}Unue vi STR_ERROR_NO_SUITABLE_RAILROAD_TRACK :{WHITE}Nekonvena fervojtrako STR_ERROR_MUST_REMOVE_RAILROAD_TRACK :{WHITE}Antaŭe devas la trako esti detruita STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Strato estas unudirekta aŭ blokita -STR_ERROR_CROSSING_DISALLOWED_RAIL :{WHITE}Traknivelaj pasejoj ne permesa por ĉi tio fervojtipo +STR_ERROR_CROSSING_DISALLOWED_RAIL :{WHITE}Traknivelaj pasejoj ne estas permesataj por tiu ĉi fervojtipo +STR_ERROR_CROSSING_DISALLOWED_ROAD :{WHITE}Traknivelaj pasejoj ne estas permesataj por tiu ĉi vojtipo STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}Ne povas konstrui signalojn ĉi tie... STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}Ne povas konstrui trakon ĉi tie... STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK :{WHITE}Ne povas forigi trakon de ĉi tie... @@ -3815,6 +4395,9 @@ STR_ERROR_CAN_T_REMOVE_ROAD_FROM :{WHITE}Ne povas STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM :{WHITE}Ne povas forigi tramvojon de ĉi tie... STR_ERROR_THERE_IS_NO_ROAD :{WHITE}... ne jen estas strato STR_ERROR_THERE_IS_NO_TRAMWAY :{WHITE}... ne jen estas tramvojo +STR_ERROR_CAN_T_CONVERT_TRAMWAY :{WHITE}Ne eblas konverti tramtipon ĉi tie... +STR_ERROR_NO_SUITABLE_ROAD :{WHITE}Mankas taŭga vojo +STR_ERROR_NO_SUITABLE_TRAMWAY :{WHITE}Mankas taŭga tramvojo # Waterway construction errors STR_ERROR_CAN_T_BUILD_CANALS :{WHITE}Ne povas konstrui kanalojn ĉi tie... @@ -3839,6 +4422,7 @@ STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST :{WHITE}Antaŭe STR_ERROR_CAN_T_START_AND_END_ON :{WHITE}Ne povas komenci kaj fini en la sama loko STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT :{WHITE}Finoj de ponto ne estas je la sama nivelo STR_ERROR_BRIDGE_TOO_LOW_FOR_TERRAIN :{WHITE}Ponto estas tro malalta por la tereno +STR_ERROR_BRIDGE_TOO_HIGH_FOR_TERRAIN :{WHITE}La ponto estas tro alta por tiu ĉi tereno. STR_ERROR_START_AND_END_MUST_BE_IN :{WHITE}Komenco kaj fino devas esti samliniaj STR_ERROR_ENDS_OF_BRIDGE_MUST_BOTH :{WHITE}... ambaŭ pontfinoj devas esti sur tero STR_ERROR_BRIDGE_TOO_LONG :{WHITE}... ponto estas tro longa @@ -3928,9 +4512,13 @@ STR_ERROR_CAN_T_SELL_AIRCRAFT :{WHITE}Ne povas STR_ERROR_TOO_MANY_VEHICLES_IN_GAME :{WHITE}Tro da veturiloj en la ludo STR_ERROR_CAN_T_CHANGE_SERVICING :{WHITE}Ne povas ŝanĝi prizorgintervalon... -STR_ERROR_VEHICLE_IS_DESTROYED :{WHITE}... veturilo estas detrua +STR_ERROR_VEHICLE_IS_DESTROYED :{WHITE}... veturilo estas detruita +STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL :{WHITE}Neniuj veturiloj estos haveblaj iam ajn +STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL_EXPLANATION :{WHITE}Ŝanĝu viajn NewGRF-agordojn +STR_ERROR_NO_VEHICLES_AVAILABLE_YET :{WHITE}Neniuj veturiloj dume haveblas +STR_ERROR_NO_VEHICLES_AVAILABLE_YET_EXPLANATION :{WHITE}Komencu novan ludon post {DATE_SHORT} aŭ uzu NewGRF-on kiu havebligas frutempajn veturilojn # Specific vehicle errors STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL :{WHITE}Ne povas igi trajnon transiri signalon je danĝero... @@ -3961,11 +4549,22 @@ STR_ERROR_TOO_FAR_FROM_PREVIOUS_DESTINATION :{WHITE}... tro STR_ERROR_AIRCRAFT_NOT_ENOUGH_RANGE :{WHITE}... aviadilon ne havas sufiĉe da atingopovo # Extra messages which go on the third line of errors, explaining why orders failed +STR_ERROR_NO_RAIL_STATION :{WHITE}Mankas fervojstacio +STR_ERROR_NO_BUS_STATION :{WHITE}Mankas busstacio +STR_ERROR_NO_TRUCK_STATION :{WHITE}Mankas ŝarĝaŭta stacio +STR_ERROR_NO_DOCK :{WHITE}Mankas haveno +STR_ERROR_NO_AIRPORT :{WHITE}Mankas flughaveno/helikopterhaveno +STR_ERROR_NO_STOP_COMPATIBLE_ROAD_TYPE :{WHITE}Mankas haltejoj kun kongrua vojtipo +STR_ERROR_NO_STOP_COMPATIBLE_TRAM_TYPE :{WHITE}Mankas haltejoj kun kongrua tipo de tramvojo +STR_ERROR_AIRPORT_NO_PLANES :{WHITE}Tiu ĉi aviadilo ne povas surteriĝi ĉe tiu ĉi helikopterhaveno +STR_ERROR_AIRPORT_NO_HELICOPTERS :{WHITE}Tiu ĉi helikoptero ne povas surteriĝi ĉe tiu ĉi flughaveno +STR_ERROR_NO_BUOY :{WHITE}Mankas buo # Timetable related errors STR_ERROR_CAN_T_TIMETABLE_VEHICLE :{WHITE}Ne povas fari katalogon por veturilo... STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS :{WHITE}Veturiloj povas atendi nur ĉe stacioj. STR_ERROR_TIMETABLE_NOT_STOPPING_HERE :{WHITE}Ĉi tiu veturilo ne haltos ĉe tiu ĉi stacio. +STR_ERROR_TIMETABLE_INCOMPLETE :{WHITE}... la horaro estas malkompleta # Sign related errors STR_ERROR_TOO_MANY_SIGNS :{WHITE}... tro da afiŝoj @@ -3979,6 +4578,16 @@ STR_DESKTOP_SHORTCUT_COMMENT :Al simulado lud # Translatable descriptions in media/baseset/*.ob* files ###external 10 +STR_BASEGRAPHICS_DOS_DESCRIPTION :Originala grafiko de la DOS-eldono de Transport Tycoon Deluxe. +STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :Originala grafiko de la germana DOS-eldono de Transport Tycoon Deluxe. +STR_BASEGRAPHICS_WIN_DESCRIPTION :Originala grafiko de la Vindoza eldono de Transport Tycoon Deluxe. +STR_BASESOUNDS_DOS_DESCRIPTION :Originalaj sonoj de la DOS-eldono de Transport Tycoon Deluxe. +STR_BASESOUNDS_WIN_DESCRIPTION :Originalaj sonoj de la Vindoza eldono de Transport Tycoon Deluxe. +STR_BASESOUNDS_NONE_DESCRIPTION :Sonaro kiu fakte ne enhavas sonojn. +STR_BASEMUSIC_WIN_DESCRIPTION :Originala muziko de la Vindoza eldono de Transport Tycoon Deluxe. +STR_BASEMUSIC_DOS_DESCRIPTION :Originala muziko de la DOS-eldono de Transport Tycoon Deluxe. +STR_BASEMUSIC_TTO_DESCRIPTION :Originala muziko de la DOS-eldono de Transport Tycoon (Original/World Editor). +STR_BASEMUSIC_NONE_DESCRIPTION :Muzikaro kiu fakte ne enhavas muzikon. ##id 0x2000 # Town building names @@ -4062,10 +4671,10 @@ STR_INDUSTRY_NAME_SUGAR_MINE :Sukerminejo ##id 0x6000 STR_SV_EMPTY : STR_SV_UNNAMED :Sennoma -STR_SV_TRAIN_NAME :Trajno {COMMA} -STR_SV_ROAD_VEHICLE_NAME :Stratveturilo {COMMA} -STR_SV_SHIP_NAME :Ŝipo {COMMA} -STR_SV_AIRCRAFT_NAME :Aviadilo {COMMA} +STR_SV_TRAIN_NAME :Trajno #{COMMA} +STR_SV_ROAD_VEHICLE_NAME :Stratveturilo #{COMMA} +STR_SV_SHIP_NAME :Ŝipo #{COMMA} +STR_SV_AIRCRAFT_NAME :Aviadilo #{COMMA} ###length 27 STR_SV_STNAME :{STRING} @@ -4374,7 +4983,9 @@ STR_FORMAT_DATE_ISO :{2:NUM}-{1:ZERO STR_FORMAT_COMPANY_NUM :(Kompanio {COMMA}) STR_FORMAT_GROUP_NAME :Group {COMMA} -STR_FORMAT_INDUSTRY_NAME :{TOWN} {STRING} +STR_FORMAT_GROUP_VEHICLE_NAME :{GROUP} #{COMMA} +STR_FORMAT_INDUSTRY_NAME :{1:STRING} de {0:TOWN} +STR_FORMAT_INDUSTRY_NAME.n :{1:STRING.n} de {0:TOWN} ###length 2 STR_FORMAT_BUOY_NAME :{TOWN} Buo @@ -4423,6 +5034,7 @@ STR_COMPANY_NAME :{COMPANY} STR_COMPANY_NAME_COMPANY_NUM :{COMPANY} {COMPANY_NUM} STR_DEPOT_NAME :{DEPOT} STR_ENGINE_NAME :{ENGINE} +STR_HIDDEN_ENGINE_NAME :{ENGINE} (kaŝita) STR_GROUP_NAME :{GROUP} STR_INDUSTRY_NAME :{INDUSTRY} STR_PRESIDENT_NAME :{PRESIDENT_NAME} From 0850193a38ff361b61795c898ce73f28d9dd32b5 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 21 May 2023 12:57:32 +0200 Subject: [PATCH 16/58] Fix: survey result on crash only worked on Linux (#10855) Every OS-specific crashlog handler has their own MakeCrashLog in some form. In result, only Linux was calling the generic one. --- src/crashlog.cpp | 11 ++++++++--- src/crashlog.h | 2 ++ src/os/macosx/crashlog_osx.cpp | 2 ++ src/os/windows/crashlog_win.cpp | 1 + 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/crashlog.cpp b/src/crashlog.cpp index e3be578da1..6382d81e4b 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -454,6 +454,13 @@ bool CrashLog::WriteScreenshot(char *filename, const char *filename_last) const return res; } +void CrashLog::SendSurvey() const +{ + if (_game_mode == GM_NORMAL) { + _survey.Transmit(NetworkSurveyHandler::Reason::CRASH, true); + } +} + /** * Makes the crash log, writes it to a file and then subsequently tries * to make a crash dump and crash savegame. It uses DEBUG to write @@ -512,9 +519,7 @@ bool CrashLog::MakeCrashLog() const printf("Writing crash screenshot failed.\n\n"); } - if (_game_mode == GM_NORMAL) { - _survey.Transmit(NetworkSurveyHandler::Reason::CRASH, true); - } + this->SendSurvey(); return ret; } diff --git a/src/crashlog.h b/src/crashlog.h index e90b13b499..9aad7658e9 100644 --- a/src/crashlog.h +++ b/src/crashlog.h @@ -99,6 +99,8 @@ public: bool WriteSavegame(char *filename, const char *filename_last) const; bool WriteScreenshot(char *filename, const char *filename_last) const; + void SendSurvey() const; + bool MakeCrashLog() const; /** diff --git a/src/os/macosx/crashlog_osx.cpp b/src/os/macosx/crashlog_osx.cpp index 78c550aa9e..df1736a6f6 100644 --- a/src/os/macosx/crashlog_osx.cpp +++ b/src/os/macosx/crashlog_osx.cpp @@ -192,6 +192,8 @@ public: ret = false; } + this->SendSurvey(); + return ret; } diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index c4b5e9597e..3b3c089ee3 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -564,6 +564,7 @@ static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep) log->AppendDecodedStacktrace(buf, lastof(log->crashlog)); log->WriteCrashLog(log->crashlog, log->crashlog_filename, lastof(log->crashlog_filename)); log->WriteScreenshot(log->screenshot_filename, lastof(log->screenshot_filename)); + log->SendSurvey(); /* Close any possible log files */ CloseConsoleLogIfActive(); From d57046e7ec10479e2e1866d14e9a22c34c9d4825 Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Sun, 21 May 2023 08:57:14 -0400 Subject: [PATCH 17/58] Change: Use "Via-Destination-Source" as default station cargodist display (#10851) --- src/table/settings/gui_settings.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/table/settings/gui_settings.ini b/src/table/settings/gui_settings.ini index 7abeed9809..08f43b6661 100644 --- a/src/table/settings/gui_settings.ini +++ b/src/table/settings/gui_settings.ini @@ -675,7 +675,7 @@ cat = SC_BASIC var = gui.station_gui_group_order type = SLE_UINT8 flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC -def = 0 +def = 3 min = 0 max = 5 interval = 1 From c518293135ce824fa71c1c02da28f0412434160f Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 21 May 2023 08:04:33 +0200 Subject: [PATCH 18/58] Codechange: replace printf with fmt::print --- src/crashlog.cpp | 28 ++++++++++++++-------------- src/network/network_chat_gui.cpp | 2 +- src/openttd.cpp | 4 ++-- src/os/macosx/crashlog_osx.cpp | 12 ++++++------ src/os/unix/crashlog_unix.cpp | 10 +++++----- src/pathfinder/npf/aystar.cpp | 12 ++++++------ src/pathfinder/npf/queue.cpp | 19 ++++++++----------- src/script/squirrel.cpp | 2 +- src/strgen/strgen.cpp | 12 ++++++------ 9 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 6382d81e4b..848424d058 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -478,45 +478,45 @@ bool CrashLog::MakeCrashLog() const char buffer[65536]; bool ret = true; - printf("Crash encountered, generating crash log...\n"); + fmt::print("Crash encountered, generating crash log...\n"); this->FillCrashLog(buffer, lastof(buffer)); - printf("%s\n", buffer); - printf("Crash log generated.\n\n"); + fmt::print("{}\n", buffer); + fmt::print("Crash log generated.\n\n"); - printf("Writing crash log to disk...\n"); + fmt::print("Writing crash log to disk...\n"); bool bret = this->WriteCrashLog(buffer, filename, lastof(filename)); if (bret) { - printf("Crash log written to %s. Please add this file to any bug reports.\n\n", filename); + fmt::print("Crash log written to {}. Please add this file to any bug reports.\n\n", filename); } else { - printf("Writing crash log failed. Please attach the output above to any bug reports.\n\n"); + fmt::print("Writing crash log failed. Please attach the output above to any bug reports.\n\n"); ret = false; } /* Don't mention writing crash dumps because not all platforms support it. */ int dret = this->WriteCrashDump(filename, lastof(filename)); if (dret < 0) { - printf("Writing crash dump failed.\n\n"); + fmt::print("Writing crash dump failed.\n\n"); ret = false; } else if (dret > 0) { - printf("Crash dump written to %s. Please add this file to any bug reports.\n\n", filename); + fmt::print("Crash dump written to {}. Please add this file to any bug reports.\n\n", filename); } - printf("Writing crash savegame...\n"); + fmt::print("Writing crash savegame...\n"); bret = this->WriteSavegame(filename, lastof(filename)); if (bret) { - printf("Crash savegame written to %s. Please add this file and the last (auto)save to any bug reports.\n\n", filename); + fmt::print("Crash savegame written to {}. Please add this file and the last (auto)save to any bug reports.\n\n", filename); } else { ret = false; - printf("Writing crash savegame failed. Please attach the last (auto)save to any bug reports.\n\n"); + fmt::print("Writing crash savegame failed. Please attach the last (auto)save to any bug reports.\n\n"); } - printf("Writing crash screenshot...\n"); + fmt::print("Writing crash screenshot...\n"); bret = this->WriteScreenshot(filename, lastof(filename)); if (bret) { - printf("Crash screenshot written to %s. Please add this file to any bug reports.\n\n", filename); + fmt::print("Crash screenshot written to {}. Please add this file to any bug reports.\n\n", filename); } else { ret = false; - printf("Writing crash screenshot failed.\n\n"); + fmt::print("Writing crash screenshot failed.\n\n"); } this->SendSurvey(); diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index aadee357e0..01aad5fb7a 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -85,7 +85,7 @@ static inline bool HaveChatMessages(bool show_all) * Add a text message to the 'chat window' to be shown * @param colour The colour this message is to be shown in * @param duration The duration of the chat message in seconds - * @param message message itself in printf() style + * @param message message itself */ void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const std::string &message) { diff --git a/src/openttd.cpp b/src/openttd.cpp index 61c0e1da9d..d7f0d064e8 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -225,7 +225,7 @@ static void ShowHelp() /* ShowInfo put output to stderr, but version information should go * to stdout; this is the only exception */ #if !defined(_WIN32) - printf("%s\n", str.c_str()); + fmt::print("{}\n", str); #else ShowInfoI(str); #endif @@ -262,7 +262,7 @@ static void WriteSavegameInfo(const char *name) /* ShowInfo put output to stderr, but version information should go * to stdout; this is the only exception */ #if !defined(_WIN32) - printf("%s\n", message.c_str()); + fmt::print("%s\n", message); #else ShowInfoI(message); #endif diff --git a/src/os/macosx/crashlog_osx.cpp b/src/os/macosx/crashlog_osx.cpp index df1736a6f6..0b1b303c2c 100644 --- a/src/os/macosx/crashlog_osx.cpp +++ b/src/os/macosx/crashlog_osx.cpp @@ -169,24 +169,24 @@ public: char buffer[65536]; bool ret = true; - printf("Crash encountered, generating crash log...\n"); + fmt::print("Crash encountered, generating crash log...\n"); this->FillCrashLog(buffer, lastof(buffer)); - printf("%s\n", buffer); - printf("Crash log generated.\n\n"); + fmt::print("{}\n", buffer); + fmt::print("Crash log generated.\n\n"); - printf("Writing crash log to disk...\n"); + fmt::print("Writing crash log to disk...\n"); if (!this->WriteCrashLog(buffer, filename_log, lastof(filename_log))) { filename_log[0] = '\0'; ret = false; } - printf("Writing crash savegame...\n"); + fmt::print("Writing crash savegame...\n"); if (!this->WriteSavegame(filename_save, lastof(filename_save))) { filename_save[0] = '\0'; ret = false; } - printf("Writing crash screenshot...\n"); + fmt::print("Writing crash screenshot...\n"); if (!this->WriteScreenshot(filename_screenshot, lastof(filename_screenshot))) { filename_screenshot[0] = '\0'; ret = false; diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index a3fc1d5e97..dcbad13d70 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -156,15 +156,15 @@ static void CDECL HandleCrash(int signum) } if (_gamelog.TestEmergency()) { - printf("A serious fault condition occurred in the game. The game will shut down.\n"); - printf("As you loaded an emergency savegame no crash information will be generated.\n"); + fmt::print("A serious fault condition occurred in the game. The game will shut down.\n"); + fmt::print("As you loaded an emergency savegame no crash information will be generated.\n"); abort(); } if (SaveloadCrashWithMissingNewGRFs()) { - printf("A serious fault condition occurred in the game. The game will shut down.\n"); - printf("As you loaded an savegame for which you do not have the required NewGRFs\n"); - printf("no crash information will be generated.\n"); + fmt::print("A serious fault condition occurred in the game. The game will shut down.\n"); + fmt::print("As you loaded an savegame for which you do not have the required NewGRFs\n"); + fmt::print("no crash information will be generated.\n"); abort(); } diff --git a/src/pathfinder/npf/aystar.cpp b/src/pathfinder/npf/aystar.cpp index 163d7859ec..698b8183ac 100644 --- a/src/pathfinder/npf/aystar.cpp +++ b/src/pathfinder/npf/aystar.cpp @@ -211,7 +211,7 @@ void AyStar::Free() this->openlist_hash.Delete(true); this->closedlist_hash.Delete(true); #ifdef AYSTAR_DEBUG - printf("[AyStar] Memory free'd\n"); + Debug(misc, 0, "[AyStar] Memory free'd"); #endif } @@ -229,7 +229,7 @@ void AyStar::Clear() this->closedlist_hash.Clear(true); #ifdef AYSTAR_DEBUG - printf("[AyStar] Cleared AyStar\n"); + Debug(misc, 0, "[AyStar] Cleared AyStar"); #endif } @@ -250,9 +250,9 @@ int AyStar::Main() while ((r = this->Loop()) == AYSTAR_STILL_BUSY && (this->loops_per_tick == 0 || ++i < this->loops_per_tick)) { } #ifdef AYSTAR_DEBUG switch (r) { - case AYSTAR_FOUND_END_NODE: printf("[AyStar] Found path!\n"); break; - case AYSTAR_EMPTY_OPENLIST: printf("[AyStar] OpenList run dry, no path found\n"); break; - case AYSTAR_LIMIT_REACHED: printf("[AyStar] Exceeded search_nodes, no path found\n"); break; + case AYSTAR_FOUND_END_NODE: Debug(misc, 0, "[AyStar] Found path!"); break; + case AYSTAR_EMPTY_OPENLIST: Debug(misc, 0, "[AyStar] OpenList run dry, no path found"); break; + case AYSTAR_LIMIT_REACHED: Debug(misc, 0, "[AyStar] Exceeded search_nodes, no path found"); break; default: break; } #endif @@ -280,7 +280,7 @@ int AyStar::Main() void AyStar::AddStartNode(AyStarNode *start_node, uint g) { #ifdef AYSTAR_DEBUG - printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n", + Debug(misc, 0, "[AyStar] Starting A* Algorithm from node ({}, {}, {})\n", TileX(start_node->tile), TileY(start_node->tile), start_node->direction); #endif this->OpenListAdd(nullptr, start_node, 0, g); diff --git a/src/pathfinder/npf/queue.cpp b/src/pathfinder/npf/queue.cpp index 960cda8e5c..f1acf26eb3 100644 --- a/src/pathfinder/npf/queue.cpp +++ b/src/pathfinder/npf/queue.cpp @@ -303,29 +303,26 @@ void Hash::PrintStatistics() const max_usage = usage[collision]; } } - printf( - "---\n" - "Hash size: %u\n" - "Nodes used: %u\n" - "Non empty buckets: %u\n" - "Max collision: %u\n", + Debug(misc, 0, "Hash size: {}, Nodes used: {}, Non empty buckets: {}, Max collision: {}", this->num_buckets, this->size, used_buckets, max_collision ); - printf("{ "); + std::string line; + line += "{ "; for (i = 0; i <= max_collision; i++) { if (usage[i] > 0) { - printf("%u:%u ", i, usage[i]); + fmt::format_to(std::back_inserter(line), "{}:{} ", i, usage[i]); #if 0 if (i > 0) { uint j; - for (j = 0; j < usage[i] * 160 / 800; j++) putchar('#'); + for (j = 0; j < usage[i] * 160 / 800; j++) line += "#"; } - printf("\n"); + line += "\n"; #endif } } - printf ("}\n"); + line += "}"; + Debug(misc, 0, "{}", line); } #endif diff --git a/src/script/squirrel.cpp b/src/script/squirrel.cpp index a59b7cff64..530e0c32d9 100644 --- a/src/script/squirrel.cpp +++ b/src/script/squirrel.cpp @@ -269,7 +269,7 @@ void Squirrel::PrintFunc(HSQUIRRELVM vm, const std::string &s) /* Check if we have a custom print function */ SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func; if (func == nullptr) { - printf("%s", s.c_str()); + fmt::print("%s", s); } else { (*func)(false, s); } diff --git a/src/strgen/strgen.cpp b/src/strgen/strgen.cpp index e4f4f55ed7..fc58f8a142 100644 --- a/src/strgen/strgen.cpp +++ b/src/strgen/strgen.cpp @@ -427,7 +427,7 @@ int CDECL main(int argc, char *argv[]) return 0; case 'C': - printf("args\tflags\tcommand\treplacement\n"); + fmt::print("args\tflags\tcommand\treplacement\n"); for (const CmdStruct *cs = _cmd_structs; cs < endof(_cmd_structs); cs++) { char flags; if (cs->proc == EmitGender) { @@ -439,21 +439,21 @@ int CDECL main(int argc, char *argv[]) } else { flags = '0'; // Command needs no parameters } - printf("%i\t%c\t\"%s\"\t\"%s\"\n", cs->consumes, flags, cs->cmd, strstr(cs->cmd, "STRING") ? "STRING" : cs->cmd); + fmt::print("{}\t{:c}\t\"{}\"\t\"{}\"\n", cs->consumes, flags, cs->cmd, strstr(cs->cmd, "STRING") ? "STRING" : cs->cmd); } return 0; case 'L': - printf("count\tdescription\tnames\n"); + fmt::print("count\tdescription\tnames\n"); for (const PluralForm *pf = _plural_forms; pf < endof(_plural_forms); pf++) { - printf("%i\t\"%s\"\t%s\n", pf->plural_count, pf->description, pf->names); + fmt::print("{}\t\"{}\"\t{}\n", pf->plural_count, pf->description, pf->names); } return 0; case 'P': - printf("name\tflags\tdefault\tdescription\n"); + fmt::print("name\tflags\tdefault\tdescription\n"); for (size_t j = 0; j < lengthof(_pragmas); j++) { - printf("\"%s\"\t%s\t\"%s\"\t\"%s\"\n", + fmt::print("\"{}\"\t{}\t\"{}\"\t\"{}\"\n", _pragmas[j][0], _pragmas[j][1], _pragmas[j][2], _pragmas[j][3]); } return 0; From 275ebf4509d4c54fdbf8f409cf6dc835524eaa30 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 21 May 2023 08:13:28 +0200 Subject: [PATCH 19/58] Codechange: replace fprintf( with fmt::print( --- src/console.cpp | 2 +- src/debug.cpp | 4 +-- src/map.cpp | 2 +- src/openttd.cpp | 6 ++--- src/os/macosx/macos.mm | 2 +- src/os/unix/unix.cpp | 6 ++--- src/os/windows/win32.cpp | 2 +- src/script/squirrel.cpp | 4 +-- src/settingsgen/settingsgen.cpp | 4 +-- src/strgen/strgen.cpp | 46 ++++++++++++++++----------------- src/video/cocoa/cocoa_wnd.mm | 2 +- 11 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/console.cpp b/src/console.cpp index 96de9ac8b8..56b0014b50 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -112,7 +112,7 @@ void IConsolePrint(TextColour colour_code, const std::string &string) if (_network_dedicated) { NetworkAdminConsole("console", str); - fprintf(stdout, "%s%s\n", GetLogPrefix(), str); + fmt::print("{}{}\n", GetLogPrefix(), str); fflush(stdout); IConsoleWriteToLogFile(str); free(str); // free duplicated string since it's not used anymore diff --git a/src/debug.cpp b/src/debug.cpp index b088af9aaa..7a411a1077 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -127,14 +127,14 @@ void DebugPrint(const char *level, const std::string &message) static FILE *f = FioFOpenFile("commands-out.log", "wb", AUTOSAVE_DIR); if (f == nullptr) return; - fprintf(f, "%s%s\n", GetLogPrefix(), message.c_str()); + fmt::print(f, "{}{}\n", GetLogPrefix(), message); fflush(f); #ifdef RANDOM_DEBUG } else if (strcmp(level, "random") == 0) { static FILE *f = FioFOpenFile("random-out.log", "wb", AUTOSAVE_DIR); if (f == nullptr) return; - fprintf(f, "%s\n", message.c_str()); + fmt::print(f, "{}\n", message); fflush(f); #endif } else { diff --git a/src/map.cpp b/src/map.cpp index 52aa621ca0..dfaa6ec866 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -87,7 +87,7 @@ TileIndex TileAdd(TileIndex tile, TileIndexDiff add, seprintf(buf, lastof(buf), "TILE_ADD(%s) when adding 0x%.4X and 0x%.4X failed", exp, (uint32)tile, add); #if !defined(_MSC_VER) - fprintf(stderr, "%s:%d %s\n", file, line, buf); + fmt::print(stderr, "{}:{} {}\n", file, line, buf); #else _assert(buf, (char*)file, line); #endif diff --git a/src/openttd.cpp b/src/openttd.cpp index d7f0d064e8..19a2bc515b 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -615,13 +615,11 @@ int openttd_main(int argc, char *argv[]) _load_check_data.Clear(); SaveOrLoadResult res = SaveOrLoad(mgo.opt, SLO_CHECK, DFT_GAME_FILE, SAVE_DIR, false); if (res != SL_OK || _load_check_data.HasErrors()) { - fprintf(stderr, "Failed to open savegame\n"); + fmt::print(stderr, "Failed to open savegame\n"); if (_load_check_data.HasErrors()) { InitializeLanguagePacks(); // A language pack is needed for GetString() - char buf[256]; SetDParamStr(0, _load_check_data.error_msg); - GetString(buf, _load_check_data.error, lastof(buf)); - fprintf(stderr, "%s\n", buf); + fmt::print(stderr, "{}\n", GetString(_load_check_data.error)); } return ret; } diff --git a/src/os/macosx/macos.mm b/src/os/macosx/macos.mm index 9db5cd049f..773c77e32f 100644 --- a/src/os/macosx/macos.mm +++ b/src/os/macosx/macos.mm @@ -135,7 +135,7 @@ void ShowMacDialog(const char *title, const char *message, const char *buttonLab */ void ShowMacDialog(const char *title, const char *message, const char *buttonLabel) { - fprintf(stderr, "%s: %s\n", title, message); + fmt::print(stderr, "{}: {}\n", title, message); } #endif diff --git a/src/os/unix/unix.cpp b/src/os/unix/unix.cpp index 249020458a..edacf2feaf 100644 --- a/src/os/unix/unix.cpp +++ b/src/os/unix/unix.cpp @@ -212,7 +212,7 @@ std::string FS2OTTD(const std::string &name) void ShowInfoI(const std::string &str) { - fprintf(stderr, "%s\n", str.c_str()); + fmt::print(stderr, "{}\n", str); } #if !defined(__APPLE__) @@ -220,9 +220,9 @@ void ShowOSErrorBox(const char *buf, bool system) { /* All unix systems, except OSX. Only use escape codes on a TTY. */ if (isatty(fileno(stderr))) { - fprintf(stderr, "\033[1;31mError: %s\033[0;39m\n", buf); + fmt::print(stderr, "\033[1;31mError: {}\033[0;39m\n", buf); } else { - fprintf(stderr, "Error: %s\n", buf); + fmt::print(stderr, "Error: {}\n", buf); } } #endif diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index b941e642ac..07987cb14d 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -321,7 +321,7 @@ static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM void ShowInfoI(const std::string &str) { if (_has_console) { - fprintf(stderr, "%s\n", str.c_str()); + fmt::print(stderr, "{}\n", str); } else { bool old; ReleaseCapture(); diff --git a/src/script/squirrel.cpp b/src/script/squirrel.cpp index 530e0c32d9..62542ad856 100644 --- a/src/script/squirrel.cpp +++ b/src/script/squirrel.cpp @@ -221,7 +221,7 @@ void Squirrel::ErrorPrintFunc(HSQUIRRELVM vm, const std::string &s) /* Check if we have a custom print function */ SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func; if (func == nullptr) { - fprintf(stderr, "%s", s.c_str()); + fmt::print(stderr, "{}", s); } else { (*func)(true, s); } @@ -238,7 +238,7 @@ void Squirrel::RunError(HSQUIRRELVM vm, const SQChar *error) Squirrel *engine = (Squirrel *)sq_getforeignptr(vm); SQPrintFunc *func = engine->print_func; if (func == nullptr) { - fprintf(stderr, "%s", msg.c_str()); + fmt::print(stderr, "{}", msg); } else { (*func)(true, msg); } diff --git a/src/settingsgen/settingsgen.cpp b/src/settingsgen/settingsgen.cpp index 603278859d..273d414b0c 100644 --- a/src/settingsgen/settingsgen.cpp +++ b/src/settingsgen/settingsgen.cpp @@ -29,7 +29,7 @@ */ void NORETURN FatalErrorI(const std::string &msg) { - fprintf(stderr, "settingsgen: FATAL: %s\n", msg.c_str()); + fmt::print(stderr, "settingsgen: FATAL: {}\n", msg); exit(1); } @@ -483,7 +483,7 @@ int CDECL main(int argc, char *argv[]) break; case -2: - fprintf(stderr, "Invalid arguments\n"); + fmt::print(stderr, "Invalid arguments\n"); return 1; } } diff --git a/src/strgen/strgen.cpp b/src/strgen/strgen.cpp index fc58f8a142..3f7b181e7a 100644 --- a/src/strgen/strgen.cpp +++ b/src/strgen/strgen.cpp @@ -34,41 +34,41 @@ #ifdef _MSC_VER -# define LINE_NUM_FMT(s) "%s (%d): warning: %s (" s ")\n" +# define LINE_NUM_FMT(s) "{} ({}): warning: {} (" s ")\n" #else -# define LINE_NUM_FMT(s) "%s:%d: " s ": %s\n" +# define LINE_NUM_FMT(s) "{}:{}: " s ": {}\n" #endif void StrgenWarningI(const std::string &msg) { if (_show_todo > 0) { - fprintf(stderr, LINE_NUM_FMT("warning"), _file, _cur_line, msg.c_str()); + fmt::print(stderr, LINE_NUM_FMT("warning"), _file, _cur_line, msg); } else { - fprintf(stderr, LINE_NUM_FMT("info"), _file, _cur_line, msg.c_str()); + fmt::print(stderr, LINE_NUM_FMT("info"), _file, _cur_line, msg); } _warnings++; } void StrgenErrorI(const std::string &msg) { - fprintf(stderr, LINE_NUM_FMT("error"), _file, _cur_line, msg.c_str()); + fmt::print(stderr, LINE_NUM_FMT("error"), _file, _cur_line, msg); _errors++; } void NORETURN StrgenFatalI(const std::string &msg) { - fprintf(stderr, LINE_NUM_FMT("FATAL"), _file, _cur_line, msg.c_str()); + fmt::print(stderr, LINE_NUM_FMT("FATAL"), _file, _cur_line, msg); #ifdef _MSC_VER - fprintf(stderr, LINE_NUM_FMT("warning"), _file, _cur_line, "language is not compiled"); + fmt::print(stderr, LINE_NUM_FMT("warning"), _file, _cur_line, "language is not compiled"); #endif throw std::exception(); } void NORETURN FatalErrorI(const std::string &msg) { - fprintf(stderr, LINE_NUM_FMT("FATAL"), _file, _cur_line, msg.c_str()); + fmt::print(stderr, LINE_NUM_FMT("FATAL"), _file, _cur_line, msg); #ifdef _MSC_VER - fprintf(stderr, LINE_NUM_FMT("warning"), _file, _cur_line, "language is not compiled"); + fmt::print(stderr, LINE_NUM_FMT("warning"), _file, _cur_line, "language is not compiled"); #endif exit(2); } @@ -266,9 +266,9 @@ struct HeaderFileWriter : HeaderWriter, FileWriter { HeaderFileWriter(const char *filename) : FileWriter("tmp.xxx"), real_filename(stredup(filename)), prev(0), total_strings(0) { - fprintf(this->fh, "/* This file is automatically generated. Do not modify */\n\n"); - fprintf(this->fh, "#ifndef TABLE_STRINGS_H\n"); - fprintf(this->fh, "#define TABLE_STRINGS_H\n"); + fmt::print(this->fh, "/* This file is automatically generated. Do not modify */\n\n"); + fmt::print(this->fh, "#ifndef TABLE_STRINGS_H\n"); + fmt::print(this->fh, "#define TABLE_STRINGS_H\n"); } /** Free the filename. */ @@ -279,8 +279,8 @@ struct HeaderFileWriter : HeaderWriter, FileWriter { void WriteStringID(const char *name, int stringid) { - if (prev + 1 != stringid) fprintf(this->fh, "\n"); - fprintf(this->fh, "static const StringID %s = 0x%X;\n", name, stringid); + if (prev + 1 != stringid) fmt::print(this->fh, "\n"); + fmt::print(this->fh, "static const StringID {} = 0x{:X};\n", name, stringid); prev = stringid; total_strings++; } @@ -293,17 +293,17 @@ struct HeaderFileWriter : HeaderWriter, FileWriter { max_plural_forms = std::max(max_plural_forms, _plural_forms[i].plural_count); } - fprintf(this->fh, + fmt::print(this->fh, "\n" - "static const uint LANGUAGE_PACK_VERSION = 0x%X;\n" - "static const uint LANGUAGE_MAX_PLURAL = %u;\n" - "static const uint LANGUAGE_MAX_PLURAL_FORMS = %d;\n" - "static const uint LANGUAGE_TOTAL_STRINGS = %u;\n" + "static const uint LANGUAGE_PACK_VERSION = 0x{:X};\n" + "static const uint LANGUAGE_MAX_PLURAL = {};\n" + "static const uint LANGUAGE_MAX_PLURAL_FORMS = {};\n" + "static const uint LANGUAGE_TOTAL_STRINGS = {};\n" "\n", - (uint)data.Version(), (uint)lengthof(_plural_forms), max_plural_forms, total_strings + data.Version(), lengthof(_plural_forms), max_plural_forms, total_strings ); - fprintf(this->fh, "#endif /* TABLE_STRINGS_H */\n"); + fmt::print(this->fh, "#endif /* TABLE_STRINGS_H */\n"); this->FileWriter::Finalise(); @@ -493,7 +493,7 @@ int CDECL main(int argc, char *argv[]) break; case -2: - fprintf(stderr, "Invalid arguments\n"); + fmt::print(stderr, "Invalid arguments\n"); return 0; } } @@ -556,7 +556,7 @@ int CDECL main(int argc, char *argv[]) /* if showing warnings, print a summary of the language */ if ((_show_todo & 2) != 0) { - fprintf(stdout, "%d warnings and %d errors for %s\n", _warnings, _errors, pathbuf); + fmt::print("{} warnings and {} errors for {}\n", _warnings, _errors, pathbuf); } } } diff --git a/src/video/cocoa/cocoa_wnd.mm b/src/video/cocoa/cocoa_wnd.mm index 88361dbc7a..df08df857f 100644 --- a/src/video/cocoa/cocoa_wnd.mm +++ b/src/video/cocoa/cocoa_wnd.mm @@ -397,7 +397,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel if (VideoDriver::GetInstance() == nullptr) { CocoaSetupApplication(); // Setup application before showing dialog } else if (!_cocoa_video_started && VideoDriver::GetInstance()->Start({}) != nullptr) { - fprintf(stderr, "%s: %s\n", title, message); + fmt::print(stderr, "{}: {}\n", title, message); return; } From 411379f587d2052d4d1f3ecc7e9d5d927c7deeb6 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 21 May 2023 08:21:40 +0200 Subject: [PATCH 20/58] Codechange: replace puts with fmt::print --- src/debug.cpp | 3 +-- src/newgrf_profiling.cpp | 4 ++-- src/settingsgen/settingsgen.cpp | 4 ++-- src/strgen/strgen.cpp | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/debug.cpp b/src/debug.cpp index 7a411a1077..5038025cba 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -138,8 +138,7 @@ void DebugPrint(const char *level, const std::string &message) fflush(f); #endif } else { - std::string msg = fmt::format("{}dbg: [{}] {}\n", GetLogPrefix(), level, message); - fputs(msg.c_str(), stderr); + fmt::print(stderr, "{}dbg: [{}] {}\n", GetLogPrefix(), level, message); if (_debug_remote_console.load()) { /* Only add to the queue when there is at least one consumer of the data. */ diff --git a/src/newgrf_profiling.cpp b/src/newgrf_profiling.cpp index f3700d453b..6b7d656c54 100644 --- a/src/newgrf_profiling.cpp +++ b/src/newgrf_profiling.cpp @@ -109,9 +109,9 @@ uint32 NewGRFProfiler::Finish() uint32 total_microseconds = 0; - fputs("Tick,Sprite,Feature,Item,CallbackID,Microseconds,Depth,Result\n", f); + fmt::print(f, "Tick,Sprite,Feature,Item,CallbackID,Microseconds,Depth,Result\n"); for (const Call &c : this->calls) { - fputs(fmt::format("{},{},{:#X},{},{:#X},{},{},{}\n", c.tick, c.root_sprite, c.feat, c.item, (uint)c.cb, c.time, c.subs, c.result).c_str(), f); + fmt::print(f, "{},{},{:#X},{},{:#X},{},{},{}\n", c.tick, c.root_sprite, c.feat, c.item, (uint)c.cb, c.time, c.subs, c.result); total_microseconds += c.time; } diff --git a/src/settingsgen/settingsgen.cpp b/src/settingsgen/settingsgen.cpp index 273d414b0c..a074c87051 100644 --- a/src/settingsgen/settingsgen.cpp +++ b/src/settingsgen/settingsgen.cpp @@ -456,11 +456,11 @@ int CDECL main(int argc, char *argv[]) switch (i) { case 'v': - puts("$Revision$"); + fmt::print("$Revision$\n"); return 0; case 'h': - puts("settingsgen - $Revision$\n" + fmt::print("settingsgen - $Revision$\n" "Usage: settingsgen [options] ini-file...\n" "with options:\n" " -v, --version Print version information and exit\n" diff --git a/src/strgen/strgen.cpp b/src/strgen/strgen.cpp index 3f7b181e7a..b1325ee28c 100644 --- a/src/strgen/strgen.cpp +++ b/src/strgen/strgen.cpp @@ -423,7 +423,7 @@ int CDECL main(int argc, char *argv[]) switch (i) { case 'v': - puts("$Revision$"); + fmt::print("$Revision$\n"); return 0; case 'C': @@ -467,7 +467,7 @@ int CDECL main(int argc, char *argv[]) break; case 'h': - puts( + fmt::print( "strgen - $Revision$\n" " -v | --version print version information and exit\n" " -t | --todo replace any untranslated strings with ''\n" @@ -480,7 +480,7 @@ int CDECL main(int argc, char *argv[]) " -export-pragmas export all pragmas and exit\n" " Run without parameters and strgen will search for english.txt and parse it,\n" " creating strings.h. Passing an argument, strgen will translate that language\n" - " file using english.txt as a reference and output .lng." + " file using english.txt as a reference and output .lng.\n" ); return 0; From 2dbd974d4c6c7501f9dbaabd5495aabe81bb0a37 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 21 May 2023 08:32:13 +0200 Subject: [PATCH 21/58] Codechange: prevent using (f)printf/(f)puts over fmt::print --- src/safeguards.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/safeguards.h b/src/safeguards.h index d56b0015a0..e3ae5326ac 100644 --- a/src/safeguards.h +++ b/src/safeguards.h @@ -54,6 +54,13 @@ /* No clear replacement. */ #define strtok SAFEGUARD_DO_NOT_USE_THIS_METHOD +/* Use fmt::print instead. */ +#define printf SAFEGUARD_DO_NOT_USE_THIS_METHOD +#define fprintf SAFEGUARD_DO_NOT_USE_THIS_METHOD +#define puts SAFEGUARD_DO_NOT_USE_THIS_METHOD +#define fputs SAFEGUARD_DO_NOT_USE_THIS_METHOD +#define putchar SAFEGUARD_DO_NOT_USE_THIS_METHOD + /* Use our own templated implementation instead of a macro or function with only one type. */ #ifdef min #undef min From 2efd88d513e8077ef2cf831c822d207fb36869d8 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 21 May 2023 08:33:04 +0200 Subject: [PATCH 22/58] Cleanup: version of strgen/settingsgen was always $Revision$, so remove it --- src/settingsgen/settingsgen.cpp | 8 +------- src/strgen/strgen.cpp | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/settingsgen/settingsgen.cpp b/src/settingsgen/settingsgen.cpp index a074c87051..396b2a66fc 100644 --- a/src/settingsgen/settingsgen.cpp +++ b/src/settingsgen/settingsgen.cpp @@ -400,7 +400,6 @@ static bool CompareFiles(const char *n1, const char *n2) /** Options of settingsgen. */ static const OptionData _opts[] = { - GETOPT_NOVAL( 'v', "--version"), GETOPT_NOVAL( 'h', "--help"), GETOPT_GENERAL('h', '?', nullptr, ODF_NO_VALUE), GETOPT_VALUE( 'o', "--output"), @@ -455,15 +454,10 @@ int CDECL main(int argc, char *argv[]) if (i == -1) break; switch (i) { - case 'v': - fmt::print("$Revision$\n"); - return 0; - case 'h': - fmt::print("settingsgen - $Revision$\n" + fmt::print("settingsgen\n" "Usage: settingsgen [options] ini-file...\n" "with options:\n" - " -v, --version Print version information and exit\n" " -h, -?, --help Print this help message and exit\n" " -b FILE, --before FILE Copy FILE before all settings\n" " -a FILE, --after FILE Copy FILE after all settings\n" diff --git a/src/strgen/strgen.cpp b/src/strgen/strgen.cpp index b1325ee28c..1be7f6ea7b 100644 --- a/src/strgen/strgen.cpp +++ b/src/strgen/strgen.cpp @@ -397,7 +397,6 @@ static inline char *replace_pathsep(char *s) { return s; } /** Options of strgen. */ static const OptionData _opts[] = { - GETOPT_NOVAL( 'v', "--version"), GETOPT_GENERAL('C', '\0', "-export-commands", ODF_NO_VALUE), GETOPT_GENERAL('L', '\0', "-export-plurals", ODF_NO_VALUE), GETOPT_GENERAL('P', '\0', "-export-pragmas", ODF_NO_VALUE), @@ -422,10 +421,6 @@ int CDECL main(int argc, char *argv[]) if (i == -1) break; switch (i) { - case 'v': - fmt::print("$Revision$\n"); - return 0; - case 'C': fmt::print("args\tflags\tcommand\treplacement\n"); for (const CmdStruct *cs = _cmd_structs; cs < endof(_cmd_structs); cs++) { @@ -468,8 +463,7 @@ int CDECL main(int argc, char *argv[]) case 'h': fmt::print( - "strgen - $Revision$\n" - " -v | --version print version information and exit\n" + "strgen\n" " -t | --todo replace any untranslated strings with ''\n" " -w | --warning print a warning for any untranslated strings\n" " -h | -? | --help print this help message and exit\n" From 60540f7df614de63a68e73d248d8f4c290181cbc Mon Sep 17 00:00:00 2001 From: axet Date: Sun, 21 May 2023 20:53:21 +0300 Subject: [PATCH 23/58] Change: Allow dedicated server to use threaded saves. (#10787) Co-authored-by: Jonathan G Rennison --- src/network/network_server.cpp | 2 +- src/saveload/saveload.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 32b5343198..78d2abc0ed 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -112,7 +112,6 @@ struct PacketWriter : SaveFilter { * we need to handle the save finish as well as the * next connection might just be requesting a map. */ WaitTillSaved(); - ProcessAsyncSaveFinish(); } /** @@ -548,6 +547,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() } if (this->status == STATUS_AUTHORIZED) { + WaitTillSaved(); this->savegame = new PacketWriter(this); /* Now send the _frame_counter and how many packets are coming */ diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index ead719c94f..abe61ac247 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -3142,7 +3142,7 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, if (fop == SLO_SAVE) { // SAVE game Debug(desync, 1, "save: {:08x}; {:02x}; {}", TimerGameCalendar::date, TimerGameCalendar::date_fract, filename); - if (_network_server || !_settings_client.gui.threaded_saves) threaded = false; + if (!_settings_client.gui.threaded_saves) threaded = false; return DoSave(new FileWriter(fh), threaded); } From 9610705f46f15c11bcfa6b9de232ee4ca620fd92 Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 21 May 2023 18:40:42 +0000 Subject: [PATCH 24/58] Update: Translations from eints esperanto: 52 changes by legoscia --- src/lang/esperanto.txt | 53 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index ffd1ad46d6..5f8b7339ef 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -282,6 +282,7 @@ STR_UNITS_POWER_SI :{DECIMAL}{NBSP} STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_IMPERIAL :{DECIMAL}{NBSP}ĉp/t STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_SI :{DECIMAL}{NBSP}ĉp/Mg +STR_UNITS_POWER_METRIC_TO_WEIGHT_IMPERIAL :{DECIMAL}{NBSP}ĉp/t STR_UNITS_POWER_METRIC_TO_WEIGHT_METRIC :{DECIMAL}{NBSP}ĉp/t STR_UNITS_POWER_METRIC_TO_WEIGHT_SI :{DECIMAL}{NBSP}ĉp/Mg STR_UNITS_POWER_SI_TO_WEIGHT_METRIC :{DECIMAL}{NBSP}kW/t @@ -454,6 +455,7 @@ STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION :{BLACK}Landaspe STR_SCENEDIT_TOOLBAR_TOWN_GENERATION :{BLACK}Urba generado STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION :{BLACK}Industria generado STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION :{BLACK}Vojkonstruado +STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION :{BLACK}Konstruado de tramvojo STR_SCENEDIT_TOOLBAR_PLANT_TREES :{BLACK}Plantu arbojn. «Shift» aktivigita konstruado/montrante kosto takso STR_SCENEDIT_TOOLBAR_PLACE_SIGN :{BLACK}Metu signon STR_SCENEDIT_TOOLBAR_PLACE_OBJECT :{BLACK}Loku objekton. «Shift» aktivigita konstruado/montrante kosto takso @@ -1056,9 +1058,12 @@ STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Ŝaltu STR_GAME_OPTIONS_RESOLUTION :{BLACK}Ekrana montrogrando STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Elektu uzotan ekranan montrograndon STR_GAME_OPTIONS_RESOLUTION_OTHER :alia +STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} +STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Aparatara plirapidigo +STR_GAME_OPTIONS_VIDEO_DRIVER_INFO :{BLACK}Nuna pelilo: {STRING} @@ -1069,6 +1074,7 @@ STR_GAME_OPTIONS_GUI_SCALE_4X :4x STR_GAME_OPTIONS_GUI_SCALE_5X :5x STR_GAME_OPTIONS_PARTICIPATE_SURVEY_TOOLTIP :{BLACK}Kiam tiu ĉi agordo estas aktiva, OpenTTD sendos sondaĵon je forlaso de ludo +STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK :{BLACK}Pri sondado kaj privateco STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :{BLACK}Montru la sondaĵrezulton de la kuranta ludo STR_GAME_OPTIONS_GRAPHICS :{BLACK}Grafiko @@ -1181,7 +1187,7 @@ STR_TERRAIN_TYPE_ALPINIST :Alpeca ###length 4 STR_CITY_APPROVAL_TOLERANT :Tolerante -STR_CITY_APPROVAL_HOSTILE :Gastige +STR_CITY_APPROVAL_HOSTILE :Malamikema STR_CITY_APPROVAL_PERMISSIVE :Permeseme (ne efikas al la agoj de la kompanio) STR_WARNING_NO_SUITABLE_AI :{WHITE}Neniu taŭga AI estas disponebla...{}Kelkaj AI-oj estas elŝuteblaj per la sistemo 'Enreta Enhavo'. @@ -1249,6 +1255,7 @@ STR_CONFIG_SETTING_CONSTRUCTION_SPEED :Konstrurapido: STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS :Veturilpaneoj: {STRING} STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS_HELPTEXT :Regu kiom ofte povas panei maladekvate prizorgataj veturiloj +STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER :Subvencia multobligo: {STRING} STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER_HELPTEXT :Ŝanĝu kio oni pagas por subvenciataj rilatoj STR_CONFIG_SETTING_SUBSIDY_DURATION :Daŭro de subvencio: {STRING} @@ -1279,6 +1286,7 @@ STR_CONFIG_SETTING_CATCHMENT :Permesu pli rea STR_CONFIG_SETTING_EXTRADYNAMITE :Permesu forigon de pli da urbaj stratoj, pontoj ktp: {STRING} +STR_CONFIG_SETTING_TRAIN_LENGTH :Maksimuma longeco de trajnoj: {STRING} STR_CONFIG_SETTING_TRAIN_LENGTH_HELPTEXT :Agordu la maksimuman longecon de trajnoj STR_CONFIG_SETTING_TILE_LENGTH :{COMMA} kvadrato{P 0 "" j} @@ -1292,6 +1300,7 @@ STR_CONFIG_SETTING_TRAIN_SLOPE_STEEPNESS :Deklivkruteco p STR_CONFIG_SETTING_TRAIN_SLOPE_STEEPNESS_HELPTEXT :Kruteco de dekliva kahelo por trajno. Kun pli alta valoro estas pli malfacile supreniri altaĵon STR_CONFIG_SETTING_PERCENTAGE :{COMMA}% +STR_CONFIG_SETTING_ROAD_VEHICLE_SLOPE_STEEPNESS :Deklivkruteco por stratveturiloj: {STRING} STR_CONFIG_SETTING_ROAD_VEHICLE_SLOPE_STEEPNESS_HELPTEXT :Kruteco de dekliva kahelo por stratveturilo. Kun pli alta valoro estas pli malfacile supreniri altaĵon STR_CONFIG_SETTING_FORBID_90_DEG :Vagonaroj kaj ŝipoj ne ort-turniĝu: {STRING} @@ -1302,8 +1311,10 @@ STR_CONFIG_SETTING_INFLATION :Inflacio: {STRI STR_CONFIG_SETTING_MAX_BRIDGE_LENGTH :Maksimuma longeco de pontoj: {STRING} +STR_CONFIG_SETTING_MAX_BRIDGE_HEIGHT :Maksimuma alteco de pontoj: {STRING} STR_CONFIG_SETTING_MAX_BRIDGE_HEIGHT_HELPTEXT :Maksimuma alteco por konstrui pontojn +STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH :Maksimuma longeco de tuneloj: {STRING} STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH_HELPTEXT :Maksimuma longeco por konstrui tunelojn STR_CONFIG_SETTING_RAW_INDUSTRY_CONSTRUCTION_METHOD :Permana ĉefa industri-konstrumaniero: {STRING} @@ -1327,6 +1338,7 @@ STR_CONFIG_SETTING_SHOWFINANCES :Montru financan STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :Kiam tiu ĉi agordo estas aktiva, la financa fenesto aperas je la fino de ĉiu jaro por ebligi facilan inspektadon de la financa stato de la kompanio STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT :Novaj ordonoj estas 'sen-halte' per defaŭlto: {STRING} +STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT_HELPTEXT :Normale, veturilo haltos ĉe ĉiu stacio kiun ĝi trapasas. Post aktivigo de tiu ĉi agordo, ĝi trairos ĉiujn stacioj survoje al sia fina celo sen halti. Atentu ke tiu ĉi agordo nur difinas defaŭltan valoron por novaj ordonoj. Individuaj ordonoj povas esti eksplicite agorditaj al ajna el la du kondutoj sendepende STR_CONFIG_SETTING_STOP_LOCATION :Novaj trajnordonoj ĉesas defaŭlte ĉe la {STRING} of the platform ###length 3 @@ -1368,6 +1380,7 @@ STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD :Permesu trairaj STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Permesu konstrui trairajn strathaltejojn sur stratoj posedataj de konkurantoj STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Ne eblas ŝanĝi ĉi tiujn agordon dum veturiloj ĉeestas +STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :Infrastruktura bontenado: {STRING} STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :Kiam tiu ĉi agordo estas aktiva, infrastrukturo kaŭzas bontenadajn kostojn. La kosto kreskas super-proporcie laŭ la grandeco de la transportreto, kaj tial pli efikas al grandaj kompanioj ol al malgrandaj STR_CONFIG_SETTING_COMPANY_STARTING_COLOUR_HELPTEXT :Elektu komencan koloron por la kompanio @@ -1464,6 +1477,7 @@ STR_CONFIG_SETTING_SE_FLAT_WORLD_HEIGHT :Kiom alta fari STR_CONFIG_SETTING_EDGES_NOT_EMPTY :{WHITE}Almenaŭ unu kahelo ĉe la norda rando ne estas malplena STR_CONFIG_SETTING_EDGES_NOT_WATER :{WHITE}Almenaŭ unu kahelo ĉe unu de la eĝoj ne estas akvo +STR_CONFIG_SETTING_STATION_SPREAD :Maksimuma stacia etendo: {STRING} STR_CONFIG_SETTING_STATION_SPREAD_HELPTEXT :Maksimuma area sur kiu povas etendiĝi unu stacio. Atentu ke altaj valoroj malrapidigos la ludon STR_CONFIG_SETTING_SERVICEATHELIPAD :Aŭtomate prizorgu helikopterojn sur helikopterejoj: {STRING} @@ -1628,8 +1642,12 @@ STR_CONFIG_SETTING_SERVINT_ISPERCENT :Prizorginterval STR_CONFIG_SETTING_SERVINT_ISPERCENT_HELPTEXT :Kiam tiu ĉi agordo estas aktiva, veturiloj klopodas iri al priservado kiam ilia fidindeco estas je certa procentaĵo malpli alta ol la maksimuma fidindeco.{}{}Ekzemple, se la maksimuma fidindeco de veturilo estas 90% kaj la priservintervalo estas 20%, la veturilo klopodos iri al priservado kiam ĝi atingas fidindecon de 72%. STR_CONFIG_SETTING_SERVINT_TRAINS :Defaŭlta priservintervalo for trajnoj: {STRING} +STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES :Defaŭlta priservintervalo por stratveturiloj: {STRING} +STR_CONFIG_SETTING_SERVINT_AIRCRAFT :Defaŭlta priservintervalo por aviadiloj: {STRING} STR_CONFIG_SETTING_SERVINT_SHIPS :Defaŭlta priservintervalo por ŝipoj: {STRING} +STR_CONFIG_SETTING_SERVINT_VALUE :{COMMA}{NBSP}tago{P 0 "" j}/% ###setting-zero-is-special +STR_CONFIG_SETTING_SERVINT_DISABLED :Malaktiva STR_CONFIG_SETTING_NOSERVICE :Malŝaltu prizorgadon se rompiĝoj ne okazas: {STRING} @@ -1751,10 +1769,12 @@ STR_CONFIG_SETTING_SOFT_LIMIT :Fenestra mollim STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} ###setting-zero-is-special +STR_CONFIG_SETTING_ZOOM_MIN :Maksimuma zom-nivelo: {STRING} STR_CONFIG_SETTING_ZOOM_MAX :Maksimuma elzom-nivelo: {STRING} STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT :La maksumuma elzom-nivelo por vidujoj. Pli altaj elzom-niveloj povas kaŭzi malrapidiĝon ###length 6 STR_CONFIG_SETTING_ZOOM_LVL_MIN :4x +STR_CONFIG_SETTING_ZOOM_LVL_IN_2X :2x STR_CONFIG_SETTING_ZOOM_LVL_NORMAL :Normala STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X :2x STR_CONFIG_SETTING_ZOOM_LVL_OUT_4X :4x @@ -1788,6 +1808,8 @@ STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :simetria ###length 5 STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :Angla-usona sistemo (mejloj hore) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC :Metra (km/h) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI :SI-unuoj (m/s) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :Ludunuoj (kaheloj tage) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_HELPTEXT :Kiam ajn montriĝas povumo de veturilo en la uzantinterfaco, montru ĝin en la indikitaj unuoj @@ -1805,18 +1827,21 @@ STR_CONFIG_SETTING_LOCALISATION_UNITS_VOLUME_HELPTEXT :Kiam ajn montri STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE_HELPTEXT :Kiam ajn montriĝas trakcia forto en la uzantinterfaco, montru ĝin en la indikitaj unuoj ###length 3 +STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE_IMPERIAL :Angla-usona sistemo (funto-forto, lbf) STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT :Altecunuoj: {STRING} ###length 3 STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_IMPERIAL :Angla-usona sistemo (futoj) STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_METRIC :Metra (m) +STR_CONFIG_SETTING_GRAPHICS :{ORANGE}Grafiko STR_CONFIG_SETTING_SOUND :{ORANGE}Sonefektoj STR_CONFIG_SETTING_INTERFACE :{ORANGE}Interfaco STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION :{ORANGE}Konstruado STR_CONFIG_SETTING_COMPANY :{ORANGE}Kompanio STR_CONFIG_SETTING_ACCOUNTING :{ORANGE}Librotenado STR_CONFIG_SETTING_VEHICLES :{ORANGE}Veturiloj +STR_CONFIG_SETTING_VEHICLES_PHYSICS :{ORANGE}Fiziko STR_CONFIG_SETTING_VEHICLES_ROUTING :{ORANGE}Navigado STR_CONFIG_SETTING_ACCIDENTS :{ORANGE}Katastrofoj / akcidentoj STR_CONFIG_SETTING_GENWORLD :{ORANGE}Mondgenerado @@ -1849,6 +1874,7 @@ STR_CONFIG_ERROR_INVALID_BASE_SOUNDS_NOT_FOUND :{WHITE}... igno # Video initalization errors STR_VIDEO_DRIVER_ERROR :{WHITE}Eraro pri videaj agordoj... +STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION :{WHITE}... ne troveblis kongrua grafika procesoro. Malŝaltis aparataran plirapidigon # Intro window STR_INTRO_CAPTION :{WHITE}OpenTTD {REV} @@ -1885,6 +1911,7 @@ STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Montru a STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Montru agordojn STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Montri NewGRF-agordojn STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Serĉi pri nova kaj ĝisdatigita enhavo por elŝuti +STR_INTRO_TOOLTIP_GAMESCRIPT_SETTINGS :{BLACK}Montru ludoskriptajn agordojn STR_INTRO_TOOLTIP_QUIT :{BLACK}Fermu 'OpenTTD' STR_INTRO_TRANSLATION :{BLACK}Ĉi tiu traduko ne havas {NUM} teksto{P "" j}. Bonvolu helpu fari OpenTTD pli bona per estigi traduktiston. Vidi readme.txt por detaloj. @@ -2116,6 +2143,7 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nomo STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE :{BLACK}Invitkodo STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Konektotipo STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Ĉu kaj kiel vian servilon povas atingi aliuloj +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Via ludantnomo STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Sendu mesaĝon al ĉiuj ludantoj de tiu ĉi kompanio STR_NETWORK_CLIENT_LIST_SPECTATORS :Spektantoj STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Kreu novan kompanion kaj aliĝu al ĝi @@ -2132,6 +2160,7 @@ STR_NETWORK_ASK_RELAY_YES_ONCE :{BLACK}Jes, ĉi STR_NETWORK_ASK_SURVEY_CAPTION :Partopreni aŭtomatan sondadon? STR_NETWORK_ASK_SURVEY_LINK :Pri sondado kaj privateco +STR_NETWORK_ASK_SURVEY_NO :Ne STR_NETWORK_ASK_SURVEY_YES :Jes STR_NETWORK_SPECTATORS :Spektantoj @@ -2182,6 +2211,7 @@ STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}Kialo: { STR_NETWORK_ERROR_CHEATER :{WHITE}Vi ne rajtas filudi en ĉi tiu servilo STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}Vi estis sendanta tro da komandojn al la servilo +STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Ebla konektoperdo ###length 21 STR_NETWORK_ERROR_CLIENT_GENERAL :ĝenerala eraro @@ -2229,6 +2259,7 @@ STR_NETWORK_MESSAGE_GIVE_MONEY :*** {0:STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}La servilo fermis la seancon STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}La servilo restartiĝas...{}Bonvolu atendi... +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Servila registrado malsukcesis STR_NETWORK_ERROR_COORDINATOR_REUSE_OF_INVITE_CODE :{WHITE}Alia servilo kun la sama invitokodo registris sin. Ŝaltas al "loka" ludotipo. # Content downloading window @@ -2337,6 +2368,7 @@ STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP :{BLACK}Ne marku STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP :{BLACK}Marku ŝarĝregionon ĉe proponata loko STR_STATION_BUILD_ACCEPTS_CARGO :{BLACK}Akceptiĝas: {GOLD}{CARGO_LIST} STR_STATION_BUILD_SUPPLIES_CARGO :{BLACK}Provizoj: {GOLD}{CARGO_LIST} +STR_STATION_BUILD_INFRASTRUCTURE_COST :{BLACK}Bontenada kosto: {GOLD}{CURRENCY_SHORT} jare # Join station window STR_JOIN_STATION_CAPTION :{WHITE}Ligi stacion @@ -2557,6 +2589,7 @@ STR_FOUND_TOWN_RANDOM_TOWN_BUTTON :{BLACK}Hazarda STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP :{BLACK}Fondi urbon en hazarda loko STR_FOUND_TOWN_MANY_RANDOM_TOWNS :{BLACK}Multaj hazardaj urboj STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP :{BLACK}Kovru la mapon per hazarde metitajn urbojn +STR_FOUND_TOWN_EXPAND_ALL_TOWNS_TOOLTIP :{BLACK}Ĉiuj urboj kreskas iomete STR_FOUND_TOWN_NAME_TITLE :{YELLOW}Urbnomo: STR_FOUND_TOWN_NAME_EDITOR_TITLE :{BLACK}Enigu urbnomon @@ -3001,6 +3034,7 @@ STR_NEWGRF_LIST_MISSING :{RED}Mankaj dos # NewGRF 'it's broken' warnings STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}Ĝi ŝanĝis la veturilan longecon de '{1:ENGINE}' dum ĝi ne estis en garaĝo +STR_BROKEN_VEHICLE_LENGTH :{WHITE}La trajno '{VEHICLE}' aparenanta al '{COMPANY}' havas malvalidan longecon. Tion verŝajne kaŭzis problemoj pri NewGRF-oj. La ludo eventuale malsinkroniĝos aŭ paneos STR_NEWGRF_BUGGY :{WHITE}NewGRF '{0:STRING}' donas malĝustan informon STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT :{WHITE}'Revoko' {1:HEX} revenis nekonatan/malvalidan rezulton {2:HEX} @@ -3110,6 +3144,7 @@ STR_GOAL_QUESTION_CAPTION_ERROR :{YELLOW}Eraro # Goal Question button list ###length 18 STR_GOAL_QUESTION_BUTTON_CANCEL :Nuligi +STR_GOAL_QUESTION_BUTTON_YES :Jes STR_GOAL_QUESTION_BUTTON_PREVIOUS :Antaŭa STR_GOAL_QUESTION_BUTTON_NEXT :Sekva STR_GOAL_QUESTION_BUTTON_CONTINUE :Daŭrigu @@ -3156,9 +3191,15 @@ STR_STATION_VIEW_SUPPLY_RATINGS_TITLE :{BLACK}Ĉiumona STR_STATION_VIEW_CARGO_SUPPLY_RATING :{WHITE}{STRING}: {YELLOW}{COMMA} / {STRING} ({COMMA}%) STR_STATION_VIEW_GROUP :{BLACK}Grupigu laŭ +STR_STATION_VIEW_PLANNED_STATION :Stacio: Planata STR_STATION_VIEW_FROM :{YELLOW}{CARGO_SHORT} de {STATION} +STR_STATION_VIEW_VIA :{YELLOW}{CARGO_SHORT} tra {STATION} STR_STATION_VIEW_TO :{YELLOW}{CARGO_SHORT} al {STATION} STR_STATION_VIEW_FROM_ANY :{RED}{CARGO_SHORT} el nekonata stacio +STR_STATION_VIEW_TO_ANY :{RED}{CARGO_SHORT} al ajna stacio +STR_STATION_VIEW_VIA_ANY :{RED}{CARGO_SHORT} tra ajna stacio +STR_STATION_VIEW_FROM_HERE :{GREEN}{CARGO_SHORT} de tiu ĉi stacio +STR_STATION_VIEW_TO_HERE :{GREEN}{CARGO_SHORT} al tiu ĉi stacio STR_STATION_VIEW_GROUP_S_V_D :Ekirpunkto-Tra-Celpunkto STR_STATION_VIEW_GROUP_S_D_V :Ekirpunkto-Celpunkto-Tra @@ -3252,11 +3293,15 @@ STR_COMPANY_VIEW_AIRCRAFT :{WHITE}{COMMA} STR_COMPANY_VIEW_SHIPS :{WHITE}{COMMA} ŝipo{P "" j} STR_COMPANY_VIEW_VEHICLES_NONE :{WHITE}Neniu STR_COMPANY_VIEW_COMPANY_VALUE :{GOLD}Kompania valoro: {WHITE}{CURRENCY_LONG} +STR_COMPANY_VIEW_INFRASTRUCTURE :{GOLD}Infrastrukturo: +STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL :{WHITE}{COMMA} fervojpeco{P "" j} STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD :{WHITE}{COMMA} vojpeco{P "" j} +STR_COMPANY_VIEW_INFRASTRUCTURE_WATER :{WHITE}{COMMA} akvokahelo{P "" j} STR_COMPANY_VIEW_INFRASTRUCTURE_STATION :{WHITE}{COMMA} stacia{P "" j} kahelo{P "" j} STR_COMPANY_VIEW_INFRASTRUCTURE_STATION.n :{WHITE}{COMMA} stacia{P "" j}n kahelo{P "" j}n STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT :{WHITE}{COMMA} flughaveno{P "" j} STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT.n :{WHITE}{COMMA} flughaveno{P "" j}n +STR_COMPANY_VIEW_INFRASTRUCTURE_NONE :{WHITE}Nenia STR_COMPANY_VIEW_BUILD_HQ_BUTTON :{BLACK}Faru HQ STR_COMPANY_VIEW_BUILD_HQ_TOOLTIP :{BLACK}Konstrui kompanian administraciejon @@ -3264,6 +3309,7 @@ STR_COMPANY_VIEW_VIEW_HQ_BUTTON :{BLACK}Vidu HQ STR_COMPANY_VIEW_VIEW_HQ_TOOLTIP :{BLACK}Rigardi kompanian administraciejon STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}Relokigu HQ STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Rekonstruu kompanian ĉefkonstruaĵon aliloke por 1% de kompanivaloro. Tenu maljuskliga klavo + Klak por (kost)taksi sen rekonstrui ĉefkonstruaĵon +STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON :{BLACK}Detaloj STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP :{BLACK}Vidu detalajn nombrojn de infrastruktureroj STR_COMPANY_VIEW_GIVE_MONEY_BUTTON :{BLACK}Donu monon STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP :{BLACK}Donu monon al tiu ĉi kompanio @@ -3302,6 +3348,7 @@ STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}Industri STR_INDUSTRY_DIRECTORY_NONE :{ORANGE}- Neniu - STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_LONG}{STRING}{YELLOW} ({COMMA}% transportiĝis){BLACK} STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} +STR_INDUSTRY_DIRECTORY_ITEM_PROD2 :{ORANGE}{INDUSTRY} {STRING}, {STRING} STR_INDUSTRY_DIRECTORY_ITEM_PROD3 :{ORANGE}{INDUSTRY} {STRING}, {STRING}, {STRING} STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}Industrionomoj - klaku nomon por centri vidon ĉe la industrio. Ctrl+Klak por malfermi novan vidujon ĉe la loko STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER :{BLACK}Ŝarĝo akceptata: {SILVER}{STRING} @@ -3375,6 +3422,7 @@ STR_GROUP_DEFAULT_ROAD_VEHICLES :Sengrupaj strat STR_GROUP_DEFAULT_SHIPS :Sengrupaj ŝipoj STR_GROUP_DEFAULT_AIRCRAFTS :Sengrupaj aviadiloj +STR_GROUP_COUNT_WITH_SUBGROUP :{TINY_FONT}{COMMA} (+{COMMA}) STR_GROUPS_CLICK_ON_GROUP_FOR_TOOLTIP :{BLACK}Grupoj - klaku grupon por listigi ĉiujn veturilojn de tiu ĉi grupo. Trenu/faligu grupojn por ordigi ĝin STR_GROUP_CREATE_TOOLTIP :{BLACK}Klaku por krei grupon @@ -3494,6 +3542,7 @@ STR_BUY_VEHICLE_AIRCRAFT_SHOW_TOGGLE_BUTTON :{BLACK}Montru ###length VEHICLE_TYPES STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP :{BLACK}Ŝaltu kaŝadon/montradon de trajnveturila tipo STR_BUY_VEHICLE_ROAD_VEHICLE_HIDE_SHOW_TOGGLE_TOOLTIP :{BLACK}Ŝaltu kaŝadon/montradon de stratveturila tipo +STR_BUY_VEHICLE_SHIP_HIDE_SHOW_TOGGLE_TOOLTIP :{BLACK}Ŝaltu kaŝadon/montradon de ŝipa tipo ###length VEHICLE_TYPES STR_QUERY_RENAME_TRAIN_TYPE_CAPTION :{WHITE}Alinomi trajnan veturiltipon @@ -3665,6 +3714,7 @@ STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE ###length VEHICLE_TYPES STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP :{BLACK}Centrigu ĉefvidon ĉe la pozicio de la trajno. Duobla klako igas la ĉefvidon sekvi la trajnon. Stir+Klak malfermas novan vidujon ĉe la pozicio de la trajno +STR_VEHICLE_VIEW_ROAD_VEHICLE_CENTER_TOOLTIP :{BLACK}Centrigu ĉefvidon ĉe la pozicio de la veturilo. Duobla klako igas la ĉefvidon sekvi la veturilon. Stir+Klak malfermas novan vidujon ĉe la pozicio de la veturilo STR_VEHICLE_VIEW_AIRCRAFT_CENTER_TOOLTIP :{BLACK}Centrigu ĉefvidon ĉe la pozicio de la aviadilo. Duobla klako igas la ĉefvidon sekvi la aviadilon. Stir+Klak malfermas novan vidujon ĉe la pozicio de la aviadilo ###length VEHICLE_TYPES @@ -3703,6 +3753,7 @@ STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Montru STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Montru aviadilajn detalojn ###length VEHICLE_TYPES +STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}Nuna ago de la trajno - klaku por haltigi/ekigi la trajnon STR_VEHICLE_VIEW_ROAD_VEHICLE_STATUS_START_STOP_TOOLTIP :{BLACK}Nuna ago de la veturilo - klaku por haltigi/ekigi la veturilon STR_VEHICLE_VIEW_SHIP_STATE_STATUS_STOP_TOOLTIP :{BLACK}Nuna ago de la ŝipo - klaku por haltigi/ekigi la ŝipon STR_VEHICLE_VIEW_AIRCRAFT_STATUS_START_STOP_TOOLTIP :{BLACK}Nuna ago de la aviadilo - klaku por haltigi/ekigi la aviadilon From 00695c29de5262f5793df3c32d26fcd2e83d915b Mon Sep 17 00:00:00 2001 From: Rubidium Date: Fri, 19 May 2023 14:22:50 +0200 Subject: [PATCH 25/58] Codechange: simplify news string drawing by using StrMakeValid to replaces newlines with spaces --- src/news_gui.cpp | 32 ++++---------------------------- src/statusbar_gui.cpp | 31 +++++-------------------------- 2 files changed, 9 insertions(+), 54 deletions(-) diff --git a/src/news_gui.cpp b/src/news_gui.cpp index 0a920ecd95..6527528741 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -1099,37 +1099,13 @@ void ShowLastNewsMessage() */ static void DrawNewsString(uint left, uint right, int y, TextColour colour, const NewsItem *ni) { - char buffer[512], buffer2[512]; - StringID str; - CopyInDParam(0, ni->params, lengthof(ni->params)); - str = ni->string_id; - - GetString(buffer, str, lastof(buffer)); - /* Copy the just gotten string to another buffer to remove any formatting - * from it such as big fonts, etc. */ - const char *ptr = buffer; - char *dest = buffer2; - WChar c_last = '\0'; - for (;;) { - WChar c = Utf8Consume(&ptr); - if (c == 0) break; - /* Make a space from a newline, but ignore multiple newlines */ - if (c == '\n' && c_last != '\n') { - dest[0] = ' '; - dest++; - } else if (c == '\r') { - dest[0] = dest[1] = dest[2] = dest[3] = ' '; - dest += 4; - } else if (IsPrintable(c)) { - dest += Utf8Encode(dest, c); - } - c_last = c; - } - *dest = '\0'; + /* Get the string, replaces newlines with spaces and remove control codes from the string. */ + std::string message = StrMakeValid(GetString(ni->string_id), SVS_REPLACE_TAB_CR_NL_WITH_SPACE); + /* Truncate and show string; postfixed by '...' if necessary */ - DrawString(left, right, y, buffer2, colour); + DrawString(left, right, y, message, colour); } struct MessageHistoryWindow : Window { diff --git a/src/statusbar_gui.cpp b/src/statusbar_gui.cpp index 303fb0050c..3bb731a706 100644 --- a/src/statusbar_gui.cpp +++ b/src/statusbar_gui.cpp @@ -40,39 +40,18 @@ static bool DrawScrollingStatusText(const NewsItem *ni, int scroll_pos, int left, int right, int top, int bottom) { CopyInDParam(0, ni->params, lengthof(ni->params)); - StringID str = ni->string_id; - - char buf[512]; - GetString(buf, str, lastof(buf)); - const char *s = buf; - - char buffer[256]; - char *d = buffer; - const char *last = lastof(buffer); - - for (;;) { - WChar c = Utf8Consume(&s); - if (c == 0) { - break; - } else if (c == '\n') { - if (d + 4 >= last) break; - d[0] = d[1] = d[2] = d[3] = ' '; - d += 4; - } else if (IsPrintable(c)) { - if (d + Utf8CharLen(c) >= last) break; - d += Utf8Encode(d, c); - } - } - *d = '\0'; + + /* Replace newlines and the likes with spaces. */ + std::string message = StrMakeValid(GetString(ni->string_id), SVS_REPLACE_TAB_CR_NL_WITH_SPACE); DrawPixelInfo tmp_dpi; if (!FillDrawPixelInfo(&tmp_dpi, left, top, right - left, bottom)) return true; - int width = GetStringBoundingBox(buffer).width; + int width = GetStringBoundingBox(message).width; int pos = (_current_text_dir == TD_RTL) ? (scroll_pos - width) : (right - scroll_pos - left); AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); - DrawString(pos, INT16_MAX, 0, buffer, TC_LIGHT_BLUE, SA_LEFT | SA_FORCE); + DrawString(pos, INT16_MAX, 0, message, TC_LIGHT_BLUE, SA_LEFT | SA_FORCE); return (_current_text_dir == TD_RTL) ? (pos < right - left) : (pos + width > 0); } From 1a179cb297a08dfb020e0994c8a64ae531119077 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Fri, 19 May 2023 14:35:10 +0200 Subject: [PATCH 26/58] Codechange: use GetString + StrMakeValid to pass string without colours/font sizes to Debug --- src/newgrf_commons.cpp | 9 +++------ src/vehicle.cpp | 8 ++------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp index 2da03dc5b5..2b25487981 100644 --- a/src/newgrf_commons.cpp +++ b/src/newgrf_commons.cpp @@ -27,6 +27,7 @@ #include "company_base.h" #include "error.h" #include "strings_func.h" +#include "string_func.h" #include "table/strings.h" @@ -508,16 +509,12 @@ void ErrorUnknownCallbackResult(uint32 grfid, uint16 cbid, uint16 cb_res) } /* debug output */ - char buffer[512]; - SetDParamStr(0, grfconfig->GetName()); - GetString(buffer, STR_NEWGRF_BUGGY, lastof(buffer)); - Debug(grf, 0, "{}", buffer + 3); + Debug(grf, 0, "{}", StrMakeValid(GetString(STR_NEWGRF_BUGGY))); SetDParam(1, cbid); SetDParam(2, cb_res); - GetString(buffer, STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT, lastof(buffer)); - Debug(grf, 0, "{}", buffer + 3); + Debug(grf, 0, "{}", StrMakeValid(GetString(STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT))); } /** diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 89880910ff..fb2f663569 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -315,15 +315,11 @@ void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRF } /* debug output */ - char buffer[512]; - SetDParamStr(0, grfconfig->GetName()); - GetString(buffer, part1, lastof(buffer)); - Debug(grf, 0, "{}", buffer + 3); + Debug(grf, 0, "{}", StrMakeValid(GetString(part1))); SetDParam(1, engine); - GetString(buffer, part2, lastof(buffer)); - Debug(grf, 0, "{}", buffer + 3); + Debug(grf, 0, "{}", StrMakeValid(GetString(part2))); } /** From 63d9bb93b8ea9063798c546a9fc8b9f8d0d356e8 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Fri, 19 May 2023 14:35:53 +0200 Subject: [PATCH 27/58] Codechange: migrate from C-style GetString to C++-style GetString --- src/company_cmd.cpp | 20 +++++++------------- src/console_cmds.cpp | 6 ++---- src/gfx.cpp | 22 +++++----------------- src/industry_gui.cpp | 32 ++++++++++---------------------- src/script/api/script_text.cpp | 4 +--- src/strings.cpp | 4 ++-- src/viewport.cpp | 10 ++++------ 7 files changed, 31 insertions(+), 67 deletions(-) diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index b1bf880859..79bd6153e4 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -356,10 +356,6 @@ CommandCost CheckTileOwnership(TileIndex tile) */ static void GenerateCompanyName(Company *c) { - /* Reserve space for extra unicode character. We need to do this to be able - * to detect too long company name. */ - char buffer[(MAX_LENGTH_COMPANY_NAME_CHARS + 1) * MAX_CHAR_LENGTH]; - if (c->name_1 != STR_SV_UNNAMED) return; if (c->last_build_coordinate == 0) return; @@ -367,6 +363,7 @@ static void GenerateCompanyName(Company *c) StringID str; uint32 strp; + std::string name; if (t->name.empty() && IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1)) { str = t->townnametype - SPECSTR_TOWNNAME_START + SPECSTR_COMPANY_NAME_START; strp = t->townnameparts; @@ -377,8 +374,8 @@ verify_name:; if (cc->name_1 == str && cc->name_2 == strp) goto bad_town_name; } - GetString(buffer, str, lastof(buffer)); - if (Utf8StringLength(buffer) >= MAX_LENGTH_COMPANY_NAME_CHARS) goto bad_town_name; + name = GetString(str); + if (Utf8StringLength(name) >= MAX_LENGTH_COMPANY_NAME_CHARS) goto bad_town_name; set_name:; c->name_1 = str; @@ -499,18 +496,15 @@ restart:; /* Reserve space for extra unicode character. We need to do this to be able * to detect too long president name. */ - char buffer[(MAX_LENGTH_PRESIDENT_NAME_CHARS + 1) * MAX_CHAR_LENGTH]; SetDParam(0, c->index); - GetString(buffer, STR_PRESIDENT_NAME, lastof(buffer)); - if (Utf8StringLength(buffer) >= MAX_LENGTH_PRESIDENT_NAME_CHARS) continue; + std::string name = GetString(STR_PRESIDENT_NAME); + if (Utf8StringLength(name) >= MAX_LENGTH_PRESIDENT_NAME_CHARS) continue; for (const Company *cc : Company::Iterate()) { if (c != cc) { - /* Reserve extra space so even overlength president names can be compared. */ - char buffer2[(MAX_LENGTH_PRESIDENT_NAME_CHARS + 1) * MAX_CHAR_LENGTH]; SetDParam(0, cc->index); - GetString(buffer2, STR_PRESIDENT_NAME, lastof(buffer2)); - if (strcmp(buffer2, buffer) == 0) goto restart; + std::string other_name = GetString(STR_PRESIDENT_NAME); + if (name == other_name) goto restart; } } return; diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 0980ffcfce..06777da6cc 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1717,9 +1717,8 @@ DEF_CONSOLE_CMD(ConCompanies) for (const Company *c : Company::Iterate()) { /* Grab the company name */ - char company_name[512]; SetDParam(0, c->index); - GetString(company_name, STR_COMPANY_NAME, lastof(company_name)); + std::string company_name = GetString(STR_COMPANY_NAME); const char *password_state = ""; if (c->is_ai) { @@ -1728,8 +1727,7 @@ DEF_CONSOLE_CMD(ConCompanies) password_state = _network_company_states[c->index].password.empty() ? "unprotected" : "protected"; } - char colour[512]; - GetString(colour, STR_COLOUR_DARK_BLUE + _company_colours[c->index], lastof(colour)); + std::string colour = GetString(STR_COLOUR_DARK_BLUE + _company_colours[c->index]); IConsolePrint(CC_INFO, "#:{}({}) Company Name: '{}' Year Founded: {} Money: {} Loan: {} Value: {} (T:{}, R:{}, P:{}, S:{}) {}", c->index + 1, colour, company_name, c->inaugurated_year, (int64)c->money, (int64)c->current_loan, (int64)CalculateCompanyValue(c), diff --git a/src/gfx.cpp b/src/gfx.cpp index d2f826366e..4d268e3dbe 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -682,9 +682,7 @@ int DrawString(int left, int right, int top, std::string_view str, TextColour co */ int DrawString(int left, int right, int top, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize) { - char buffer[DRAW_STRING_BUFFER]; - GetString(buffer, str, lastof(buffer)); - return DrawString(left, right, top, buffer, colour, align, underline, fontsize); + return DrawString(left, right, top, GetString(str), colour, align, underline, fontsize); } /** @@ -707,9 +705,7 @@ int GetStringHeight(std::string_view str, int maxw, FontSize fontsize) */ int GetStringHeight(StringID str, int maxw) { - char buffer[DRAW_STRING_BUFFER]; - GetString(buffer, str, lastof(buffer)); - return GetStringHeight(buffer, maxw); + return GetStringHeight(GetString(str), maxw); } /** @@ -720,10 +716,7 @@ int GetStringHeight(StringID str, int maxw) */ int GetStringLineCount(StringID str, int maxw) { - char buffer[DRAW_STRING_BUFFER]; - GetString(buffer, str, lastof(buffer)); - - Layouter layout(buffer, maxw); + Layouter layout(GetString(str), maxw); return (uint)layout.size(); } @@ -831,9 +824,7 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_vi */ int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize) { - char buffer[DRAW_STRING_BUFFER]; - GetString(buffer, str, lastof(buffer)); - return DrawStringMultiLine(left, right, top, bottom, buffer, colour, align, underline, fontsize); + return DrawStringMultiLine(left, right, top, bottom, GetString(str), colour, align, underline, fontsize); } /** @@ -860,10 +851,7 @@ Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize) */ Dimension GetStringBoundingBox(StringID strid, FontSize start_fontsize) { - char buffer[DRAW_STRING_BUFFER]; - - GetString(buffer, strid, lastof(buffer)); - return GetStringBoundingBox(buffer, start_fontsize); + return GetStringBoundingBox(GetString(strid), start_fontsize); } /** diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 073ea88b14..52110634ab 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -72,7 +72,7 @@ enum CargoSuffixDisplay { /** Transfer storage of cargo suffix information. */ struct CargoSuffix { CargoSuffixDisplay display; ///< How to display the cargo and text. - char text[512]; ///< Cargo suffix text. + std::string text; ///< Cargo suffix text. }; extern void GenerateIndustries(); @@ -89,7 +89,7 @@ static void ShowIndustryCargoesWindow(IndustryType id); */ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix) { - suffix.text[0] = '\0'; + suffix.text.clear(); suffix.display = CSD_CARGO_AMOUNT; if (HasBit(indspec->callback_mask, CBM_IND_CARGO_SUFFIX)) { @@ -101,7 +101,7 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, if (GB(callback, 0, 8) == 0xFF) return; if (callback < 0x400) { StartTextRefStackUsage(indspec->grf_prop.grffile, 6); - GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text)); + suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback)); StopTextRefStackUsage(); suffix.display = CSD_CARGO_AMOUNT_TEXT; return; @@ -117,14 +117,14 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, } if (callback < 0x400) { StartTextRefStackUsage(indspec->grf_prop.grffile, 6); - GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text)); + suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback)); StopTextRefStackUsage(); suffix.display = CSD_CARGO_AMOUNT_TEXT; return; } if (callback >= 0x800 && callback < 0xC00) { StartTextRefStackUsage(indspec->grf_prop.grffile, 6); - GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 - 0x800 + callback), lastof(suffix.text)); + suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 - 0x800 + callback)); StopTextRefStackUsage(); suffix.display = CSD_CARGO_TEXT; return; @@ -194,15 +194,7 @@ std::array _sorted_industry_types; ///< Industr /** Sort industry types by their name. */ static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b) { - static char industry_name[2][64]; - - const IndustrySpec *indsp1 = GetIndustrySpec(a); - GetString(industry_name[0], indsp1->name, lastof(industry_name[0])); - - const IndustrySpec *indsp2 = GetIndustrySpec(b); - GetString(industry_name[1], indsp2->name, lastof(industry_name[1])); - - int r = StrNaturalCompare(industry_name[0], industry_name[1]); // Sort by name (natural sorting). + int r = StrNaturalCompare(GetString(GetIndustrySpec(a)->name), GetString(GetIndustrySpec(b)->name)); // Sort by name (natural sorting). /* If the names are equal, sort by industry type. */ return (r != 0) ? r < 0 : (a < b); @@ -350,7 +342,6 @@ class BuildIndustryWindow : public Window { std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, int cargolistlen, StringID prefixstr) const { std::string cargostring; - char buf[1024]; int numcargo = 0; int firstcargo = -1; @@ -363,20 +354,17 @@ class BuildIndustryWindow : public Window { } SetDParam(0, CargoSpec::Get(cargolist[j])->name); SetDParamStr(1, cargo_suffix[j].text); - GetString(buf, STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION, lastof(buf)); - cargostring += buf; + cargostring += GetString(STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION); } if (numcargo > 0) { SetDParam(0, CargoSpec::Get(cargolist[firstcargo])->name); SetDParamStr(1, cargo_suffix[firstcargo].text); - GetString(buf, prefixstr, lastof(buf)); - cargostring = std::string(buf) + cargostring; + cargostring = GetString(prefixstr) + cargostring; } else { SetDParam(0, STR_JUST_NOTHING); SetDParamStr(1, ""); - GetString(buf, prefixstr, lastof(buf)); - cargostring = std::string(buf); + cargostring = GetString(prefixstr); } return cargostring; @@ -1547,7 +1535,7 @@ protected: for (byte j = 0; j < lengthof(i->produced_cargo); j++) { if (i->produced_cargo[j] == CT_INVALID) continue; - cargos.push_back({ i->produced_cargo[j], i->last_month_production[j], cargo_suffix[j].text, ToPercent8(i->last_month_pct_transported[j]) }); + cargos.push_back({ i->produced_cargo[j], i->last_month_production[j], cargo_suffix[j].text.c_str(), ToPercent8(i->last_month_pct_transported[j]) }); } switch (static_cast(this->industries.SortType())) { diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp index b6f46e9f59..3d2a95650c 100644 --- a/src/script/api/script_text.cpp +++ b/src/script/api/script_text.cpp @@ -250,8 +250,6 @@ const std::string Text::GetDecodedText() { const std::string &encoded_text = this->GetEncodedText(); - static char buf[1024]; ::SetDParamStr(0, encoded_text); - ::GetString(buf, STR_JUST_RAW_STRING, lastof(buf)); - return buf; + return ::GetString(STR_JUST_RAW_STRING); } diff --git a/src/strings.cpp b/src/strings.cpp index 0e13ef13bc..ed8d1bfc94 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -155,8 +155,8 @@ void CopyOutDParam(uint64 *dst, int offs, int num) */ void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num) { - char buf[DRAW_STRING_BUFFER]; - GetString(buf, string, lastof(buf)); + /* Just get the string to extract the type information. */ + GetString(string); MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num); for (int i = 0; i < num; i++) { diff --git a/src/viewport.cpp b/src/viewport.cpp index 185f8bf082..8e5e7ead81 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1449,17 +1449,15 @@ void ViewportSign::UpdatePosition(int center, int top, StringID str, StringID st this->top = top; - char buffer[DRAW_STRING_BUFFER]; - - GetString(buffer, str, lastof(buffer)); - this->width_normal = WidgetDimensions::scaled.fullbevel.left + Align(GetStringBoundingBox(buffer).width, 2) + WidgetDimensions::scaled.fullbevel.right; + std::string name = GetString(str); + this->width_normal = WidgetDimensions::scaled.fullbevel.left + Align(GetStringBoundingBox(name).width, 2) + WidgetDimensions::scaled.fullbevel.right; this->center = center; /* zoomed out version */ if (str_small != STR_NULL) { - GetString(buffer, str_small, lastof(buffer)); + name = GetString(str_small); } - this->width_small = WidgetDimensions::scaled.fullbevel.left + Align(GetStringBoundingBox(buffer, FS_SMALL).width, 2) + WidgetDimensions::scaled.fullbevel.right; + this->width_small = WidgetDimensions::scaled.fullbevel.left + Align(GetStringBoundingBox(name, FS_SMALL).width, 2) + WidgetDimensions::scaled.fullbevel.right; this->MarkDirty(); } From bc45c3f66c6538bc7e048e2b768b76add7bb07be Mon Sep 17 00:00:00 2001 From: PeterN Date: Mon, 22 May 2023 08:03:20 +0100 Subject: [PATCH 28/58] Change: Remember waypoint filter string. (#10857) --- src/rail_gui.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index e3ac337845..0ea7272217 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -2001,9 +2001,9 @@ struct BuildRailWaypointWindow : PickerWindowBase { const StationClass *waypoints; WaypointList list; StringFilter string_filter; ///< Filter for waypoint name - QueryString editbox; ///< Filter editbox + static QueryString editbox; ///< Filter editbox - BuildRailWaypointWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent), editbox(FILTER_LENGTH * MAX_CHAR_LENGTH, FILTER_LENGTH) + BuildRailWaypointWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent) { this->waypoints = StationClass::Get(STAT_CLASS_WAYP); @@ -2016,6 +2016,7 @@ struct BuildRailWaypointWindow : PickerWindowBase { this->querystrings[WID_BRW_FILTER] = &this->editbox; this->editbox.cancel_button = QueryString::ACTION_CLEAR; + this->string_filter.SetFilterTerm(this->editbox.text.buf); this->list.ForceRebuild(); this->BuildPickerList(); @@ -2170,6 +2171,8 @@ struct BuildRailWaypointWindow : PickerWindowBase { } }; +/* static */ QueryString BuildRailWaypointWindow::editbox(BuildRailWaypointWindow::FILTER_LENGTH * MAX_CHAR_LENGTH, BuildRailWaypointWindow::FILTER_LENGTH); + /** Nested widget definition for the build NewGRF rail waypoint window */ static const NWidgetPart _nested_build_waypoint_widgets[] = { NWidget(NWID_HORIZONTAL), From 6f2f38b3ed68b52eaa2bbba5f58a2b92a1e093f0 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 20 May 2023 11:46:46 +0200 Subject: [PATCH 29/58] Codechange: fmt (and std::format) do explicitly not support enums out-of-the-box That it works for the version we have packaged it pure coincidence, as that is one of the few versions that due to a bug allow it. So add the appropriate template specialisations to support it out-of-the-box within OpenTTD. --- CMakeLists.txt | 2 +- src/console_func.h | 2 +- src/core/CMakeLists.txt | 1 + src/core/format.hpp | 44 +++++++++++++++++++++++++++++++++++++++ src/debug.h | 2 +- src/misc_gui.cpp | 2 +- src/os/unix/font_unix.cpp | 2 +- 7 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 src/core/format.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ca0b261d85..f0f5ff1dce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,7 +250,7 @@ endif() target_precompile_headers(openttd_lib PRIVATE src/stdafx.h - src/3rdparty/fmt/format.h + src/core/format.hpp ) add_subdirectory(${CMAKE_SOURCE_DIR}/bin) diff --git a/src/console_func.h b/src/console_func.h index 36227bc56f..484a71573c 100644 --- a/src/console_func.h +++ b/src/console_func.h @@ -11,7 +11,7 @@ #define CONSOLE_FUNC_H #include "console_type.h" -#include "3rdparty/fmt/format.h" +#include "core/format.hpp" /* console modes */ extern IConsoleModes _iconsole_mode; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index afe1112316..6dd7bbd69b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -8,6 +8,7 @@ add_files( endian_func.hpp endian_type.hpp enum_type.hpp + format.hpp geometry_func.cpp geometry_func.hpp geometry_type.hpp diff --git a/src/core/format.hpp b/src/core/format.hpp new file mode 100644 index 0000000000..f701ea6dfe --- /dev/null +++ b/src/core/format.hpp @@ -0,0 +1,44 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file format.hpp String formatting functions and helpers. */ + +#ifndef FORMAT_HPP +#define FORMAT_HPP + +#include "../3rdparty/fmt/format.h" +#include "strong_typedef_type.hpp" + +template +struct fmt::formatter::value>> : fmt::formatter::type> { + using underlying_type = typename std::underlying_type::type; + using parent = typename fmt::formatter; + + constexpr fmt::format_parse_context::iterator parse(fmt::format_parse_context &ctx) { + return parent::parse(ctx); + } + + fmt::format_context::iterator format(const E &e, format_context &ctx) const { + return parent::format(underlying_type(e), ctx); + } +}; + +template +struct fmt::formatter::value>> : fmt::formatter { + using underlying_type = typename T::Type; + using parent = typename fmt::formatter; + + constexpr fmt::format_parse_context::iterator parse(fmt::format_parse_context &ctx) { + return parent::parse(ctx); + } + + fmt::format_context::iterator format(const T &t, format_context &ctx) const { + return parent::format(underlying_type(t), ctx); + } +}; + +#endif /* FORMAT_HPP */ diff --git a/src/debug.h b/src/debug.h index bb939ccc4c..2db1d1132b 100644 --- a/src/debug.h +++ b/src/debug.h @@ -12,7 +12,7 @@ #include "cpu.h" #include -#include "3rdparty/fmt/format.h" +#include "core/format.hpp" /* Debugging messages policy: * These should be the severities used for direct Debug() calls diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 49a46e5b1c..c130102bd4 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -117,7 +117,7 @@ public: #else # define LANDINFOD_LEVEL 1 #endif - Debug(misc, LANDINFOD_LEVEL, "TILE: 0x{:x} ({},{})", tile, TileX(tile), TileY(tile)); + Debug(misc, LANDINFOD_LEVEL, "TILE: 0x{:x} ({},{})", (TileIndex)tile, TileX(tile), TileY(tile)); Debug(misc, LANDINFOD_LEVEL, "type = 0x{:x}", tile.type()); Debug(misc, LANDINFOD_LEVEL, "height = 0x{:x}", tile.height()); Debug(misc, LANDINFOD_LEVEL, "m1 = 0x{:x}", tile.m1()); diff --git a/src/os/unix/font_unix.cpp b/src/os/unix/font_unix.cpp index 5027929be0..f5284f4ed9 100644 --- a/src/os/unix/font_unix.cpp +++ b/src/os/unix/font_unix.cpp @@ -148,7 +148,7 @@ bool SetFallbackFont(FontCacheSettings *settings, const char *language_isocode, callback->SetFontNames(settings, (const char *)file); bool missing = callback->FindMissingGlyphs(); - Debug(fontcache, 1, "Font \"{}\" misses{} glyphs", file, missing ? "" : " no"); + Debug(fontcache, 1, "Font \"{}\" misses{} glyphs", (char *)file, missing ? "" : " no"); if (!missing) { best_weight = value; From 187fa3f2143a15acd36054239f2b36b3e01cd0c8 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 20 May 2023 11:50:13 +0200 Subject: [PATCH 30/58] Codechange: update to fmt 10.0.0 and add formatting support for chrono and std types --- src/3rdparty/fmt/CMakeLists.txt | 4 + src/3rdparty/fmt/LICENSE.rst | 2 +- src/3rdparty/fmt/chrono.h | 2267 +++++++++++ src/3rdparty/fmt/core.h | 2818 +++++++++----- src/3rdparty/fmt/format-inl.h | 3668 ++++++------------ src/3rdparty/fmt/format.h | 6375 +++++++++++++++++-------------- src/3rdparty/fmt/ostream.h | 209 + src/3rdparty/fmt/ranges.h | 732 ++++ src/3rdparty/fmt/std.h | 349 ++ 9 files changed, 10235 insertions(+), 6189 deletions(-) create mode 100644 src/3rdparty/fmt/chrono.h create mode 100644 src/3rdparty/fmt/ostream.h create mode 100644 src/3rdparty/fmt/ranges.h create mode 100644 src/3rdparty/fmt/std.h diff --git a/src/3rdparty/fmt/CMakeLists.txt b/src/3rdparty/fmt/CMakeLists.txt index 22a02416c3..e687b2b3d8 100644 --- a/src/3rdparty/fmt/CMakeLists.txt +++ b/src/3rdparty/fmt/CMakeLists.txt @@ -1,5 +1,9 @@ add_files( + chrono.h core.h format.h format-inl.h + ostream.h + ranges.h + std.h ) diff --git a/src/3rdparty/fmt/LICENSE.rst b/src/3rdparty/fmt/LICENSE.rst index f0ec3db4d2..1cd1ef9269 100644 --- a/src/3rdparty/fmt/LICENSE.rst +++ b/src/3rdparty/fmt/LICENSE.rst @@ -1,4 +1,4 @@ -Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/3rdparty/fmt/chrono.h b/src/3rdparty/fmt/chrono.h new file mode 100644 index 0000000000..55e8a50670 --- /dev/null +++ b/src/3rdparty/fmt/chrono.h @@ -0,0 +1,2267 @@ +// Formatting library for C++ - chrono support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CHRONO_H_ +#define FMT_CHRONO_H_ + +#include +#include +#include // std::isfinite +#include // std::memcpy +#include +#include +#include +#include +#include + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +// Check if std::chrono::local_t is available. +#ifndef FMT_USE_LOCAL_TIME +# ifdef __cpp_lib_chrono +# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) +# else +# define FMT_USE_LOCAL_TIME 0 +# endif +#endif + +// Check if std::chrono::utc_timestamp is available. +#ifndef FMT_USE_UTC_TIME +# ifdef __cpp_lib_chrono +# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) +# else +# define FMT_USE_UTC_TIME 0 +# endif +#endif + +// Enable tzset. +#ifndef FMT_USE_TZSET +// UWP doesn't provide _tzset. +# if FMT_HAS_INCLUDE("winapifamily.h") +# include +# endif +# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# define FMT_USE_TZSET 1 +# else +# define FMT_USE_TZSET 0 +# endif +#endif + +// Enable safe chrono durations, unless explicitly disabled. +#ifndef FMT_SAFE_DURATION_CAST +# define FMT_SAFE_DURATION_CAST 1 +#endif +#if FMT_SAFE_DURATION_CAST + +// For conversion between std::chrono::durations without undefined +// behaviour or erroneous results. +// This is a stripped down version of duration_cast, for inclusion in fmt. +// See https://github.com/pauldreik/safe_duration_cast +// +// Copyright Paul Dreik 2019 +namespace safe_duration_cast { + +template ::value && + std::numeric_limits::is_signed == + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + // A and B are both signed, or both unsigned. + if (detail::const_check(F::digits <= T::digits)) { + // From fits in To without any problem. + } else { + // From does not always fit in To, resort to a dynamic check. + if (from < (T::min)() || from > (T::max)()) { + // outside range. + ec = 1; + return {}; + } + } + return static_cast(from); +} + +/** + * converts From to To, without loss. If the dynamic value of from + * can't be converted to To without loss, ec is set. + */ +template ::value && + std::numeric_limits::is_signed != + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + if (detail::const_check(F::is_signed && !T::is_signed)) { + // From may be negative, not allowed! + if (fmt::detail::is_negative(from)) { + ec = 1; + return {}; + } + // From is positive. Can it always fit in To? + if (detail::const_check(F::digits > T::digits) && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + } + + if (detail::const_check(!F::is_signed && T::is_signed && + F::digits >= T::digits) && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + return static_cast(from); // Lossless conversion. +} + +template ::value)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + return from; +} // function + +// clang-format off +/** + * converts From to To if possible, otherwise ec is set. + * + * input | output + * ---------------------------------|--------------- + * NaN | NaN + * Inf | Inf + * normal, fits in output | converted (possibly lossy) + * normal, does not fit in output | ec is set + * subnormal | best effort + * -Inf | -Inf + */ +// clang-format on +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + using T = std::numeric_limits; + static_assert(std::is_floating_point::value, "From must be floating"); + static_assert(std::is_floating_point::value, "To must be floating"); + + // catch the only happy case + if (std::isfinite(from)) { + if (from >= T::lowest() && from <= (T::max)()) { + return static_cast(from); + } + // not within range. + ec = 1; + return {}; + } + + // nan and inf will be preserved + return static_cast(from); +} // function + +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + static_assert(std::is_floating_point::value, "From must be floating"); + return from; +} + +/** + * safe duration cast between integral durations + */ +template ::value), + FMT_ENABLE_IF(std::is_integral::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // safe conversion to IntermediateRep + IntermediateRep count = + lossless_integral_conversion(from.count(), ec); + if (ec) return {}; + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + const auto max1 = detail::max_value() / Factor::num; + if (count > max1) { + ec = 1; + return {}; + } + const auto min1 = + (std::numeric_limits::min)() / Factor::num; + if (detail::const_check(!std::is_unsigned::value) && + count < min1) { + ec = 1; + return {}; + } + count *= Factor::num; + } + + if (detail::const_check(Factor::den != 1)) count /= Factor::den; + auto tocount = lossless_integral_conversion(count, ec); + return ec ? To() : To(tocount); +} + +/** + * safe duration_cast between floating point durations + */ +template ::value), + FMT_ENABLE_IF(std::is_floating_point::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + if (std::isnan(from.count())) { + // nan in, gives nan out. easy. + return To{std::numeric_limits::quiet_NaN()}; + } + // maybe we should also check if from is denormal, and decide what to do about + // it. + + // +-inf should be preserved. + if (std::isinf(from.count())) { + return To{from.count()}; + } + + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // force conversion of From::rep -> IntermediateRep to be safe, + // even if it will never happen be narrowing in this context. + IntermediateRep count = + safe_float_conversion(from.count(), ec); + if (ec) { + return {}; + } + + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + constexpr auto max1 = detail::max_value() / + static_cast(Factor::num); + if (count > max1) { + ec = 1; + return {}; + } + constexpr auto min1 = std::numeric_limits::lowest() / + static_cast(Factor::num); + if (count < min1) { + ec = 1; + return {}; + } + count *= static_cast(Factor::num); + } + + // this can't go wrong, right? den>0 is checked earlier. + if (detail::const_check(Factor::den != 1)) { + using common_t = typename std::common_type::type; + count /= static_cast(Factor::den); + } + + // convert to the to type, safely + using ToRep = typename To::rep; + + const ToRep tocount = safe_float_conversion(count, ec); + if (ec) { + return {}; + } + return To{tocount}; +} +} // namespace safe_duration_cast +#endif + +// Prevents expansion of a preceding token as a function-style macro. +// Usage: f FMT_NOMACRO() +#define FMT_NOMACRO + +namespace detail { +template struct null {}; +inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } +inline null<> localtime_s(...) { return null<>(); } +inline null<> gmtime_r(...) { return null<>(); } +inline null<> gmtime_s(...) { return null<>(); } + +inline const std::locale& get_classic_locale() { + static const auto& locale = std::locale::classic(); + return locale; +} + +template struct codecvt_result { + static constexpr const size_t max_size = 32; + CodeUnit buf[max_size]; + CodeUnit* end; +}; +template +constexpr const size_t codecvt_result::max_size; + +template +void write_codecvt(codecvt_result& out, string_view in_buf, + const std::locale& loc) { +#if FMT_CLANG_VERSION +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" + auto& f = std::use_facet>(loc); +# pragma clang diagnostic pop +#else + auto& f = std::use_facet>(loc); +#endif + auto mb = std::mbstate_t(); + const char* from_next = nullptr; + auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, + std::begin(out.buf), std::end(out.buf), out.end); + if (result != std::codecvt_base::ok) + FMT_THROW(format_error("failed to format time")); +} + +template +auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) + -> OutputIt { + if (detail::is_utf8() && loc != get_classic_locale()) { + // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and + // gcc-4. +#if FMT_MSC_VERSION != 0 || \ + (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) + // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 + // and newer. + using code_unit = wchar_t; +#else + using code_unit = char32_t; +#endif + + using unit_t = codecvt_result; + unit_t unit; + write_codecvt(unit, in, loc); + // In UTF-8 is used one to four one-byte code units. + unicode_to_utf8> + u; + if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) + FMT_THROW(format_error("failed to format time")); + return copy_str(u.c_str(), u.c_str() + u.size(), out); + } + return copy_str(in.data(), in.data() + in.size(), out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + codecvt_result unit; + write_codecvt(unit, sv, loc); + return copy_str(unit.buf, unit.end, out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + return write_encoded_tm_str(out, sv, loc); +} + +template +inline void do_write(buffer& buf, const std::tm& time, + const std::locale& loc, char format, char modifier) { + auto&& format_buf = formatbuf>(buf); + auto&& os = std::basic_ostream(&format_buf); + os.imbue(loc); + using iterator = std::ostreambuf_iterator; + const auto& facet = std::use_facet>(loc); + auto end = facet.put(os, os, Char(' '), &time, format, modifier); + if (end.failed()) FMT_THROW(format_error("failed to format time")); +} + +template ::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = get_buffer(out); + do_write(buf, time, loc, format, modifier); + return get_iterator(buf, out); +} + +template ::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = basic_memory_buffer(); + do_write(buf, time, loc, format, modifier); + return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); +} + +} // namespace detail + +FMT_BEGIN_EXPORT + +/** + Converts given time since epoch as ``std::time_t`` value into calendar time, + expressed in local time. Unlike ``std::localtime``, this function is + thread-safe on most platforms. + */ +inline std::tm localtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(localtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(localtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VERSION + bool fallback(detail::null<>) { + using namespace fmt::detail; + std::tm* tm = std::localtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher lt(time); + // Too big time values may be unsupported. + if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); + return lt.tm_; +} + +#if FMT_USE_LOCAL_TIME +template +inline auto localtime(std::chrono::local_time time) -> std::tm { + return localtime(std::chrono::system_clock::to_time_t( + std::chrono::current_zone()->to_sys(time))); +} +#endif + +/** + Converts given time since epoch as ``std::time_t`` value into calendar time, + expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this + function is thread-safe on most platforms. + */ +inline std::tm gmtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(gmtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(gmtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VERSION + bool fallback(detail::null<>) { + std::tm* tm = std::gmtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher gt(time); + // Too big time values may be unsupported. + if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); + return gt.tm_; +} + +inline std::tm gmtime( + std::chrono::time_point time_point) { + return gmtime(std::chrono::system_clock::to_time_t(time_point)); +} + +FMT_BEGIN_DETAIL_NAMESPACE + +// DEPRECATED! +template +FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, + format_specs& specs) -> const Char* { + FMT_ASSERT(begin != end, ""); + auto align = align::none; + auto p = begin + code_point_length(begin); + if (end - p <= 0) p = begin; + for (;;) { + switch (to_ascii(*p)) { + case '<': + align = align::left; + break; + case '>': + align = align::right; + break; + case '^': + align = align::center; + break; + } + if (align != align::none) { + if (p != begin) { + auto c = *begin; + if (c == '}') return begin; + if (c == '{') { + throw_format_error("invalid fill character '{'"); + return begin; + } + specs.fill = {begin, to_unsigned(p - begin)}; + begin = p + 1; + } else { + ++begin; + } + break; + } else if (p == begin) { + break; + } + p = begin; + } + specs.align = align; + return begin; +} + +// Writes two-digit numbers a, b and c separated by sep to buf. +// The method by Pavel Novikov based on +// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. +inline void write_digit2_separated(char* buf, unsigned a, unsigned b, + unsigned c, char sep) { + unsigned long long digits = + a | (b << 24) | (static_cast(c) << 48); + // Convert each value to BCD. + // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b. + // The difference is + // y - x = a * 6 + // a can be found from x: + // a = floor(x / 10) + // then + // y = x + a * 6 = x + floor(x / 10) * 6 + // floor(x / 10) is (x * 205) >> 11 (needs 16 bits). + digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6; + // Put low nibbles to high bytes and high nibbles to low bytes. + digits = ((digits & 0x00f00000f00000f0) >> 4) | + ((digits & 0x000f00000f00000f) << 8); + auto usep = static_cast(sep); + // Add ASCII '0' to each digit byte and insert separators. + digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); + + constexpr const size_t len = 8; + if (const_check(is_big_endian())) { + char tmp[len]; + std::memcpy(tmp, &digits, len); + std::reverse_copy(tmp, tmp + len, buf); + } else { + std::memcpy(buf, &digits, len); + } +} + +template FMT_CONSTEXPR inline const char* get_units() { + if (std::is_same::value) return "as"; + if (std::is_same::value) return "fs"; + if (std::is_same::value) return "ps"; + if (std::is_same::value) return "ns"; + if (std::is_same::value) return "µs"; + if (std::is_same::value) return "ms"; + if (std::is_same::value) return "cs"; + if (std::is_same::value) return "ds"; + if (std::is_same>::value) return "s"; + if (std::is_same::value) return "das"; + if (std::is_same::value) return "hs"; + if (std::is_same::value) return "ks"; + if (std::is_same::value) return "Ms"; + if (std::is_same::value) return "Gs"; + if (std::is_same::value) return "Ts"; + if (std::is_same::value) return "Ps"; + if (std::is_same::value) return "Es"; + if (std::is_same>::value) return "m"; + if (std::is_same>::value) return "h"; + return nullptr; +} + +enum class numeric_system { + standard, + // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. + alternative +}; + +// Glibc extensions for formatting numeric values. +enum class pad_type { + unspecified, + // Do not pad a numeric result string. + none, + // Pad a numeric result string with zeros even if the conversion specifier + // character uses space-padding by default. + zero, + // Pad a numeric result string with spaces. + space, +}; + +template +auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { + if (pad == pad_type::none) return out; + return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); +} + +template +auto write_padding(OutputIt out, pad_type pad) -> OutputIt { + if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0'; + return out; +} + +// Parses a put_time-like format string and invokes handler actions. +template +FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, + const Char* end, + Handler&& handler) { + if (begin == end || *begin == '}') return begin; + if (*begin != '%') FMT_THROW(format_error("invalid format")); + auto ptr = begin; + pad_type pad = pad_type::unspecified; + while (ptr != end) { + auto c = *ptr; + if (c == '}') break; + if (c != '%') { + ++ptr; + continue; + } + if (begin != ptr) handler.on_text(begin, ptr); + ++ptr; // consume '%' + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr; + switch (c) { + case '_': + pad = pad_type::space; + ++ptr; + break; + case '-': + pad = pad_type::none; + ++ptr; + break; + case '0': + pad = pad_type::zero; + ++ptr; + break; + } + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case '%': + handler.on_text(ptr - 1, ptr); + break; + case 'n': { + const Char newline[] = {'\n'}; + handler.on_text(newline, newline + 1); + break; + } + case 't': { + const Char tab[] = {'\t'}; + handler.on_text(tab, tab + 1); + break; + } + // Year: + case 'Y': + handler.on_year(numeric_system::standard); + break; + case 'y': + handler.on_short_year(numeric_system::standard); + break; + case 'C': + handler.on_century(numeric_system::standard); + break; + case 'G': + handler.on_iso_week_based_year(); + break; + case 'g': + handler.on_iso_week_based_short_year(); + break; + // Day of the week: + case 'a': + handler.on_abbr_weekday(); + break; + case 'A': + handler.on_full_weekday(); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::standard); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::standard); + break; + // Month: + case 'b': + case 'h': + handler.on_abbr_month(); + break; + case 'B': + handler.on_full_month(); + break; + case 'm': + handler.on_dec_month(numeric_system::standard); + break; + // Day of the year/month: + case 'U': + handler.on_dec0_week_of_year(numeric_system::standard); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::standard); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::standard); + break; + case 'j': + handler.on_day_of_year(); + break; + case 'd': + handler.on_day_of_month(numeric_system::standard); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::standard); + break; + // Hour, minute, second: + case 'H': + handler.on_24_hour(numeric_system::standard, pad); + break; + case 'I': + handler.on_12_hour(numeric_system::standard, pad); + break; + case 'M': + handler.on_minute(numeric_system::standard, pad); + break; + case 'S': + handler.on_second(numeric_system::standard, pad); + break; + // Other: + case 'c': + handler.on_datetime(numeric_system::standard); + break; + case 'x': + handler.on_loc_date(numeric_system::standard); + break; + case 'X': + handler.on_loc_time(numeric_system::standard); + break; + case 'D': + handler.on_us_date(); + break; + case 'F': + handler.on_iso_date(); + break; + case 'r': + handler.on_12_hour_time(); + break; + case 'R': + handler.on_24_hour_time(); + break; + case 'T': + handler.on_iso_time(); + break; + case 'p': + handler.on_am_pm(); + break; + case 'Q': + handler.on_duration_value(); + break; + case 'q': + handler.on_duration_unit(); + break; + case 'z': + handler.on_utc_offset(numeric_system::standard); + break; + case 'Z': + handler.on_tz_name(); + break; + // Alternative representation: + case 'E': { + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'Y': + handler.on_year(numeric_system::alternative); + break; + case 'y': + handler.on_offset_year(); + break; + case 'C': + handler.on_century(numeric_system::alternative); + break; + case 'c': + handler.on_datetime(numeric_system::alternative); + break; + case 'x': + handler.on_loc_date(numeric_system::alternative); + break; + case 'X': + handler.on_loc_time(numeric_system::alternative); + break; + case 'z': + handler.on_utc_offset(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + } + case 'O': + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'y': + handler.on_short_year(numeric_system::alternative); + break; + case 'm': + handler.on_dec_month(numeric_system::alternative); + break; + case 'U': + handler.on_dec0_week_of_year(numeric_system::alternative); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::alternative); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::alternative); + break; + case 'd': + handler.on_day_of_month(numeric_system::alternative); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::alternative); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::alternative); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::alternative); + break; + case 'H': + handler.on_24_hour(numeric_system::alternative, pad); + break; + case 'I': + handler.on_12_hour(numeric_system::alternative, pad); + break; + case 'M': + handler.on_minute(numeric_system::alternative, pad); + break; + case 'S': + handler.on_second(numeric_system::alternative, pad); + break; + case 'z': + handler.on_utc_offset(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + default: + FMT_THROW(format_error("invalid format")); + } + begin = ptr; + } + if (begin != ptr) handler.on_text(begin, ptr); + return ptr; +} + +template struct null_chrono_spec_handler { + FMT_CONSTEXPR void unsupported() { + static_cast(this)->unsupported(); + } + FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_offset_year() { unsupported(); } + FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); } + FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } + FMT_CONSTEXPR void on_full_weekday() { unsupported(); } + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_abbr_month() { unsupported(); } + FMT_CONSTEXPR void on_full_month() { unsupported(); } + FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_year() { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_us_date() { unsupported(); } + FMT_CONSTEXPR void on_iso_date() { unsupported(); } + FMT_CONSTEXPR void on_12_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_iso_time() { unsupported(); } + FMT_CONSTEXPR void on_am_pm() { unsupported(); } + FMT_CONSTEXPR void on_duration_value() { unsupported(); } + FMT_CONSTEXPR void on_duration_unit() { unsupported(); } + FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_tz_name() { unsupported(); } +}; + +struct tm_format_checker : null_chrono_spec_handler { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } + + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_year(numeric_system) {} + FMT_CONSTEXPR void on_short_year(numeric_system) {} + FMT_CONSTEXPR void on_offset_year() {} + FMT_CONSTEXPR void on_century(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_based_year() {} + FMT_CONSTEXPR void on_iso_week_based_short_year() {} + FMT_CONSTEXPR void on_abbr_weekday() {} + FMT_CONSTEXPR void on_full_weekday() {} + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {} + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} + FMT_CONSTEXPR void on_abbr_month() {} + FMT_CONSTEXPR void on_full_month() {} + FMT_CONSTEXPR void on_dec_month(numeric_system) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_day_of_year() {} + FMT_CONSTEXPR void on_day_of_month(numeric_system) {} + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} + FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_datetime(numeric_system) {} + FMT_CONSTEXPR void on_loc_date(numeric_system) {} + FMT_CONSTEXPR void on_loc_time(numeric_system) {} + FMT_CONSTEXPR void on_us_date() {} + FMT_CONSTEXPR void on_iso_date() {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_utc_offset(numeric_system) {} + FMT_CONSTEXPR void on_tz_name() {} +}; + +inline const char* tm_wday_full_name(int wday) { + static constexpr const char* full_name_list[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; + return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; +} +inline const char* tm_wday_short_name(int wday) { + static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; +} + +inline const char* tm_mon_full_name(int mon) { + static constexpr const char* full_name_list[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}; + return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; +} +inline const char* tm_mon_short_name(int mon) { + static constexpr const char* short_name_list[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; +} + +template +struct has_member_data_tm_gmtoff : std::false_type {}; +template +struct has_member_data_tm_gmtoff> + : std::true_type {}; + +template +struct has_member_data_tm_zone : std::false_type {}; +template +struct has_member_data_tm_zone> + : std::true_type {}; + +#if FMT_USE_TZSET +inline void tzset_once() { + static bool init = []() -> bool { + _tzset(); + return true; + }(); + ignore_unused(init); +} +#endif + +// Converts value to Int and checks that it's in the range [0, upper). +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { + FMT_ASSERT(std::is_unsigned::value || + (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), + "invalid value"); + (void)upper; + return static_cast(value); +} +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { + if (value < 0 || value > static_cast(upper)) + FMT_THROW(format_error("invalid value")); + return static_cast(value); +} + +constexpr long long pow10(std::uint32_t n) { + return n == 0 ? 1 : 10 * pow10(n - 1); +} + +// Counts the number of fractional digits in the range [0, 18] according to the +// C++20 spec. If more than 18 fractional digits are required then returns 6 for +// microseconds precision. +template () / 10)> +struct count_fractional_digits { + static constexpr int value = + Num % Den == 0 ? N : count_fractional_digits::value; +}; + +// Base case that doesn't instantiate any more templates +// in order to avoid overflow. +template +struct count_fractional_digits { + static constexpr int value = (Num % Den == 0) ? N : 6; +}; + +// Format subseconds which are given as an integer type with an appropriate +// number of digits. +template +void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { + constexpr auto num_fractional_digits = + count_fractional_digits::value; + + using subsecond_precision = std::chrono::duration< + typename std::common_type::type, + std::ratio<1, detail::pow10(num_fractional_digits)>>; + + const auto fractional = + d - std::chrono::duration_cast(d); + const auto subseconds = + std::chrono::treat_as_floating_point< + typename subsecond_precision::rep>::value + ? fractional.count() + : std::chrono::duration_cast(fractional).count(); + auto n = static_cast>(subseconds); + const int num_digits = detail::count_digits(n); + + int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); + if (precision < 0) { + FMT_ASSERT(!std::is_floating_point::value, ""); + if (std::ratio_less::value) { + *out++ = '.'; + out = std::fill_n(out, leading_zeroes, '0'); + out = format_decimal(out, n, num_digits).end; + } + } else { + *out++ = '.'; + leading_zeroes = (std::min)(leading_zeroes, precision); + out = std::fill_n(out, leading_zeroes, '0'); + int remaining = precision - leading_zeroes; + if (remaining != 0 && remaining < num_digits) { + n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); + out = format_decimal(out, n, remaining).end; + return; + } + out = format_decimal(out, n, num_digits).end; + remaining -= num_digits; + out = std::fill_n(out, remaining, '0'); + } +} + +// Format subseconds which are given as a floating point type with an +// appropriate number of digits. We cannot pass the Duration here, as we +// explicitly need to pass the Rep value in the chrono_formatter. +template +void write_floating_seconds(memory_buffer& buf, Duration duration, + int num_fractional_digits = -1) { + using rep = typename Duration::rep; + FMT_ASSERT(std::is_floating_point::value, ""); + + auto val = duration.count(); + + if (num_fractional_digits < 0) { + // For `std::round` with fallback to `round`: + // On some toolchains `std::round` is not available (e.g. GCC 6). + using namespace std; + num_fractional_digits = + count_fractional_digits::value; + if (num_fractional_digits < 6 && static_cast(round(val)) != val) + num_fractional_digits = 6; + } + + format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), + std::fmod(val * static_cast(Duration::period::num) / + static_cast(Duration::period::den), + static_cast(60)), + num_fractional_digits); +} + +template +class tm_writer { + private: + static constexpr int days_per_week = 7; + + const std::locale& loc_; + const bool is_classic_; + OutputIt out_; + const Duration* subsecs_; + const std::tm& tm_; + + auto tm_sec() const noexcept -> int { + FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, ""); + return tm_.tm_sec; + } + auto tm_min() const noexcept -> int { + FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, ""); + return tm_.tm_min; + } + auto tm_hour() const noexcept -> int { + FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, ""); + return tm_.tm_hour; + } + auto tm_mday() const noexcept -> int { + FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, ""); + return tm_.tm_mday; + } + auto tm_mon() const noexcept -> int { + FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, ""); + return tm_.tm_mon; + } + auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; } + auto tm_wday() const noexcept -> int { + FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, ""); + return tm_.tm_wday; + } + auto tm_yday() const noexcept -> int { + FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, ""); + return tm_.tm_yday; + } + + auto tm_hour12() const noexcept -> int { + const auto h = tm_hour(); + const auto z = h < 12 ? h : h - 12; + return z == 0 ? 12 : z; + } + + // POSIX and the C Standard are unclear or inconsistent about what %C and %y + // do if the year is negative or exceeds 9999. Use the convention that %C + // concatenated with %y yields the same output as %Y, and that %Y contains at + // least 4 characters, with more only if necessary. + auto split_year_lower(long long year) const noexcept -> int { + auto l = year % 100; + if (l < 0) l = -l; // l in [0, 99] + return static_cast(l); + } + + // Algorithm: + // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date + auto iso_year_weeks(long long curr_year) const noexcept -> int { + const auto prev_year = curr_year - 1; + const auto curr_p = + (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % + days_per_week; + const auto prev_p = + (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % + days_per_week; + return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); + } + auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int { + return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / + days_per_week; + } + auto tm_iso_week_year() const noexcept -> long long { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return year - 1; + if (w > iso_year_weeks(year)) return year + 1; + return year; + } + auto tm_iso_week_of_year() const noexcept -> int { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return iso_year_weeks(year - 1); + if (w > iso_year_weeks(year)) return 1; + return w; + } + + void write1(int value) { + *out_++ = static_cast('0' + to_unsigned(value) % 10); + } + void write2(int value) { + const char* d = digits2(to_unsigned(value) % 100); + *out_++ = *d++; + *out_++ = *d; + } + void write2(int value, pad_type pad) { + unsigned int v = to_unsigned(value) % 100; + if (v >= 10) { + const char* d = digits2(v); + *out_++ = *d++; + *out_++ = *d; + } else { + out_ = detail::write_padding(out_, pad); + *out_++ = static_cast('0' + v); + } + } + + void write_year_extended(long long year) { + // At least 4 characters. + int width = 4; + if (year < 0) { + *out_++ = '-'; + year = 0 - year; + --width; + } + uint32_or_64_or_128_t n = to_unsigned(year); + const int num_digits = count_digits(n); + if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); + out_ = format_decimal(out_, n, num_digits).end; + } + void write_year(long long year) { + if (year >= 0 && year < 10000) { + write2(static_cast(year / 100)); + write2(static_cast(year % 100)); + } else { + write_year_extended(year); + } + } + + void write_utc_offset(long offset, numeric_system ns) { + if (offset < 0) { + *out_++ = '-'; + offset = -offset; + } else { + *out_++ = '+'; + } + offset /= 60; + write2(static_cast(offset / 60)); + if (ns != numeric_system::standard) *out_++ = ':'; + write2(static_cast(offset % 60)); + } + template ::value)> + void format_utc_offset_impl(const T& tm, numeric_system ns) { + write_utc_offset(tm.tm_gmtoff, ns); + } + template ::value)> + void format_utc_offset_impl(const T& tm, numeric_system ns) { +#if defined(_WIN32) && defined(_UCRT) +# if FMT_USE_TZSET + tzset_once(); +# endif + long offset = 0; + _get_timezone(&offset); + if (tm.tm_isdst) { + long dstbias = 0; + _get_dstbias(&dstbias); + offset += dstbias; + } + write_utc_offset(-offset, ns); +#else + if (ns == numeric_system::standard) return format_localized('z'); + + // Extract timezone offset from timezone conversion functions. + std::tm gtm = tm; + std::time_t gt = std::mktime(>m); + std::tm ltm = gmtime(gt); + std::time_t lt = std::mktime(<m); + long offset = gt - lt; + write_utc_offset(offset, ns); +#endif + } + + template ::value)> + void format_tz_name_impl(const T& tm) { + if (is_classic_) + out_ = write_tm_str(out_, tm.tm_zone, loc_); + else + format_localized('Z'); + } + template ::value)> + void format_tz_name_impl(const T&) { + format_localized('Z'); + } + + void format_localized(char format, char modifier = 0) { + out_ = write(out_, tm_, loc_, format, modifier); + } + + public: + tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm, + const Duration* subsecs = nullptr) + : loc_(loc), + is_classic_(loc_ == get_classic_locale()), + out_(out), + subsecs_(subsecs), + tm_(tm) {} + + OutputIt out() const { return out_; } + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + out_ = copy_str(begin, end, out_); + } + + void on_abbr_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_short_name(tm_wday())); + else + format_localized('a'); + } + void on_full_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_full_name(tm_wday())); + else + format_localized('A'); + } + void on_dec0_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); + format_localized('w', 'O'); + } + void on_dec1_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write1(wday == 0 ? days_per_week : wday); + } else { + format_localized('u', 'O'); + } + } + + void on_abbr_month() { + if (is_classic_) + out_ = write(out_, tm_mon_short_name(tm_mon())); + else + format_localized('b'); + } + void on_full_month() { + if (is_classic_) + out_ = write(out_, tm_mon_full_name(tm_mon())); + else + format_localized('B'); + } + + void on_datetime(numeric_system ns) { + if (is_classic_) { + on_abbr_weekday(); + *out_++ = ' '; + on_abbr_month(); + *out_++ = ' '; + on_day_of_month_space(numeric_system::standard); + *out_++ = ' '; + on_iso_time(); + *out_++ = ' '; + on_year(numeric_system::standard); + } else { + format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); + } + } + void on_loc_date(numeric_system ns) { + if (is_classic_) + on_us_date(); + else + format_localized('x', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_loc_time(numeric_system ns) { + if (is_classic_) + on_iso_time(); + else + format_localized('X', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_us_date() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_mon() + 1), + to_unsigned(tm_mday()), + to_unsigned(split_year_lower(tm_year())), '/'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + } + void on_iso_date() { + auto year = tm_year(); + char buf[10]; + size_t offset = 0; + if (year >= 0 && year < 10000) { + copy2(buf, digits2(static_cast(year / 100))); + } else { + offset = 4; + write_year_extended(year); + year = 0; + } + write_digit2_separated(buf + 2, static_cast(year % 100), + to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), + '-'); + out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); + } + + void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } + void on_tz_name() { format_tz_name_impl(tm_); } + + void on_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write_year(tm_year()); + format_localized('Y', 'E'); + } + void on_short_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(split_year_lower(tm_year())); + format_localized('y', 'O'); + } + void on_offset_year() { + if (is_classic_) return write2(split_year_lower(tm_year())); + format_localized('y', 'E'); + } + + void on_century(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto year = tm_year(); + auto upper = year / 100; + if (year >= -99 && year < 0) { + // Zero upper on negative year. + *out_++ = '-'; + *out_++ = '0'; + } else if (upper >= 0 && upper < 100) { + write2(static_cast(upper)); + } else { + out_ = write(out_, upper); + } + } else { + format_localized('C', 'E'); + } + } + + void on_dec_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mon() + 1); + format_localized('m', 'O'); + } + + void on_dec0_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + format_localized('U', 'O'); + } + void on_dec1_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write2((tm_yday() + days_per_week - + (wday == 0 ? (days_per_week - 1) : (wday - 1))) / + days_per_week); + } else { + format_localized('W', 'O'); + } + } + void on_iso_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_iso_week_of_year()); + format_localized('V', 'O'); + } + + void on_iso_week_based_year() { write_year(tm_iso_week_year()); } + void on_iso_week_based_short_year() { + write2(split_year_lower(tm_iso_week_year())); + } + + void on_day_of_year() { + auto yday = tm_yday() + 1; + write1(yday / 100); + write2(yday % 100); + } + void on_day_of_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); + format_localized('d', 'O'); + } + void on_day_of_month_space(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto mday = to_unsigned(tm_mday()) % 100; + const char* d2 = digits2(mday); + *out_++ = mday < 10 ? ' ' : d2[0]; + *out_++ = d2[1]; + } else { + format_localized('e', 'O'); + } + } + + void on_24_hour(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour(), pad); + format_localized('H', 'O'); + } + void on_12_hour(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour12(), pad); + format_localized('I', 'O'); + } + void on_minute(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_min(), pad); + format_localized('M', 'O'); + } + + void on_second(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) { + write2(tm_sec(), pad); + if (subsecs_) { + if (std::is_floating_point::value) { + auto buf = memory_buffer(); + write_floating_seconds(buf, *subsecs_); + if (buf.size() > 1) { + // Remove the leading "0", write something like ".123". + out_ = std::copy(buf.begin() + 1, buf.end(), out_); + } + } else { + write_fractional_seconds(out_, *subsecs_); + } + } + } else { + // Currently no formatting of subseconds when a locale is set. + format_localized('S', 'O'); + } + } + + void on_12_hour_time() { + if (is_classic_) { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour12()), + to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + *out_++ = ' '; + on_am_pm(); + } else { + format_localized('r'); + } + } + void on_24_hour_time() { + write2(tm_hour()); + *out_++ = ':'; + write2(tm_min()); + } + void on_iso_time() { + on_24_hour_time(); + *out_++ = ':'; + on_second(numeric_system::standard, pad_type::unspecified); + } + + void on_am_pm() { + if (is_classic_) { + *out_++ = tm_hour() < 12 ? 'A' : 'P'; + *out_++ = 'M'; + } else { + format_localized('p'); + } + } + + // These apply to chrono durations but not tm. + void on_duration_value() {} + void on_duration_unit() {} +}; + +struct chrono_format_checker : null_chrono_spec_handler { + bool has_precision_integral = false; + + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } + + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_duration_value() const { + if (has_precision_integral) { + FMT_THROW(format_error("precision not allowed for this argument type")); + } + } + FMT_CONSTEXPR void on_duration_unit() {} +}; + +template ::value&& has_isfinite::value)> +inline bool isfinite(T) { + return true; +} + +template ::value)> +inline T mod(T x, int y) { + return x % static_cast(y); +} +template ::value)> +inline T mod(T x, int y) { + return std::fmod(x, static_cast(y)); +} + +// If T is an integral type, maps T to its unsigned counterpart, otherwise +// leaves it unchanged (unlike std::make_unsigned). +template ::value> +struct make_unsigned_or_unchanged { + using type = T; +}; + +template struct make_unsigned_or_unchanged { + using type = typename std::make_unsigned::type; +}; + +#if FMT_SAFE_DURATION_CAST +// throwing version of safe_duration_cast +template +To fmt_safe_duration_cast(std::chrono::duration from) { + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) FMT_THROW(format_error("cannot format duration")); + return to; +} +#endif + +template ::value)> +inline std::chrono::duration get_milliseconds( + std::chrono::duration d) { + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + using CommonSecondsType = + typename std::common_type::type; + const auto d_as_common = fmt_safe_duration_cast(d); + const auto d_as_whole_seconds = + fmt_safe_duration_cast(d_as_common); + // this conversion should be nonproblematic + const auto diff = d_as_common - d_as_whole_seconds; + const auto ms = + fmt_safe_duration_cast>(diff); + return ms; +#else + auto s = std::chrono::duration_cast(d); + return std::chrono::duration_cast(d - s); +#endif +} + +template ::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int) { + return write(out, val); +} + +template ::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int precision) { + auto specs = format_specs(); + specs.precision = precision; + specs.type = precision >= 0 ? presentation_type::fixed_lower + : presentation_type::general_lower; + return write(out, val, specs); +} + +template +OutputIt copy_unit(string_view unit, OutputIt out, Char) { + return std::copy(unit.begin(), unit.end(), out); +} + +template +OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { + // This works when wchar_t is UTF-32 because units only contain characters + // that have the same representation in UTF-16 and UTF-32. + utf8_to_utf16 u(unit); + return std::copy(u.c_str(), u.c_str() + u.size(), out); +} + +template +OutputIt format_duration_unit(OutputIt out) { + if (const char* unit = get_units()) + return copy_unit(string_view(unit), out, Char()); + *out++ = '['; + out = write(out, Period::num); + if (const_check(Period::den != 1)) { + *out++ = '/'; + out = write(out, Period::den); + } + *out++ = ']'; + *out++ = 's'; + return out; +} + +class get_locale { + private: + union { + std::locale locale_; + }; + bool has_locale_ = false; + + public: + get_locale(bool localized, locale_ref loc) : has_locale_(localized) { + if (localized) + ::new (&locale_) std::locale(loc.template get()); + } + ~get_locale() { + if (has_locale_) locale_.~locale(); + } + operator const std::locale&() const { + return has_locale_ ? locale_ : get_classic_locale(); + } +}; + +template +struct chrono_formatter { + FormatContext& context; + OutputIt out; + int precision; + bool localized = false; + // rep is unsigned to avoid overflow. + using rep = + conditional_t::value && sizeof(Rep) < sizeof(int), + unsigned, typename make_unsigned_or_unchanged::type>; + rep val; + using seconds = std::chrono::duration; + seconds s; + using milliseconds = std::chrono::duration; + bool negative; + + using char_type = typename FormatContext::char_type; + using tm_writer_type = tm_writer; + + chrono_formatter(FormatContext& ctx, OutputIt o, + std::chrono::duration d) + : context(ctx), + out(o), + val(static_cast(d.count())), + negative(false) { + if (d.count() < 0) { + val = 0 - val; + negative = true; + } + + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + // might need checked conversion (rep!=Rep) + auto tmpval = std::chrono::duration(val); + s = fmt_safe_duration_cast(tmpval); +#else + s = std::chrono::duration_cast( + std::chrono::duration(val)); +#endif + } + + // returns true if nan or inf, writes to out. + bool handle_nan_inf() { + if (isfinite(val)) { + return false; + } + if (isnan(val)) { + write_nan(); + return true; + } + // must be +-inf + if (val > 0) { + write_pinf(); + } else { + write_ninf(); + } + return true; + } + + Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } + + Rep hour12() const { + Rep hour = static_cast(mod((s.count() / 3600), 12)); + return hour <= 0 ? 12 : hour; + } + + Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } + Rep second() const { return static_cast(mod(s.count(), 60)); } + + std::tm time() const { + auto time = std::tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + time.tm_min = to_nonnegative_int(minute(), 60); + time.tm_sec = to_nonnegative_int(second(), 60); + return time; + } + + void write_sign() { + if (negative) { + *out++ = '-'; + negative = false; + } + } + + void write(Rep value, int width, pad_type pad = pad_type::unspecified) { + write_sign(); + if (isnan(value)) return write_nan(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(value, max_value())); + int num_digits = detail::count_digits(n); + if (width > num_digits) { + out = detail::write_padding(out, pad, width - num_digits); + } + out = format_decimal(out, n, num_digits).end; + } + + void write_nan() { std::copy_n("nan", 3, out); } + void write_pinf() { std::copy_n("inf", 3, out); } + void write_ninf() { std::copy_n("-inf", 4, out); } + + template + void format_tm(const tm& time, Callback cb, Args... args) { + if (isnan(val)) return write_nan(); + get_locale loc(localized, context.locale()); + auto w = tm_writer_type(loc, out, time); + (w.*cb)(args...); + out = w.out(); + } + + void on_text(const char_type* begin, const char_type* end) { + std::copy(begin, end, out); + } + + // These are not implemented because durations don't have date information. + void on_abbr_weekday() {} + void on_full_weekday() {} + void on_dec0_weekday(numeric_system) {} + void on_dec1_weekday(numeric_system) {} + void on_abbr_month() {} + void on_full_month() {} + void on_datetime(numeric_system) {} + void on_loc_date(numeric_system) {} + void on_loc_time(numeric_system) {} + void on_us_date() {} + void on_iso_date() {} + void on_utc_offset(numeric_system) {} + void on_tz_name() {} + void on_year(numeric_system) {} + void on_short_year(numeric_system) {} + void on_offset_year() {} + void on_century(numeric_system) {} + void on_iso_week_based_year() {} + void on_iso_week_based_short_year() {} + void on_dec_month(numeric_system) {} + void on_dec0_week_of_year(numeric_system) {} + void on_dec1_week_of_year(numeric_system) {} + void on_iso_week_of_year(numeric_system) {} + void on_day_of_year() {} + void on_day_of_month(numeric_system) {} + void on_day_of_month_space(numeric_system) {} + + void on_24_hour(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour(), 2, pad); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + format_tm(time, &tm_writer_type::on_24_hour, ns, pad); + } + + void on_12_hour(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour12(), 2, pad); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour12(), 12); + format_tm(time, &tm_writer_type::on_12_hour, ns, pad); + } + + void on_minute(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(minute(), 2, pad); + auto time = tm(); + time.tm_min = to_nonnegative_int(minute(), 60); + format_tm(time, &tm_writer_type::on_minute, ns, pad); + } + + void on_second(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) { + if (std::is_floating_point::value) { + auto buf = memory_buffer(); + write_floating_seconds(buf, std::chrono::duration(val), + precision); + if (negative) *out++ = '-'; + if (buf.size() < 2 || buf[1] == '.') { + out = detail::write_padding(out, pad); + } + out = std::copy(buf.begin(), buf.end(), out); + } else { + write(second(), 2, pad); + write_fractional_seconds( + out, std::chrono::duration(val), precision); + } + return; + } + auto time = tm(); + time.tm_sec = to_nonnegative_int(second(), 60); + format_tm(time, &tm_writer_type::on_second, ns, pad); + } + + void on_12_hour_time() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_12_hour_time); + } + + void on_24_hour_time() { + if (handle_nan_inf()) { + *out++ = ':'; + handle_nan_inf(); + return; + } + + write(hour(), 2); + *out++ = ':'; + write(minute(), 2); + } + + void on_iso_time() { + on_24_hour_time(); + *out++ = ':'; + if (handle_nan_inf()) return; + on_second(numeric_system::standard, pad_type::unspecified); + } + + void on_am_pm() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_am_pm); + } + + void on_duration_value() { + if (handle_nan_inf()) return; + write_sign(); + out = format_duration_value(out, val, precision); + } + + void on_duration_unit() { + out = format_duration_unit(out); + } +}; + +FMT_END_DETAIL_NAMESPACE + +#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 +using weekday = std::chrono::weekday; +#else +// A fallback version of weekday. +class weekday { + private: + unsigned char value; + + public: + weekday() = default; + explicit constexpr weekday(unsigned wd) noexcept + : value(static_cast(wd != 7 ? wd : 0)) {} + constexpr unsigned c_encoding() const noexcept { return value; } +}; + +class year_month_day {}; +#endif + +// A rudimentary weekday formatter. +template struct formatter { + private: + bool localized = false; + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + return begin; + } + + template + auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_wday = static_cast(wd.c_encoding()); + detail::get_locale loc(localized, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_weekday(); + return w.out(); + } +}; + +template +struct formatter, Char> { + private: + format_specs specs; + int precision = -1; + using arg_ref_type = detail::arg_ref; + arg_ref_type width_ref; + arg_ref_type precision_ref; + bool localized = false; + basic_string_view format_str; + using duration = std::chrono::duration; + + using iterator = typename basic_format_parse_context::iterator; + struct parse_range { + iterator begin; + iterator end; + }; + + FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return {begin, begin}; + + begin = detail::parse_align(begin, end, specs); + if (begin == end) return {begin, begin}; + + begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + if (begin == end) return {begin, begin}; + + auto checker = detail::chrono_format_checker(); + if (*begin == '.') { + checker.has_precision_integral = !std::is_floating_point::value; + begin = + detail::parse_precision(begin, end, precision, precision_ref, ctx); + } + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + end = detail::parse_chrono_format(begin, end, checker); + return {begin, end}; + } + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto range = do_parse(ctx); + format_str = basic_string_view( + &*range.begin, detail::to_unsigned(range.end - range.begin)); + return range.end; + } + + template + auto format(const duration& d, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs_copy = specs; + auto precision_copy = precision; + auto begin = format_str.begin(), end = format_str.end(); + // As a possible future optimization, we could avoid extra copying if width + // is not specified. + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec(specs_copy.width, + width_ref, ctx); + detail::handle_dynamic_spec(precision_copy, + precision_ref, ctx); + if (begin == end || *begin == '}') { + out = detail::format_duration_value(out, d.count(), precision_copy); + detail::format_duration_unit(out); + } else { + detail::chrono_formatter f( + ctx, out, d); + f.precision = precision_copy; + f.localized = localized; + detail::parse_chrono_format(begin, end, f); + } + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); + } +}; + +template +struct formatter, + Char> : formatter { + FMT_CONSTEXPR formatter() { + this->format_str = detail::string_literal{}; + } + + template + auto format(std::chrono::time_point val, + FormatContext& ctx) const -> decltype(ctx.out()) { + using period = typename Duration::period; + if (period::num != 1 || period::den != 1 || + std::is_floating_point::value) { + const auto epoch = val.time_since_epoch(); + auto subsecs = std::chrono::duration_cast( + epoch - std::chrono::duration_cast(epoch)); + + if (subsecs.count() < 0) { + auto second = std::chrono::seconds(1); + if (epoch.count() < ((Duration::min)() + second).count()) + FMT_THROW(format_error("duration is too small")); + subsecs += second; + val -= second; + } + + return formatter::do_format( + gmtime(std::chrono::time_point_cast(val)), ctx, + &subsecs); + } + + return formatter::format( + gmtime(std::chrono::time_point_cast(val)), ctx); + } +}; + +#if FMT_USE_LOCAL_TIME +template +struct formatter, Char> + : formatter { + FMT_CONSTEXPR formatter() { + this->format_str = detail::string_literal{}; + } + + template + auto format(std::chrono::local_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + using period = typename Duration::period; + if (period::num != 1 || period::den != 1 || + std::is_floating_point::value) { + const auto epoch = val.time_since_epoch(); + const auto subsecs = std::chrono::duration_cast( + epoch - std::chrono::duration_cast(epoch)); + + return formatter::do_format( + localtime(std::chrono::time_point_cast(val)), + ctx, &subsecs); + } + + return formatter::format( + localtime(std::chrono::time_point_cast(val)), + ctx); + } +}; +#endif + +#if FMT_USE_UTC_TIME +template +struct formatter, + Char> + : formatter, + Char> { + template + auto format(std::chrono::time_point val, + FormatContext& ctx) const -> decltype(ctx.out()) { + return formatter< + std::chrono::time_point, + Char>::format(std::chrono::utc_clock::to_sys(val), ctx); + } +}; +#endif + +template struct formatter { + private: + format_specs specs; + detail::arg_ref width_ref; + + protected: + basic_string_view format_str; + + FMT_CONSTEXPR auto do_parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return begin; + + begin = detail::parse_align(begin, end, specs); + if (begin == end) return end; + + begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + if (begin == end) return end; + + end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); + // Replace default format_str only if the new spec is not empty. + if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)}; + return end; + } + + template + auto do_format(const std::tm& tm, FormatContext& ctx, + const Duration* subsecs) const -> decltype(ctx.out()) { + auto specs_copy = specs; + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec(specs_copy.width, + width_ref, ctx); + + const auto loc_ref = ctx.locale(); + detail::get_locale loc(static_cast(loc_ref), loc_ref); + auto w = + detail::tm_writer(loc, out, tm, subsecs); + detail::parse_chrono_format(format_str.begin(), format_str.end(), w); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); + } + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return this->do_parse(ctx); + } + + template + auto format(const std::tm& tm, FormatContext& ctx) const + -> decltype(ctx.out()) { + return do_format(tm, ctx, nullptr); + } +}; + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_CHRONO_H_ diff --git a/src/3rdparty/fmt/core.h b/src/3rdparty/fmt/core.h index bddad78ec4..46723d5988 100644 --- a/src/3rdparty/fmt/core.h +++ b/src/3rdparty/fmt/core.h @@ -1,4 +1,4 @@ -// Formatting library for C++ - the core API +// Formatting library for C++ - the core API for char/UTF-8 // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. @@ -8,54 +8,59 @@ #ifndef FMT_CORE_H_ #define FMT_CORE_H_ -#include // std::FILE -#include -#include +#include // std::byte +#include // std::FILE +#include // std::strlen #include -#include +#include #include #include -#include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 70103 +#define FMT_VERSION 100000 -#ifdef __clang__ +#if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) #else # define FMT_CLANG_VERSION 0 #endif -#if defined(__GNUC__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ + !defined(__NVCOMPILER) # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) #else # define FMT_GCC_VERSION 0 #endif -#if defined(__INTEL_COMPILER) -# define FMT_ICC_VERSION __INTEL_COMPILER -#else -# define FMT_ICC_VERSION 0 +#ifndef FMT_GCC_PRAGMA +// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. +# if FMT_GCC_VERSION >= 504 +# define FMT_GCC_PRAGMA(arg) _Pragma(arg) +# else +# define FMT_GCC_PRAGMA(arg) +# endif #endif -#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) -# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION +#ifdef __ICL +# define FMT_ICC_VERSION __ICL +#elif defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER #else -# define FMT_HAS_GXX_CXX11 0 +# define FMT_ICC_VERSION 0 #endif -#ifdef __NVCC__ -# define FMT_NVCC __NVCC__ +#ifdef _MSC_VER +# define FMT_MSC_VERSION _MSC_VER +# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) #else -# define FMT_NVCC 0 +# define FMT_MSC_VERSION 0 +# define FMT_MSC_WARNING(...) #endif -#ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER -# define FMT_SUPPRESS_MSC_WARNING(n) __pragma(warning(suppress : n)) +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG #else -# define FMT_MSC_VER 0 -# define FMT_SUPPRESS_MSC_WARNING(n) +# define FMT_CPLUSPLUS __cplusplus #endif #ifdef __has_feature @@ -64,8 +69,7 @@ # define FMT_HAS_FEATURE(x) 0 #endif -#if defined(__has_include) && !defined(__INTELLISENSE__) && \ - (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) +#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900 # define FMT_HAS_INCLUDE(x) __has_include(x) #else # define FMT_HAS_INCLUDE(x) 0 @@ -78,98 +82,78 @@ #endif #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ - (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) #define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ - (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) // Check if relaxed C++14 constexpr is supported. // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR -# define FMT_USE_CONSTEXPR \ - (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ - (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ - !FMT_NVCC && !FMT_ICC_VERSION +# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ + (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ + !FMT_ICC_VERSION && !defined(__NVCC__) +# define FMT_USE_CONSTEXPR 1 +# else +# define FMT_USE_CONSTEXPR 0 +# endif #endif #if FMT_USE_CONSTEXPR # define FMT_CONSTEXPR constexpr -# define FMT_CONSTEXPR_DECL constexpr #else -# define FMT_CONSTEXPR inline -# define FMT_CONSTEXPR_DECL +# define FMT_CONSTEXPR #endif -#ifndef FMT_OVERRIDE -# if FMT_HAS_FEATURE(cxx_override_control) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 -# define FMT_OVERRIDE override -# else -# define FMT_OVERRIDE +#if ((FMT_CPLUSPLUS >= 202002L) && \ + (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEXPR20 +#endif + +// Check if constexpr std::char_traits<>::{compare,length} are supported. +#if defined(__GLIBCXX__) +# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ + _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr # endif +#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ + _LIBCPP_VERSION >= 4000 +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#endif +#ifndef FMT_CONSTEXPR_CHAR_TRAITS +# define FMT_CONSTEXPR_CHAR_TRAITS #endif // Check if exceptions are disabled. #ifndef FMT_EXCEPTIONS # if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - FMT_MSC_VER && !_HAS_EXCEPTIONS + (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) # define FMT_EXCEPTIONS 0 # else # define FMT_EXCEPTIONS 1 # endif #endif -// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). -#ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 -#endif - -#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 -# define FMT_DETECTED_NOEXCEPT noexcept -# define FMT_HAS_CXX11_NOEXCEPT 1 -#else -# define FMT_DETECTED_NOEXCEPT throw() -# define FMT_HAS_CXX11_NOEXCEPT 0 -#endif - -#ifndef FMT_NOEXCEPT -# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT -# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT -# else -# define FMT_NOEXCEPT -# endif -#endif - -// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code -// warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ - !FMT_NVCC +// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ + !defined(__NVCC__) # define FMT_NORETURN [[noreturn]] #else # define FMT_NORETURN #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 -# define FMT_DEPRECATED [[deprecated]] +#ifndef FMT_NODISCARD +# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] # else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VER -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif +# define FMT_NODISCARD # endif #endif -// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. -#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC -# define FMT_DEPRECATED_ALIAS -#else -# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED -#endif - #ifndef FMT_INLINE # if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_INLINE inline __attribute__((always_inline)) @@ -178,86 +162,107 @@ # endif #endif -#ifndef FMT_USE_INLINE_NAMESPACES -# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ - (FMT_MSC_VER >= 1900 && !_MANAGED) -# define FMT_USE_INLINE_NAMESPACES 1 -# else -# define FMT_USE_INLINE_NAMESPACES 0 -# endif +// An inline std::forward replacement. +#define FMT_FORWARD(...) static_cast(__VA_ARGS__) + +#ifdef _MSC_VER +# define FMT_UNCHECKED_ITERATOR(It) \ + using _Unchecked_type = It // Mark iterator as checked. +#else +# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It #endif #ifndef FMT_BEGIN_NAMESPACE -# if FMT_USE_INLINE_NAMESPACES -# define FMT_INLINE_NAMESPACE inline namespace -# define FMT_END_NAMESPACE \ - } \ - } -# else -# define FMT_INLINE_NAMESPACE namespace -# define FMT_END_NAMESPACE \ - } \ - using namespace v7; \ - } -# endif # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - FMT_INLINE_NAMESPACE v7 { + inline namespace v10 { +# define FMT_END_NAMESPACE \ + } \ + } +#endif + +#ifndef FMT_MODULE_EXPORT +# define FMT_MODULE_EXPORT +# define FMT_BEGIN_EXPORT +# define FMT_END_EXPORT #endif #if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# define FMT_CLASS_API FMT_SUPPRESS_MSC_WARNING(4275) -# ifdef FMT_EXPORT +# ifdef FMT_LIB_EXPORT # define FMT_API __declspec(dllexport) -# define FMT_EXTERN_TEMPLATE_API FMT_API -# define FMT_EXPORTED # elif defined(FMT_SHARED) # define FMT_API __declspec(dllimport) -# define FMT_EXTERN_TEMPLATE_API FMT_API # endif #else -# define FMT_CLASS_API +# if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# if defined(__GNUC__) || defined(__clang__) +# define FMT_API __attribute__((visibility("default"))) +# endif +# endif #endif #ifndef FMT_API # define FMT_API #endif -#ifndef FMT_EXTERN_TEMPLATE_API -# define FMT_EXTERN_TEMPLATE_API -#endif -#ifndef FMT_INSTANTIATION_DEF_API -# define FMT_INSTANTIATION_DEF_API FMT_API -#endif - -#ifndef FMT_HEADER_ONLY -# define FMT_EXTERN extern -#else -# define FMT_EXTERN -#endif // libc++ supports string_view in pre-c++17. -#if (FMT_HAS_INCLUDE() && \ - (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ - (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +#if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) # include # define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L # include # define FMT_USE_EXPERIMENTAL_STRING_VIEW #endif #ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VER +# define FMT_UNICODE !FMT_MSC_VERSION #endif -#if FMT_UNICODE && FMT_MSC_VER -# pragma execution_character_set("utf-8") + +#ifndef FMT_CONSTEVAL +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + (!defined(__apple_build_version__) || \ + __apple_build_version__ >= 14000029L) && \ + FMT_CPLUSPLUS >= 202002L) || \ + (defined(__cpp_consteval) && \ + (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) +// consteval is broken in MSVC before VS2022 and Apple clang before 14. +# define FMT_CONSTEVAL consteval +# define FMT_HAS_CONSTEVAL +# else +# define FMT_CONSTEVAL +# endif +#endif + +#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS +# if defined(__cpp_nontype_template_args) && \ + ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ + __cpp_nontype_template_args >= 201911L) && \ + !defined(__NVCOMPILER) && !defined(__LCC__) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +# else +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +# endif +#endif + +#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L +# define FMT_INLINE_VARIABLE inline +#else +# define FMT_INLINE_VARIABLE +#endif + +// Enable minimal optimizations for more compact code in debug mode. +FMT_GCC_PRAGMA("GCC push_options") +#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ + !defined(__CUDACC__) +FMT_GCC_PRAGMA("GCC optimize(\"Og\")") #endif FMT_BEGIN_NAMESPACE // Implementations of enable_if_t and other metafunctions for older systems. -template +template using enable_if_t = typename std::enable_if::type; -template +template using conditional_t = typename std::conditional::type; template using bool_constant = std::integral_constant; template @@ -268,31 +273,70 @@ template using remove_cvref_t = typename std::remove_cv>::type; template struct type_identity { using type = T; }; template using type_identity_t = typename type_identity::type; +template +using underlying_t = typename std::underlying_type::type; -struct monostate {}; +struct monostate { + constexpr monostate() {} +}; // An enable_if helper to be used in template parameters which results in much // shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed // to workaround a bug in MSVC 2019 (see #1140 and #1186). -#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 +#ifdef FMT_DOC +# define FMT_ENABLE_IF(...) +#else +# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 +#endif + +#ifdef __cpp_lib_byte +inline auto format_as(std::byte b) -> unsigned char { + return static_cast(b); +} +#endif namespace detail { +// Suppresses "unused variable" warnings with the method described in +// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. +// (void)var does not work on many Intel compilers. +template FMT_CONSTEXPR void ignore_unused(const T&...) {} + +constexpr FMT_INLINE auto is_constant_evaluated( + bool default_value = false) noexcept -> bool { +// Workaround for incompatibility between libstdc++ consteval-based +// std::is_constant_evaluated() implementation and clang-14. +// https://github.com/fmtlib/fmt/issues/3247 +#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \ + _GLIBCXX_RELEASE >= 12 && \ + (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) + ignore_unused(default_value); + return __builtin_is_constant_evaluated(); +#elif defined(__cpp_lib_is_constant_evaluated) + ignore_unused(default_value); + return std::is_constant_evaluated(); +#else + return default_value; +#endif +} -// A helper function to suppress "conditional expression is constant" warnings. -template constexpr T const_check(T value) { return value; } +// Suppresses "conditional expression is constant" warnings. +template constexpr FMT_INLINE auto const_check(T value) -> T { + return value; +} FMT_NORETURN FMT_API void assert_fail(const char* file, int line, const char* message); #ifndef FMT_ASSERT # ifdef NDEBUG -// FMT_ASSERT is not empty to avoid -Werror=empty-body. -# define FMT_ASSERT(condition, message) ((void)0) +// FMT_ASSERT is not empty to avoid -Wempty-body. +# define FMT_ASSERT(condition, message) \ + fmt::detail::ignore_unused((condition), (message)) # else # define FMT_ASSERT(condition, message) \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ ? (void)0 \ - : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message))) + : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) # endif #endif @@ -307,44 +351,42 @@ template struct std_string_view {}; #ifdef FMT_USE_INT128 // Do nothing. -#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ - !(FMT_CLANG_VERSION && FMT_MSC_VER) +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) # define FMT_USE_INT128 1 -using int128_t = __int128_t; -using uint128_t = __uint128_t; +using int128_opt = __int128_t; // An optional native 128-bit integer. +using uint128_opt = __uint128_t; +template inline auto convert_for_visit(T value) -> T { + return value; +} #else # define FMT_USE_INT128 0 #endif #if !FMT_USE_INT128 -struct int128_t {}; -struct uint128_t {}; +enum class int128_opt {}; +enum class uint128_opt {}; +// Reduce template instantiations. +template auto convert_for_visit(T) -> monostate { return {}; } #endif // Casts a nonnegative integer to unsigned. template -FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { - FMT_ASSERT(value >= 0, "negative value"); +FMT_CONSTEXPR auto to_unsigned(Int value) -> + typename std::make_unsigned::type { + FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); return static_cast::type>(value); } -FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5"; +FMT_CONSTEXPR inline auto is_utf8() -> bool { + FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; -template constexpr bool is_unicode() { - return FMT_UNICODE || sizeof(Char) != 1 || - (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5); + // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). + using uchar = unsigned char; + return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && + uchar(section[1]) == 0xA7); } - -#ifdef __cpp_char8_t -using char8_type = char8_t; -#else -enum char8_type : unsigned char {}; -#endif } // namespace detail -#ifdef FMT_USE_INTERNAL -namespace internal = detail; // DEPRECATED -#endif - /** An implementation of ``std::basic_string_view`` for pre-C++17. It provides a subset of the API. ``fmt::basic_string_view`` is used for format strings even @@ -352,6 +394,7 @@ namespace internal = detail; // DEPRECATED compiled with a different ``-std`` option than the client code (which is not recommended). */ +FMT_MODULE_EXPORT template class basic_string_view { private: const Char* data_; @@ -361,12 +404,11 @@ template class basic_string_view { using value_type = Char; using iterator = const Char*; - constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} + constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} /** Constructs a string reference object from a C string and a size. */ - constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT - : data_(s), - size_(count) {} + constexpr basic_string_view(const Char* s, size_t count) noexcept + : data_(s), size_(count) {} /** \rst @@ -374,42 +416,58 @@ template class basic_string_view { the size with ``std::char_traits::length``. \endrst */ -#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr. - FMT_CONSTEXPR -#endif + FMT_CONSTEXPR_CHAR_TRAITS + FMT_INLINE basic_string_view(const Char* s) - : data_(s), size_(std::char_traits::length(s)) {} + : data_(s), + size_(detail::const_check(std::is_same::value && + !detail::is_constant_evaluated(true)) + ? std::strlen(reinterpret_cast(s)) + : std::char_traits::length(s)) {} /** Constructs a string reference from a ``std::basic_string`` object. */ template FMT_CONSTEXPR basic_string_view( - const std::basic_string& s) FMT_NOEXCEPT - : data_(s.data()), - size_(s.size()) {} + const std::basic_string& s) noexcept + : data_(s.data()), size_(s.size()) {} template >::value)> - FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), - size_(s.size()) {} + FMT_CONSTEXPR basic_string_view(S s) noexcept + : data_(s.data()), size_(s.size()) {} /** Returns a pointer to the string data. */ - constexpr const Char* data() const { return data_; } + constexpr auto data() const noexcept -> const Char* { return data_; } /** Returns the string size. */ - constexpr size_t size() const { return size_; } + constexpr auto size() const noexcept -> size_t { return size_; } - constexpr iterator begin() const { return data_; } - constexpr iterator end() const { return data_ + size_; } + constexpr auto begin() const noexcept -> iterator { return data_; } + constexpr auto end() const noexcept -> iterator { return data_ + size_; } - constexpr const Char& operator[](size_t pos) const { return data_[pos]; } + constexpr auto operator[](size_t pos) const noexcept -> const Char& { + return data_[pos]; + } - FMT_CONSTEXPR void remove_prefix(size_t n) { + FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { data_ += n; size_ -= n; } + FMT_CONSTEXPR_CHAR_TRAITS bool starts_with( + basic_string_view sv) const noexcept { + return size_ >= sv.size_ && + std::char_traits::compare(data_, sv.data_, sv.size_) == 0; + } + FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept { + return size_ >= 1 && std::char_traits::eq(*data_, c); + } + FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const { + return starts_with(basic_string_view(s)); + } + // Lexicographically compare this string reference to other. - int compare(basic_string_view other) const { + FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { size_t str_size = size_ < other.size_ ? size_ : other.size_; int result = std::char_traits::compare(data_, other.data_, str_size); if (result == 0) @@ -417,97 +475,77 @@ template class basic_string_view { return result; } - friend bool operator==(basic_string_view lhs, basic_string_view rhs) { + FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, + basic_string_view rhs) + -> bool { return lhs.compare(rhs) == 0; } - friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { + friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) != 0; } - friend bool operator<(basic_string_view lhs, basic_string_view rhs) { + friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) < 0; } - friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { + friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) <= 0; } - friend bool operator>(basic_string_view lhs, basic_string_view rhs) { + friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) > 0; } - friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { + friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) >= 0; } }; +FMT_MODULE_EXPORT using string_view = basic_string_view; -using wstring_view = basic_string_view; /** Specifies if ``T`` is a character type. Can be specialized by users. */ +FMT_MODULE_EXPORT template struct is_char : std::false_type {}; template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -/** - \rst - Returns a string view of `s`. In order to add custom string type support to - {fmt} provide an overload of `to_string_view` for it in the same namespace as - the type for the argument-dependent lookup to work. +namespace detail { - **Example**:: +// A base class for compile-time strings. +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; - namespace my_ns { - inline string_view to_string_view(const my_string& s) { - return {s.data(), s.length()}; - } - } - std::string message = fmt::format(my_string("The answer is {}"), 42); - \endrst - */ template ::value)> -inline basic_string_view to_string_view(const Char* s) { +FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { return s; } - template -inline basic_string_view to_string_view( - const std::basic_string& s) { +inline auto to_string_view(const std::basic_string& s) + -> basic_string_view { return s; } - template -inline basic_string_view to_string_view(basic_string_view s) { +constexpr auto to_string_view(basic_string_view s) + -> basic_string_view { return s; } - template >::value)> -inline basic_string_view to_string_view(detail::std_string_view s) { + FMT_ENABLE_IF(!std::is_empty>::value)> +inline auto to_string_view(std_string_view s) -> basic_string_view { return s; } - -// A base class for compile-time strings. It is defined in the fmt namespace to -// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42). -struct compile_string {}; - -template -struct is_compile_string : std::is_base_of {}; - template ::value)> -constexpr basic_string_view to_string_view(const S& s) { - return s; +constexpr auto to_string_view(const S& s) + -> basic_string_view { + return basic_string_view(s); } - -namespace detail { void to_string_view(...); -using fmt::v7::to_string_view; // Specifies whether S is a string type convertible to fmt::basic_string_view. // It should be a constexpr function but MSVC 2017 fails to compile it in // enable_if and MSVC 2015 fails to compile it as an alias template. +// ADL is intentionally disabled as to_string_view is not an extension point. template -struct is_string : std::is_class()))> { -}; +struct is_string + : std::is_class()))> {}; template struct char_t_impl {}; template struct char_t_impl::value>> { @@ -515,23 +553,89 @@ template struct char_t_impl::value>> { using type = typename result::value_type; }; -// Reports a compile-time error if S is not a valid format string. -template ::value)> -FMT_INLINE void check_format_string(const S&) { -#ifdef FMT_ENFORCE_COMPILE_STRING - static_assert(is_compile_string::value, - "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " - "FMT_STRING."); -#endif +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; +} +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; +} + +constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } +constexpr auto in(type t, int set) -> bool { + return ((set >> static_cast(t)) & 1) != 0; } -template ::value)> -void check_format_string(S); + +// Bitsets of types. +enum { + sint_set = + set(type::int_type) | set(type::long_long_type) | set(type::int128_type), + uint_set = set(type::uint_type) | set(type::ulong_long_type) | + set(type::uint128_type), + bool_set = set(type::bool_type), + char_set = set(type::char_type), + float_set = set(type::float_type) | set(type::double_type) | + set(type::long_double_type), + string_set = set(type::string_type), + cstring_set = set(type::cstring_type), + pointer_set = set(type::pointer_type) +}; + +FMT_NORETURN FMT_API void throw_format_error(const char* message); struct error_handler { constexpr error_handler() = default; // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN FMT_API void on_error(const char* message); + FMT_NORETURN void on_error(const char* message) { + throw_format_error(message); + } }; } // namespace detail @@ -542,43 +646,37 @@ template using char_t = typename detail::char_t_impl::type; \rst Parsing context consisting of a format string range being parsed and an argument counter for automatic indexing. - - You can use one of the following type aliases for common character types: - - +-----------------------+-------------------------------------+ - | Type | Definition | - +=======================+=====================================+ - | format_parse_context | basic_format_parse_context | - +-----------------------+-------------------------------------+ - | wformat_parse_context | basic_format_parse_context | - +-----------------------+-------------------------------------+ + You can use the ``format_parse_context`` type alias for ``char`` instead. \endrst */ -template -class basic_format_parse_context : private ErrorHandler { +FMT_MODULE_EXPORT +template class basic_format_parse_context { private: basic_string_view format_str_; int next_arg_id_; + FMT_CONSTEXPR void do_check_arg_id(int id); + public: using char_type = Char; - using iterator = typename basic_string_view::iterator; + using iterator = const Char*; explicit constexpr basic_format_parse_context( - basic_string_view format_str, ErrorHandler eh = {}, - int next_arg_id = 0) - : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {} + basic_string_view format_str, int next_arg_id = 0) + : format_str_(format_str), next_arg_id_(next_arg_id) {} /** Returns an iterator to the beginning of the format string range being parsed. */ - constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); } + constexpr auto begin() const noexcept -> iterator { + return format_str_.begin(); + } /** Returns an iterator past the end of the format string range being parsed. */ - constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); } + constexpr auto end() const noexcept -> iterator { return format_str_.end(); } /** Advances the begin iterator to ``it``. */ FMT_CONSTEXPR void advance_to(iterator it) { @@ -589,42 +687,105 @@ class basic_format_parse_context : private ErrorHandler { Reports an error if using the manual argument indexing; otherwise returns the next argument index and switches to the automatic indexing. */ - FMT_CONSTEXPR int next_arg_id() { - // Don't check if the argument id is valid to avoid overhead and because it - // will be checked during formatting anyway. - if (next_arg_id_ >= 0) return next_arg_id_++; - on_error("cannot switch from manual to automatic argument indexing"); - return 0; + FMT_CONSTEXPR auto next_arg_id() -> int { + if (next_arg_id_ < 0) { + detail::throw_format_error( + "cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; } /** Reports an error if using the automatic argument indexing; otherwise switches to the manual indexing. */ - FMT_CONSTEXPR void check_arg_id(int) { - if (next_arg_id_ > 0) - on_error("cannot switch from automatic to manual argument indexing"); - else - next_arg_id_ = -1; + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { + detail::throw_format_error( + "cannot switch from automatic to manual argument indexing"); + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); } - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); +}; - FMT_CONSTEXPR void on_error(const char* message) { - ErrorHandler::on_error(message); +FMT_MODULE_EXPORT +using format_parse_context = basic_format_parse_context; + +namespace detail { +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context : public basic_format_parse_context { + private: + int num_args_; + const type* types_; + using base = basic_format_parse_context; + + public: + explicit FMT_CONSTEXPR compile_parse_context( + basic_string_view format_str, int num_args, const type* types, + int next_arg_id = 0) + : base(format_str, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) throw_format_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) throw_format_error("argument not found"); } + using base::check_arg_id; - constexpr ErrorHandler error_handler() const { return *this; } + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + detail::ignore_unused(arg_id); +#if !defined(__LCC__) + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + throw_format_error("width/precision is not integer"); +#endif + } }; +} // namespace detail -using format_parse_context = basic_format_parse_context; -using wformat_parse_context = basic_format_parse_context; +template +FMT_CONSTEXPR void basic_format_parse_context::do_check_arg_id(int id) { + // Argument id is only checked at compile-time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && + (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { + using context = detail::compile_parse_context; + if (id >= static_cast(this)->num_args()) + detail::throw_format_error("argument not found"); + } +} + +template +FMT_CONSTEXPR void basic_format_parse_context::check_dynamic_spec( + int arg_id) { + if (detail::is_constant_evaluated() && + (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { + using context = detail::compile_parse_context; + static_cast(this)->check_dynamic_spec(arg_id); + } +} -template class basic_format_arg; -template class basic_format_args; -template class dynamic_format_arg_store; +FMT_MODULE_EXPORT template class basic_format_arg; +FMT_MODULE_EXPORT template class basic_format_args; +FMT_MODULE_EXPORT template class dynamic_format_arg_store; // A formatter for objects of type T. +FMT_MODULE_EXPORT template struct formatter { // A deleted default constructor indicates a disabled formatter. @@ -642,19 +803,55 @@ template struct is_contiguous : std::false_type {}; template struct is_contiguous> : std::true_type {}; +class appender; + namespace detail { +template +constexpr auto has_const_formatter_impl(T*) + -> decltype(typename Context::template formatter_type().format( + std::declval(), std::declval()), + true) { + return true; +} +template +constexpr auto has_const_formatter_impl(...) -> bool { + return false; +} +template +constexpr auto has_const_formatter() -> bool { + return has_const_formatter_impl(static_cast(nullptr)); +} + // Extracts a reference to the container from back_insert_iterator. template -inline Container& get_container(std::back_insert_iterator it) { - using bi_iterator = std::back_insert_iterator; - struct accessor : bi_iterator { - accessor(bi_iterator iter) : bi_iterator(iter) {} - using bi_iterator::container; +inline auto get_container(std::back_insert_iterator it) + -> Container& { + using base = std::back_insert_iterator; + struct accessor : base { + accessor(base b) : base(b) {} + using base::container; }; return *accessor(it).container; } +template +FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template , U>::value&& is_char::value)> +FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { + if (is_constant_evaluated()) return copy_str(begin, end, out); + auto size = to_unsigned(end - begin); + if (size > 0) memcpy(out, begin, size * sizeof(U)); + return out + size; +} + /** \rst A contiguous memory buffer with an optional growing ability. It is an internal @@ -669,24 +866,23 @@ template class buffer { protected: // Don't initialize ptr_ since it is not accessed to save a few cycles. - FMT_SUPPRESS_MSC_WARNING(26495) - buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} + FMT_MSC_WARNING(suppress : 26495) + buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} - buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT - : ptr_(p), - size_(sz), - capacity_(cap) {} + FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept + : ptr_(p), size_(sz), capacity_(cap) {} - ~buffer() = default; + FMT_CONSTEXPR20 ~buffer() = default; + buffer(buffer&&) = default; /** Sets the buffer data and capacity. */ - void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { ptr_ = buf_data; capacity_ = buf_capacity; } /** Increases the buffer capacity to hold at least *capacity* elements. */ - virtual void grow(size_t capacity) = 0; + virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; public: using value_type = T; @@ -695,30 +891,30 @@ template class buffer { buffer(const buffer&) = delete; void operator=(const buffer&) = delete; - T* begin() FMT_NOEXCEPT { return ptr_; } - T* end() FMT_NOEXCEPT { return ptr_ + size_; } + FMT_INLINE auto begin() noexcept -> T* { return ptr_; } + FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; } - const T* begin() const FMT_NOEXCEPT { return ptr_; } - const T* end() const FMT_NOEXCEPT { return ptr_ + size_; } + FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; } + FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; } /** Returns the size of this buffer. */ - size_t size() const FMT_NOEXCEPT { return size_; } + constexpr auto size() const noexcept -> size_t { return size_; } /** Returns the capacity of this buffer. */ - size_t capacity() const FMT_NOEXCEPT { return capacity_; } + constexpr auto capacity() const noexcept -> size_t { return capacity_; } /** Returns a pointer to the buffer data. */ - T* data() FMT_NOEXCEPT { return ptr_; } + FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } /** Returns a pointer to the buffer data. */ - const T* data() const FMT_NOEXCEPT { return ptr_; } + FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } /** Clears this buffer. */ void clear() { size_ = 0; } // Tries resizing the buffer to contain *count* elements. If T is a POD type // the new elements may not be initialized. - void try_resize(size_t count) { + FMT_CONSTEXPR20 void try_resize(size_t count) { try_reserve(count); size_ = count <= capacity_ ? count : capacity_; } @@ -727,11 +923,11 @@ template class buffer { // capacity by a smaller amount than requested but guarantees there is space // for at least one additional element either by increasing the capacity or by // flushing the buffer if it is full. - void try_reserve(size_t new_capacity) { + FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { if (new_capacity > capacity_) grow(new_capacity); } - void push_back(const T& value) { + FMT_CONSTEXPR20 void push_back(const T& value) { try_reserve(size_ + 1); ptr_[size_++] = value; } @@ -739,16 +935,19 @@ template class buffer { /** Appends data to the end of the buffer. */ template void append(const U* begin, const U* end); - template T& operator[](I index) { return ptr_[index]; } - template const T& operator[](I index) const { + template FMT_CONSTEXPR auto operator[](Idx index) -> T& { + return ptr_[index]; + } + template + FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { return ptr_[index]; } }; struct buffer_traits { explicit buffer_traits(size_t) {} - size_t count() const { return 0; } - size_t limit(size_t size) { return size; } + auto count() const -> size_t { return 0; } + auto limit(size_t size) -> size_t { return size; } }; class fixed_buffer_traits { @@ -758,8 +957,8 @@ class fixed_buffer_traits { public: explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} - size_t count() const { return count_; } - size_t limit(size_t size) { + auto count() const -> size_t { return count_; } + auto limit(size_t size) -> size_t { size_t n = limit_ > count_ ? limit_ - count_ : 0; count_ += size; return size < n ? size : n; @@ -775,33 +974,84 @@ class iterator_buffer final : public Traits, public buffer { T data_[buffer_size]; protected: - void grow(size_t) final FMT_OVERRIDE { + FMT_CONSTEXPR20 void grow(size_t) override { if (this->size() == buffer_size) flush(); } - void flush(); + + void flush() { + auto size = this->size(); + this->clear(); + out_ = copy_str(data_, data_ + this->limit(size), out_); + } public: explicit iterator_buffer(OutputIt out, size_t n = buffer_size) - : Traits(n), - buffer(data_, 0, buffer_size), - out_(out) {} + : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} + ~iterator_buffer() { flush(); } + + auto out() -> OutputIt { + flush(); + return out_; + } + auto count() const -> size_t { return Traits::count() + this->size(); } +}; + +template +class iterator_buffer final + : public fixed_buffer_traits, + public buffer { + private: + T* out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() == this->capacity()) flush(); + } + + void flush() { + size_t n = this->limit(this->size()); + if (this->data() == out_) { + out_ += n; + this->set(data_, buffer_size); + } + this->clear(); + } + + public: + explicit iterator_buffer(T* out, size_t n = buffer_size) + : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : fixed_buffer_traits(other), + buffer(std::move(other)), + out_(other.out_) { + if (this->data() != out_) { + this->set(data_, buffer_size); + this->clear(); + } + } ~iterator_buffer() { flush(); } - OutputIt out() { + auto out() -> T* { flush(); return out_; } - size_t count() const { return Traits::count() + this->size(); } + auto count() const -> size_t { + return fixed_buffer_traits::count() + this->size(); + } }; template class iterator_buffer final : public buffer { protected: - void grow(size_t) final FMT_OVERRIDE {} + FMT_CONSTEXPR20 void grow(size_t) override {} public: explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} - T* out() { return &*this->end(); } + auto out() -> T* { return &*this->end(); } }; // A buffer that writes to a container with the contiguous storage. @@ -814,7 +1064,7 @@ class iterator_buffer, Container& container_; protected: - void grow(size_t capacity) final FMT_OVERRIDE { + FMT_CONSTEXPR20 void grow(size_t capacity) override { container_.resize(capacity); this->set(&container_[0], capacity); } @@ -824,7 +1074,8 @@ class iterator_buffer, : buffer(c.size()), container_(c) {} explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) : iterator_buffer(get_container(out)) {} - std::back_insert_iterator out() { + + auto out() -> std::back_insert_iterator { return std::back_inserter(container_); } }; @@ -837,7 +1088,7 @@ template class counting_buffer final : public buffer { size_t count_ = 0; protected: - void grow(size_t) final FMT_OVERRIDE { + FMT_CONSTEXPR20 void grow(size_t) override { if (this->size() != buffer_size) return; count_ += this->size(); this->clear(); @@ -846,61 +1097,33 @@ template class counting_buffer final : public buffer { public: counting_buffer() : buffer(data_, 0, buffer_size) {} - size_t count() { return count_ + this->size(); } + auto count() -> size_t { return count_ + this->size(); } }; -// An output iterator that appends to the buffer. -// It is used to reduce symbol sizes for the common case. template -class buffer_appender : public std::back_insert_iterator> { - using base = std::back_insert_iterator>; - - public: - explicit buffer_appender(buffer& buf) : base(buf) {} - buffer_appender(base it) : base(it) {} +using buffer_appender = conditional_t::value, appender, + std::back_insert_iterator>>; - buffer_appender& operator++() { - base::operator++(); - return *this; - } - - buffer_appender operator++(int) { - buffer_appender tmp = *this; - ++*this; - return tmp; - } -}; - -// Maps an output iterator into a buffer. +// Maps an output iterator to a buffer. template -iterator_buffer get_buffer(OutputIt); -template buffer& get_buffer(buffer_appender); - -template OutputIt get_buffer_init(OutputIt out) { - return out; +auto get_buffer(OutputIt out) -> iterator_buffer { + return iterator_buffer(out); } -template buffer& get_buffer_init(buffer_appender out) { +template , Buf>::value)> +auto get_buffer(std::back_insert_iterator out) -> buffer& { return get_container(out); } -template -auto get_iterator(Buffer& buf) -> decltype(buf.out()) { +template +FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { return buf.out(); } -template buffer_appender get_iterator(buffer& buf) { - return buffer_appender(buf); +template +auto get_iterator(buffer&, OutputIt out) -> OutputIt { + return out; } -template -struct fallback_formatter { - fallback_formatter() = delete; -}; - -// Specifies if T has an enabled fallback_formatter specialization. -template -using has_fallback_formatter = - std::is_constructible>; - struct view {}; template struct named_arg : view { @@ -924,8 +1147,8 @@ struct arg_data { template arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} arg_data(const arg_data& other) = delete; - const T* args() const { return args_ + 1; } - named_arg_info* named_args() { return named_args_; } + auto args() const -> const T* { return args_ + 1; } + auto named_args() -> named_arg_info* { return named_args_; } }; template @@ -934,99 +1157,58 @@ struct arg_data { T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; template - FMT_INLINE arg_data(const U&... init) : args_{init...} {} - FMT_INLINE const T* args() const { return args_; } - FMT_INLINE std::nullptr_t named_args() { return nullptr; } + FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} + FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } + FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { + return nullptr; + } }; template inline void init_named_args(named_arg_info*, int, int) {} -template +template struct is_named_arg : std::false_type {}; +template struct is_statically_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template ::value)> void init_named_args(named_arg_info* named_args, int arg_count, int named_arg_count, const T&, const Tail&... args) { init_named_args(named_args, arg_count + 1, named_arg_count, args...); } -template +template ::value)> void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const named_arg& arg, - const Tail&... args) { + int named_arg_count, const T& arg, const Tail&... args) { named_args[named_arg_count++] = {arg.name, arg_count}; init_named_args(named_args, arg_count + 1, named_arg_count, args...); } template -FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {} - -template struct is_named_arg : std::false_type {}; +FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, + const Args&...) {} -template -struct is_named_arg> : std::true_type {}; - -template constexpr size_t count() { return B ? 1 : 0; } -template constexpr size_t count() { +template constexpr auto count() -> size_t { return B ? 1 : 0; } +template constexpr auto count() -> size_t { return (B1 ? 1 : 0) + count(); } -template constexpr size_t count_named_args() { +template constexpr auto count_named_args() -> size_t { return count::value...>(); } -enum class type { - none_type, - // Integer types should go first, - int_type, - uint_type, - long_long_type, - ulong_long_type, - int128_type, - uint128_type, - bool_type, - char_type, - last_integer_type = char_type, - // followed by floating-point types. - float_type, - double_type, - long_double_type, - last_numeric_type = long_double_type, - cstring_type, - string_type, - pointer_type, - custom_type -}; - -// Maps core type T to the corresponding type enum constant. -template -struct type_constant : std::integral_constant {}; - -#define FMT_TYPE_CONSTANT(Type, constant) \ - template \ - struct type_constant \ - : std::integral_constant {} - -FMT_TYPE_CONSTANT(int, int_type); -FMT_TYPE_CONSTANT(unsigned, uint_type); -FMT_TYPE_CONSTANT(long long, long_long_type); -FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); -FMT_TYPE_CONSTANT(int128_t, int128_type); -FMT_TYPE_CONSTANT(uint128_t, uint128_type); -FMT_TYPE_CONSTANT(bool, bool_type); -FMT_TYPE_CONSTANT(Char, char_type); -FMT_TYPE_CONSTANT(float, float_type); -FMT_TYPE_CONSTANT(double, double_type); -FMT_TYPE_CONSTANT(long double, long_double_type); -FMT_TYPE_CONSTANT(const Char*, cstring_type); -FMT_TYPE_CONSTANT(basic_string_view, string_type); -FMT_TYPE_CONSTANT(const void*, pointer_type); - -constexpr bool is_integral_type(type t) { - return t > type::none_type && t <= type::last_integer_type; +template +constexpr auto count_statically_named_args() -> size_t { + return count::value...>(); } -constexpr bool is_arithmetic_type(type t) { - return t > type::none_type && t <= type::last_numeric_type; -} +struct unformattable {}; +struct unformattable_char : unformattable {}; +struct unformattable_pointer : unformattable {}; template struct string_value { const Char* data; @@ -1040,8 +1222,8 @@ template struct named_arg_value { template struct custom_value { using parse_context = typename Context::parse_context_type; - const void* value; - void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); + void* value; + void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); }; // A formatting argument value. @@ -1050,12 +1232,13 @@ template class value { using char_type = typename Context::char_type; union { + monostate no_value; int int_value; unsigned uint_value; long long long_long_value; unsigned long long ulong_long_value; - int128_t int128_value; - uint128_t uint128_value; + int128_opt int128_value; + uint128_opt uint128_value; bool bool_value; char_type char_value; float float_value; @@ -1067,19 +1250,23 @@ template class value { named_arg_value named_args; }; - constexpr FMT_INLINE value(int val = 0) : int_value(val) {} + constexpr FMT_INLINE value() : no_value() {} + constexpr FMT_INLINE value(int val) : int_value(val) {} constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} - FMT_INLINE value(long long val) : long_long_value(val) {} - FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} - FMT_INLINE value(int128_t val) : int128_value(val) {} - FMT_INLINE value(uint128_t val) : uint128_value(val) {} - FMT_INLINE value(float val) : float_value(val) {} - FMT_INLINE value(double val) : double_value(val) {} + constexpr FMT_INLINE value(long long val) : long_long_value(val) {} + constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} + FMT_INLINE value(int128_opt val) : int128_value(val) {} + FMT_INLINE value(uint128_opt val) : uint128_value(val) {} + constexpr FMT_INLINE value(float val) : float_value(val) {} + constexpr FMT_INLINE value(double val) : double_value(val) {} FMT_INLINE value(long double val) : long_double_value(val) {} - FMT_INLINE value(bool val) : bool_value(val) {} - FMT_INLINE value(char_type val) : char_value(val) {} - FMT_INLINE value(const char_type* val) { string.data = val; } - FMT_INLINE value(basic_string_view val) { + constexpr FMT_INLINE value(bool val) : bool_value(val) {} + constexpr FMT_INLINE value(char_type val) : char_value(val) {} + FMT_CONSTEXPR FMT_INLINE value(const char_type* val) { + string.data = val; + if (is_constant_evaluated()) string.size = {}; + } + FMT_CONSTEXPR FMT_INLINE value(basic_string_view val) { string.data = val.data(); string.size = val.size(); } @@ -1087,31 +1274,35 @@ template class value { FMT_INLINE value(const named_arg_info* args, size_t size) : named_args{args, size} {} - template FMT_INLINE value(const T& val) { - custom.value = &val; + template FMT_CONSTEXPR FMT_INLINE value(T& val) { + using value_type = remove_cvref_t; + custom.value = const_cast(&val); // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter` for `format` and // `printf_formatter` for `printf`. custom.format = format_custom_arg< - T, conditional_t::value, - typename Context::template formatter_type, - fallback_formatter>>; + value_type, typename Context::template formatter_type>; } + value(unformattable); + value(unformattable_char); + value(unformattable_pointer); private: // Formats an argument of a custom type, such as a user-defined class. template - static void format_custom_arg(const void* arg, + static void format_custom_arg(void* arg, typename Context::parse_context_type& parse_ctx, Context& ctx) { - Formatter f; + auto f = Formatter(); parse_ctx.advance_to(f.parse(parse_ctx)); - ctx.advance_to(f.format(*static_cast(arg), ctx)); + using qualified_type = + conditional_t(), const T, T>; + ctx.advance_to(f.format(*static_cast(arg), ctx)); } }; template -FMT_CONSTEXPR basic_format_arg make_arg(const T& value); +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg; // To minimize the number of types we need to deal with, long is translated // either to int or to long long depending on its size. @@ -1119,117 +1310,164 @@ enum { long_short = sizeof(long) == sizeof(int) }; using long_type = conditional_t; using ulong_type = conditional_t; -struct unformattable {}; +template struct format_as_result { + template ::value || std::is_class::value)> + static auto map(U*) -> decltype(format_as(std::declval())); + static auto map(...) -> void; + + using type = decltype(map(static_cast(nullptr))); +}; +template using format_as_t = typename format_as_result::type; + +template +struct has_format_as + : bool_constant, void>::value> {}; // Maps formatting arguments to core types. +// arg_mapper reports errors by returning unformattable instead of using +// static_assert because it's used in the is_formattable trait. template struct arg_mapper { using char_type = typename Context::char_type; - FMT_CONSTEXPR int map(signed char val) { return val; } - FMT_CONSTEXPR unsigned map(unsigned char val) { return val; } - FMT_CONSTEXPR int map(short val) { return val; } - FMT_CONSTEXPR unsigned map(unsigned short val) { return val; } - FMT_CONSTEXPR int map(int val) { return val; } - FMT_CONSTEXPR unsigned map(unsigned val) { return val; } - FMT_CONSTEXPR long_type map(long val) { return val; } - FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; } - FMT_CONSTEXPR long long map(long long val) { return val; } - FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; } - FMT_CONSTEXPR int128_t map(int128_t val) { return val; } - FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; } - FMT_CONSTEXPR bool map(bool val) { return val; } - - template ::value)> - FMT_CONSTEXPR char_type map(T val) { - static_assert( - std::is_same::value || std::is_same::value, - "mixing character types is disallowed"); + FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) + -> unsigned long long { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } - FMT_CONSTEXPR float map(float val) { return val; } - FMT_CONSTEXPR double map(double val) { return val; } - FMT_CONSTEXPR long double map(long double val) { return val; } + template ::value || + std::is_same::value)> + FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { + return val; + } + template ::value || +#ifdef __cpp_char8_t + std::is_same::value || +#endif + std::is_same::value || + std::is_same::value) && + !std::is_same::value, + int> = 0> + FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { + return {}; + } - FMT_CONSTEXPR const char_type* map(char_type* val) { return val; } - FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; } - template ::value)> - FMT_CONSTEXPR basic_string_view map(const T& val) { - static_assert(std::is_same>::value, - "mixing character types is disallowed"); + FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { + return val; + } + + FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { + return val; + } + template ::value && !std::is_pointer::value && + std::is_same>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { return to_string_view(val); } template , T>::value && - !is_string::value && !has_formatter::value && - !has_fallback_formatter::value)> - FMT_CONSTEXPR basic_string_view map(const T& val) { - return basic_string_view(val); + FMT_ENABLE_IF(is_string::value && !std::is_pointer::value && + !std::is_same>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { + return {}; } + + FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { + return val; + } + + // Use SFINAE instead of a const T* parameter to avoid a conflict with the + // array overload. template < typename T, FMT_ENABLE_IF( - std::is_constructible, T>::value && - !std::is_constructible, T>::value && - !is_string::value && !has_formatter::value && - !has_fallback_formatter::value)> - FMT_CONSTEXPR basic_string_view map(const T& val) { - return std_string_view(val); - } - FMT_CONSTEXPR const char* map(const signed char* val) { - static_assert(std::is_same::value, "invalid string type"); - return reinterpret_cast(val); - } - FMT_CONSTEXPR const char* map(const unsigned char* val) { - static_assert(std::is_same::value, "invalid string type"); - return reinterpret_cast(val); - } - FMT_CONSTEXPR const char* map(signed char* val) { - const auto* const_val = val; - return map(const_val); - } - FMT_CONSTEXPR const char* map(unsigned char* val) { - const auto* const_val = val; - return map(const_val); - } - - FMT_CONSTEXPR const void* map(void* val) { return val; } - FMT_CONSTEXPR const void* map(const void* val) { return val; } - FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; } - template FMT_CONSTEXPR int map(const T*) { - // Formatting of arbitrary pointers is disallowed. If you want to output - // a pointer cast it to "void *" or "const void *". In particular, this - // forbids formatting of "[const] volatile char *" which is printed as bool - // by iostreams. - static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); - return 0; + std::is_pointer::value || std::is_member_pointer::value || + std::is_function::type>::value || + (std::is_convertible::value && + !std::is_convertible::value && + !has_formatter::value))> + FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { + return {}; } - template ::value && - !has_formatter::value && - !has_fallback_formatter::value)> - FMT_CONSTEXPR auto map(const T& val) - -> decltype(std::declval().map( - static_cast::type>(val))) { - return map(static_cast::type>(val)); + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { + return values; } - template ::value && !is_char::value && - (has_formatter::value || - has_fallback_formatter::value))> - FMT_CONSTEXPR const T& map(const T& val) { + + // Only map owning types because mapping views can be unsafe. + template , + FMT_ENABLE_IF(std::is_arithmetic::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) { + return map(format_as(val)); + } + + template > + struct formattable + : bool_constant() || + (has_formatter::value && + !std::is_const>::value)> {}; + + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { return val; } + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable { + return {}; + } + + template , + FMT_ENABLE_IF((std::is_class::value || std::is_enum::value || + std::is_union::value) && + !is_string::value && !is_char::value && + !is_named_arg::value && + !std::is_arithmetic>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(T&& val) + -> decltype(this->do_map(std::forward(val))) { + return do_map(std::forward(val)); + } - template - FMT_CONSTEXPR auto map(const named_arg& val) - -> decltype(std::declval().map(val.value)) { - return map(val.value); + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) + -> decltype(this->map(named_arg.value)) { + return map(named_arg.value); } - unformattable map(...) { return {}; } + auto map(...) -> unformattable { return {}; } }; // A type constant after applying arg_mapper. @@ -1245,6 +1483,20 @@ enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; } // namespace detail +// An output iterator that appends to a buffer. +// It is used to reduce symbol sizes for the common case. +class appender : public std::back_insert_iterator> { + using base = std::back_insert_iterator>; + + public: + using std::back_insert_iterator>::back_insert_iterator; + appender(base it) noexcept : base(it) {} + FMT_UNCHECKED_ITERATOR(appender); + + auto operator++() noexcept -> appender& { return *this; } + auto operator++(int) noexcept -> appender { return *this; } +}; + // A formatting argument. It is a trivially copyable/constructible type to // allow storage in basic_memory_buffer. template class basic_format_arg { @@ -1253,8 +1505,8 @@ template class basic_format_arg { detail::type type_; template - friend FMT_CONSTEXPR basic_format_arg detail::make_arg( - const T& value); + friend FMT_CONSTEXPR auto detail::make_arg(T&& value) + -> basic_format_arg; template friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, @@ -1288,14 +1540,16 @@ template class basic_format_arg { constexpr basic_format_arg() : type_(detail::type::none_type) {} - constexpr explicit operator bool() const FMT_NOEXCEPT { + constexpr explicit operator bool() const noexcept { return type_ != detail::type::none_type; } - detail::type type() const { return type_; } + auto type() const -> detail::type { return type_; } - bool is_integral() const { return detail::is_integral_type(type_); } - bool is_arithmetic() const { return detail::is_arithmetic_type(type_); } + auto is_integral() const -> bool { return detail::is_integral_type(type_); } + auto is_arithmetic() const -> bool { + return detail::is_arithmetic_type(type_); + } }; /** @@ -1305,10 +1559,10 @@ template class basic_format_arg { ``vis(value)`` will be called with the value of type ``double``. \endrst */ +FMT_MODULE_EXPORT template -FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( +FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { - using char_type = typename Context::char_type; switch (arg.type_) { case detail::type::none_type: break; @@ -1320,16 +1574,10 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( return vis(arg.value_.long_long_value); case detail::type::ulong_long_type: return vis(arg.value_.ulong_long_value); -#if FMT_USE_INT128 - case detail::type::int128_type: - return vis(arg.value_.int128_value); - case detail::type::uint128_type: - return vis(arg.value_.uint128_value); -#else case detail::type::int128_type: + return vis(detail::convert_for_visit(arg.value_.int128_value)); case detail::type::uint128_type: - break; -#endif + return vis(detail::convert_for_visit(arg.value_.uint128_value)); case detail::type::bool_type: return vis(arg.value_.bool_value); case detail::type::char_type: @@ -1343,8 +1591,8 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( case detail::type::cstring_type: return vis(arg.value_.string.data); case detail::type::string_type: - return vis(basic_string_view(arg.value_.string.data, - arg.value_.string.size)); + using sv = basic_string_view; + return vis(sv(arg.value_.string.data, arg.value_.string.size)); case detail::type::pointer_type: return vis(arg.value_.pointer); case detail::type::custom_type: @@ -1353,14 +1601,26 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( return vis(monostate()); } -template struct formattable : std::false_type {}; - namespace detail { +template +auto copy_str(InputIt begin, InputIt end, appender out) -> appender { + get_container(out).append(begin, end); + return out; +} + +template +FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { + return detail::copy_str(rng.begin(), rng.end(), out); +} + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 // A workaround for gcc 4.8 to make void_t work in a SFINAE context. -template struct void_t_impl { using type = void; }; -template -using void_t = typename detail::void_t_impl::type; +template struct void_t_impl { using type = void; }; +template using void_t = typename void_t_impl::type; +#else +template using void_t = void; +#endif template struct is_output_iterator : std::false_type {}; @@ -1372,125 +1632,95 @@ struct is_output_iterator< decltype(*std::declval() = std::declval())>> : std::true_type {}; -template -struct is_back_insert_iterator : std::false_type {}; +template struct is_back_insert_iterator : std::false_type {}; template struct is_back_insert_iterator> : std::true_type {}; -template +template struct is_contiguous_back_insert_iterator : std::false_type {}; template struct is_contiguous_back_insert_iterator> : is_contiguous {}; -template -struct is_contiguous_back_insert_iterator> - : std::true_type {}; +template <> +struct is_contiguous_back_insert_iterator : std::true_type {}; -// A type-erased reference to an std::locale to avoid heavy include. +// A type-erased reference to an std::locale to avoid a heavy include. class locale_ref { private: const void* locale_; // A type-erased pointer to std::locale. public: - locale_ref() : locale_(nullptr) {} + constexpr FMT_INLINE locale_ref() : locale_(nullptr) {} template explicit locale_ref(const Locale& loc); - explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } + explicit operator bool() const noexcept { return locale_ != nullptr; } - template Locale get() const; + template auto get() const -> Locale; }; -template constexpr unsigned long long encode_types() { return 0; } +template constexpr auto encode_types() -> unsigned long long { + return 0; +} template -constexpr unsigned long long encode_types() { +constexpr auto encode_types() -> unsigned long long { return static_cast(mapped_type_constant::value) | (encode_types() << packed_arg_bits); } template -FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { - basic_format_arg arg; - arg.type_ = mapped_type_constant::value; - arg.value_ = arg_mapper().map(value); - return arg; -} - -template int check(unformattable) { +FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { + auto&& arg = arg_mapper().map(FMT_FORWARD(val)); + using arg_type = remove_cvref_t; + + constexpr bool formattable_char = + !std::is_same::value; + static_assert(formattable_char, "Mixing character types is disallowed."); + + // Formatting of arbitrary pointers is disallowed. If you want to format a + // pointer cast it to `void*` or `const void*`. In particular, this forbids + // formatting of `[const] volatile char*` printed as bool by iostreams. + constexpr bool formattable_pointer = + !std::is_same::value; + static_assert(formattable_pointer, + "Formatting of non-void pointers is disallowed."); + + constexpr bool formattable = !std::is_same::value; static_assert( - formattable(), + formattable, "Cannot format an argument. To make type T formattable provide a " "formatter specialization: https://fmt.dev/latest/api.html#udt"); - return 0; + return {arg}; } -template inline const U& check(const U& val) { - return val; + +template +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg { + auto arg = basic_format_arg(); + arg.type_ = mapped_type_constant::value; + arg.value_ = make_value(value); + return arg; } -// The type template parameter is there to avoid an ODR violation when using -// a fallback formatter in one translation unit and an implicit conversion in -// another (not recommended). +// The DEPRECATED type template parameter is there to avoid an ODR violation +// when using a fallback formatter in one translation unit and an implicit +// conversion in another (not recommended). template -inline value make_arg(const T& val) { - return check(arg_mapper().map(val)); +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { + return make_value(val); } template -inline basic_format_arg make_arg(const T& value) { +FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg { return make_arg(value); } - -template struct is_reference_wrapper : std::false_type {}; -template -struct is_reference_wrapper> : std::true_type {}; - -template const T& unwrap(const T& v) { return v; } -template const T& unwrap(const std::reference_wrapper& v) { - return static_cast(v); -} - -class dynamic_arg_list { - // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for - // templates it doesn't complain about inability to deduce single translation - // unit for placing vtable. So storage_node_base is made a fake template. - template struct node { - virtual ~node() = default; - std::unique_ptr> next; - }; - - template struct typed_node : node<> { - T value; - - template - FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} - - template - FMT_CONSTEXPR typed_node(const basic_string_view& arg) - : value(arg.data(), arg.size()) {} - }; - - std::unique_ptr> head_; - - public: - template const T& push(const Arg& arg) { - auto new_node = std::unique_ptr>(new typed_node(arg)); - auto& value = new_node->value; - new_node->next = std::move(head_); - head_ = std::move(new_node); - return value; - } -}; } // namespace detail +FMT_BEGIN_EXPORT // Formatting context. template class basic_format_context { - public: - /** The character type for the output. */ - using char_type = Char; - private: OutputIt out_; basic_format_args args_; @@ -1499,48 +1729,56 @@ template class basic_format_context { public: using iterator = OutputIt; using format_arg = basic_format_arg; + using format_args = basic_format_args; using parse_context_type = basic_format_parse_context; - template using formatter_type = formatter; + template using formatter_type = formatter; + /** The character type for the output. */ + using char_type = Char; + + basic_format_context(basic_format_context&&) = default; basic_format_context(const basic_format_context&) = delete; void operator=(const basic_format_context&) = delete; /** - Constructs a ``basic_format_context`` object. References to the arguments are - stored in the object so make sure they have appropriate lifetimes. + Constructs a ``basic_format_context`` object. References to the arguments + are stored in the object so make sure they have appropriate lifetimes. */ - basic_format_context(OutputIt out, - basic_format_args ctx_args, - detail::locale_ref loc = detail::locale_ref()) + constexpr basic_format_context(OutputIt out, format_args ctx_args, + detail::locale_ref loc = {}) : out_(out), args_(ctx_args), loc_(loc) {} - format_arg arg(int id) const { return args_.get(id); } - format_arg arg(basic_string_view name) { return args_.get(name); } - int arg_id(basic_string_view name) { return args_.get_id(name); } - const basic_format_args& args() const { return args_; } + constexpr auto arg(int id) const -> format_arg { return args_.get(id); } + FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { + return args_.get(name); + } + FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { + return args_.get_id(name); + } + auto args() const -> const format_args& { return args_; } - detail::error_handler error_handler() { return {}; } + FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } void on_error(const char* message) { error_handler().on_error(message); } // Returns an iterator to the beginning of the output range. - iterator out() { return out_; } + FMT_CONSTEXPR auto out() -> iterator { return out_; } // Advances the begin iterator to ``it``. void advance_to(iterator it) { if (!detail::is_back_insert_iterator()) out_ = it; } - detail::locale_ref locale() { return loc_; } + FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } }; template using buffer_context = basic_format_context, Char>; using format_context = buffer_context; -using wformat_context = buffer_context; -// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. -#define FMT_BUFFER_CONTEXT(Char) \ - basic_format_context, Char> +template +using is_formattable = bool_constant>() + .map(std::declval()))>::value>; /** \rst @@ -1578,14 +1816,16 @@ class format_arg_store : 0); public: - format_arg_store(const Args&... args) + template + FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args) : #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 basic_format_args(*this), #endif data_{detail::make_arg< is_packed, Context, - detail::mapped_type_constant::value>(args)...} { + detail::mapped_type_constant, Context>::value>( + FMT_FORWARD(args))...} { detail::init_named_args(data_.named_args(), 0, 0, args...); } }; @@ -1598,37 +1838,17 @@ class format_arg_store See `~fmt::arg` for lifetime considerations. \endrst */ -template -inline format_arg_store make_format_args( - const Args&... args) { - return {args...}; +template +constexpr auto make_format_args(T&&... args) + -> format_arg_store...> { + return {FMT_FORWARD(args)...}; } /** \rst - Constructs a `~fmt::format_arg_store` object that contains references - to arguments and can be implicitly converted to `~fmt::format_args`. - If ``format_str`` is a compile-time string then `make_args_checked` checks - its validity at compile time. - \endrst - */ -template > -inline auto make_args_checked(const S& format_str, - const remove_reference_t&... args) - -> format_arg_store, remove_reference_t...> { - static_assert( - detail::count<( - std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); - detail::check_format_string(format_str); - return {args...}; -} - -/** - \rst - Returns a named argument to be used in a formatting function. It should only - be used in a call to a formatting function. + Returns a named argument to be used in a formatting function. + It should only be used in a call to a formatting function or + `dynamic_format_arg_store::push_back`. **Example**:: @@ -1636,183 +1856,11 @@ inline auto make_args_checked(const S& format_str, \endrst */ template -inline detail::named_arg arg(const Char* name, const T& arg) { +inline auto arg(const Char* name, const T& arg) -> detail::named_arg { static_assert(!detail::is_named_arg(), "nested named arguments"); return {name, arg}; } - -/** - \rst - A dynamic version of `fmt::format_arg_store`. - It's equipped with a storage to potentially temporary objects which lifetimes - could be shorter than the format arguments object. - - It can be implicitly converted into `~fmt::basic_format_args` for passing - into type-erased formatting functions such as `~fmt::vformat`. - \endrst - */ -template -class dynamic_format_arg_store -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - // Workaround a GCC template argument substitution bug. - : public basic_format_args -#endif -{ - private: - using char_type = typename Context::char_type; - - template struct need_copy { - static constexpr detail::type mapped_type = - detail::mapped_type_constant::value; - - enum { - value = !(detail::is_reference_wrapper::value || - std::is_same>::value || - std::is_same>::value || - (mapped_type != detail::type::cstring_type && - mapped_type != detail::type::string_type && - mapped_type != detail::type::custom_type)) - }; - }; - - template - using stored_type = conditional_t::value, - std::basic_string, T>; - - // Storage of basic_format_arg must be contiguous. - std::vector> data_; - std::vector> named_info_; - - // Storage of arguments not fitting into basic_format_arg must grow - // without relocation because items in data_ refer to it. - detail::dynamic_arg_list dynamic_args_; - - friend class basic_format_args; - - unsigned long long get_types() const { - return detail::is_unpacked_bit | data_.size() | - (named_info_.empty() - ? 0ULL - : static_cast(detail::has_named_args_bit)); - } - - const basic_format_arg* data() const { - return named_info_.empty() ? data_.data() : data_.data() + 1; - } - - template void emplace_arg(const T& arg) { - data_.emplace_back(detail::make_arg(arg)); - } - - template - void emplace_arg(const detail::named_arg& arg) { - if (named_info_.empty()) { - constexpr const detail::named_arg_info* zero_ptr{nullptr}; - data_.insert(data_.begin(), {zero_ptr, 0}); - } - data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); - auto pop_one = [](std::vector>* data) { - data->pop_back(); - }; - std::unique_ptr>, decltype(pop_one)> - guard{&data_, pop_one}; - named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); - data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; - guard.release(); - } - - public: - /** - \rst - Adds an argument into the dynamic store for later passing to a formatting - function. - - Note that custom types and string types (but not string views) are copied - into the store dynamically allocating memory if necessary. - - **Example**:: - - fmt::dynamic_format_arg_store store; - store.push_back(42); - store.push_back("abc"); - store.push_back(1.5f); - std::string result = fmt::vformat("{} and {} and {}", store); - \endrst - */ - template void push_back(const T& arg) { - if (detail::const_check(need_copy::value)) - emplace_arg(dynamic_args_.push>(arg)); - else - emplace_arg(detail::unwrap(arg)); - } - - /** - \rst - Adds a reference to the argument into the dynamic store for later passing to - a formatting function. Supports named arguments wrapped in - ``std::reference_wrapper`` via ``std::ref()``/``std::cref()``. - - **Example**:: - - fmt::dynamic_format_arg_store store; - char str[] = "1234567890"; - store.push_back(std::cref(str)); - int a1_val{42}; - auto a1 = fmt::arg("a1_", a1_val); - store.push_back(std::cref(a1)); - - // Changing str affects the output but only for string and custom types. - str[0] = 'X'; - - std::string result = fmt::vformat("{} and {a1_}"); - assert(result == "X234567890 and 42"); - \endrst - */ - template void push_back(std::reference_wrapper arg) { - static_assert( - detail::is_named_arg::type>::value || - need_copy::value, - "objects of built-in types and string views are always copied"); - emplace_arg(arg.get()); - } - - /** - Adds named argument into the dynamic store for later passing to a formatting - function. ``std::reference_wrapper`` is supported to avoid copying of the - argument. - */ - template - void push_back(const detail::named_arg& arg) { - const char_type* arg_name = - dynamic_args_.push>(arg.name).c_str(); - if (detail::const_check(need_copy::value)) { - emplace_arg( - fmt::arg(arg_name, dynamic_args_.push>(arg.value))); - } else { - emplace_arg(fmt::arg(arg_name, arg.value)); - } - } - - /** Erase all elements from the store */ - void clear() { - data_.clear(); - named_info_.clear(); - dynamic_args_ = detail::dynamic_arg_list(); - } - - /** - \rst - Reserves space to store at least *new_cap* arguments including - *new_cap_named* named arguments. - \endrst - */ - void reserve(size_t new_cap, size_t new_cap_named) { - FMT_ASSERT(new_cap >= new_cap_named, - "Set of arguments includes set of named arguments"); - data_.reserve(new_cap); - named_info_.reserve(new_cap_named); - } -}; +FMT_END_EXPORT /** \rst @@ -1845,25 +1893,27 @@ template class basic_format_args { const format_arg* args_; }; - bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; } - bool has_named_args() const { + constexpr auto is_packed() const -> bool { + return (desc_ & detail::is_unpacked_bit) == 0; + } + auto has_named_args() const -> bool { return (desc_ & detail::has_named_args_bit) != 0; } - detail::type type(int index) const { + FMT_CONSTEXPR auto type(int index) const -> detail::type { int shift = index * detail::packed_arg_bits; unsigned int mask = (1 << detail::packed_arg_bits) - 1; return static_cast((desc_ >> shift) & mask); } - basic_format_args(unsigned long long desc, - const detail::value* values) + constexpr FMT_INLINE basic_format_args(unsigned long long desc, + const detail::value* values) : desc_(desc), values_(values) {} - basic_format_args(unsigned long long desc, const format_arg* args) + constexpr basic_format_args(unsigned long long desc, const format_arg* args) : desc_(desc), args_(args) {} public: - basic_format_args() : desc_(0) {} + constexpr basic_format_args() : desc_(0), args_(nullptr) {} /** \rst @@ -1871,8 +1921,10 @@ template class basic_format_args { \endrst */ template - FMT_INLINE basic_format_args(const format_arg_store& store) - : basic_format_args(store.desc, store.data_.args()) {} + constexpr FMT_INLINE basic_format_args( + const format_arg_store& store) + : basic_format_args(format_arg_store::desc, + store.data_.args()) {} /** \rst @@ -1880,7 +1932,8 @@ template class basic_format_args { `~fmt::dynamic_format_arg_store`. \endrst */ - FMT_INLINE basic_format_args(const dynamic_format_arg_store& store) + constexpr FMT_INLINE basic_format_args( + const dynamic_format_arg_store& store) : basic_format_args(store.get_types(), store.data()) {} /** @@ -1888,12 +1941,12 @@ template class basic_format_args { Constructs a `basic_format_args` object from a dynamic set of arguments. \endrst */ - basic_format_args(const format_arg* args, int count) + constexpr basic_format_args(const format_arg* args, int count) : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), args) {} /** Returns the argument with the specified id. */ - format_arg get(int id) const { + FMT_CONSTEXPR auto get(int id) const -> format_arg { format_arg arg; if (!is_packed()) { if (id < max_size()) arg = args_[id]; @@ -1906,12 +1959,14 @@ template class basic_format_args { return arg; } - template format_arg get(basic_string_view name) const { + template + auto get(basic_string_view name) const -> format_arg { int id = get_id(name); return id >= 0 ? get(id) : format_arg(); } - template int get_id(basic_string_view name) const { + template + auto get_id(basic_string_view name) const -> int { if (!has_named_args()) return -1; const auto& named_args = (is_packed() ? values_[-1] : args_[-1].value_).named_args; @@ -1921,49 +1976,711 @@ template class basic_format_args { return -1; } - int max_size() const { + auto max_size() const -> int { unsigned long long max_packed = detail::max_packed_args; return static_cast(is_packed() ? max_packed : desc_ & ~detail::is_unpacked_bit); } }; -#ifdef FMT_ARM_ABI_COMPATIBILITY /** An alias to ``basic_format_args``. */ -// Separate types would result in shorter symbols but break ABI compatibility +// A separate type would result in shorter symbols but break ABI compatibility // between clang and gcc on ARM (#1919). -using format_args = basic_format_args; -using wformat_args = basic_format_args; +FMT_MODULE_EXPORT using format_args = basic_format_args; + +// We cannot use enum classes as bit fields because of a gcc bug, so we put them +// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). +// Additionally, if an underlying type is specified, older gcc incorrectly warns +// that the type is too small. Both bugs are fixed in gcc 9.3. +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 +# define FMT_ENUM_UNDERLYING_TYPE(type) #else -// DEPRECATED! These are kept for ABI compatibility. -// It is a separate type rather than an alias to make symbols readable. -struct format_args : basic_format_args { - template - FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {} +# define FMT_ENUM_UNDERLYING_TYPE(type) : type +#endif +namespace align { +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, + numeric}; +} +using align_t = align::type; +namespace sign { +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; +} +using sign_t = sign::type; + +namespace detail { + +// Workaround an array initialization issue in gcc 4.8. +template struct fill_t { + private: + enum { max_size = 4 }; + Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; + unsigned char size_ = 1; + + public: + FMT_CONSTEXPR void operator=(basic_string_view s) { + auto size = s.size(); + FMT_ASSERT(size <= max_size, "invalid fill"); + for (size_t i = 0; i < size; ++i) data_[i] = s[i]; + size_ = static_cast(size); + } + + constexpr auto size() const -> size_t { return size_; } + constexpr auto data() const -> const Char* { return data_; } + + FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; } + FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& { + return data_[index]; + } +}; +} // namespace detail + +enum class presentation_type : unsigned char { + none, + dec, // 'd' + oct, // 'o' + hex_lower, // 'x' + hex_upper, // 'X' + bin_lower, // 'b' + bin_upper, // 'B' + hexfloat_lower, // 'a' + hexfloat_upper, // 'A' + exp_lower, // 'e' + exp_upper, // 'E' + fixed_lower, // 'f' + fixed_upper, // 'F' + general_lower, // 'g' + general_upper, // 'G' + chr, // 'c' + string, // 's' + pointer, // 'p' + debug // '?' }; -struct wformat_args : basic_format_args { - using basic_format_args::basic_format_args; + +// Format specifiers for built-in and string types. +template struct format_specs { + int width; + int precision; + presentation_type type; + align_t align : 4; + sign_t sign : 3; + bool alt : 1; // Alternate form ('#'). + bool localized : 1; + detail::fill_t fill; + + constexpr format_specs() + : width(0), + precision(-1), + type(presentation_type::none), + align(align::none), + sign(sign::none), + alt(false), + localized(false) {} }; -#endif namespace detail { -template ::value)> -std::basic_string vformat( - basic_string_view format_str, - basic_format_args>> args); +enum class arg_id_kind { none, index, name }; + +// An argument reference. +template struct arg_ref { + FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} + + FMT_CONSTEXPR explicit arg_ref(int index) + : kind(arg_id_kind::index), val(index) {} + FMT_CONSTEXPR explicit arg_ref(basic_string_view name) + : kind(arg_id_kind::name), val(name) {} + + FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& { + kind = arg_id_kind::index; + val.index = idx; + return *this; + } + + arg_id_kind kind; + union value { + FMT_CONSTEXPR value(int idx = 0) : index(idx) {} + FMT_CONSTEXPR value(basic_string_view n) : name(n) {} + + int index; + basic_string_view name; + } val; +}; + +// Format specifiers with width and precision resolved at formatting rather +// than parsing time to allow reusing the same parsed specifiers with +// different sets of arguments (precompilation of format strings). +template +struct dynamic_format_specs : format_specs { + arg_ref width_ref; + arg_ref precision_ref; +}; + +// Converts a character to ASCII. Returns '\0' on conversion failure. +template ::value)> +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; +} +template ::value)> +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; +} + +// Returns the number of code units in a code point or 1 on error. +template +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { + if (const_check(sizeof(Char) != 1)) return 1; + auto c = static_cast(*begin); + return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1; +} + +// Return the result via the out param to workaround gcc bug 77539. +template +FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { + for (out = first; out != last; ++out) { + if (*out == value) return true; + } + return false; +} + +template <> +inline auto find(const char* first, const char* last, char value, + const char*& out) -> bool { + out = static_cast( + std::memchr(first, value, to_unsigned(last - first))); + return out != nullptr; +} + +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +template +FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, + int error_value) noexcept -> int { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0, prev = 0; + auto p = begin; + do { + prev = value; + value = value * 10 + unsigned(*p - '0'); + ++p; + } while (p != end && '0' <= *p && *p <= '9'); + auto num_digits = p - begin; + begin = p; + if (num_digits <= std::numeric_limits::digits10) + return static_cast(value); + // Check for overflow. + const unsigned max = to_unsigned((std::numeric_limits::max)()); + return num_digits == std::numeric_limits::digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; +} + +FMT_CONSTEXPR inline auto parse_align(char c) -> align_t { + switch (c) { + case '<': + return align::left; + case '>': + return align::right; + case '^': + return align::center; + } + return align::none; +} + +template constexpr auto is_name_start(Char c) -> bool { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; +} + +template +FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + Char c = *begin; + if (c >= '0' && c <= '9') { + int index = 0; + constexpr int max = (std::numeric_limits::max)(); + if (c != '0') + index = parse_nonnegative_int(begin, end, max); + else + ++begin; + if (begin == end || (*begin != '}' && *begin != ':')) + throw_format_error("invalid format string"); + else + handler.on_index(index); + return begin; + } + if (!is_name_start(c)) { + throw_format_error("invalid format string"); + return begin; + } + auto it = begin; + do { + ++it; + } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); + handler.on_name({begin, to_unsigned(it - begin)}); + return it; +} + +template +FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + FMT_ASSERT(begin != end, ""); + Char c = *begin; + if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); + handler.on_auto(); + return begin; +} + +template struct dynamic_spec_id_handler { + basic_format_parse_context& ctx; + arg_ref& ref; + + FMT_CONSTEXPR void on_auto() { + int id = ctx.next_arg_id(); + ref = arg_ref(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_index(int id) { + ref = arg_ref(id); + ctx.check_arg_id(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_name(basic_string_view id) { + ref = arg_ref(id); + ctx.check_arg_id(id); + } +}; + +// Parses [integer | "{" [arg_id] "}"]. +template +FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, + int& value, arg_ref& ref, + basic_format_parse_context& ctx) + -> const Char* { + FMT_ASSERT(begin != end, ""); + if ('0' <= *begin && *begin <= '9') { + int val = parse_nonnegative_int(begin, end, -1); + if (val != -1) + value = val; + else + throw_format_error("number is too big"); + } else if (*begin == '{') { + ++begin; + auto handler = dynamic_spec_id_handler{ctx, ref}; + if (begin != end) begin = parse_arg_id(begin, end, handler); + if (begin != end && *begin == '}') return ++begin; + throw_format_error("invalid format string"); + } + return begin; +} + +template +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + int& value, arg_ref& ref, + basic_format_parse_context& ctx) + -> const Char* { + ++begin; + if (begin == end || *begin == '}') { + throw_format_error("invalid precision"); + return begin; + } + return parse_dynamic_spec(begin, end, value, ref, ctx); +} + +enum class state { start, align, sign, hash, zero, width, precision, locale }; + +// Parses standard format specifiers. +template +FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( + const Char* begin, const Char* end, dynamic_format_specs& specs, + basic_format_parse_context& ctx, type arg_type) -> const Char* { + auto c = '\0'; + if (end - begin > 1) { + auto next = to_ascii(begin[1]); + c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; + } else { + if (begin == end) return begin; + c = to_ascii(*begin); + } + + struct { + state current_state = state::start; + FMT_CONSTEXPR void operator()(state s, bool valid = true) { + if (current_state >= s || !valid) + throw_format_error("invalid format specifier"); + current_state = s; + } + } enter_state; + + using pres = presentation_type; + constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; + struct { + const Char*& begin; + dynamic_format_specs& specs; + type arg_type; + + FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* { + if (!in(arg_type, set)) throw_format_error("invalid format specifier"); + specs.type = type; + return begin + 1; + } + } parse_presentation_type{begin, specs, arg_type}; + + for (;;) { + switch (c) { + case '<': + case '>': + case '^': + enter_state(state::align); + specs.align = parse_align(c); + ++begin; + break; + case '+': + case '-': + case ' ': + enter_state(state::sign, in(arg_type, sint_set | float_set)); + switch (c) { + case '+': + specs.sign = sign::plus; + break; + case '-': + specs.sign = sign::minus; + break; + case ' ': + specs.sign = sign::space; + break; + } + ++begin; + break; + case '#': + enter_state(state::hash, is_arithmetic_type(arg_type)); + specs.alt = true; + ++begin; + break; + case '0': + enter_state(state::zero); + if (!is_arithmetic_type(arg_type)) + throw_format_error("format specifier requires numeric argument"); + if (specs.align == align::none) { + // Ignore 0 if align is specified for compatibility with std::format. + specs.align = align::numeric; + specs.fill[0] = Char('0'); + } + ++begin; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '{': + enter_state(state::width); + begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); + break; + case '.': + enter_state(state::precision, + in(arg_type, float_set | string_set | cstring_set)); + begin = parse_precision(begin, end, specs.precision, specs.precision_ref, + ctx); + break; + case 'L': + enter_state(state::locale, is_arithmetic_type(arg_type)); + specs.localized = true; + ++begin; + break; + case 'd': + return parse_presentation_type(pres::dec, integral_set); + case 'o': + return parse_presentation_type(pres::oct, integral_set); + case 'x': + return parse_presentation_type(pres::hex_lower, integral_set); + case 'X': + return parse_presentation_type(pres::hex_upper, integral_set); + case 'b': + return parse_presentation_type(pres::bin_lower, integral_set); + case 'B': + return parse_presentation_type(pres::bin_upper, integral_set); + case 'a': + return parse_presentation_type(pres::hexfloat_lower, float_set); + case 'A': + return parse_presentation_type(pres::hexfloat_upper, float_set); + case 'e': + return parse_presentation_type(pres::exp_lower, float_set); + case 'E': + return parse_presentation_type(pres::exp_upper, float_set); + case 'f': + return parse_presentation_type(pres::fixed_lower, float_set); + case 'F': + return parse_presentation_type(pres::fixed_upper, float_set); + case 'g': + return parse_presentation_type(pres::general_lower, float_set); + case 'G': + return parse_presentation_type(pres::general_upper, float_set); + case 'c': + return parse_presentation_type(pres::chr, integral_set); + case 's': + return parse_presentation_type(pres::string, + bool_set | string_set | cstring_set); + case 'p': + return parse_presentation_type(pres::pointer, pointer_set | cstring_set); + case '?': + return parse_presentation_type(pres::debug, + char_set | string_set | cstring_set); + case '}': + return begin; + default: { + if (*begin == '}') return begin; + // Parse fill and alignment. + auto fill_end = begin + code_point_length(begin); + if (end - fill_end <= 0) { + throw_format_error("invalid format specifier"); + return begin; + } + if (*begin == '{') { + throw_format_error("invalid fill character '{'"); + return begin; + } + auto align = parse_align(to_ascii(*fill_end)); + enter_state(state::align, align != align::none); + specs.fill = {begin, to_unsigned(fill_end - begin)}; + specs.align = align; + begin = fill_end + 1; + } + } + if (begin == end) return begin; + c = to_ascii(*begin); + } +} + +template +FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + struct id_adapter { + Handler& handler; + int arg_id; + + FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); } + FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void on_name(basic_string_view id) { + arg_id = handler.on_arg_id(id); + } + }; + + ++begin; + if (begin == end) return handler.on_error("invalid format string"), end; + if (*begin == '}') { + handler.on_replacement_field(handler.on_arg_id(), begin); + } else if (*begin == '{') { + handler.on_text(begin, begin + 1); + } else { + auto adapter = id_adapter{handler, 0}; + begin = parse_arg_id(begin, end, adapter); + Char c = begin != end ? *begin : Char(); + if (c == '}') { + handler.on_replacement_field(adapter.arg_id, begin); + } else if (c == ':') { + begin = handler.on_format_specs(adapter.arg_id, begin + 1, end); + if (begin == end || *begin != '}') + return handler.on_error("unknown format specifier"), end; + } else { + return handler.on_error("missing '}' in format string"), end; + } + } + return begin + 1; +} + +template +FMT_CONSTEXPR FMT_INLINE void parse_format_string( + basic_string_view format_str, Handler&& handler) { + auto begin = format_str.data(); + auto end = begin + format_str.size(); + if (end - begin < 32) { + // Use a simple loop instead of memchr for small strings. + const Char* p = begin; + while (p != end) { + auto c = *p++; + if (c == '{') { + handler.on_text(begin, p - 1); + begin = p = parse_replacement_field(p - 1, end, handler); + } else if (c == '}') { + if (p == end || *p != '}') + return handler.on_error("unmatched '}' in format string"); + handler.on_text(begin, p); + begin = ++p; + } + } + handler.on_text(begin, end); + return; + } + struct writer { + FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { + if (from == to) return; + for (;;) { + const Char* p = nullptr; + if (!find(from, to, Char('}'), p)) + return handler_.on_text(from, to); + ++p; + if (p == to || *p != '}') + return handler_.on_error("unmatched '}' in format string"); + handler_.on_text(from, p); + from = p + 1; + } + } + Handler& handler_; + } write = {handler}; + while (begin != end) { + // Doing two passes with memchr (one for '{' and another for '}') is up to + // 2.5x faster than the naive one-pass implementation on big format strings. + const Char* p = begin; + if (*begin != '{' && !find(begin + 1, end, Char('{'), p)) + return write(begin, end); + write(begin, p); + begin = parse_replacement_field(p, end, handler); + } +} + +template ::value> struct strip_named_arg { + using type = T; +}; +template struct strip_named_arg { + using type = remove_cvref_t; +}; -FMT_API std::string vformat(string_view format_str, format_args args); +template +FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) + -> decltype(ctx.begin()) { + using char_type = typename ParseContext::char_type; + using context = buffer_context; + using mapped_type = conditional_t< + mapped_type_constant::value != type::custom_type, + decltype(arg_mapper().map(std::declval())), + typename strip_named_arg::type>; + return formatter().parse(ctx); +} +// Checks char specs and returns true iff the presentation type is char-like. template -void vformat_to( - buffer& buf, basic_string_view format_str, - basic_format_args)> args, - detail::locale_ref loc = {}); +FMT_CONSTEXPR auto check_char_specs(const format_specs& specs) -> bool { + if (specs.type != presentation_type::none && + specs.type != presentation_type::chr && + specs.type != presentation_type::debug) { + return false; + } + if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) + throw_format_error("invalid format specifier for char"); + return true; +} + +constexpr FMT_INLINE_VARIABLE int invalid_arg_index = -1; + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template +constexpr auto get_arg_index_by_name(basic_string_view name) -> int { + if constexpr (is_statically_named_arg()) { + if (name == T::name) return N; + } + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name(name); + (void)name; // Workaround an MSVC bug about "unused" parameter. + return invalid_arg_index; +} +#endif + +template +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<0, Args...>(name); +#endif + (void)name; + return invalid_arg_index; +} + +template class format_string_checker { + private: + using parse_context_type = compile_parse_context; + static constexpr int num_args = sizeof...(Args); + + // Format specifier parsing function. + // In the future basic_format_parse_context will replace compile_parse_context + // here and will use is_constant_evaluated and downcasting to access the data + // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. + using parse_func = const Char* (*)(parse_context_type&); + + parse_context_type context_; + parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; + type types_[num_args > 0 ? static_cast(num_args) : 1]; + + public: + explicit FMT_CONSTEXPR format_string_checker(basic_string_view fmt) + : context_(fmt, num_args, types_), + parse_funcs_{&parse_format_specs...}, + types_{mapped_type_constant>::value...} {} + + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + + FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + return context_.check_arg_id(id), id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS + auto index = get_arg_index_by_name(id); + if (index == invalid_arg_index) on_error("named argument is not found"); + return index; +#else + (void)id; + on_error("compile-time checks for named arguments require C++20 support"); + return 0; +#endif + } + + FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} + + FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) + -> const Char* { + context_.advance_to(begin); + // id >= 0 check is a workaround for gcc 10 bug (#2065). + return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; + } + + FMT_CONSTEXPR void on_error(const char* message) { + throw_format_error(message); + } +}; + +// Reports a compile-time error if S is not a valid format string. +template ::value)> +FMT_INLINE void check_format_string(const S&) { +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); +#endif +} +template ::value)> +void check_format_string(S format_str) { + using char_t = typename S::char_type; + FMT_CONSTEXPR auto s = basic_string_view(format_str); + using checker = format_string_checker...>; + FMT_CONSTEXPR bool error = (parse_format_string(s, checker(s)), true); + ignore_unused(error); +} + +template struct vformat_args { + using type = basic_format_args< + basic_format_context>, Char>>; +}; +template <> struct vformat_args { using type = format_args; }; -template ::value)> -inline void vprint_mojibake(std::FILE*, basic_string_view, const Args&) {} +// Use vformat_args and avoid type_identity to keep symbols short. +template +void vformat_to(buffer& buf, basic_string_view fmt, + typename vformat_args::type args, locale_ref loc = {}); FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); #ifndef _WIN32 @@ -1971,37 +2688,161 @@ inline void vprint_mojibake(std::FILE*, string_view, format_args) {} #endif } // namespace detail +FMT_BEGIN_EXPORT + +// A formatter specialization for natively supported types. +template +struct formatter::value != + detail::type::custom_type>> { + private: + detail::dynamic_format_specs specs_; + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { + auto type = detail::type_constant::value; + auto end = + detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type); + if (type == detail::type::char_type) detail::check_char_specs(specs_); + return end; + } + + template ::value, + FMT_ENABLE_IF(U == detail::type::string_type || + U == detail::type::cstring_type || + U == detail::type::char_type)> + FMT_CONSTEXPR void set_debug_format(bool set = true) { + specs_.type = set ? presentation_type::debug : presentation_type::none; + } + + template + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +#define FMT_FORMAT_AS(Type, Base) \ + template \ + struct formatter : formatter { \ + template \ + auto format(const Type& val, FormatContext& ctx) const \ + -> decltype(ctx.out()) { \ + return formatter::format(static_cast(val), ctx); \ + } \ + } + +FMT_FORMAT_AS(signed char, int); +FMT_FORMAT_AS(unsigned char, unsigned); +FMT_FORMAT_AS(short, int); +FMT_FORMAT_AS(unsigned short, unsigned); +FMT_FORMAT_AS(long, long long); +FMT_FORMAT_AS(unsigned long, unsigned long long); +FMT_FORMAT_AS(Char*, const Char*); +FMT_FORMAT_AS(std::basic_string, basic_string_view); +FMT_FORMAT_AS(std::nullptr_t, const void*); +FMT_FORMAT_AS(detail::std_string_view, basic_string_view); + +template struct runtime_format_string { + basic_string_view str; +}; + +/** A compile-time format string. */ +template class basic_format_string { + private: + basic_string_view str_; + + public: + template >::value)> + FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { + static_assert( + detail::count< + (std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); +#ifdef FMT_HAS_CONSTEVAL + if constexpr (detail::count_named_args() == + detail::count_statically_named_args()) { + using checker = + detail::format_string_checker...>; + detail::parse_format_string(str_, checker(s)); + } +#else + detail::check_format_string(s); +#endif + } + basic_format_string(runtime_format_string fmt) : str_(fmt.str) {} + + FMT_INLINE operator basic_string_view() const { return str_; } + FMT_INLINE auto get() const -> basic_string_view { return str_; } +}; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template using format_string = string_view; +inline auto runtime(string_view s) -> string_view { return s; } +#else +template +using format_string = basic_format_string...>; +/** + \rst + Creates a runtime format string. + + **Example**:: + + // Check format string at runtime instead of compile-time. + fmt::print(fmt::runtime("{:d}"), "I am not a number"); + \endrst + */ +inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } +#endif + +FMT_API auto vformat(string_view fmt, format_args args) -> std::string; + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and returns the result + as a string. + + **Example**:: + + #include + std::string message = fmt::format("The answer is {}.", 42); + \endrst +*/ +template +FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) + -> std::string { + return vformat(fmt, fmt::make_format_args(args...)); +} + /** Formats a string and writes the output to ``out``. */ -// GCC 8 and earlier cannot handle std::back_insert_iterator with -// vformat_to(...) overload, so SFINAE on iterator type instead. -template , - bool enable = detail::is_output_iterator::value> -auto vformat_to(OutputIt out, const S& format_str, - basic_format_args>> args) - -> typename std::enable_if::type { - decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); - detail::vformat_to(buf, to_string_view(format_str), args); - return detail::get_iterator(buf); +template ::value)> +auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, fmt, args, {}); + return detail::get_iterator(buf, out); } /** \rst - Formats arguments, writes the result to the output iterator ``out`` and returns - the iterator past the end of the output range. + Formats ``args`` according to specifications in ``fmt``, writes the result to + the output iterator ``out`` and returns the iterator past the end of the output + range. `format_to` does not append a terminating null character. **Example**:: - std::vector out; + auto out = std::vector(); fmt::format_to(std::back_inserter(out), "{}", 42); \endrst */ -// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3. -template >::value> -inline auto format_to(OutputIt out, const S& format_str, Args&&... args) -> - typename std::enable_if::type { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return vformat_to(out, to_string_view(format_str), vargs); +template ::value)> +FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) + -> OutputIt { + return vformat_to(out, fmt, fmt::make_format_args(args...)); } template struct format_to_n_result { @@ -2011,111 +2852,100 @@ template struct format_to_n_result { size_t size; }; -template ::value)> -inline format_to_n_result vformat_to_n( - OutputIt out, size_t n, basic_string_view format_str, - basic_format_args>> args) { - detail::iterator_buffer buf(out, - n); - detail::vformat_to(buf, format_str, args); +template ::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + detail::vformat_to(buf, fmt, args, {}); return {buf.out(), buf.count()}; } /** - \rst - Formats arguments, writes up to ``n`` characters of the result to the output - iterator ``out`` and returns the total output size and the iterator past the - end of the output range. - \endrst + \rst + Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` + characters of the result to the output iterator ``out`` and returns the total + (not truncated) output size and the iterator past the end of the output range. + `format_to_n` does not append a terminating null character. + \endrst */ -template >::value> -inline auto format_to_n(OutputIt out, size_t n, const S& format_str, - const Args&... args) -> - typename std::enable_if>::type { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return vformat_to_n(out, n, to_string_view(format_str), vargs); +template ::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, + T&&... args) -> format_to_n_result { + return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); } -/** - Returns the number of characters in the output of - ``format(format_str, args...)``. - */ -template -inline size_t formatted_size(string_view format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - detail::counting_buffer<> buf; - detail::vformat_to(buf, format_str, vargs); +/** Returns the number of chars in the output of ``format(fmt, args...)``. */ +template +FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, + T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, fmt, fmt::make_format_args(args...), {}); return buf.count(); } -template > -FMT_INLINE std::basic_string vformat( - const S& format_str, - basic_format_args>> args) { - return detail::vformat(to_string_view(format_str), args); -} +FMT_API void vprint(string_view fmt, format_args args); +FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); /** \rst - Formats arguments and returns the result as a string. + Formats ``args`` according to specifications in ``fmt`` and writes the output + to ``stdout``. **Example**:: - #include - std::string message = fmt::format("The answer is {}", 42); + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); \endrst -*/ -// Pass char_t as a default template parameter instead of using -// std::basic_string> to reduce the symbol size. -template > -FMT_INLINE std::basic_string format(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return detail::vformat(to_string_view(format_str), vargs); + */ +template +FMT_INLINE void print(format_string fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(fmt, vargs) + : detail::vprint_mojibake(stdout, fmt, vargs); } -FMT_API void vprint(string_view, format_args); -FMT_API void vprint(std::FILE*, string_view, format_args); - /** \rst - Formats ``args`` according to specifications in ``format_str`` and writes the - output to the file ``f``. Strings are assumed to be Unicode-encoded unless the - ``FMT_UNICODE`` macro is set to 0. + Formats ``args`` according to specifications in ``fmt`` and writes the + output to the file ``f``. **Example**:: fmt::print(stderr, "Don't {}!", "panic"); \endrst */ -template > -inline void print(std::FILE* f, const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return detail::is_unicode() - ? vprint(f, to_string_view(format_str), vargs) - : detail::vprint_mojibake(f, to_string_view(format_str), vargs); +template +FMT_INLINE void print(std::FILE* f, format_string fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(f, fmt, vargs) + : detail::vprint_mojibake(f, fmt, vargs); } /** - \rst - Formats ``args`` according to specifications in ``format_str`` and writes - the output to ``stdout``. Strings are assumed to be Unicode-encoded unless - the ``FMT_UNICODE`` macro is set to 0. - - **Example**:: + Formats ``args`` according to specifications in ``fmt`` and writes the + output to the file ``f`` followed by a newline. + */ +template +FMT_INLINE void println(std::FILE* f, format_string fmt, T&&... args) { + return fmt::print(f, "{}\n", fmt::format(fmt, std::forward(args)...)); +} - fmt::print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst +/** + Formats ``args`` according to specifications in ``fmt`` and writes the output + to ``stdout`` followed by a newline. */ -template > -inline void print(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return detail::is_unicode() - ? vprint(to_string_view(format_str), vargs) - : detail::vprint_mojibake(stdout, to_string_view(format_str), - vargs); +template +FMT_INLINE void println(format_string fmt, T&&... args) { + return fmt::println(stdout, fmt, std::forward(args)...); } + +FMT_END_EXPORT +FMT_GCC_PRAGMA("GCC pop_options") FMT_END_NAMESPACE +#ifdef FMT_HEADER_ONLY +# include "format.h" +#endif #endif // FMT_CORE_H_ diff --git a/src/3rdparty/fmt/format-inl.h b/src/3rdparty/fmt/format-inl.h index 6cd9db93c4..5bae3c7b2c 100644 --- a/src/3rdparty/fmt/format-inl.h +++ b/src/3rdparty/fmt/format-inl.h @@ -8,13 +8,10 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -/* Do not include cassert as that breaks our own asserts. */ -#include +#include +#include // errno #include #include -#include -#include // std::memmove -#include #include #ifndef FMT_STATIC_THOUSANDS_SEPARATOR @@ -27,11 +24,6 @@ #include "format.h" -// Dummy implementations of strerror_r and strerror_s called if corresponding -// system functions are not available. -inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; } -inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; } - FMT_BEGIN_NAMESPACE namespace detail { @@ -44,91 +36,12 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) { std::terminate(); } -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - -// A portable thread-safe version of strerror. -// Sets buffer to point to a string describing the error code. -// This can be either a pointer to a string stored in buffer, -// or a pointer to some static immutable string. -// Returns one of the following values: -// 0 - success -// ERANGE - buffer is not large enough to store the error message -// other - failure -// Buffer should be at least of size 1. -inline int safe_strerror(int error_code, char*& buffer, - size_t buffer_size) FMT_NOEXCEPT { - FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer"); - - class dispatcher { - private: - int error_code_; - char*& buffer_; - size_t buffer_size_; - - // A noop assignment operator to avoid bogus warnings. - void operator=(const dispatcher&) {} - - // Handle the result of XSI-compliant version of strerror_r. - int handle(int result) { - // glibc versions before 2.13 return result in errno. - return result == -1 ? errno : result; - } - - // Handle the result of GNU-specific version of strerror_r. - FMT_MAYBE_UNUSED - int handle(char* message) { - // If the buffer is full then the message is probably truncated. - if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) - return ERANGE; - buffer_ = message; - return 0; - } - - // Handle the case when strerror_r is not available. - FMT_MAYBE_UNUSED - int handle(detail::null<>) { - return fallback(strerror_s(buffer_, buffer_size_, error_code_)); - } - - // Fallback to strerror_s when strerror_r is not available. - FMT_MAYBE_UNUSED - int fallback(int result) { - // If the buffer is full then the message is probably truncated. - return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE - : result; - } - -#if !FMT_MSC_VER - // Fallback to strerror if strerror_r and strerror_s are not available. - int fallback(detail::null<>) { - errno = 0; - buffer_ = strerror(error_code_); - return errno; - } -#endif - - public: - dispatcher(int err_code, char*& buf, size_t buf_size) - : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} - - int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); } - }; - return dispatcher(error_code, buffer, buffer_size).run(); +FMT_FUNC void throw_format_error(const char* message) { + FMT_THROW(format_error(message)); } FMT_FUNC void format_error_code(detail::buffer& out, int error_code, - string_view message) FMT_NOEXCEPT { + string_view message) noexcept { // Report error code making sure that the output fits into // inline_buffer_size to avoid dynamic memory allocation and potential // bad_alloc. @@ -145,31 +58,29 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); auto it = buffer_appender(out); if (message.size() <= inline_buffer_size - error_code_size) - format_to(it, "{}{}", message, SEP); - format_to(it, "{}{}", ERROR_STR, error_code); - assert(out.size() <= inline_buffer_size); + format_to(it, FMT_STRING("{}{}"), message, SEP); + format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); + FMT_ASSERT(out.size() <= inline_buffer_size, ""); } FMT_FUNC void report_error(format_func func, int error_code, - string_view message) FMT_NOEXCEPT { + const char* message) noexcept { memory_buffer full_message; func(full_message, error_code, message); // Don't use fwrite_fully because the latter may throw. - (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr); - std::fputc('\n', stderr); + if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) + std::fputc('\n', stderr); } // A wrapper around fwrite that throws on error. inline void fwrite_fully(const void* ptr, size_t size, size_t count, FILE* stream) { size_t written = std::fwrite(ptr, size, count, stream); - if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); + if (written < count) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } -} // namespace detail - -#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) -namespace detail { +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR template locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { static_assert(std::is_same::value, ""); @@ -180,1698 +91,252 @@ template Locale locale_ref::get() const { return locale_ ? *static_cast(locale_) : std::locale(); } -template FMT_FUNC std::string grouping_impl(locale_ref loc) { - return std::use_facet>(loc.get()).grouping(); -} -template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { - return std::use_facet>(loc.get()) - .thousands_sep(); +template +FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { + auto& facet = std::use_facet>(loc.get()); + auto grouping = facet.grouping(); + auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); + return {std::move(grouping), thousands_sep}; } template FMT_FUNC Char decimal_point_impl(locale_ref loc) { return std::use_facet>(loc.get()) .decimal_point(); } -} // namespace detail #else template -FMT_FUNC std::string detail::grouping_impl(locale_ref) { - return "\03"; -} -template FMT_FUNC Char detail::thousands_sep_impl(locale_ref) { - return FMT_STATIC_THOUSANDS_SEPARATOR; +FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { + return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; } -template FMT_FUNC Char detail::decimal_point_impl(locale_ref) { +template FMT_FUNC Char decimal_point_impl(locale_ref) { return '.'; } #endif -FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; -FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default; - -FMT_FUNC void system_error::init(int err_code, string_view format_str, - format_args args) { - error_code_ = err_code; - memory_buffer buffer; - format_system_error(buffer, err_code, vformat(format_str, args)); - std::runtime_error& base = *this; - base = std::runtime_error(to_string(buffer)); +FMT_FUNC auto write_loc(appender out, loc_value value, + const format_specs<>& specs, locale_ref loc) -> bool { +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR + auto locale = loc.get(); + // We cannot use the num_put facet because it may produce output in + // a wrong encoding. + using facet = format_facet; + if (std::has_facet(locale)) + return std::use_facet(locale).put(out, value, specs); + return facet(locale).put(out, value, specs); +#endif + return false; } +} // namespace detail -namespace detail { +template typename Locale::id format_facet::id; -template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { - // fallback_uintptr is always stored in little endian. - int i = static_cast(sizeof(void*)) - 1; - while (i > 0 && n.value[i] == 0) --i; - auto char_digits = std::numeric_limits::digits / 4; - return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template format_facet::format_facet(Locale& loc) { + auto& numpunct = std::use_facet>(loc); + grouping_ = numpunct.grouping(); + if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); } -template -const typename basic_data::digit_pair basic_data::digits[] = { - {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, - {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, - {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, - {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, - {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, - {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, - {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, - {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, - {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, - {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, - {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, - {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, - {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, - {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, - {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, - {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, - {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; - -template -const char basic_data::hex_digits[] = "0123456789abcdef"; - -#define FMT_POWERS_OF_10(factor) \ - factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ - (factor)*1000000, (factor)*10000000, (factor)*100000000, \ - (factor)*1000000000 - -template -const uint64_t basic_data::powers_of_10_64[] = { - 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; - -template -const uint32_t basic_data::zero_or_powers_of_10_32[] = {0, - FMT_POWERS_OF_10(1)}; -template -const uint64_t basic_data::zero_or_powers_of_10_64[] = { - 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; - -template -const uint32_t basic_data::zero_or_powers_of_10_32_new[] = { - 0, 0, FMT_POWERS_OF_10(1)}; - -template -const uint64_t basic_data::zero_or_powers_of_10_64_new[] = { - 0, 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; - -// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. -// These are generated by support/compute-powers.py. -template -const uint64_t basic_data::grisu_pow10_significands[] = { - 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, - 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, - 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, - 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, - 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, - 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, - 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, - 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, - 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, - 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, - 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, - 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, - 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, - 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, - 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, - 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, - 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, - 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, - 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, - 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, - 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, - 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, - 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, - 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, - 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, - 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, - 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, - 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, - 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, -}; - -// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding -// to significands above. -template -const int16_t basic_data::grisu_pow10_exponents[] = { - -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, - -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, - -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, - -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, - -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, - 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, - 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, - 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; - -template -const divtest_table_entry basic_data::divtest_table_for_pow5_32[] = - {{0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333}, - {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba}, - {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5}, - {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf}, - {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897}, - {0x3ed61f49, 0x000001b7}}; - -template -const divtest_table_entry basic_data::divtest_table_for_pow5_64[] = - {{0x0000000000000001, 0xffffffffffffffff}, - {0xcccccccccccccccd, 0x3333333333333333}, - {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70}, - {0x1cac083126e978d5, 0x020c49ba5e353f7c}, - {0xd288ce703afb7e91, 0x0068db8bac710cb2}, - {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0}, - {0x790fb65668c26139, 0x000431bde82d7b63}, - {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a}, - {0xc767074b22e90e21, 0x00002af31dc46118}, - {0x8e47ce423a2e9c6d, 0x0000089705f4136b}, - {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b}, - {0x0fee64690c913975, 0x00000057f5ff85e5}, - {0x3662e0e1cf503eb1, 0x000000119799812d}, - {0xa47a2cf9f6433fbd, 0x0000000384b84d09}, - {0x54186f653140a659, 0x00000000b424dc35}, - {0x7738164770402145, 0x0000000024075f3d}, - {0xe4a4d1417cd9a041, 0x000000000734aca5}, - {0xc75429d9e5c5200d, 0x000000000170ef54}, - {0xc1773b91fac10669, 0x000000000049c977}, - {0x26b172506559ce15, 0x00000000000ec1e4}, - {0xd489e3a9addec2d1, 0x000000000002f394}, - {0x90e860bb892c8d5d, 0x000000000000971d}, - {0x502e79bf1b6f4f79, 0x0000000000001e39}, - {0xdcd618596be30fe5, 0x000000000000060b}}; - -template -const uint64_t basic_data::dragonbox_pow10_significands_64[] = { - 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, - 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, - 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, - 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, - 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, - 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, - 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, - 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, - 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, - 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, - 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, - 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, - 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, - 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, - 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, - 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, - 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, - 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, - 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, - 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, - 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296, - 0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6, - 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, - 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, - 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719, - 0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e}; - -template -const uint128_wrapper basic_data::dragonbox_pow10_significands_128[] = { -#if FMT_USE_FULL_CACHE_DRAGONBOX - {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, - {0x9faacf3df73609b1, 0x77b191618c54e9ad}, - {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, - {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, - {0x9becce62836ac577, 0x4ee367f9430aec33}, - {0xc2e801fb244576d5, 0x229c41f793cda740}, - {0xf3a20279ed56d48a, 0x6b43527578c11110}, - {0x9845418c345644d6, 0x830a13896b78aaaa}, - {0xbe5691ef416bd60c, 0x23cc986bc656d554}, - {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, - {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, - {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, - {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, - {0x91376c36d99995be, 0x23100809b9c21fa2}, - {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, - {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, - {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, - {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, - {0xdd95317f31c7fa1d, 0x40405643d711d584}, - {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, - {0xad1c8eab5ee43b66, 0xda3243650005eed0}, - {0xd863b256369d4a40, 0x90bed43e40076a83}, - {0x873e4f75e2224e68, 0x5a7744a6e804a292}, - {0xa90de3535aaae202, 0x711515d0a205cb37}, - {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, - {0x8412d9991ed58091, 0xe858790afe9486c3}, - {0xa5178fff668ae0b6, 0x626e974dbe39a873}, - {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, - {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, - {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, - {0xc987434744ac874e, 0xa327ffb266b56221}, - {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, - {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, - {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, - {0xf6019da07f549b2b, 0x7e2a53a146606a49}, - {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, - {0xc0314325637a1939, 0xfa911155fefb5309}, - {0xf03d93eebc589f88, 0x793555ab7eba27cb}, - {0x96267c7535b763b5, 0x4bc1558b2f3458df}, - {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, - {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, - {0x92a1958a7675175f, 0x0bfacd89ec191eca}, - {0xb749faed14125d36, 0xcef980ec671f667c}, - {0xe51c79a85916f484, 0x82b7e12780e7401b}, - {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, - {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, - {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, - {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, - {0xaecc49914078536d, 0x58fae9f773886e19}, - {0xda7f5bf590966848, 0xaf39a475506a899f}, - {0x888f99797a5e012d, 0x6d8406c952429604}, - {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, - {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, - {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, - {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, - {0xd0601d8efc57b08b, 0xf13b94daf124da27}, - {0x823c12795db6ce57, 0x76c53d08d6b70859}, - {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, - {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, - {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, - {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, - {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, - {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, - {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, - {0xc21094364dfb5636, 0x985915fc12f542e5}, - {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, - {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, - {0xbd8430bd08277231, 0x50c6ff782a838354}, - {0xece53cec4a314ebd, 0xa4f8bf5635246429}, - {0x940f4613ae5ed136, 0x871b7795e136be9a}, - {0xb913179899f68584, 0x28e2557b59846e40}, - {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, - {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, - {0xb4bca50b065abe63, 0x0fed077a756b53aa}, - {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, - {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, - {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, - {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, - {0x89e42caaf9491b60, 0xf41686c49db57245}, - {0xac5d37d5b79b6239, 0x311c2875c522ced6}, - {0xd77485cb25823ac7, 0x7d633293366b828c}, - {0x86a8d39ef77164bc, 0xae5dff9c02033198}, - {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, - {0xd267caa862a12d66, 0xd072df63c324fd7c}, - {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, - {0xa46116538d0deb78, 0x52d9be85f074e609}, - {0xcd795be870516656, 0x67902e276c921f8c}, - {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, - {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, - {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, - {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, - {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, - {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, - {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, - {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, - {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, - {0xef340a98172aace4, 0x86fb897116c87c35}, - {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, - {0xbae0a846d2195712, 0x8974836059cca10a}, - {0xe998d258869facd7, 0x2bd1a438703fc94c}, - {0x91ff83775423cc06, 0x7b6306a34627ddd0}, - {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, - {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, - {0x8e938662882af53e, 0x547eb47b7282ee9d}, - {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, - {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, - {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, - {0xae0b158b4738705e, 0x9624ab50b148d446}, - {0xd98ddaee19068c76, 0x3badd624dd9b0958}, - {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, - {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, - {0xd47487cc8470652b, 0x7647c32000696720}, - {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, - {0xa5fb0a17c777cf09, 0xf468107100525891}, - {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, - {0x81ac1fe293d599bf, 0xc6f14cd848405531}, - {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, - {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, - {0xfd442e4688bd304a, 0x908f4a166d1da664}, - {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, - {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, - {0xf7549530e188c128, 0xd12bee59e68ef47d}, - {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, - {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, - {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, - {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, - {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, - {0xebdf661791d60f56, 0x111b495b3464ad22}, - {0x936b9fcebb25c995, 0xcab10dd900beec35}, - {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, - {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, - {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, - {0xb3f4e093db73a093, 0x59ed216765690f57}, - {0xe0f218b8d25088b8, 0x306869c13ec3532d}, - {0x8c974f7383725573, 0x1e414218c73a13fc}, - {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, - {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, - {0x894bc396ce5da772, 0x6b8bba8c328eb784}, - {0xab9eb47c81f5114f, 0x066ea92f3f326565}, - {0xd686619ba27255a2, 0xc80a537b0efefebe}, - {0x8613fd0145877585, 0xbd06742ce95f5f37}, - {0xa798fc4196e952e7, 0x2c48113823b73705}, - {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, - {0x82ef85133de648c4, 0x9a984d73dbe722fc}, - {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, - {0xcc963fee10b7d1b3, 0x318df905079926a9}, - {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, - {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, - {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, - {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, - {0x9c1661a651213e2d, 0x06bea10ca65c084f}, - {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, - {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, - {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, - {0xbe89523386091465, 0xf6bbb397f1135824}, - {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, - {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, - {0xba121a4650e4ddeb, 0x92f34d62616ce414}, - {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, - {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, - {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, - {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, - {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, - {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, - {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, - {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, - {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, - {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, - {0x87625f056c7c4a8b, 0x11471cd764ad4973}, - {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, - {0xd389b47879823479, 0x4aff1d108d4ec2c4}, - {0x843610cb4bf160cb, 0xcedf722a585139bb}, - {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, - {0xce947a3da6a9273e, 0x733d226229feea33}, - {0x811ccc668829b887, 0x0806357d5a3f5260}, - {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, - {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, - {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, - {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, - {0xc5029163f384a931, 0x0a9e795e65d4df12}, - {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, - {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, - {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, - {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, - {0x964e858c91ba2655, 0x3a6a07f8d510f870}, - {0xbbe226efb628afea, 0x890489f70a55368c}, - {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, - {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, - {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, - {0xe55990879ddcaabd, 0xcc420a6a101d0516}, - {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, - {0xb32df8e9f3546564, 0x47939822dc96abfa}, - {0xdff9772470297ebd, 0x59787e2b93bc56f8}, - {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, - {0xaefae51477a06b03, 0xede622920b6b23f2}, - {0xdab99e59958885c4, 0xe95fab368e45ecee}, - {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, - {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, - {0xd59944a37c0752a2, 0x4be76d3346f04960}, - {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, - {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, - {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, - {0x825ecc24c873782f, 0x8ed400668c0c28c9}, - {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, - {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, - {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, - {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, - {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, - {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, - {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, - {0xc24452da229b021b, 0xfbe85badce996169}, - {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, - {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, - {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, - {0xed246723473e3813, 0x290123e9aab23b69}, - {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, - {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, - {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, - {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, - {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, - {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, - {0x8d590723948a535f, 0x579c487e5a38ad0f}, - {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, - {0xdcdb1b2798182244, 0xf8e431456cf88e66}, - {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, - {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, - {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, - {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, - {0xa87fea27a539e9a5, 0x3f2398d747b36225}, - {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, - {0x83a3eeeef9153e89, 0x1953cf68300424ad}, - {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, - {0xcdb02555653131b6, 0x3792f412cb06794e}, - {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, - {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, - {0xc8de047564d20a8b, 0xf245825a5a445276}, - {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, - {0x9ced737bb6c4183d, 0x55464dd69685606c}, - {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, - {0xf53304714d9265df, 0xd53dd99f4b3066a9}, - {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, - {0xbf8fdb78849a5f96, 0xde98520472bdd034}, - {0xef73d256a5c0f77c, 0x963e66858f6d4441}, - {0x95a8637627989aad, 0xdde7001379a44aa9}, - {0xbb127c53b17ec159, 0x5560c018580d5d53}, - {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, - {0x9226712162ab070d, 0xcab3961304ca70e9}, - {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, - {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, - {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, - {0xb267ed1940f1c61c, 0x55f038b237591ed4}, - {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, - {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, - {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, - {0xd9c7dced53c72255, 0x96e7bd358c904a22}, - {0x881cea14545c7575, 0x7e50d64177da2e55}, - {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, - {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, - {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, - {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, - {0xcfb11ead453994ba, 0x67de18eda5814af3}, - {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, - {0xa2425ff75e14fc31, 0xa1258379a94d028e}, - {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, - {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, - {0x9e74d1b791e07e48, 0x775ea264cf55347e}, - {0xc612062576589dda, 0x95364afe032a819e}, - {0xf79687aed3eec551, 0x3a83ddbd83f52205}, - {0x9abe14cd44753b52, 0xc4926a9672793543}, - {0xc16d9a0095928a27, 0x75b7053c0f178294}, - {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, - {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, - {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, - {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, - {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, - {0xb877aa3236a4b449, 0x09befeb9fad487c3}, - {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, - {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, - {0xb424dc35095cd80f, 0x538484c19ef38c95}, - {0xe12e13424bb40e13, 0x2865a5f206b06fba}, - {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, - {0xafebff0bcb24aafe, 0xf78f69a51539d749}, - {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, - {0x89705f4136b4a597, 0x31680a88f8953031}, - {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, - {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, - {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, - {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, - {0xd1b71758e219652b, 0xd3c36113404ea4a9}, - {0x83126e978d4fdf3b, 0x645a1cac083126ea}, - {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, - {0xcccccccccccccccc, 0xcccccccccccccccd}, - {0x8000000000000000, 0x0000000000000000}, - {0xa000000000000000, 0x0000000000000000}, - {0xc800000000000000, 0x0000000000000000}, - {0xfa00000000000000, 0x0000000000000000}, - {0x9c40000000000000, 0x0000000000000000}, - {0xc350000000000000, 0x0000000000000000}, - {0xf424000000000000, 0x0000000000000000}, - {0x9896800000000000, 0x0000000000000000}, - {0xbebc200000000000, 0x0000000000000000}, - {0xee6b280000000000, 0x0000000000000000}, - {0x9502f90000000000, 0x0000000000000000}, - {0xba43b74000000000, 0x0000000000000000}, - {0xe8d4a51000000000, 0x0000000000000000}, - {0x9184e72a00000000, 0x0000000000000000}, - {0xb5e620f480000000, 0x0000000000000000}, - {0xe35fa931a0000000, 0x0000000000000000}, - {0x8e1bc9bf04000000, 0x0000000000000000}, - {0xb1a2bc2ec5000000, 0x0000000000000000}, - {0xde0b6b3a76400000, 0x0000000000000000}, - {0x8ac7230489e80000, 0x0000000000000000}, - {0xad78ebc5ac620000, 0x0000000000000000}, - {0xd8d726b7177a8000, 0x0000000000000000}, - {0x878678326eac9000, 0x0000000000000000}, - {0xa968163f0a57b400, 0x0000000000000000}, - {0xd3c21bcecceda100, 0x0000000000000000}, - {0x84595161401484a0, 0x0000000000000000}, - {0xa56fa5b99019a5c8, 0x0000000000000000}, - {0xcecb8f27f4200f3a, 0x0000000000000000}, - {0x813f3978f8940984, 0x4000000000000000}, - {0xa18f07d736b90be5, 0x5000000000000000}, - {0xc9f2c9cd04674ede, 0xa400000000000000}, - {0xfc6f7c4045812296, 0x4d00000000000000}, - {0x9dc5ada82b70b59d, 0xf020000000000000}, - {0xc5371912364ce305, 0x6c28000000000000}, - {0xf684df56c3e01bc6, 0xc732000000000000}, - {0x9a130b963a6c115c, 0x3c7f400000000000}, - {0xc097ce7bc90715b3, 0x4b9f100000000000}, - {0xf0bdc21abb48db20, 0x1e86d40000000000}, - {0x96769950b50d88f4, 0x1314448000000000}, - {0xbc143fa4e250eb31, 0x17d955a000000000}, - {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, - {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, - {0xb7abc627050305ad, 0xf14a3d9e40000000}, - {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, - {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, - {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, - {0xe0352f62a19e306e, 0xd50b2037ad200000}, - {0x8c213d9da502de45, 0x4526f422cc340000}, - {0xaf298d050e4395d6, 0x9670b12b7f410000}, - {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, - {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, - {0xab0e93b6efee0053, 0x8eea0d047a457a00}, - {0xd5d238a4abe98068, 0x72a4904598d6d880}, - {0x85a36366eb71f041, 0x47a6da2b7f864750}, - {0xa70c3c40a64e6c51, 0x999090b65f67d924}, - {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, - {0x82818f1281ed449f, 0xbff8f10e7a8921a4}, - {0xa321f2d7226895c7, 0xaff72d52192b6a0d}, - {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490}, - {0xfee50b7025c36a08, 0x02f236d04753d5b4}, - {0x9f4f2726179a2245, 0x01d762422c946590}, - {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5}, - {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2}, - {0x9b934c3b330c8577, 0x63cc55f49f88eb2f}, - {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb}, - {0xf316271c7fc3908a, 0x8bef464e3945ef7a}, - {0x97edd871cfda3a56, 0x97758bf0e3cbb5ac}, - {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317}, - {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd}, - {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a}, - {0xb975d6b6ee39e436, 0xb3e2fd538e122b44}, - {0xe7d34c64a9c85d44, 0x60dbbca87196b616}, - {0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd}, - {0xb51d13aea4a488dd, 0x6babab6398bdbe41}, - {0xe264589a4dcdab14, 0xc696963c7eed2dd1}, - {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2}, - {0xb0de65388cc8ada8, 0x3b25a55f43294bcb}, - {0xdd15fe86affad912, 0x49ef0eb713f39ebe}, - {0x8a2dbf142dfcc7ab, 0x6e3569326c784337}, - {0xacb92ed9397bf996, 0x49c2c37f07965404}, - {0xd7e77a8f87daf7fb, 0xdc33745ec97be906}, - {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3}, - {0xa8acd7c0222311bc, 0xc40832ea0d68ce0c}, - {0xd2d80db02aabd62b, 0xf50a3fa490c30190}, - {0x83c7088e1aab65db, 0x792667c6da79e0fa}, - {0xa4b8cab1a1563f52, 0x577001b891185938}, - {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, - {0x80b05e5ac60b6178, 0x544f8158315b05b4}, - {0xa0dc75f1778e39d6, 0x696361ae3db1c721}, - {0xc913936dd571c84c, 0x03bc3a19cd1e38e9}, - {0xfb5878494ace3a5f, 0x04ab48a04065c723}, - {0x9d174b2dcec0e47b, 0x62eb0d64283f9c76}, - {0xc45d1df942711d9a, 0x3ba5d0bd324f8394}, - {0xf5746577930d6500, 0xca8f44ec7ee36479}, - {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb}, - {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e}, - {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e}, - {0x95d04aee3b80ece5, 0xbba1f1d158724a12}, - {0xbb445da9ca61281f, 0x2a8a6e45ae8edc97}, - {0xea1575143cf97226, 0xf52d09d71a3293bd}, - {0x924d692ca61be758, 0x593c2626705f9c56}, - {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c}, - {0xe498f455c38b997a, 0x0b6dfb9c0f956447}, - {0x8edf98b59a373fec, 0x4724bd4189bd5eac}, - {0xb2977ee300c50fe7, 0x58edec91ec2cb657}, - {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed}, - {0x8b865b215899f46c, 0xbd79e0d20082ee74}, - {0xae67f1e9aec07187, 0xecd8590680a3aa11}, - {0xda01ee641a708de9, 0xe80e6f4820cc9495}, - {0x884134fe908658b2, 0x3109058d147fdcdd}, - {0xaa51823e34a7eede, 0xbd4b46f0599fd415}, - {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a}, - {0x850fadc09923329e, 0x03e2cf6bc604ddb0}, - {0xa6539930bf6bff45, 0x84db8346b786151c}, - {0xcfe87f7cef46ff16, 0xe612641865679a63}, - {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e}, - {0xa26da3999aef7749, 0xe3be5e330f38f09d}, - {0xcb090c8001ab551c, 0x5cadf5bfd3072cc5}, - {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6}, - {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa}, - {0xc646d63501a1511d, 0xb281e1fd541501b8}, - {0xf7d88bc24209a565, 0x1f225a7ca91a4226}, - {0x9ae757596946075f, 0x3375788de9b06958}, - {0xc1a12d2fc3978937, 0x0052d6b1641c83ae}, - {0xf209787bb47d6b84, 0xc0678c5dbd23a49a}, - {0x9745eb4d50ce6332, 0xf840b7ba963646e0}, - {0xbd176620a501fbff, 0xb650e5a93bc3d898}, - {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe}, - {0x93ba47c980e98cdf, 0xc66f336c36b10137}, - {0xb8a8d9bbe123f017, 0xb80b0047445d4184}, - {0xe6d3102ad96cec1d, 0xa60dc059157491e5}, - {0x9043ea1ac7e41392, 0x87c89837ad68db2f}, - {0xb454e4a179dd1877, 0x29babe4598c311fb}, - {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a}, - {0x8ce2529e2734bb1d, 0x1899e4a65f58660c}, - {0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f}, - {0xdc21a1171d42645d, 0x76707543f4fa1f73}, - {0x899504ae72497eba, 0x6a06494a791c53a8}, - {0xabfa45da0edbde69, 0x0487db9d17636892}, - {0xd6f8d7509292d603, 0x45a9d2845d3c42b6}, - {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, - {0xa7f26836f282b732, 0x8e6cac7768d7141e}, - {0xd1ef0244af2364ff, 0x3207d795430cd926}, - {0x8335616aed761f1f, 0x7f44e6bd49e807b8}, - {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6}, - {0xcd036837130890a1, 0x36dba887c37a8c0f}, - {0x802221226be55a64, 0xc2494954da2c9789}, - {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c}, - {0xc83553c5c8965d3d, 0x6f92829494e5acc7}, - {0xfa42a8b73abbf48c, 0xcb772339ba1f17f9}, - {0x9c69a97284b578d7, 0xff2a760414536efb}, - {0xc38413cf25e2d70d, 0xfef5138519684aba}, - {0xf46518c2ef5b8cd1, 0x7eb258665fc25d69}, - {0x98bf2f79d5993802, 0xef2f773ffbd97a61}, - {0xbeeefb584aff8603, 0xaafb550ffacfd8fa}, - {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38}, - {0x952ab45cfa97a0b2, 0xdd945a747bf26183}, - {0xba756174393d88df, 0x94f971119aeef9e4}, - {0xe912b9d1478ceb17, 0x7a37cd5601aab85d}, - {0x91abb422ccb812ee, 0xac62e055c10ab33a}, - {0xb616a12b7fe617aa, 0x577b986b314d6009}, - {0xe39c49765fdf9d94, 0xed5a7e85fda0b80b}, - {0x8e41ade9fbebc27d, 0x14588f13be847307}, - {0xb1d219647ae6b31c, 0x596eb2d8ae258fc8}, - {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb}, - {0x8aec23d680043bee, 0x25de7bb9480d5854}, - {0xada72ccc20054ae9, 0xaf561aa79a10ae6a}, - {0xd910f7ff28069da4, 0x1b2ba1518094da04}, - {0x87aa9aff79042286, 0x90fb44d2f05d0842}, - {0xa99541bf57452b28, 0x353a1607ac744a53}, - {0xd3fa922f2d1675f2, 0x42889b8997915ce8}, - {0x847c9b5d7c2e09b7, 0x69956135febada11}, - {0xa59bc234db398c25, 0x43fab9837e699095}, - {0xcf02b2c21207ef2e, 0x94f967e45e03f4bb}, - {0x8161afb94b44f57d, 0x1d1be0eebac278f5}, - {0xa1ba1ba79e1632dc, 0x6462d92a69731732}, - {0xca28a291859bbf93, 0x7d7b8f7503cfdcfe}, - {0xfcb2cb35e702af78, 0x5cda735244c3d43e}, - {0x9defbf01b061adab, 0x3a0888136afa64a7}, - {0xc56baec21c7a1916, 0x088aaa1845b8fdd0}, - {0xf6c69a72a3989f5b, 0x8aad549e57273d45}, - {0x9a3c2087a63f6399, 0x36ac54e2f678864b}, - {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd}, - {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5}, - {0x969eb7c47859e743, 0x9f644ae5a4b1b325}, - {0xbc4665b596706114, 0x873d5d9f0dde1fee}, - {0xeb57ff22fc0c7959, 0xa90cb506d155a7ea}, - {0x9316ff75dd87cbd8, 0x09a7f12442d588f2}, - {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f}, - {0xe5d3ef282a242e81, 0x8f1668c8a86da5fa}, - {0x8fa475791a569d10, 0xf96e017d694487bc}, - {0xb38d92d760ec4455, 0x37c981dcc395a9ac}, - {0xe070f78d3927556a, 0x85bbe253f47b1417}, - {0x8c469ab843b89562, 0x93956d7478ccec8e}, - {0xaf58416654a6babb, 0x387ac8d1970027b2}, - {0xdb2e51bfe9d0696a, 0x06997b05fcc0319e}, - {0x88fcf317f22241e2, 0x441fece3bdf81f03}, - {0xab3c2fddeeaad25a, 0xd527e81cad7626c3}, - {0xd60b3bd56a5586f1, 0x8a71e223d8d3b074}, - {0x85c7056562757456, 0xf6872d5667844e49}, - {0xa738c6bebb12d16c, 0xb428f8ac016561db}, - {0xd106f86e69d785c7, 0xe13336d701beba52}, - {0x82a45b450226b39c, 0xecc0024661173473}, - {0xa34d721642b06084, 0x27f002d7f95d0190}, - {0xcc20ce9bd35c78a5, 0x31ec038df7b441f4}, - {0xff290242c83396ce, 0x7e67047175a15271}, - {0x9f79a169bd203e41, 0x0f0062c6e984d386}, - {0xc75809c42c684dd1, 0x52c07b78a3e60868}, - {0xf92e0c3537826145, 0xa7709a56ccdf8a82}, - {0x9bbcc7a142b17ccb, 0x88a66076400bb691}, - {0xc2abf989935ddbfe, 0x6acff893d00ea435}, - {0xf356f7ebf83552fe, 0x0583f6b8c4124d43}, - {0x98165af37b2153de, 0xc3727a337a8b704a}, - {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c}, - {0xeda2ee1c7064130c, 0x1162def06f79df73}, - {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8}, - {0xb9a74a0637ce2ee1, 0x6d953e2bd7173692}, - {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437}, - {0x910ab1d4db9914a0, 0x1d9c9892400a22a2}, - {0xb54d5e4a127f59c8, 0x2503beb6d00cab4b}, - {0xe2a0b5dc971f303a, 0x2e44ae64840fd61d}, - {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, - {0xb10d8e1456105dad, 0x7425a83e872c5f47}, - {0xdd50f1996b947518, 0xd12f124e28f77719}, - {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f}, - {0xace73cbfdc0bfb7b, 0x636cc64d1001550b}, - {0xd8210befd30efa5a, 0x3c47f7e05401aa4e}, - {0x8714a775e3e95c78, 0x65acfaec34810a71}, - {0xa8d9d1535ce3b396, 0x7f1839a741a14d0d}, - {0xd31045a8341ca07c, 0x1ede48111209a050}, - {0x83ea2b892091e44d, 0x934aed0aab460432}, - {0xa4e4b66b68b65d60, 0xf81da84d5617853f}, - {0xce1de40642e3f4b9, 0x36251260ab9d668e}, - {0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019}, - {0xa1075a24e4421730, 0xb24cf65b8612f81f}, - {0xc94930ae1d529cfc, 0xdee033f26797b627}, - {0xfb9b7cd9a4a7443c, 0x169840ef017da3b1}, - {0x9d412e0806e88aa5, 0x8e1f289560ee864e}, - {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2}, - {0xf5b5d7ec8acb58a2, 0xae10af696774b1db}, - {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29}, - {0xbff610b0cc6edd3f, 0x17fd090a58d32af3}, - {0xeff394dcff8a948e, 0xddfc4b4cef07f5b0}, - {0x95f83d0a1fb69cd9, 0x4abdaf101564f98e}, - {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1}, - {0xea53df5fd18d5513, 0x84c86189216dc5ed}, - {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4}, - {0xb7118682dbb66a77, 0x3fbc8c33221dc2a1}, - {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, - {0x8f05b1163ba6832d, 0x29cb4d87f2a7400e}, - {0xb2c71d5bca9023f8, 0x743e20e9ef511012}, - {0xdf78e4b2bd342cf6, 0x914da9246b255416}, - {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e}, - {0xae9672aba3d0c320, 0xa184ac2473b529b1}, - {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e}, - {0x8865899617fb1871, 0x7e2fa67c7a658892}, - {0xaa7eebfb9df9de8d, 0xddbb901b98feeab7}, - {0xd51ea6fa85785631, 0x552a74227f3ea565}, - {0x8533285c936b35de, 0xd53a88958f87275f}, - {0xa67ff273b8460356, 0x8a892abaf368f137}, - {0xd01fef10a657842c, 0x2d2b7569b0432d85}, - {0x8213f56a67f6b29b, 0x9c3b29620e29fc73}, - {0xa298f2c501f45f42, 0x8349f3ba91b47b8f}, - {0xcb3f2f7642717713, 0x241c70a936219a73}, - {0xfe0efb53d30dd4d7, 0xed238cd383aa0110}, - {0x9ec95d1463e8a506, 0xf4363804324a40aa}, - {0xc67bb4597ce2ce48, 0xb143c6053edcd0d5}, - {0xf81aa16fdc1b81da, 0xdd94b7868e94050a}, - {0x9b10a4e5e9913128, 0xca7cf2b4191c8326}, - {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0}, - {0xf24a01a73cf2dccf, 0xbc633b39673c8cec}, - {0x976e41088617ca01, 0xd5be0503e085d813}, - {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18}, - {0xec9c459d51852ba2, 0xddf8e7d60ed1219e}, - {0x93e1ab8252f33b45, 0xcabb90e5c942b503}, - {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, - {0xe7109bfba19c0c9d, 0x0cc512670a783ad4}, - {0x906a617d450187e2, 0x27fb2b80668b24c5}, - {0xb484f9dc9641e9da, 0xb1f9f660802dedf6}, - {0xe1a63853bbd26451, 0x5e7873f8a0396973}, - {0x8d07e33455637eb2, 0xdb0b487b6423e1e8}, - {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62}, - {0xdc5c5301c56b75f7, 0x7641a140cc7810fb}, - {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d}, - {0xac2820d9623bf429, 0x546345fa9fbdcd44}, - {0xd732290fbacaf133, 0xa97c177947ad4095}, - {0x867f59a9d4bed6c0, 0x49ed8eabcccc485d}, - {0xa81f301449ee8c70, 0x5c68f256bfff5a74}, - {0xd226fc195c6a2f8c, 0x73832eec6fff3111}, - {0x83585d8fd9c25db7, 0xc831fd53c5ff7eab}, - {0xa42e74f3d032f525, 0xba3e7ca8b77f5e55}, - {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb}, - {0x80444b5e7aa7cf85, 0x7980d163cf5b81b3}, - {0xa0555e361951c366, 0xd7e105bcc332621f}, - {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7}, - {0xfa856334878fc150, 0xb14f98f6f0feb951}, - {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3}, - {0xc3b8358109e84f07, 0x0a862f80ec4700c8}, - {0xf4a642e14c6262c8, 0xcd27bb612758c0fa}, - {0x98e7e9cccfbd7dbd, 0x8038d51cb897789c}, - {0xbf21e44003acdd2c, 0xe0470a63e6bd56c3}, - {0xeeea5d5004981478, 0x1858ccfce06cac74}, - {0x95527a5202df0ccb, 0x0f37801e0c43ebc8}, - {0xbaa718e68396cffd, 0xd30560258f54e6ba}, - {0xe950df20247c83fd, 0x47c6b82ef32a2069}, - {0x91d28b7416cdd27e, 0x4cdc331d57fa5441}, - {0xb6472e511c81471d, 0xe0133fe4adf8e952}, - {0xe3d8f9e563a198e5, 0x58180fddd97723a6}, - {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648}, - {0xb201833b35d63f73, 0x2cd2cc6551e513da}, - {0xde81e40a034bcf4f, 0xf8077f7ea65e58d1}, - {0x8b112e86420f6191, 0xfb04afaf27faf782}, - {0xadd57a27d29339f6, 0x79c5db9af1f9b563}, - {0xd94ad8b1c7380874, 0x18375281ae7822bc}, - {0x87cec76f1c830548, 0x8f2293910d0b15b5}, - {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22}, - {0xd433179d9c8cb841, 0x5fa60692a46151eb}, - {0x849feec281d7f328, 0xdbc7c41ba6bcd333}, - {0xa5c7ea73224deff3, 0x12b9b522906c0800}, - {0xcf39e50feae16bef, 0xd768226b34870a00}, - {0x81842f29f2cce375, 0xe6a1158300d46640}, - {0xa1e53af46f801c53, 0x60495ae3c1097fd0}, - {0xca5e89b18b602368, 0x385bb19cb14bdfc4}, - {0xfcf62c1dee382c42, 0x46729e03dd9ed7b5}, - {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1}, - {0xc5a05277621be293, 0xc7098b7305241885}, - {0xf70867153aa2db38, 0xb8cbee4fc66d1ea7} -#else - {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, - {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, - {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, - {0x86a8d39ef77164bc, 0xae5dff9c02033198}, - {0xd98ddaee19068c76, 0x3badd624dd9b0958}, - {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, - {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, - {0xe55990879ddcaabd, 0xcc420a6a101d0516}, - {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, - {0x95a8637627989aad, 0xdde7001379a44aa9}, - {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, - {0xc350000000000000, 0x0000000000000000}, - {0x9dc5ada82b70b59d, 0xf020000000000000}, - {0xfee50b7025c36a08, 0x02f236d04753d5b4}, - {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, - {0xa6539930bf6bff45, 0x84db8346b786151c}, - {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, - {0xd910f7ff28069da4, 0x1b2ba1518094da04}, - {0xaf58416654a6babb, 0x387ac8d1970027b2}, - {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, - {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, - {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, - {0x95527a5202df0ccb, 0x0f37801e0c43ebc8} -#endif -}; - -#if !FMT_USE_FULL_CACHE_DRAGONBOX -template -const uint64_t basic_data::powers_of_5_64[] = { - 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, - 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, - 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, - 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, - 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, - 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, - 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, - 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, - 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; - -template -const uint32_t basic_data::dragonbox_pow10_recovery_errors[] = { - 0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001, - 0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555, - 0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110, - 0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454, - 0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014, - 0x69514555, 0x05151109, 0x00155555}; -#endif - -template -const char basic_data::foreground_color[] = "\x1b[38;2;"; -template -const char basic_data::background_color[] = "\x1b[48;2;"; -template const char basic_data::reset_color[] = "\x1b[0m"; -template const wchar_t basic_data::wreset_color[] = L"\x1b[0m"; -template const char basic_data::signs[] = {0, '-', '+', ' '}; -template -const char basic_data::left_padding_shifts[] = {31, 31, 0, 1, 0}; -template -const char basic_data::right_padding_shifts[] = {0, 31, 0, 1, 0}; - -template struct bits { - static FMT_CONSTEXPR_DECL const int value = - static_cast(sizeof(T) * std::numeric_limits::digits); -}; - -class fp; -template fp normalize(fp value); - -// Lower (upper) boundary is a value half way between a floating-point value -// and its predecessor (successor). Boundaries have the same exponent as the -// value so only significands are stored. -struct boundaries { - uint64_t lower; - uint64_t upper; -}; - -// A handmade floating-point number f * pow(2, e). -class fp { - private: - using significand_type = uint64_t; - - template - using is_supported_float = bool_constant; - - public: - significand_type f; - int e; - - // All sizes are in bits. - // Subtract 1 to account for an implicit most significant bit in the - // normalized form. - static FMT_CONSTEXPR_DECL const int double_significand_size = - std::numeric_limits::digits - 1; - static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = - 1ULL << double_significand_size; - static FMT_CONSTEXPR_DECL const int significand_size = - bits::value; - - fp() : f(0), e(0) {} - fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} - - // Constructs fp from an IEEE754 double. It is a template to prevent compile - // errors on platforms where double is not IEEE754. - template explicit fp(Double d) { assign(d); } - - // Assigns d to this and return true iff predecessor is closer than successor. - template ::value)> - bool assign(Float d) { - // Assume float is in the format [sign][exponent][significand]. - using limits = std::numeric_limits; - const int float_significand_size = limits::digits - 1; - const int exponent_size = - bits::value - float_significand_size - 1; // -1 for sign - const uint64_t float_implicit_bit = 1ULL << float_significand_size; - const uint64_t significand_mask = float_implicit_bit - 1; - const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; - const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; - constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); - auto u = bit_cast>(d); - f = u & significand_mask; - int biased_e = - static_cast((u & exponent_mask) >> float_significand_size); - // Predecessor is closer if d is a normalized power of 2 (f == 0) other than - // the smallest normalized number (biased_e > 1). - bool is_predecessor_closer = f == 0 && biased_e > 1; - if (biased_e != 0) - f += float_implicit_bit; - else - biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - e = biased_e - exponent_bias - float_significand_size; - return is_predecessor_closer; - } - - template ::value)> - bool assign(Float) { - *this = fp(); - return false; - } -}; - -// Normalizes the value converted from double and multiplied by (1 << SHIFT). -template fp normalize(fp value) { - // Handle subnormals. - const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; - while ((value.f & shifted_implicit_bit) == 0) { - value.f <<= 1; - --value.e; - } - // Subtract 1 to account for hidden bit. - const auto offset = - fp::significand_size - fp::double_significand_size - SHIFT - 1; - value.f <<= offset; - value.e -= offset; - return value; +template <> +FMT_API FMT_FUNC auto format_facet::do_put( + appender out, loc_value val, const format_specs<>& specs) const -> bool { + return val.visit( + detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); } - -inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } - -// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. -inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { -#if FMT_USE_INT128 - auto product = static_cast<__uint128_t>(lhs) * rhs; - auto f = static_cast(product >> 64); - return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; -#else - // Multiply 32-bit parts of significands. - uint64_t mask = (1ULL << 32) - 1; - uint64_t a = lhs >> 32, b = lhs & mask; - uint64_t c = rhs >> 32, d = rhs & mask; - uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; - // Compute mid 64-bit of result and round. - uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); - return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); #endif -} -inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; } - -// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its -// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. -inline fp get_cached_power(int min_exponent, int& pow10_exponent) { - const int shift = 32; - const auto significand = static_cast(data::log10_2_significand); - int index = static_cast( - ((min_exponent + fp::significand_size - 1) * (significand >> shift) + - ((int64_t(1) << shift) - 1)) // ceil - >> 32 // arithmetic shift - ); - // Decimal exponent of the first (smallest) cached power of 10. - const int first_dec_exp = -348; - // Difference between 2 consecutive decimal exponents in cached powers of 10. - const int dec_exp_step = 8; - index = (index - first_dec_exp - 1) / dec_exp_step + 1; - pow10_exponent = first_dec_exp + index * dec_exp_step; - return {data::grisu_pow10_significands[index], - data::grisu_pow10_exponents[index]}; +FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt, + format_args args) { + auto ec = std::error_code(error_code, std::generic_category()); + return std::system_error(ec, vformat(fmt, args)); } -// A simple accumulator to hold the sums of terms in bigint::square if uint128_t -// is not available. -struct accumulator { - uint64_t lower; - uint64_t upper; - - accumulator() : lower(0), upper(0) {} - explicit operator uint32_t() const { return static_cast(lower); } - - void operator+=(uint64_t n) { - lower += n; - if (lower < n) ++upper; - } - void operator>>=(int shift) { - assert(shift == 32); - (void)shift; - lower = (upper << 32) | (lower >> 32); - upper >>= 32; - } -}; - -class bigint { - private: - // A bigint is stored as an array of bigits (big digits), with bigit at index - // 0 being the least significant one. - using bigit = uint32_t; - using double_bigit = uint64_t; - enum { bigits_capacity = 32 }; - basic_memory_buffer bigits_; - int exp_; - - bigit operator[](int index) const { return bigits_[to_unsigned(index)]; } - bigit& operator[](int index) { return bigits_[to_unsigned(index)]; } - - static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; - - friend struct formatter; - - void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast((*this)[index]) - other - borrow; - (*this)[index] = static_cast(result); - borrow = static_cast(result >> (bigit_bits * 2 - 1)); - } - - void remove_leading_zeros() { - int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; - bigits_.resize(to_unsigned(num_bigits + 1)); - } - - // Computes *this -= other assuming aligned bigints and *this >= other. - void subtract_aligned(const bigint& other) { - FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); - FMT_ASSERT(compare(*this, other) >= 0, ""); - bigit borrow = 0; - int i = other.exp_ - exp_; - for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) - subtract_bigits(i, other.bigits_[j], borrow); - while (borrow > 0) subtract_bigits(i, 0, borrow); - remove_leading_zeros(); - } - - void multiply(uint32_t value) { - const double_bigit wide_value = value; - bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - double_bigit result = bigits_[i] * wide_value + carry; - bigits_[i] = static_cast(result); - carry = static_cast(result >> bigit_bits); - } - if (carry != 0) bigits_.push_back(carry); - } - - void multiply(uint64_t value) { - const bigit mask = ~bigit(0); - const double_bigit lower = value & mask; - const double_bigit upper = value >> bigit_bits; - double_bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - double_bigit result = bigits_[i] * lower + (carry & mask); - carry = - bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); - bigits_[i] = static_cast(result); - } - while (carry != 0) { - bigits_.push_back(carry & mask); - carry >>= bigit_bits; - } - } - - public: - bigint() : exp_(0) {} - explicit bigint(uint64_t n) { assign(n); } - ~bigint() { assert(bigits_.capacity() <= bigits_capacity); } - - bigint(const bigint&) = delete; - void operator=(const bigint&) = delete; - - void assign(const bigint& other) { - auto size = other.bigits_.size(); - bigits_.resize(size); - auto data = other.bigits_.data(); - std::copy(data, data + size, make_checked(bigits_.data(), size)); - exp_ = other.exp_; - } - - void assign(uint64_t n) { - size_t num_bigits = 0; - do { - bigits_[num_bigits++] = n & ~bigit(0); - n >>= bigit_bits; - } while (n != 0); - bigits_.resize(num_bigits); - exp_ = 0; - } - - int num_bigits() const { return static_cast(bigits_.size()) + exp_; } - - FMT_NOINLINE bigint& operator<<=(int shift) { - assert(shift >= 0); - exp_ += shift / bigit_bits; - shift %= bigit_bits; - if (shift == 0) return *this; - bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - bigit c = bigits_[i] >> (bigit_bits - shift); - bigits_[i] = (bigits_[i] << shift) + carry; - carry = c; - } - if (carry != 0) bigits_.push_back(carry); - return *this; - } - - template bigint& operator*=(Int value) { - FMT_ASSERT(value > 0, ""); - multiply(uint32_or_64_or_128_t(value)); - return *this; - } - - friend int compare(const bigint& lhs, const bigint& rhs) { - int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); - if (num_lhs_bigits != num_rhs_bigits) - return num_lhs_bigits > num_rhs_bigits ? 1 : -1; - int i = static_cast(lhs.bigits_.size()) - 1; - int j = static_cast(rhs.bigits_.size()) - 1; - int end = i - j; - if (end < 0) end = 0; - for (; i >= end; --i, --j) { - bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; - if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; - } - if (i != j) return i > j ? 1 : -1; - return 0; - } - - // Returns compare(lhs1 + lhs2, rhs). - friend int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { - int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); - int num_rhs_bigits = rhs.num_bigits(); - if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; - if (max_lhs_bigits > num_rhs_bigits) return 1; - auto get_bigit = [](const bigint& n, int i) -> bigit { - return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; - }; - double_bigit borrow = 0; - int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); - for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { - double_bigit sum = - static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); - bigit rhs_bigit = get_bigit(rhs, i); - if (sum > rhs_bigit + borrow) return 1; - borrow = rhs_bigit + borrow - sum; - if (borrow > 1) return -1; - borrow <<= bigit_bits; - } - return borrow != 0 ? -1 : 0; - } - - // Assigns pow(10, exp) to this bigint. - void assign_pow10(int exp) { - assert(exp >= 0); - if (exp == 0) return assign(1); - // Find the top bit. - int bitmask = 1; - while (exp >= bitmask) bitmask <<= 1; - bitmask >>= 1; - // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by - // repeated squaring and multiplication. - assign(5); - bitmask >>= 1; - while (bitmask != 0) { - square(); - if ((exp & bitmask) != 0) *this *= 5; - bitmask >>= 1; - } - *this <<= exp; // Multiply by pow(2, exp) by shifting. - } - - void square() { - basic_memory_buffer n(std::move(bigits_)); - int num_bigits = static_cast(bigits_.size()); - int num_result_bigits = 2 * num_bigits; - bigits_.resize(to_unsigned(num_result_bigits)); - using accumulator_t = conditional_t; - auto sum = accumulator_t(); - for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { - // Compute bigit at position bigit_index of the result by adding - // cross-product terms n[i] * n[j] such that i + j == bigit_index. - for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { - // Most terms are multiplied twice which can be optimized in the future. - sum += static_cast(n[i]) * n[j]; - } - (*this)[bigit_index] = static_cast(sum); - sum >>= bits::value; // Compute the carry. - } - // Do the same for the top half. - for (int bigit_index = num_bigits; bigit_index < num_result_bigits; - ++bigit_index) { - for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) - sum += static_cast(n[i++]) * n[j--]; - (*this)[bigit_index] = static_cast(sum); - sum >>= bits::value; - } - --num_result_bigits; - remove_leading_zeros(); - exp_ *= 2; - } - - // If this bigint has a bigger exponent than other, adds trailing zero to make - // exponents equal. This simplifies some operations such as subtraction. - void align(const bigint& other) { - int exp_difference = exp_ - other.exp_; - if (exp_difference <= 0) return; - int num_bigits = static_cast(bigits_.size()); - bigits_.resize(to_unsigned(num_bigits + exp_difference)); - for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) - bigits_[j] = bigits_[i]; - std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); - exp_ -= exp_difference; - } - - // Divides this bignum by divisor, assigning the remainder to this and - // returning the quotient. - int divmod_assign(const bigint& divisor) { - FMT_ASSERT(this != &divisor, ""); - if (compare(*this, divisor) < 0) return 0; - FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); - align(divisor); - int quotient = 0; - do { - subtract_aligned(divisor); - ++quotient; - } while (compare(*this, divisor) >= 0); - return quotient; - } -}; +namespace detail { -enum class round_direction { unknown, up, down }; - -// Given the divisor (normally a power of 10), the remainder = v % divisor for -// some number v and the error, returns whether v should be rounded up, down, or -// whether the rounding direction can't be determined due to error. -// error should be less than divisor / 2. -inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, - uint64_t error) { - FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. - FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. - FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. - // Round down if (remainder + error) * 2 <= divisor. - if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) - return round_direction::down; - // Round up if (remainder - error) * 2 >= divisor. - if (remainder >= error && - remainder - error >= divisor - (remainder - error)) { - return round_direction::up; - } - return round_direction::unknown; +template inline bool operator==(basic_fp x, basic_fp y) { + return x.f == y.f && x.e == y.e; } -namespace digits { -enum result { - more, // Generate more digits. - done, // Done generating digits. - error // Digit generation cancelled due to an error. -}; +// Compilers should be able to optimize this into the ror instruction. +FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { + r &= 31; + return (n >> r) | (n << (32 - r)); } - -// Generates output using the Grisu digit-gen algorithm. -// error: the size of the region (lower, upper) outside of which numbers -// definitely do not round to value (Delta in Grisu3). -template -FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, - int& exp, Handler& handler) { - const fp one(1ULL << -value.e, value.e); - // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be - // zero because it contains a product of two 64-bit numbers with MSB set (due - // to normalization) - 1, shifted right by at most 60 bits. - auto integral = static_cast(value.f >> -one.e); - FMT_ASSERT(integral != 0, ""); - FMT_ASSERT(integral == value.f >> -one.e, ""); - // The fractional part of scaled value (p2 in Grisu) c = value % one. - uint64_t fractional = value.f & (one.f - 1); - exp = count_digits(integral); // kappa in Grisu. - // Divide by 10 to prevent overflow. - auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e, - value.f / 10, error * 10, exp); - if (result != digits::more) return result; - // Generate digits for the integral part. This can produce up to 10 digits. - do { - uint32_t digit = 0; - auto divmod_integral = [&](uint32_t divisor) { - digit = integral / divisor; - integral %= divisor; - }; - // This optimization by Milo Yip reduces the number of integer divisions by - // one per iteration. - switch (exp) { - case 10: - divmod_integral(1000000000); - break; - case 9: - divmod_integral(100000000); - break; - case 8: - divmod_integral(10000000); - break; - case 7: - divmod_integral(1000000); - break; - case 6: - divmod_integral(100000); - break; - case 5: - divmod_integral(10000); - break; - case 4: - divmod_integral(1000); - break; - case 3: - divmod_integral(100); - break; - case 2: - divmod_integral(10); - break; - case 1: - digit = integral; - integral = 0; - break; - default: - FMT_ASSERT(false, "invalid number of digits"); - } - --exp; - auto remainder = (static_cast(integral) << -one.e) + fractional; - result = handler.on_digit(static_cast('0' + digit), - data::powers_of_10_64[exp] << -one.e, remainder, - error, exp, true); - if (result != digits::more) return result; - } while (exp > 0); - // Generate digits for the fractional part. - for (;;) { - fractional *= 10; - error *= 10; - char digit = static_cast('0' + (fractional >> -one.e)); - fractional &= one.f - 1; - --exp; - result = handler.on_digit(digit, one.f, fractional, error, exp, false); - if (result != digits::more) return result; - } +FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { + r &= 63; + return (n >> r) | (n << (64 - r)); } -// The fixed precision digit handler. -struct fixed_handler { - char* buf; - int size; - int precision; - int exp10; - bool fixed; - - digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error, - int& exp) { - // Non-fixed formats require at least one digit and no precision adjustment. - if (!fixed) return digits::more; - // Adjust fixed precision by exponent because it is relative to decimal - // point. - precision += exp + exp10; - // Check if precision is satisfied just by leading zeros, e.g. - // format("{:.2f}", 0.001) gives "0.00" without generating any digits. - if (precision > 0) return digits::more; - if (precision < 0) return digits::done; - auto dir = get_round_direction(divisor, remainder, error); - if (dir == round_direction::unknown) return digits::error; - buf[size++] = dir == round_direction::up ? '1' : '0'; - return digits::done; - } - - digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, - uint64_t error, int, bool integral) { - FMT_ASSERT(remainder < divisor, ""); - buf[size++] = digit; - if (!integral && error >= remainder) return digits::error; - if (size < precision) return digits::more; - if (!integral) { - // Check if error * 2 < divisor with overflow prevention. - // The check is not needed for the integral part because error = 1 - // and divisor > (1 << 32) there. - if (error >= divisor || error >= divisor - error) return digits::error; - } else { - FMT_ASSERT(error == 1 && divisor > 2, ""); - } - auto dir = get_round_direction(divisor, remainder, error); - if (dir != round_direction::up) - return dir == round_direction::down ? digits::done : digits::error; - ++buf[size - 1]; - for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { - buf[i] = '0'; - ++buf[i - 1]; - } - if (buf[0] > '9') { - buf[0] = '1'; - if (fixed) - buf[size++] = '0'; - else - ++exp10; - } - return digits::done; - } -}; - // Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. namespace dragonbox { -// Computes 128-bit result of multiplication of two 64-bit unsigned integers. -FMT_SAFEBUFFERS inline uint128_wrapper umul128(uint64_t x, - uint64_t y) FMT_NOEXCEPT { -#if FMT_USE_INT128 - return static_cast(x) * static_cast(y); -#elif defined(_MSC_VER) && defined(_M_X64) - uint128_wrapper result; - result.low_ = _umul128(x, y, &result.high_); - return result; -#else - const uint64_t mask = (uint64_t(1) << 32) - uint64_t(1); - - uint64_t a = x >> 32; - uint64_t b = x & mask; - uint64_t c = y >> 32; - uint64_t d = y & mask; - - uint64_t ac = a * c; - uint64_t bc = b * c; - uint64_t ad = a * d; - uint64_t bd = b * d; - - uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); - - return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), - (intermediate << 32) + (bd & mask)}; -#endif -} - -// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. -FMT_SAFEBUFFERS inline uint64_t umul128_upper64(uint64_t x, - uint64_t y) FMT_NOEXCEPT { -#if FMT_USE_INT128 - auto p = static_cast(x) * static_cast(y); - return static_cast(p >> 64); -#elif defined(_MSC_VER) && defined(_M_X64) - return __umulh(x, y); -#else - return umul128(x, y).high(); -#endif -} - -// Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a -// 128-bit unsigned integer. -FMT_SAFEBUFFERS inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) - FMT_NOEXCEPT { - uint128_wrapper g0 = umul128(x, y.high()); - g0 += umul128_upper64(x, y.low()); - return g0.high(); -} - -// Computes upper 32 bits of multiplication of a 32-bit unsigned integer and a +// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT { - return static_cast(umul128_upper64(x, y)); +inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { + return umul128_upper64(static_cast(x) << 32, y); } -// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a +// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -FMT_SAFEBUFFERS inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) - FMT_NOEXCEPT { - uint64_t g01 = x * y.high(); - uint64_t g10 = umul128_upper64(x, y.low()); - return g01 + g10; +inline uint128_fallback umul192_lower128(uint64_t x, + uint128_fallback y) noexcept { + uint64_t high = x * y.high(); + uint128_fallback high_low = umul128(x, y.low()); + return {high + high_low.high(), high_low.low()}; } // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT { +inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { return x * y; } -// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from -// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4. -inline int floor_log10_pow2(int e) FMT_NOEXCEPT { - FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); - const int shift = 22; - return (e * static_cast(data::log10_2_significand >> (64 - shift))) >> - shift; -} - // Various fast log computations. -inline int floor_log2_pow10(int e) FMT_NOEXCEPT { - FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); - const uint64_t log2_10_integer_part = 3; - const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9; - const int shift_amount = 19; - return (e * static_cast( - (log2_10_integer_part << shift_amount) | - (log2_10_fractional_digits >> (64 - shift_amount)))) >> - shift_amount; -} -inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT { - FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); - const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375; - const int shift_amount = 22; - return (e * static_cast(data::log10_2_significand >> - (64 - shift_amount)) - - static_cast(log10_4_over_3_fractional_digits >> - (64 - shift_amount))) >> - shift_amount; +inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { + FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); + return (e * 631305 - 261663) >> 21; } -// Returns true iff x is divisible by pow(2, exp). -inline bool divisible_by_power_of_2(uint32_t x, int exp) FMT_NOEXCEPT { - FMT_ASSERT(exp >= 1, ""); - FMT_ASSERT(x != 0, ""); -#ifdef FMT_BUILTIN_CTZ - return FMT_BUILTIN_CTZ(x) >= exp; -#else - return exp < num_bits() && x == ((x >> exp) << exp); -#endif -} -inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT { - FMT_ASSERT(exp >= 1, ""); - FMT_ASSERT(x != 0, ""); -#ifdef FMT_BUILTIN_CTZLL - return FMT_BUILTIN_CTZLL(x) >= exp; -#else - return exp < num_bits() && x == ((x >> exp) << exp); -#endif -} +FMT_INLINE_VARIABLE constexpr struct { + uint32_t divisor; + int shift_amount; +} div_small_pow10_infos[] = {{10, 16}, {100, 16}}; -// Returns true iff x is divisible by pow(5, exp). -inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT { - FMT_ASSERT(exp <= 10, "too large exponent"); - return x * data::divtest_table_for_pow5_32[exp].mod_inv <= - data::divtest_table_for_pow5_32[exp].max_quotient; -} -inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT { - FMT_ASSERT(exp <= 23, "too large exponent"); - return x * data::divtest_table_for_pow5_64[exp].mod_inv <= - data::divtest_table_for_pow5_64[exp].max_quotient; -} - -// Replaces n by floor(n / pow(5, N)) returning true if and only if n is -// divisible by pow(5, N). -// Precondition: n <= 2 * pow(5, N + 1). +// Replaces n by floor(n / pow(10, N)) returning true if and only if n is +// divisible by pow(10, N). +// Precondition: n <= pow(10, N + 1). template -bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT { - static constexpr struct { - uint32_t magic_number; - int bits_for_comparison; - uint32_t threshold; - int shift_amount; - } infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}}; - constexpr auto info = infos[N - 1]; - n *= info.magic_number; - const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1; - bool result = (n & comparison_mask) <= info.threshold; +bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { + // The numbers below are chosen such that: + // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, + // 2. nm mod 2^k < m if and only if n is divisible by d, + // where m is magic_number, k is shift_amount + // and d is divisor. + // + // Item 1 is a common technique of replacing division by a constant with + // multiplication, see e.g. "Division by Invariant Integers Using + // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set + // to ceil(2^k/d) for large enough k. + // The idea for item 2 originates from Schubfach. + constexpr auto info = div_small_pow10_infos[N - 1]; + FMT_ASSERT(n <= info.divisor * 10, "n is too large"); + constexpr uint32_t magic_number = + (1u << info.shift_amount) / info.divisor + 1; + n *= magic_number; + const uint32_t comparison_mask = (1u << info.shift_amount) - 1; + bool result = (n & comparison_mask) < magic_number; n >>= info.shift_amount; return result; } // Computes floor(n / pow(10, N)) for small n and N. // Precondition: n <= pow(10, N + 1). -template uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT { - static constexpr struct { - uint32_t magic_number; - int shift_amount; - uint32_t divisor_times_10; - } infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}}; - constexpr auto info = infos[N - 1]; - FMT_ASSERT(n <= info.divisor_times_10, "n is too large"); - return n * info.magic_number >> info.shift_amount; +template uint32_t small_division_by_pow10(uint32_t n) noexcept { + constexpr auto info = div_small_pow10_infos[N - 1]; + FMT_ASSERT(n <= info.divisor * 10, "n is too large"); + constexpr uint32_t magic_number = + (1u << info.shift_amount) / info.divisor + 1; + return (n * magic_number) >> info.shift_amount; } // Computes floor(n / 10^(kappa + 1)) (float) -inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) FMT_NOEXCEPT { - return n / float_info::big_divisor; +inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { + // 1374389535 = ceil(2^37/100) + return static_cast((static_cast(n) * 1374389535) >> 37); } // Computes floor(n / 10^(kappa + 1)) (double) -inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT { - return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9; +inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { + // 2361183241434822607 = ceil(2^(64+7)/1000) + return umul128_upper64(n, 2361183241434822607ull) >> 7; } // Various subroutines using pow10 cache -template struct cache_accessor; +template struct cache_accessor; template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint64_t; - static uint64_t get_cached_power(int k) FMT_NOEXCEPT { + static uint64_t get_cached_power(int k) noexcept { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); - return data::dragonbox_pow10_significands_64[k - float_info::min_k]; - } + static constexpr const uint64_t pow10_significands[] = { + 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, + 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, + 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, + 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, + 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, + 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, + 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, + 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, + 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, + 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, + 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, + 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, + 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, + 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, + 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, + 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, + 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, + 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, + 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985, + 0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297, + 0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7, + 0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21, + 0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe, + 0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a, + 0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f}; + return pow10_significands[k - float_info::min_k]; + } + + struct compute_mul_result { + carrier_uint result; + bool is_integer; + }; + struct compute_mul_parity_result { + bool parity; + bool is_integer; + }; - static carrier_uint compute_mul(carrier_uint u, - const cache_entry_type& cache) FMT_NOEXCEPT { - return umul96_upper32(u, cache); + static compute_mul_result compute_mul( + carrier_uint u, const cache_entry_type& cache) noexcept { + auto r = umul96_upper64(u, cache); + return {static_cast(r >> 32), + static_cast(r) == 0}; } static uint32_t compute_delta(const cache_entry_type& cache, - int beta_minus_1) FMT_NOEXCEPT { - return static_cast(cache >> (64 - 1 - beta_minus_1)); + int beta) noexcept { + return static_cast(cache >> (64 - 1 - beta)); } - static bool compute_mul_parity(carrier_uint two_f, - const cache_entry_type& cache, - int beta_minus_1) FMT_NOEXCEPT { - FMT_ASSERT(beta_minus_1 >= 1, ""); - FMT_ASSERT(beta_minus_1 < 64, ""); + static compute_mul_parity_result compute_mul_parity( + carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + FMT_ASSERT(beta >= 1, ""); + FMT_ASSERT(beta < 64, ""); - return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + auto r = umul96_lower64(two_f, cache); + return {((r >> (64 - beta)) & 1) != 0, + static_cast(r >> (32 - beta)) == 0}; } static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return static_cast( - (cache - (cache >> (float_info::significand_bits + 2))) >> - (64 - float_info::significand_bits - 1 - beta_minus_1)); + (cache - (cache >> (num_significand_bits() + 2))) >> + (64 - num_significand_bits() - 1 - beta)); } static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return static_cast( - (cache + (cache >> (float_info::significand_bits + 1))) >> - (64 - float_info::significand_bits - 1 - beta_minus_1)); + (cache + (cache >> (num_significand_bits() + 1))) >> + (64 - num_significand_bits() - 1 - beta)); } static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return (static_cast( - cache >> - (64 - float_info::significand_bits - 2 - beta_minus_1)) + + cache >> (64 - num_significand_bits() - 2 - beta)) + 1) / 2; } @@ -1879,16 +344,691 @@ template <> struct cache_accessor { template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; - using cache_entry_type = uint128_wrapper; + using cache_entry_type = uint128_fallback; - static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT { + static uint128_fallback get_cached_power(int k) noexcept { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); + static constexpr const uint128_fallback pow10_significands[] = { #if FMT_USE_FULL_CACHE_DRAGONBOX - return data::dragonbox_pow10_significands_128[k - - float_info::min_k]; + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0x9faacf3df73609b1, 0x77b191618c54e9ad}, + {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, + {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, + {0x9becce62836ac577, 0x4ee367f9430aec33}, + {0xc2e801fb244576d5, 0x229c41f793cda740}, + {0xf3a20279ed56d48a, 0x6b43527578c11110}, + {0x9845418c345644d6, 0x830a13896b78aaaa}, + {0xbe5691ef416bd60c, 0x23cc986bc656d554}, + {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, + {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, + {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, + {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, + {0x91376c36d99995be, 0x23100809b9c21fa2}, + {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, + {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, + {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, + {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, + {0xdd95317f31c7fa1d, 0x40405643d711d584}, + {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, + {0xad1c8eab5ee43b66, 0xda3243650005eed0}, + {0xd863b256369d4a40, 0x90bed43e40076a83}, + {0x873e4f75e2224e68, 0x5a7744a6e804a292}, + {0xa90de3535aaae202, 0x711515d0a205cb37}, + {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, + {0x8412d9991ed58091, 0xe858790afe9486c3}, + {0xa5178fff668ae0b6, 0x626e974dbe39a873}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, + {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, + {0xc987434744ac874e, 0xa327ffb266b56221}, + {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, + {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, + {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, + {0xf6019da07f549b2b, 0x7e2a53a146606a49}, + {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, + {0xc0314325637a1939, 0xfa911155fefb5309}, + {0xf03d93eebc589f88, 0x793555ab7eba27cb}, + {0x96267c7535b763b5, 0x4bc1558b2f3458df}, + {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, + {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, + {0x92a1958a7675175f, 0x0bfacd89ec191eca}, + {0xb749faed14125d36, 0xcef980ec671f667c}, + {0xe51c79a85916f484, 0x82b7e12780e7401b}, + {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, + {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, + {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, + {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, + {0xaecc49914078536d, 0x58fae9f773886e19}, + {0xda7f5bf590966848, 0xaf39a475506a899f}, + {0x888f99797a5e012d, 0x6d8406c952429604}, + {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, + {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, + {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0xd0601d8efc57b08b, 0xf13b94daf124da27}, + {0x823c12795db6ce57, 0x76c53d08d6b70859}, + {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, + {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, + {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, + {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, + {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, + {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, + {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, + {0xc21094364dfb5636, 0x985915fc12f542e5}, + {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, + {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, + {0xbd8430bd08277231, 0x50c6ff782a838354}, + {0xece53cec4a314ebd, 0xa4f8bf5635246429}, + {0x940f4613ae5ed136, 0x871b7795e136be9a}, + {0xb913179899f68584, 0x28e2557b59846e40}, + {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, + {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, + {0xb4bca50b065abe63, 0x0fed077a756b53aa}, + {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, + {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, + {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, + {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, + {0x89e42caaf9491b60, 0xf41686c49db57245}, + {0xac5d37d5b79b6239, 0x311c2875c522ced6}, + {0xd77485cb25823ac7, 0x7d633293366b828c}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, + {0xd267caa862a12d66, 0xd072df63c324fd7c}, + {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, + {0xa46116538d0deb78, 0x52d9be85f074e609}, + {0xcd795be870516656, 0x67902e276c921f8c}, + {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, + {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, + {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, + {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, + {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, + {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, + {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, + {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, + {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, + {0xef340a98172aace4, 0x86fb897116c87c35}, + {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, + {0xbae0a846d2195712, 0x8974836059cca10a}, + {0xe998d258869facd7, 0x2bd1a438703fc94c}, + {0x91ff83775423cc06, 0x7b6306a34627ddd0}, + {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, + {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, + {0x8e938662882af53e, 0x547eb47b7282ee9d}, + {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, + {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, + {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, + {0xae0b158b4738705e, 0x9624ab50b148d446}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, + {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, + {0xd47487cc8470652b, 0x7647c32000696720}, + {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, + {0xa5fb0a17c777cf09, 0xf468107100525891}, + {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, + {0x81ac1fe293d599bf, 0xc6f14cd848405531}, + {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, + {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, + {0xfd442e4688bd304a, 0x908f4a166d1da664}, + {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, + {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, + {0xf7549530e188c128, 0xd12bee59e68ef47d}, + {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, + {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, + {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, + {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, + {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, + {0xebdf661791d60f56, 0x111b495b3464ad22}, + {0x936b9fcebb25c995, 0xcab10dd900beec35}, + {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, + {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, + {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, + {0xb3f4e093db73a093, 0x59ed216765690f57}, + {0xe0f218b8d25088b8, 0x306869c13ec3532d}, + {0x8c974f7383725573, 0x1e414218c73a13fc}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, + {0x894bc396ce5da772, 0x6b8bba8c328eb784}, + {0xab9eb47c81f5114f, 0x066ea92f3f326565}, + {0xd686619ba27255a2, 0xc80a537b0efefebe}, + {0x8613fd0145877585, 0xbd06742ce95f5f37}, + {0xa798fc4196e952e7, 0x2c48113823b73705}, + {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, + {0x82ef85133de648c4, 0x9a984d73dbe722fc}, + {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, + {0xcc963fee10b7d1b3, 0x318df905079926a9}, + {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, + {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, + {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, + {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, + {0x9c1661a651213e2d, 0x06bea10ca65c084f}, + {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, + {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, + {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, + {0xbe89523386091465, 0xf6bbb397f1135824}, + {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, + {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, + {0xba121a4650e4ddeb, 0x92f34d62616ce414}, + {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, + {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, + {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, + {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, + {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, + {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, + {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, + {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, + {0x87625f056c7c4a8b, 0x11471cd764ad4973}, + {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, + {0xd389b47879823479, 0x4aff1d108d4ec2c4}, + {0x843610cb4bf160cb, 0xcedf722a585139bb}, + {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, + {0xce947a3da6a9273e, 0x733d226229feea33}, + {0x811ccc668829b887, 0x0806357d5a3f5260}, + {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, + {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, + {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, + {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, + {0xc5029163f384a931, 0x0a9e795e65d4df12}, + {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, + {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, + {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, + {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, + {0x964e858c91ba2655, 0x3a6a07f8d510f870}, + {0xbbe226efb628afea, 0x890489f70a55368c}, + {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, + {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, + {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, + {0xb32df8e9f3546564, 0x47939822dc96abfa}, + {0xdff9772470297ebd, 0x59787e2b93bc56f8}, + {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, + {0xaefae51477a06b03, 0xede622920b6b23f2}, + {0xdab99e59958885c4, 0xe95fab368e45ecee}, + {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, + {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, + {0xd59944a37c0752a2, 0x4be76d3346f04960}, + {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, + {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, + {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, + {0x825ecc24c873782f, 0x8ed400668c0c28c9}, + {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, + {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, + {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, + {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, + {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, + {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, + {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, + {0xc24452da229b021b, 0xfbe85badce996169}, + {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, + {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, + {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, + {0xed246723473e3813, 0x290123e9aab23b69}, + {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, + {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, + {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, + {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, + {0x8d590723948a535f, 0x579c487e5a38ad0f}, + {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, + {0xdcdb1b2798182244, 0xf8e431456cf88e66}, + {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, + {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, + {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, + {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, + {0xa87fea27a539e9a5, 0x3f2398d747b36225}, + {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, + {0x83a3eeeef9153e89, 0x1953cf68300424ad}, + {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, + {0xcdb02555653131b6, 0x3792f412cb06794e}, + {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, + {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, + {0xc8de047564d20a8b, 0xf245825a5a445276}, + {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, + {0x9ced737bb6c4183d, 0x55464dd69685606c}, + {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, + {0xf53304714d9265df, 0xd53dd99f4b3066a9}, + {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, + {0xbf8fdb78849a5f96, 0xde98520472bdd034}, + {0xef73d256a5c0f77c, 0x963e66858f6d4441}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xbb127c53b17ec159, 0x5560c018580d5d53}, + {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, + {0x9226712162ab070d, 0xcab3961304ca70e9}, + {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, + {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, + {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, + {0xb267ed1940f1c61c, 0x55f038b237591ed4}, + {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, + {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, + {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, + {0xd9c7dced53c72255, 0x96e7bd358c904a22}, + {0x881cea14545c7575, 0x7e50d64177da2e55}, + {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, + {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, + {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, + {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, + {0xcfb11ead453994ba, 0x67de18eda5814af3}, + {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, + {0xa2425ff75e14fc31, 0xa1258379a94d028e}, + {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, + {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, + {0x9e74d1b791e07e48, 0x775ea264cf55347e}, + {0xc612062576589dda, 0x95364afe032a819e}, + {0xf79687aed3eec551, 0x3a83ddbd83f52205}, + {0x9abe14cd44753b52, 0xc4926a9672793543}, + {0xc16d9a0095928a27, 0x75b7053c0f178294}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, + {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, + {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, + {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, + {0xb877aa3236a4b449, 0x09befeb9fad487c3}, + {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, + {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, + {0xb424dc35095cd80f, 0x538484c19ef38c95}, + {0xe12e13424bb40e13, 0x2865a5f206b06fba}, + {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, + {0xafebff0bcb24aafe, 0xf78f69a51539d749}, + {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, + {0x89705f4136b4a597, 0x31680a88f8953031}, + {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, + {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, + {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, + {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, + {0xd1b71758e219652b, 0xd3c36113404ea4a9}, + {0x83126e978d4fdf3b, 0x645a1cac083126ea}, + {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, + {0xcccccccccccccccc, 0xcccccccccccccccd}, + {0x8000000000000000, 0x0000000000000000}, + {0xa000000000000000, 0x0000000000000000}, + {0xc800000000000000, 0x0000000000000000}, + {0xfa00000000000000, 0x0000000000000000}, + {0x9c40000000000000, 0x0000000000000000}, + {0xc350000000000000, 0x0000000000000000}, + {0xf424000000000000, 0x0000000000000000}, + {0x9896800000000000, 0x0000000000000000}, + {0xbebc200000000000, 0x0000000000000000}, + {0xee6b280000000000, 0x0000000000000000}, + {0x9502f90000000000, 0x0000000000000000}, + {0xba43b74000000000, 0x0000000000000000}, + {0xe8d4a51000000000, 0x0000000000000000}, + {0x9184e72a00000000, 0x0000000000000000}, + {0xb5e620f480000000, 0x0000000000000000}, + {0xe35fa931a0000000, 0x0000000000000000}, + {0x8e1bc9bf04000000, 0x0000000000000000}, + {0xb1a2bc2ec5000000, 0x0000000000000000}, + {0xde0b6b3a76400000, 0x0000000000000000}, + {0x8ac7230489e80000, 0x0000000000000000}, + {0xad78ebc5ac620000, 0x0000000000000000}, + {0xd8d726b7177a8000, 0x0000000000000000}, + {0x878678326eac9000, 0x0000000000000000}, + {0xa968163f0a57b400, 0x0000000000000000}, + {0xd3c21bcecceda100, 0x0000000000000000}, + {0x84595161401484a0, 0x0000000000000000}, + {0xa56fa5b99019a5c8, 0x0000000000000000}, + {0xcecb8f27f4200f3a, 0x0000000000000000}, + {0x813f3978f8940984, 0x4000000000000000}, + {0xa18f07d736b90be5, 0x5000000000000000}, + {0xc9f2c9cd04674ede, 0xa400000000000000}, + {0xfc6f7c4045812296, 0x4d00000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xc5371912364ce305, 0x6c28000000000000}, + {0xf684df56c3e01bc6, 0xc732000000000000}, + {0x9a130b963a6c115c, 0x3c7f400000000000}, + {0xc097ce7bc90715b3, 0x4b9f100000000000}, + {0xf0bdc21abb48db20, 0x1e86d40000000000}, + {0x96769950b50d88f4, 0x1314448000000000}, + {0xbc143fa4e250eb31, 0x17d955a000000000}, + {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, + {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, + {0xb7abc627050305ad, 0xf14a3d9e40000000}, + {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, + {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, + {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, + {0xe0352f62a19e306e, 0xd50b2037ad200000}, + {0x8c213d9da502de45, 0x4526f422cc340000}, + {0xaf298d050e4395d6, 0x9670b12b7f410000}, + {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, + {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, + {0xab0e93b6efee0053, 0x8eea0d047a457a00}, + {0xd5d238a4abe98068, 0x72a4904598d6d880}, + {0x85a36366eb71f041, 0x47a6da2b7f864750}, + {0xa70c3c40a64e6c51, 0x999090b65f67d924}, + {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, + {0x82818f1281ed449f, 0xbff8f10e7a8921a5}, + {0xa321f2d7226895c7, 0xaff72d52192b6a0e}, + {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491}, + {0xfee50b7025c36a08, 0x02f236d04753d5b5}, + {0x9f4f2726179a2245, 0x01d762422c946591}, + {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6}, + {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3}, + {0x9b934c3b330c8577, 0x63cc55f49f88eb30}, + {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc}, + {0xf316271c7fc3908a, 0x8bef464e3945ef7b}, + {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad}, + {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318}, + {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde}, + {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b}, + {0xb975d6b6ee39e436, 0xb3e2fd538e122b45}, + {0xe7d34c64a9c85d44, 0x60dbbca87196b617}, + {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce}, + {0xb51d13aea4a488dd, 0x6babab6398bdbe42}, + {0xe264589a4dcdab14, 0xc696963c7eed2dd2}, + {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3}, + {0xb0de65388cc8ada8, 0x3b25a55f43294bcc}, + {0xdd15fe86affad912, 0x49ef0eb713f39ebf}, + {0x8a2dbf142dfcc7ab, 0x6e3569326c784338}, + {0xacb92ed9397bf996, 0x49c2c37f07965405}, + {0xd7e77a8f87daf7fb, 0xdc33745ec97be907}, + {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4}, + {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d}, + {0xd2d80db02aabd62b, 0xf50a3fa490c30191}, + {0x83c7088e1aab65db, 0x792667c6da79e0fb}, + {0xa4b8cab1a1563f52, 0x577001b891185939}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, + {0x80b05e5ac60b6178, 0x544f8158315b05b5}, + {0xa0dc75f1778e39d6, 0x696361ae3db1c722}, + {0xc913936dd571c84c, 0x03bc3a19cd1e38ea}, + {0xfb5878494ace3a5f, 0x04ab48a04065c724}, + {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77}, + {0xc45d1df942711d9a, 0x3ba5d0bd324f8395}, + {0xf5746577930d6500, 0xca8f44ec7ee3647a}, + {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc}, + {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f}, + {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f}, + {0x95d04aee3b80ece5, 0xbba1f1d158724a13}, + {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98}, + {0xea1575143cf97226, 0xf52d09d71a3293be}, + {0x924d692ca61be758, 0x593c2626705f9c57}, + {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d}, + {0xe498f455c38b997a, 0x0b6dfb9c0f956448}, + {0x8edf98b59a373fec, 0x4724bd4189bd5ead}, + {0xb2977ee300c50fe7, 0x58edec91ec2cb658}, + {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee}, + {0x8b865b215899f46c, 0xbd79e0d20082ee75}, + {0xae67f1e9aec07187, 0xecd8590680a3aa12}, + {0xda01ee641a708de9, 0xe80e6f4820cc9496}, + {0x884134fe908658b2, 0x3109058d147fdcde}, + {0xaa51823e34a7eede, 0xbd4b46f0599fd416}, + {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b}, + {0x850fadc09923329e, 0x03e2cf6bc604ddb1}, + {0xa6539930bf6bff45, 0x84db8346b786151d}, + {0xcfe87f7cef46ff16, 0xe612641865679a64}, + {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f}, + {0xa26da3999aef7749, 0xe3be5e330f38f09e}, + {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6}, + {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7}, + {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb}, + {0xc646d63501a1511d, 0xb281e1fd541501b9}, + {0xf7d88bc24209a565, 0x1f225a7ca91a4227}, + {0x9ae757596946075f, 0x3375788de9b06959}, + {0xc1a12d2fc3978937, 0x0052d6b1641c83af}, + {0xf209787bb47d6b84, 0xc0678c5dbd23a49b}, + {0x9745eb4d50ce6332, 0xf840b7ba963646e1}, + {0xbd176620a501fbff, 0xb650e5a93bc3d899}, + {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf}, + {0x93ba47c980e98cdf, 0xc66f336c36b10138}, + {0xb8a8d9bbe123f017, 0xb80b0047445d4185}, + {0xe6d3102ad96cec1d, 0xa60dc059157491e6}, + {0x9043ea1ac7e41392, 0x87c89837ad68db30}, + {0xb454e4a179dd1877, 0x29babe4598c311fc}, + {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b}, + {0x8ce2529e2734bb1d, 0x1899e4a65f58660d}, + {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90}, + {0xdc21a1171d42645d, 0x76707543f4fa1f74}, + {0x899504ae72497eba, 0x6a06494a791c53a9}, + {0xabfa45da0edbde69, 0x0487db9d17636893}, + {0xd6f8d7509292d603, 0x45a9d2845d3c42b7}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, + {0xa7f26836f282b732, 0x8e6cac7768d7141f}, + {0xd1ef0244af2364ff, 0x3207d795430cd927}, + {0x8335616aed761f1f, 0x7f44e6bd49e807b9}, + {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7}, + {0xcd036837130890a1, 0x36dba887c37a8c10}, + {0x802221226be55a64, 0xc2494954da2c978a}, + {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d}, + {0xc83553c5c8965d3d, 0x6f92829494e5acc8}, + {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa}, + {0x9c69a97284b578d7, 0xff2a760414536efc}, + {0xc38413cf25e2d70d, 0xfef5138519684abb}, + {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a}, + {0x98bf2f79d5993802, 0xef2f773ffbd97a62}, + {0xbeeefb584aff8603, 0xaafb550ffacfd8fb}, + {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39}, + {0x952ab45cfa97a0b2, 0xdd945a747bf26184}, + {0xba756174393d88df, 0x94f971119aeef9e5}, + {0xe912b9d1478ceb17, 0x7a37cd5601aab85e}, + {0x91abb422ccb812ee, 0xac62e055c10ab33b}, + {0xb616a12b7fe617aa, 0x577b986b314d600a}, + {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c}, + {0x8e41ade9fbebc27d, 0x14588f13be847308}, + {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9}, + {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc}, + {0x8aec23d680043bee, 0x25de7bb9480d5855}, + {0xada72ccc20054ae9, 0xaf561aa79a10ae6b}, + {0xd910f7ff28069da4, 0x1b2ba1518094da05}, + {0x87aa9aff79042286, 0x90fb44d2f05d0843}, + {0xa99541bf57452b28, 0x353a1607ac744a54}, + {0xd3fa922f2d1675f2, 0x42889b8997915ce9}, + {0x847c9b5d7c2e09b7, 0x69956135febada12}, + {0xa59bc234db398c25, 0x43fab9837e699096}, + {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc}, + {0x8161afb94b44f57d, 0x1d1be0eebac278f6}, + {0xa1ba1ba79e1632dc, 0x6462d92a69731733}, + {0xca28a291859bbf93, 0x7d7b8f7503cfdcff}, + {0xfcb2cb35e702af78, 0x5cda735244c3d43f}, + {0x9defbf01b061adab, 0x3a0888136afa64a8}, + {0xc56baec21c7a1916, 0x088aaa1845b8fdd1}, + {0xf6c69a72a3989f5b, 0x8aad549e57273d46}, + {0x9a3c2087a63f6399, 0x36ac54e2f678864c}, + {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de}, + {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6}, + {0x969eb7c47859e743, 0x9f644ae5a4b1b326}, + {0xbc4665b596706114, 0x873d5d9f0dde1fef}, + {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb}, + {0x9316ff75dd87cbd8, 0x09a7f12442d588f3}, + {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30}, + {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb}, + {0x8fa475791a569d10, 0xf96e017d694487bd}, + {0xb38d92d760ec4455, 0x37c981dcc395a9ad}, + {0xe070f78d3927556a, 0x85bbe253f47b1418}, + {0x8c469ab843b89562, 0x93956d7478ccec8f}, + {0xaf58416654a6babb, 0x387ac8d1970027b3}, + {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f}, + {0x88fcf317f22241e2, 0x441fece3bdf81f04}, + {0xab3c2fddeeaad25a, 0xd527e81cad7626c4}, + {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075}, + {0x85c7056562757456, 0xf6872d5667844e4a}, + {0xa738c6bebb12d16c, 0xb428f8ac016561dc}, + {0xd106f86e69d785c7, 0xe13336d701beba53}, + {0x82a45b450226b39c, 0xecc0024661173474}, + {0xa34d721642b06084, 0x27f002d7f95d0191}, + {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5}, + {0xff290242c83396ce, 0x7e67047175a15272}, + {0x9f79a169bd203e41, 0x0f0062c6e984d387}, + {0xc75809c42c684dd1, 0x52c07b78a3e60869}, + {0xf92e0c3537826145, 0xa7709a56ccdf8a83}, + {0x9bbcc7a142b17ccb, 0x88a66076400bb692}, + {0xc2abf989935ddbfe, 0x6acff893d00ea436}, + {0xf356f7ebf83552fe, 0x0583f6b8c4124d44}, + {0x98165af37b2153de, 0xc3727a337a8b704b}, + {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d}, + {0xeda2ee1c7064130c, 0x1162def06f79df74}, + {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9}, + {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693}, + {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438}, + {0x910ab1d4db9914a0, 0x1d9c9892400a22a3}, + {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c}, + {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, + {0xb10d8e1456105dad, 0x7425a83e872c5f48}, + {0xdd50f1996b947518, 0xd12f124e28f7771a}, + {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70}, + {0xace73cbfdc0bfb7b, 0x636cc64d1001550c}, + {0xd8210befd30efa5a, 0x3c47f7e05401aa4f}, + {0x8714a775e3e95c78, 0x65acfaec34810a72}, + {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e}, + {0xd31045a8341ca07c, 0x1ede48111209a051}, + {0x83ea2b892091e44d, 0x934aed0aab460433}, + {0xa4e4b66b68b65d60, 0xf81da84d56178540}, + {0xce1de40642e3f4b9, 0x36251260ab9d668f}, + {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a}, + {0xa1075a24e4421730, 0xb24cf65b8612f820}, + {0xc94930ae1d529cfc, 0xdee033f26797b628}, + {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2}, + {0x9d412e0806e88aa5, 0x8e1f289560ee864f}, + {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3}, + {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc}, + {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a}, + {0xbff610b0cc6edd3f, 0x17fd090a58d32af4}, + {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1}, + {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f}, + {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2}, + {0xea53df5fd18d5513, 0x84c86189216dc5ee}, + {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5}, + {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, + {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f}, + {0xb2c71d5bca9023f8, 0x743e20e9ef511013}, + {0xdf78e4b2bd342cf6, 0x914da9246b255417}, + {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f}, + {0xae9672aba3d0c320, 0xa184ac2473b529b2}, + {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f}, + {0x8865899617fb1871, 0x7e2fa67c7a658893}, + {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8}, + {0xd51ea6fa85785631, 0x552a74227f3ea566}, + {0x8533285c936b35de, 0xd53a88958f872760}, + {0xa67ff273b8460356, 0x8a892abaf368f138}, + {0xd01fef10a657842c, 0x2d2b7569b0432d86}, + {0x8213f56a67f6b29b, 0x9c3b29620e29fc74}, + {0xa298f2c501f45f42, 0x8349f3ba91b47b90}, + {0xcb3f2f7642717713, 0x241c70a936219a74}, + {0xfe0efb53d30dd4d7, 0xed238cd383aa0111}, + {0x9ec95d1463e8a506, 0xf4363804324a40ab}, + {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6}, + {0xf81aa16fdc1b81da, 0xdd94b7868e94050b}, + {0x9b10a4e5e9913128, 0xca7cf2b4191c8327}, + {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1}, + {0xf24a01a73cf2dccf, 0xbc633b39673c8ced}, + {0x976e41088617ca01, 0xd5be0503e085d814}, + {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19}, + {0xec9c459d51852ba2, 0xddf8e7d60ed1219f}, + {0x93e1ab8252f33b45, 0xcabb90e5c942b504}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, + {0xe7109bfba19c0c9d, 0x0cc512670a783ad5}, + {0x906a617d450187e2, 0x27fb2b80668b24c6}, + {0xb484f9dc9641e9da, 0xb1f9f660802dedf7}, + {0xe1a63853bbd26451, 0x5e7873f8a0396974}, + {0x8d07e33455637eb2, 0xdb0b487b6423e1e9}, + {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63}, + {0xdc5c5301c56b75f7, 0x7641a140cc7810fc}, + {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e}, + {0xac2820d9623bf429, 0x546345fa9fbdcd45}, + {0xd732290fbacaf133, 0xa97c177947ad4096}, + {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e}, + {0xa81f301449ee8c70, 0x5c68f256bfff5a75}, + {0xd226fc195c6a2f8c, 0x73832eec6fff3112}, + {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac}, + {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56}, + {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec}, + {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4}, + {0xa0555e361951c366, 0xd7e105bcc3326220}, + {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8}, + {0xfa856334878fc150, 0xb14f98f6f0feb952}, + {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4}, + {0xc3b8358109e84f07, 0x0a862f80ec4700c9}, + {0xf4a642e14c6262c8, 0xcd27bb612758c0fb}, + {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d}, + {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4}, + {0xeeea5d5004981478, 0x1858ccfce06cac75}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, + {0xbaa718e68396cffd, 0xd30560258f54e6bb}, + {0xe950df20247c83fd, 0x47c6b82ef32a206a}, + {0x91d28b7416cdd27e, 0x4cdc331d57fa5442}, + {0xb6472e511c81471d, 0xe0133fe4adf8e953}, + {0xe3d8f9e563a198e5, 0x58180fddd97723a7}, + {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649}, + {0xb201833b35d63f73, 0x2cd2cc6551e513db}, + {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2}, + {0x8b112e86420f6191, 0xfb04afaf27faf783}, + {0xadd57a27d29339f6, 0x79c5db9af1f9b564}, + {0xd94ad8b1c7380874, 0x18375281ae7822bd}, + {0x87cec76f1c830548, 0x8f2293910d0b15b6}, + {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23}, + {0xd433179d9c8cb841, 0x5fa60692a46151ec}, + {0x849feec281d7f328, 0xdbc7c41ba6bcd334}, + {0xa5c7ea73224deff3, 0x12b9b522906c0801}, + {0xcf39e50feae16bef, 0xd768226b34870a01}, + {0x81842f29f2cce375, 0xe6a1158300d46641}, + {0xa1e53af46f801c53, 0x60495ae3c1097fd1}, + {0xca5e89b18b602368, 0x385bb19cb14bdfc5}, + {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6}, + {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2}, + {0xc5a05277621be293, 0xc7098b7305241886}, + {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8}, + {0x9a65406d44a5c903, 0x737f74f1dc043329}, + {0xc0fe908895cf3b44, 0x505f522e53053ff3}, + {0xf13e34aabb430a15, 0x647726b9e7c68ff0}, + {0x96c6e0eab509e64d, 0x5eca783430dc19f6}, + {0xbc789925624c5fe0, 0xb67d16413d132073}, + {0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890}, + {0x933e37a534cbaae7, 0x8e91b962f7b6f15a}, + {0xb80dc58e81fe95a1, 0x723627bbb5a4adb1}, + {0xe61136f2227e3b09, 0xcec3b1aaa30dd91d}, + {0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2}, + {0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e}, + {0xe0accfa875af45a7, 0x93eb1b80a33b8606}, + {0x8c6c01c9498d8b88, 0xbc72f130660533c4}, + {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, + { 0xdb68c2ca82ed2a05, + 0xa67398db9f6820e2 } #else + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0xc350000000000000, 0x0000000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xfee50b7025c36a08, 0x02f236d04753d5b5}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87}, + {0xa6539930bf6bff45, 0x84db8346b786151d}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3}, + {0xd910f7ff28069da4, 0x1b2ba1518094da05}, + {0xaf58416654a6babb, 0x387ac8d1970027b3}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, + {0xf13e34aabb430a15, 0x647726b9e7c68ff0} +#endif + }; + +#if FMT_USE_FULL_CACHE_DRAGONBOX + return pow10_significands[k - float_info::min_k]; +#else + static constexpr const uint64_t powers_of_5_64[] = { + 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, + 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, + 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, + 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, + 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, + 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, + 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, + 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, + 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; + static const int compression_ratio = 27; // Compute base index. @@ -1897,8 +1037,7 @@ template <> struct cache_accessor { int offset = k - kb; // Get base cache. - uint128_wrapper base_cache = - data::dragonbox_pow10_significands_128[cache_index]; + uint128_fallback base_cache = pow10_significands[cache_index]; if (offset == 0) return base_cache; // Compute the required amount of bit-shift. @@ -1906,10 +1045,9 @@ template <> struct cache_accessor { FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected"); // Try to recover the real cache. - uint64_t pow5 = data::powers_of_5_64[offset]; - uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); - uint128_wrapper middle_low = - umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); + uint64_t pow5 = powers_of_5_64[offset]; + uint128_fallback recovered_cache = umul128(base_cache.high(), pow5); + uint128_fallback middle_low = umul128(base_cache.low(), pow5); recovered_cache += middle_low.high(); @@ -1917,228 +1055,172 @@ template <> struct cache_accessor { uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); recovered_cache = - uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, - ((middle_low.low() >> alpha) | middle_to_low)}; - - if (kb < 0) recovered_cache += 1; - - // Get error. - int error_idx = (k - float_info::min_k) / 16; - uint32_t error = (data::dragonbox_pow10_recovery_errors[error_idx] >> - ((k - float_info::min_k) % 16) * 2) & - 0x3; - - // Add the error back. - FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), ""); - return {recovered_cache.high(), recovered_cache.low() + error}; + uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low)}; + FMT_ASSERT(recovered_cache.low() + 1 != 0, ""); + return {recovered_cache.high(), recovered_cache.low() + 1}; #endif } - static carrier_uint compute_mul(carrier_uint u, - const cache_entry_type& cache) FMT_NOEXCEPT { - return umul192_upper64(u, cache); + struct compute_mul_result { + carrier_uint result; + bool is_integer; + }; + struct compute_mul_parity_result { + bool parity; + bool is_integer; + }; + + static compute_mul_result compute_mul( + carrier_uint u, const cache_entry_type& cache) noexcept { + auto r = umul192_upper128(u, cache); + return {r.high(), r.low() == 0}; } static uint32_t compute_delta(cache_entry_type const& cache, - int beta_minus_1) FMT_NOEXCEPT { - return static_cast(cache.high() >> (64 - 1 - beta_minus_1)); + int beta) noexcept { + return static_cast(cache.high() >> (64 - 1 - beta)); } - static bool compute_mul_parity(carrier_uint two_f, - const cache_entry_type& cache, - int beta_minus_1) FMT_NOEXCEPT { - FMT_ASSERT(beta_minus_1 >= 1, ""); - FMT_ASSERT(beta_minus_1 < 64, ""); + static compute_mul_parity_result compute_mul_parity( + carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + FMT_ASSERT(beta >= 1, ""); + FMT_ASSERT(beta < 64, ""); - return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + auto r = umul192_lower128(two_f, cache); + return {((r.high() >> (64 - beta)) & 1) != 0, + ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; } static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return (cache.high() - - (cache.high() >> (float_info::significand_bits + 2))) >> - (64 - float_info::significand_bits - 1 - beta_minus_1); + (cache.high() >> (num_significand_bits() + 2))) >> + (64 - num_significand_bits() - 1 - beta); } static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + const cache_entry_type& cache, int beta) noexcept { return (cache.high() + - (cache.high() >> (float_info::significand_bits + 1))) >> - (64 - float_info::significand_bits - 1 - beta_minus_1); + (cache.high() >> (num_significand_bits() + 1))) >> + (64 - num_significand_bits() - 1 - beta); } static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { - return ((cache.high() >> - (64 - float_info::significand_bits - 2 - beta_minus_1)) + + const cache_entry_type& cache, int beta) noexcept { + return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + 1) / 2; } }; -// Various integer checks -template -bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT { - return exponent >= - float_info< - T>::case_shorter_interval_left_endpoint_lower_threshold && - exponent <= - float_info::case_shorter_interval_left_endpoint_upper_threshold; -} -template -bool is_endpoint_integer(typename float_info::carrier_uint two_f, - int exponent, int minus_k) FMT_NOEXCEPT { - if (exponent < float_info::case_fc_pm_half_lower_threshold) return false; - // For k >= 0. - if (exponent <= float_info::case_fc_pm_half_upper_threshold) return true; - // For k < 0. - if (exponent > float_info::divisibility_check_by_5_threshold) return false; - return divisible_by_power_of_5(two_f, minus_k); +FMT_FUNC uint128_fallback get_cached_power(int k) noexcept { + return cache_accessor::get_cached_power(k); } -template -bool is_center_integer(typename float_info::carrier_uint two_f, int exponent, - int minus_k) FMT_NOEXCEPT { - // Exponent for 5 is negative. - if (exponent > float_info::divisibility_check_by_5_threshold) return false; - if (exponent > float_info::case_fc_upper_threshold) - return divisible_by_power_of_5(two_f, minus_k); - // Both exponents are nonnegative. - if (exponent >= float_info::case_fc_lower_threshold) return true; - // Exponent for 2 is negative. - return divisible_by_power_of_2(two_f, minus_k - exponent + 1); +// Various integer checks +template +bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { + const int case_shorter_interval_left_endpoint_lower_threshold = 2; + const int case_shorter_interval_left_endpoint_upper_threshold = 3; + return exponent >= case_shorter_interval_left_endpoint_lower_threshold && + exponent <= case_shorter_interval_left_endpoint_upper_threshold; } // Remove trailing zeros from n and return the number of zeros removed (float) -FMT_ALWAYS_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { -#ifdef FMT_BUILTIN_CTZ - int t = FMT_BUILTIN_CTZ(n); -#else - int t = ctz(n); -#endif - if (t > float_info::max_trailing_zeros) - t = float_info::max_trailing_zeros; - - const uint32_t mod_inv1 = 0xcccccccd; - const uint32_t max_quotient1 = 0x33333333; - const uint32_t mod_inv2 = 0xc28f5c29; - const uint32_t max_quotient2 = 0x0a3d70a3; +FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { + FMT_ASSERT(n != 0, ""); + // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. + // See https://github.com/fmtlib/fmt/issues/3163 for more details. + const uint32_t mod_inv_5 = 0xcccccccd; + // Casts are needed to workaround a bug in MSVC 19.22 and older. + const uint32_t mod_inv_25 = + static_cast(uint64_t(mod_inv_5) * mod_inv_5); int s = 0; - for (; s < t - 1; s += 2) { - if (n * mod_inv2 > max_quotient2) break; - n *= mod_inv2; + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value() / 100) break; + n = q; + s += 2; } - if (s < t && n * mod_inv1 <= max_quotient1) { - n *= mod_inv1; - ++s; + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value() / 10) { + n = q; + s |= 1; } - n >>= s; return s; } // Removes trailing zeros and returns the number of zeros removed (double) -FMT_ALWAYS_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { -#ifdef FMT_BUILTIN_CTZLL - int t = FMT_BUILTIN_CTZLL(n); -#else - int t = ctzll(n); -#endif - if (t > float_info::max_trailing_zeros) - t = float_info::max_trailing_zeros; - // Divide by 10^8 and reduce to 32-bits - // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17, - // both of the quotient and the r should fit in 32-bits - - const uint32_t mod_inv1 = 0xcccccccd; - const uint32_t max_quotient1 = 0x33333333; - const uint64_t mod_inv8 = 0xc767074b22e90e21; - const uint64_t max_quotient8 = 0x00002af31dc46118; - - // If the number is divisible by 1'0000'0000, work with the quotient - if (t >= 8) { - auto quotient_candidate = n * mod_inv8; - - if (quotient_candidate <= max_quotient8) { - auto quotient = static_cast(quotient_candidate >> 8); - - int s = 8; - for (; s < t; ++s) { - if (quotient * mod_inv1 > max_quotient1) break; - quotient *= mod_inv1; - } - quotient >>= (s - 8); - n = quotient; - return s; +FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { + FMT_ASSERT(n != 0, ""); + + // This magic number is ceil(2^90 / 10^8). + constexpr uint64_t magic_number = 12379400392853802749ull; + auto nm = umul128(n, magic_number); + + // Is n is divisible by 10^8? + if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { + // If yes, work with the quotient. + auto n32 = static_cast(nm.high() >> (90 - 64)); + + const uint32_t mod_inv_5 = 0xcccccccd; + const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; + + int s = 8; + while (true) { + auto q = rotr(n32 * mod_inv_25, 2); + if (q > max_value() / 100) break; + n32 = q; + s += 2; + } + auto q = rotr(n32 * mod_inv_5, 1); + if (q <= max_value() / 10) { + n32 = q; + s |= 1; } - } - - // Otherwise, work with the remainder - auto quotient = static_cast(n / 100000000); - auto remainder = static_cast(n - 100000000 * quotient); - - if (t == 0 || remainder * mod_inv1 > max_quotient1) { - return 0; - } - remainder *= mod_inv1; - - if (t == 1 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 1) + quotient * 10000000ull; - return 1; - } - remainder *= mod_inv1; - - if (t == 2 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 2) + quotient * 1000000ull; - return 2; - } - remainder *= mod_inv1; - if (t == 3 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 3) + quotient * 100000ull; - return 3; + n = n32; + return s; } - remainder *= mod_inv1; - if (t == 4 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 4) + quotient * 10000ull; - return 4; - } - remainder *= mod_inv1; + // If n is not divisible by 10^8, work with n itself. + const uint64_t mod_inv_5 = 0xcccccccccccccccd; + const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5; - if (t == 5 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 5) + quotient * 1000ull; - return 5; + int s = 0; + while (true) { + auto q = rotr(n * mod_inv_25, 2); + if (q > max_value() / 100) break; + n = q; + s += 2; } - remainder *= mod_inv1; - - if (t == 6 || remainder * mod_inv1 > max_quotient1) { - n = (remainder >> 6) + quotient * 100ull; - return 6; + auto q = rotr(n * mod_inv_5, 1); + if (q <= max_value() / 10) { + n = q; + s |= 1; } - remainder *= mod_inv1; - n = (remainder >> 7) + quotient * 10ull; - return 7; + return s; } // The main algorithm for shorter interval case -template -FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decimal_fp shorter_interval_case( - int exponent) FMT_NOEXCEPT { +template +FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); - const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + const int beta = exponent + floor_log2_pow10(-minus_k); // Compute xi and zi using cache_entry_type = typename cache_accessor::cache_entry_type; const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( - cache, beta_minus_1); + cache, beta); auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( - cache, beta_minus_1); + cache, beta); // If the left endpoint is not an integer, increase it if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; @@ -2155,8 +1237,8 @@ FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decimal_fp shorter_interval_case( // Otherwise, compute the round-up of y ret_value.significand = - cache_accessor::compute_round_up_for_shorter_interval_case( - cache, beta_minus_1); + cache_accessor::compute_round_up_for_shorter_interval_case(cache, + beta); ret_value.exponent = minus_k; // When tie occurs, choose one of them according to the rule @@ -2171,8 +1253,7 @@ FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decimal_fp shorter_interval_case( return ret_value; } -template -FMT_SAFEBUFFERS decimal_fp to_decimal(T x) FMT_NOEXCEPT { +template decimal_fp to_decimal(T x) noexcept { // Step 1: integer promotion & Schubfach multiplier calculation. using carrier_uint = typename float_info::carrier_uint; @@ -2181,23 +1262,25 @@ FMT_SAFEBUFFERS decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Extract significand bits and exponent bits. const carrier_uint significand_mask = - (static_cast(1) << float_info::significand_bits) - 1; + (static_cast(1) << num_significand_bits()) - 1; carrier_uint significand = (br & significand_mask); - int exponent = static_cast((br & exponent_mask()) >> - float_info::significand_bits); + int exponent = + static_cast((br & exponent_mask()) >> num_significand_bits()); if (exponent != 0) { // Check if normal. - exponent += float_info::exponent_bias - float_info::significand_bits; + exponent -= exponent_bias() + num_significand_bits(); // Shorter interval case; proceed like Schubfach. + // In fact, when exponent == 1 and significand == 0, the interval is + // regular. However, it can be shown that the end-results are anyway same. if (significand == 0) return shorter_interval_case(exponent); - significand |= - (static_cast(1) << float_info::significand_bits); + significand |= (static_cast(1) << num_significand_bits()); } else { // Subnormal case; the interval is always regular. if (significand == 0) return {0, 0}; - exponent = float_info::min_exponent - float_info::significand_bits; + exponent = + std::numeric_limits::min_exponent - num_significand_bits() - 1; } const bool include_left_endpoint = (significand % 2 == 0); @@ -2206,485 +1289,125 @@ FMT_SAFEBUFFERS decimal_fp to_decimal(T x) FMT_NOEXCEPT { // Compute k and beta. const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); - const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + const int beta = exponent + floor_log2_pow10(-minus_k); - // Compute zi and deltai + // Compute zi and deltai. // 10^kappa <= deltai < 10^(kappa + 1) - const uint32_t deltai = cache_accessor::compute_delta(cache, beta_minus_1); + const uint32_t deltai = cache_accessor::compute_delta(cache, beta); const carrier_uint two_fc = significand << 1; - const carrier_uint two_fr = two_fc | 1; - const carrier_uint zi = - cache_accessor::compute_mul(two_fr << beta_minus_1, cache); - // Step 2: Try larger divisor; remove trailing zeros if necessary + // For the case of binary32, the result of integer check is not correct for + // 29711844 * 2^-82 + // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18 + // and 29711844 * 2^-81 + // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17, + // and they are the unique counterexamples. However, since 29711844 is even, + // this does not cause any problem for the endpoints calculations; it can only + // cause a problem when we need to perform integer check for the center. + // Fortunately, with these inputs, that branch is never executed, so we are + // fine. + const typename cache_accessor::compute_mul_result z_mul = + cache_accessor::compute_mul((two_fc | 1) << beta, cache); + + // Step 2: Try larger divisor; remove trailing zeros if necessary. // Using an upper bound on zi, we might be able to optimize the division - // better than the compiler; we are computing zi / big_divisor here + // better than the compiler; we are computing zi / big_divisor here. decimal_fp ret_value; - ret_value.significand = divide_by_10_to_kappa_plus_1(zi); - uint32_t r = static_cast(zi - float_info::big_divisor * - ret_value.significand); + ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result); + uint32_t r = static_cast(z_mul.result - float_info::big_divisor * + ret_value.significand); - if (r > deltai) { - goto small_divisor_case_label; - } else if (r < deltai) { - // Exclude the right endpoint if necessary - if (r == 0 && !include_right_endpoint && - is_endpoint_integer(two_fr, exponent, minus_k)) { + if (r < deltai) { + // Exclude the right endpoint if necessary. + if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) { --ret_value.significand; r = float_info::big_divisor; goto small_divisor_case_label; } + } else if (r > deltai) { + goto small_divisor_case_label; } else { - // r == deltai; compare fractional parts - // Check conditions in the order different from the paper - // to take advantage of short-circuiting - const carrier_uint two_fl = two_fc - 1; - if ((!include_left_endpoint || - !is_endpoint_integer(two_fl, exponent, minus_k)) && - !cache_accessor::compute_mul_parity(two_fl, cache, beta_minus_1)) { + // r == deltai; compare fractional parts. + const typename cache_accessor::compute_mul_parity_result x_mul = + cache_accessor::compute_mul_parity(two_fc - 1, cache, beta); + + if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint))) goto small_divisor_case_label; - } } ret_value.exponent = minus_k + float_info::kappa + 1; - // We may need to remove trailing zeros + // We may need to remove trailing zeros. ret_value.exponent += remove_trailing_zeros(ret_value.significand); return ret_value; - // Step 3: Find the significand with the smaller divisor + // Step 3: Find the significand with the smaller divisor. small_divisor_case_label: ret_value.significand *= 10; ret_value.exponent = minus_k + float_info::kappa; - const uint32_t mask = (1u << float_info::kappa) - 1; - auto dist = r - (deltai / 2) + (float_info::small_divisor / 2); - - // Is dist divisible by 2^kappa? - if ((dist & mask) == 0) { - const bool approx_y_parity = - ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; - dist >>= float_info::kappa; - - // Is dist divisible by 5^kappa? - if (check_divisibility_and_divide_by_pow5::kappa>(dist)) { - ret_value.significand += dist; - - // Check z^(f) >= epsilon^(f) - // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, - // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) - // Since there are only 2 possibilities, we only need to care about the - // parity. Also, zi and r should have the same parity since the divisor - // is an even number - if (cache_accessor::compute_mul_parity(two_fc, cache, beta_minus_1) != - approx_y_parity) { - --ret_value.significand; - } else { - // If z^(f) >= epsilon^(f), we might have a tie - // when z^(f) == epsilon^(f), or equivalently, when y is an integer - if (is_center_integer(two_fc, exponent, minus_k)) { - ret_value.significand = ret_value.significand % 2 == 0 - ? ret_value.significand - : ret_value.significand - 1; - } - } - } - // Is dist not divisible by 5^kappa? - else { - ret_value.significand += dist; - } - } - // Is dist not divisible by 2^kappa? - else { - // Since we know dist is small, we might be able to optimize the division - // better than the compiler; we are computing dist / small_divisor here - ret_value.significand += - small_division_by_pow10::kappa>(dist); - } + uint32_t dist = r - (deltai / 2) + (float_info::small_divisor / 2); + const bool approx_y_parity = + ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; + + // Is dist divisible by 10^kappa? + const bool divisible_by_small_divisor = + check_divisibility_and_divide_by_pow10::kappa>(dist); + + // Add dist / 10^kappa to the significand. + ret_value.significand += dist; + + if (!divisible_by_small_divisor) return ret_value; + + // Check z^(f) >= epsilon^(f). + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f). + // Since there are only 2 possibilities, we only need to care about the + // parity. Also, zi and r should have the same parity since the divisor + // is an even number. + const auto y_mul = cache_accessor::compute_mul_parity(two_fc, cache, beta); + + // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f), + // or equivalently, when y is an integer. + if (y_mul.parity != approx_y_parity) + --ret_value.significand; + else if (y_mul.is_integer & (ret_value.significand % 2 != 0)) + --ret_value.significand; return ret_value; } } // namespace dragonbox - -// Formats value using a variation of the Fixed-Precision Positive -// Floating-Point Printout ((FPP)^2) algorithm by Steele & White: -// https://fmt.dev/p372-steele.pdf. -template -void fallback_format(Double d, int num_digits, bool binary32, buffer& buf, - int& exp10) { - bigint numerator; // 2 * R in (FPP)^2. - bigint denominator; // 2 * S in (FPP)^2. - // lower and upper are differences between value and corresponding boundaries. - bigint lower; // (M^- in (FPP)^2). - bigint upper_store; // upper's value if different from lower. - bigint* upper = nullptr; // (M^+ in (FPP)^2). - fp value; - // Shift numerator and denominator by an extra bit or two (if lower boundary - // is closer) to make lower and upper integers. This eliminates multiplication - // by 2 during later computations. - const bool is_predecessor_closer = - binary32 ? value.assign(static_cast(d)) : value.assign(d); - int shift = is_predecessor_closer ? 2 : 1; - uint64_t significand = value.f << shift; - if (value.e >= 0) { - numerator.assign(significand); - numerator <<= value.e; - lower.assign(1); - lower <<= value.e; - if (shift != 1) { - upper_store.assign(1); - upper_store <<= value.e + 1; - upper = &upper_store; - } - denominator.assign_pow10(exp10); - denominator <<= shift; - } else if (exp10 < 0) { - numerator.assign_pow10(-exp10); - lower.assign(numerator); - if (shift != 1) { - upper_store.assign(numerator); - upper_store <<= 1; - upper = &upper_store; - } - numerator *= significand; - denominator.assign(1); - denominator <<= shift - value.e; - } else { - numerator.assign(significand); - denominator.assign_pow10(exp10); - denominator <<= shift - value.e; - lower.assign(1); - if (shift != 1) { - upper_store.assign(1ULL << 1); - upper = &upper_store; - } - } - // Invariant: value == (numerator / denominator) * pow(10, exp10). - if (num_digits < 0) { - // Generate the shortest representation. - if (!upper) upper = &lower; - bool even = (value.f & 1) == 0; - num_digits = 0; - char* data = buf.data(); - for (;;) { - int digit = numerator.divmod_assign(denominator); - bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. - // numerator + upper >[=] pow10: - bool high = add_compare(numerator, *upper, denominator) + even > 0; - data[num_digits++] = static_cast('0' + digit); - if (low || high) { - if (!low) { - ++data[num_digits - 1]; - } else if (high) { - int result = add_compare(numerator, numerator, denominator); - // Round half to even. - if (result > 0 || (result == 0 && (digit % 2) != 0)) - ++data[num_digits - 1]; - } - buf.try_resize(to_unsigned(num_digits)); - exp10 -= num_digits - 1; - return; - } - numerator *= 10; - lower *= 10; - if (upper != &lower) *upper *= 10; - } - } - // Generate the given number of digits. - exp10 -= num_digits - 1; - if (num_digits == 0) { - buf.try_resize(1); - denominator *= 10; - buf[0] = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; - return; - } - buf.try_resize(to_unsigned(num_digits)); - for (int i = 0; i < num_digits - 1; ++i) { - int digit = numerator.divmod_assign(denominator); - buf[i] = static_cast('0' + digit); - numerator *= 10; - } - int digit = numerator.divmod_assign(denominator); - auto result = add_compare(numerator, numerator, denominator); - if (result > 0 || (result == 0 && (digit % 2) != 0)) { - if (digit == 9) { - const auto overflow = '0' + 10; - buf[num_digits - 1] = overflow; - // Propagate the carry. - for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { - buf[i] = '0'; - ++buf[i - 1]; - } - if (buf[0] == overflow) { - buf[0] = '1'; - ++exp10; - } - return; - } - ++digit; - } - buf[num_digits - 1] = static_cast('0' + digit); -} - -template -int format_float(T value, int precision, float_specs specs, buffer& buf) { - static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); - - const bool fixed = specs.format == float_format::fixed; - if (value <= 0) { // <= instead of == to silence a warning. - if (precision <= 0 || !fixed) { - buf.push_back('0'); - return 0; - } - buf.try_resize(to_unsigned(precision)); - std::uninitialized_fill_n(buf.data(), precision, '0'); - return -precision; - } - - if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf); - - if (precision < 0) { - // Use Dragonbox for the shortest format. - if (specs.binary32) { - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } - - // Use Grisu + Dragon4 for the given precision: - // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. - int exp = 0; - const int min_exp = -60; // alpha in Grisu. - int cached_exp10 = 0; // K in Grisu. - fp normalized = normalize(fp(value)); - const auto cached_pow = get_cached_power( - min_exp - (normalized.e + fp::significand_size), cached_exp10); - normalized = normalized * cached_pow; - // Limit precision to the maximum possible number of significant digits in an - // IEEE754 double because we don't need to generate zeros. - const int max_double_digits = 767; - if (precision > max_double_digits) precision = max_double_digits; - fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; - if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) { - exp += handler.size - cached_exp10 - 1; - fallback_format(value, handler.precision, specs.binary32, buf, exp); - } else { - exp += handler.exp10; - buf.try_resize(to_unsigned(handler.size)); - } - if (!fixed && !specs.showpoint) { - // Remove trailing zeros. - auto num_digits = buf.size(); - while (num_digits > 0 && buf[num_digits - 1] == '0') { - --num_digits; - ++exp; - } - buf.try_resize(num_digits); - } - return exp; -} // namespace detail - -template -int snprintf_float(T value, int precision, float_specs specs, - buffer& buf) { - // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. - FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); - static_assert(!std::is_same::value, ""); - - // Subtract 1 to account for the difference in precision since we use %e for - // both general and exponent format. - if (specs.format == float_format::general || - specs.format == float_format::exp) - precision = (precision >= 0 ? precision : 6) - 1; - - // Build the format string. - enum { max_format_size = 7 }; // The longest format is "%#.*Le". - char format[max_format_size]; - char* format_ptr = format; - *format_ptr++ = '%'; - if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; - if (precision >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - if (std::is_same()) *format_ptr++ = 'L'; - *format_ptr++ = specs.format != float_format::hex - ? (specs.format == float_format::fixed ? 'f' : 'e') - : (specs.upper ? 'A' : 'a'); - *format_ptr = '\0'; - - // Format using snprintf. - auto offset = buf.size(); - for (;;) { - auto begin = buf.data() + offset; - auto capacity = buf.capacity() - offset; -#ifdef FMT_FUZZ - if (precision > 100000) - throw std::runtime_error( - "fuzz mode - avoid large allocation inside snprintf"); -#endif - // Suppress the warning about a nonliteral format string. - // Cannot use auto because of a bug in MinGW (#1532). - int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; - int result = precision >= 0 - ? snprintf_ptr(begin, capacity, format, precision, value) - : snprintf_ptr(begin, capacity, format, value); - if (result < 0) { - // The buffer will grow exponentially. - buf.try_reserve(buf.capacity() + 1); - continue; - } - auto size = to_unsigned(result); - // Size equal to capacity means that the last character was truncated. - if (size >= capacity) { - buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. - continue; - } - auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; - if (specs.format == float_format::fixed) { - if (precision == 0) { - buf.try_resize(size); - return 0; - } - // Find and remove the decimal point. - auto end = begin + size, p = end; - do { - --p; - } while (is_digit(*p)); - int fraction_size = static_cast(end - p - 1); - std::memmove(p, p + 1, to_unsigned(fraction_size)); - buf.try_resize(size - 1); - return -fraction_size; - } - if (specs.format == float_format::hex) { - buf.try_resize(size + offset); - return 0; - } - // Find and parse the exponent. - auto end = begin + size, exp_pos = end; - do { - --exp_pos; - } while (*exp_pos != 'e'); - char sign = exp_pos[1]; - assert(sign == '+' || sign == '-'); - int exp = 0; - auto p = exp_pos + 2; // Skip 'e' and sign. - do { - assert(is_digit(*p)); - exp = exp * 10 + (*p++ - '0'); - } while (p != end); - if (sign == '-') exp = -exp; - int fraction_size = 0; - if (exp_pos != begin + 1) { - // Remove trailing zeros. - auto fraction_end = exp_pos - 1; - while (*fraction_end == '0') --fraction_end; - // Move the fractional part left to get rid of the decimal point. - fraction_size = static_cast(fraction_end - begin - 1); - std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); - } - buf.try_resize(to_unsigned(fraction_size) + offset + 1); - return exp - fraction_size; - } -} - -// A public domain branchless UTF-8 decoder by Christopher Wellons: -// https://github.com/skeeto/branchless-utf8 -/* Decode the next character, c, from buf, reporting errors in e. - * - * Since this is a branchless decoder, four bytes will be read from the - * buffer regardless of the actual length of the next character. This - * means the buffer _must_ have at least three bytes of zero padding - * following the end of the data stream. - * - * Errors are reported in e, which will be non-zero if the parsed - * character was somehow invalid: invalid byte sequence, non-canonical - * encoding, or a surrogate half. - * - * The function returns a pointer to the next character. When an error - * occurs, this pointer will be a guess that depends on the particular - * error, but it will always advance at least one byte. - */ -inline const char* utf8_decode(const char* buf, uint32_t* c, int* e) { - static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; - static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; - static const int shiftc[] = {0, 18, 12, 6, 0}; - static const int shifte[] = {0, 6, 4, 2, 0}; - - int len = code_point_length(buf); - const char* next = buf + len; - - // Assume a four-byte character and load four bytes. Unused bits are - // shifted out. - auto s = reinterpret_cast(buf); - *c = uint32_t(s[0] & masks[len]) << 18; - *c |= uint32_t(s[1] & 0x3f) << 12; - *c |= uint32_t(s[2] & 0x3f) << 6; - *c |= uint32_t(s[3] & 0x3f) << 0; - *c >>= shiftc[len]; - - // Accumulate the various error conditions. - *e = (*c < mins[len]) << 6; // non-canonical encoding - *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? - *e |= (*c > 0x10FFFF) << 8; // out of range? - *e |= (s[1] & 0xc0) >> 2; - *e |= (s[2] & 0xc0) >> 4; - *e |= (s[3]) >> 6; - *e ^= 0x2a; // top two bits of each tail byte correct? - *e >>= shifte[len]; - - return next; -} - -struct stringifier { - template FMT_INLINE std::string operator()(T value) const { - return to_string(value); - } - std::string operator()(basic_format_arg::handle h) const { - memory_buffer buf; - format_parse_context parse_ctx({}); - format_context format_ctx(buffer_appender(buf), {}, {}); - h.format(parse_ctx, format_ctx); - return to_string(buf); - } -}; } // namespace detail template <> struct formatter { - format_parse_context::iterator parse(format_parse_context& ctx) { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) + -> format_parse_context::iterator { return ctx.begin(); } - format_context::iterator format(const detail::bigint& n, - format_context& ctx) { + auto format(const detail::bigint& n, format_context& ctx) const + -> format_context::iterator { auto out = ctx.out(); bool first = true; for (auto i = n.bigits_.size(); i > 0; --i) { auto value = n.bigits_[i - 1u]; if (first) { - out = format_to(out, "{:x}", value); + out = format_to(out, FMT_STRING("{:x}"), value); first = false; continue; } - out = format_to(out, "{:08x}", value); + out = format_to(out, FMT_STRING("{:08x}"), value); } if (n.exp_ > 0) - out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits); + out = format_to(out, FMT_STRING("p{}"), + n.exp_ * detail::bigint::bigit_bits); return out; } }; FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { - auto transcode = [this](const char* p) { - auto cp = uint32_t(); - auto error = 0; - p = utf8_decode(p, &cp, &error); - if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); + for_each_codepoint(s, [this](uint32_t cp, string_view) { + if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8")); if (cp <= 0xFFFF) { buffer_.push_back(static_cast(cp)); } else { @@ -2692,110 +1415,267 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { buffer_.push_back(static_cast(0xD800 + (cp >> 10))); buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); } - return p; - }; - auto p = s.data(); - const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. - if (s.size() >= block_size) { - for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p); - } - if (auto num_chars_left = s.data() + s.size() - p) { - char buf[2 * block_size - 1] = {}; - memcpy(buf, p, to_unsigned(num_chars_left)); - p = buf; - do { - p = transcode(p); - } while (p - buf < num_chars_left); - } + return true; + }); buffer_.push_back(0); } FMT_FUNC void format_system_error(detail::buffer& out, int error_code, - string_view message) FMT_NOEXCEPT { + const char* message) noexcept { FMT_TRY { - memory_buffer buf; - buf.resize(inline_buffer_size); - for (;;) { - char* system_message = &buf[0]; - int result = - detail::safe_strerror(error_code, system_message, buf.size()); - if (result == 0) { - format_to(detail::buffer_appender(out), "{}: {}", message, - system_message); - return; - } - if (result != ERANGE) - break; // Can't get error message, report error code instead. - buf.resize(buf.size() * 2); - } + auto ec = std::error_code(error_code, std::generic_category()); + write(std::back_inserter(out), std::system_error(ec, message).what()); + return; } FMT_CATCH(...) {} format_error_code(out, error_code, message); } -FMT_FUNC void detail::error_handler::on_error(const char* message) { - FMT_THROW(format_error(message)); -} - FMT_FUNC void report_system_error(int error_code, - fmt::string_view message) FMT_NOEXCEPT { + const char* message) noexcept { report_error(format_system_error, error_code, message); } -FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) { - if (format_str.size() == 2 && equal2(format_str.data(), "{}")) { - auto arg = args.get(0); - if (!arg) error_handler().on_error("argument not found"); - return visit_format_arg(stringifier(), arg); - } - memory_buffer buffer; - detail::vformat_to(buffer, format_str, args); +FMT_FUNC std::string vformat(string_view fmt, format_args args) { + // Don't optimize the "{}" case to keep the binary size small and because it + // can be better optimized in fmt::format anyway. + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); return to_string(buffer); } -#ifdef _WIN32 namespace detail { +#ifndef _WIN32 +FMT_FUNC bool write_console(std::FILE*, string_view) { return false; } +#else using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // void*, const void*, dword, dword*, void*); -} // namespace detail -#endif -FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { - memory_buffer buffer; - detail::vformat_to(buffer, format_str, - basic_format_args>(args)); -#ifdef _WIN32 +FMT_FUNC bool write_console(std::FILE* f, string_view text) { auto fd = _fileno(f); - if (_isatty(fd)) { - detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); - auto written = detail::dword(); - if (!detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), - u16.c_str(), static_cast(u16.size()), - &written, nullptr)) { - FMT_THROW(format_error("failed to write to console")); - } - return; - } -#endif - detail::fwrite_fully(buffer.data(), 1, buffer.size(), f); + if (!_isatty(fd)) return false; + auto u16 = utf8_to_utf16(text); + auto written = dword(); + return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), + static_cast(u16.size()), &written, nullptr); } -#ifdef _WIN32 // Print assuming legacy (non-Unicode) encoding. -FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, - format_args args) { - memory_buffer buffer; - detail::vformat_to(buffer, format_str, +FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, basic_format_args>(args)); fwrite_fully(buffer.data(), 1, buffer.size(), f); } #endif -FMT_FUNC void vprint(string_view format_str, format_args args) { - vprint(stdout, format_str, args); +FMT_FUNC void print(std::FILE* f, string_view text) { + if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f); +} +} // namespace detail + +FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + detail::print(f, {buffer.data(), buffer.size()}); } +FMT_FUNC void vprint(string_view fmt, format_args args) { + vprint(stdout, fmt, args); +} + +namespace detail { + +struct singleton { + unsigned char upper; + unsigned char lower_count; +}; + +inline auto is_printable(uint16_t x, const singleton* singletons, + size_t singletons_size, + const unsigned char* singleton_lowers, + const unsigned char* normal, size_t normal_size) + -> bool { + auto upper = x >> 8; + auto lower_start = 0; + for (size_t i = 0; i < singletons_size; ++i) { + auto s = singletons[i]; + auto lower_end = lower_start + s.lower_count; + if (upper < s.upper) break; + if (upper == s.upper) { + for (auto j = lower_start; j < lower_end; ++j) { + if (singleton_lowers[j] == (x & 0xff)) return false; + } + } + lower_start = lower_end; + } + + auto xsigned = static_cast(x); + auto current = true; + for (size_t i = 0; i < normal_size; ++i) { + auto v = static_cast(normal[i]); + auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; + xsigned -= len; + if (xsigned < 0) break; + current = !current; + } + return current; +} + +// This code is generated by support/printable.py. +FMT_FUNC auto is_printable(uint32_t cp) -> bool { + static constexpr singleton singletons0[] = { + {0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8}, + {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, + {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5}, + {0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, + {0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3}, + {0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8}, + {0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9}, + }; + static constexpr unsigned char singletons0_lower[] = { + 0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, + 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, + 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1, + 0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, + 0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, + 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, + 0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, + 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, + 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, + 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, + 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, + 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, + 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, + 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, + 0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, + 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, + 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, + 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, + 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, + 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, + 0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, + 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, + 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, + 0xfe, 0xff, + }; + static constexpr singleton singletons1[] = { + {0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2}, + {0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5}, + {0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5}, + {0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2}, + {0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5}, + {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2}, + {0xfa, 2}, {0xfb, 1}, + }; + static constexpr unsigned char singletons1_lower[] = { + 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, + 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, + 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87, + 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, + 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, + 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, + 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, + 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc, + 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, + 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, + 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, + 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, + 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, + 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, + }; + static constexpr unsigned char normal0[] = { + 0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, + 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, + 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01, + 0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, + 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03, + 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a, + 0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, + 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, + 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80, + 0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, + 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, + 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04, + 0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, + 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, + 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11, + 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, + 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, + 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, + 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, + 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, + 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06, + 0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, + 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, + 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80, + 0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, + 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d, + }; + static constexpr unsigned char normal1[] = { + 0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, + 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, + 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04, + 0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, + 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16, + 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f, + 0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, + 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, + 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08, + 0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, + 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, + 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03, + 0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, + 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, + 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45, + 0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, + 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, + 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, + 0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, + 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, + 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11, + 0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, + 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, + 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6, + 0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, + 0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, + 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05, + 0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, + 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, + 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80, + 0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, + 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, + 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e, + 0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, + 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06, + }; + auto lower = static_cast(cp); + if (cp < 0x10000) { + return is_printable(lower, singletons0, + sizeof(singletons0) / sizeof(*singletons0), + singletons0_lower, normal0, sizeof(normal0)); + } + if (cp < 0x20000) { + return is_printable(lower, singletons1, + sizeof(singletons1) / sizeof(*singletons1), + singletons1_lower, normal1, sizeof(normal1)); + } + if (0x2a6de <= cp && cp < 0x2a700) return false; + if (0x2b735 <= cp && cp < 0x2b740) return false; + if (0x2b81e <= cp && cp < 0x2b820) return false; + if (0x2cea2 <= cp && cp < 0x2ceb0) return false; + if (0x2ebe1 <= cp && cp < 0x2f800) return false; + if (0x2fa1e <= cp && cp < 0x30000) return false; + if (0x3134b <= cp && cp < 0xe0100) return false; + if (0xe01f0 <= cp && cp < 0x110000) return false; + return cp < 0x110000; +} + +} // namespace detail + FMT_END_NAMESPACE #endif // FMT_FORMAT_INL_H_ diff --git a/src/3rdparty/fmt/format.h b/src/3rdparty/fmt/format.h index 1a037b02b7..ed8b29eba9 100644 --- a/src/3rdparty/fmt/format.h +++ b/src/3rdparty/fmt/format.h @@ -1,54 +1,87 @@ /* - Formatting library for C++ - - Copyright (c) 2012 - present, Victor Zverovich - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - --- Optional exception to the license --- - - As an exception, if, as a result of your compiling your source code, portions - of this Software are embedded into a machine-executable object form of such - source code, you may redistribute such embedded portions in such object form - without including the above copyright and permission notices. + Formatting library for C++ + + Copyright (c) 2012 - present, Victor Zverovich + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + --- Optional exception to the license --- + + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into a machine-executable object form of such + source code, you may redistribute such embedded portions in such object form + without including the above copyright and permission notices. */ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include -#include -#include -#include -#include -#include -#include +#include // std::signbit +#include // uint32_t +#include // std::memcpy +#include // std::initializer_list +#include // std::numeric_limits +#include // std::uninitialized_copy +#include // std::runtime_error +#include // std::system_error + +#ifdef __cpp_lib_bit_cast +# include // std::bitcast +#endif #include "core.h" -#ifdef __INTEL_COMPILER -# define FMT_ICC_VERSION __INTEL_COMPILER -#elif defined(__ICL) -# define FMT_ICC_VERSION __ICL +#ifndef FMT_BEGIN_DETAIL_NAMESPACE +# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { +# define FMT_END_DETAIL_NAMESPACE } +#endif + +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +# define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VERSION +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + +#if FMT_GCC_VERSION +# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) #else -# define FMT_ICC_VERSION 0 +# define FMT_GCC_VISIBILITY_HIDDEN #endif #ifdef __NVCC__ @@ -69,35 +102,9 @@ # define FMT_NOINLINE #endif -#if __cplusplus == 201103L || __cplusplus == 201402L -# if defined(__INTEL_COMPILER) || defined(__PGI) -# define FMT_FALLTHROUGH -# elif defined(__clang__) -# define FMT_FALLTHROUGH [[clang::fallthrough]] -# elif FMT_GCC_VERSION >= 700 && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -# define FMT_FALLTHROUGH [[gnu::fallthrough]] -# else -# define FMT_FALLTHROUGH -# endif -#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ - (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define FMT_FALLTHROUGH [[fallthrough]] -#else -# define FMT_FALLTHROUGH -#endif - -#ifndef FMT_MAYBE_UNUSED -# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) -# define FMT_MAYBE_UNUSED [[maybe_unused]] -# else -# define FMT_MAYBE_UNUSED -# endif -#endif - #ifndef FMT_THROW # if FMT_EXCEPTIONS -# if FMT_MSC_VER || FMT_NVCC +# if FMT_MSC_VERSION || defined(__NVCC__) FMT_BEGIN_NAMESPACE namespace detail { template inline void do_throw(const Exception& x) { @@ -113,10 +120,9 @@ FMT_END_NAMESPACE # define FMT_THROW(x) throw x # endif # else -# define FMT_THROW(x) \ - do { \ - static_cast(sizeof(x)); \ - FMT_ASSERT(false, ""); \ +# define FMT_THROW(x) \ + do { \ + FMT_ASSERT(false, (x).what()); \ } while (false) # endif #endif @@ -129,10 +135,18 @@ FMT_END_NAMESPACE # define FMT_CATCH(x) if (false) #endif +#ifndef FMT_MAYBE_UNUSED +# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +# define FMT_MAYBE_UNUSED [[maybe_unused]] +# else +# define FMT_MAYBE_UNUSED +# endif +#endif + #ifndef FMT_USE_USER_DEFINED_LITERALS // EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. # if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ - FMT_MSC_VER >= 1900) && \ + FMT_MSC_VERSION >= 1900) && \ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) # define FMT_USE_USER_DEFINED_LITERALS 1 # else @@ -140,117 +154,101 @@ FMT_END_NAMESPACE # endif #endif -#ifndef FMT_USE_UDL_TEMPLATE -// EDG frontend based compilers (icc, nvcc, PGI, etc) and GCC < 6.4 do not -// properly support UDL templates and GCC >= 9 warns about them. -# if FMT_USE_USER_DEFINED_LITERALS && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 501) && \ - ((FMT_GCC_VERSION >= 604 && __cplusplus >= 201402L) || \ - FMT_CLANG_VERSION >= 304) && \ - !defined(__PGI) && !defined(__NVCC__) -# define FMT_USE_UDL_TEMPLATE 1 -# else -# define FMT_USE_UDL_TEMPLATE 0 -# endif -#endif - -#ifndef FMT_USE_FLOAT -# define FMT_USE_FLOAT 1 -#endif - -#ifndef FMT_USE_DOUBLE -# define FMT_USE_DOUBLE 1 -#endif - -#ifndef FMT_USE_LONG_DOUBLE -# define FMT_USE_LONG_DOUBLE 1 -#endif - // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of -// int_writer template instances to just one by only using the largest integer -// type. This results in a reduction in binary size but will cause a decrease in -// integer formatting performance. +// integer formatter template instantiations to just one by only using the +// largest integer type. This results in a reduction in binary size but will +// cause a decrease in integer formatting performance. #if !defined(FMT_REDUCE_INT_INSTANTIATIONS) # define FMT_REDUCE_INT_INSTANTIATIONS 0 #endif // __builtin_clz is broken in clang with Microsoft CodeGen: -// https://github.com/fmtlib/fmt/issues/519 -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -#endif -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -#endif -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctz)) -# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) +// https://github.com/fmtlib/fmt/issues/519. +#if !FMT_MSC_VERSION +# if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +# endif #endif -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctzll)) -# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) + +// __builtin_ctz is broken in Intel Compiler Classic on Windows: +// https://github.com/fmtlib/fmt/issues/2510. +#ifndef __ICL +# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ + defined(__NVCOMPILER) +# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ + FMT_ICC_VERSION || defined(__NVCOMPILER) +# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) +# endif #endif -#if FMT_MSC_VER +#if FMT_MSC_VERSION # include // _BitScanReverse[64], _BitScanForward[64], _umul128 #endif // Some compilers masquerade as both MSVC and GCC-likes or otherwise support // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the // MSVC intrinsics if the clz and clzll builtins are not available. -#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && \ - !defined(FMT_BUILTIN_CTZLL) && !defined(_MANAGED) +#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \ + !defined(FMT_BUILTIN_CTZLL) FMT_BEGIN_NAMESPACE namespace detail { // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. -# ifndef __clang__ +# if !defined(__clang__) # pragma intrinsic(_BitScanForward) # pragma intrinsic(_BitScanReverse) -# endif -# if defined(_WIN64) && !defined(__clang__) -# pragma intrinsic(_BitScanForward64) -# pragma intrinsic(_BitScanReverse64) +# if defined(_WIN64) +# pragma intrinsic(_BitScanForward64) +# pragma intrinsic(_BitScanReverse64) +# endif # endif -inline int clz(uint32_t x) { +inline auto clz(uint32_t x) -> int { unsigned long r = 0; _BitScanReverse(&r, x); FMT_ASSERT(x != 0, ""); // Static analysis complains about using uninitialized data // "r", but the only way that can happen is if "x" is 0, // which the callers guarantee to not happen. - FMT_SUPPRESS_MSC_WARNING(6102) + FMT_MSC_WARNING(suppress : 6102) return 31 ^ static_cast(r); } # define FMT_BUILTIN_CLZ(n) detail::clz(n) -inline int clzll(uint64_t x) { +inline auto clzll(uint64_t x) -> int { unsigned long r = 0; # ifdef _WIN64 _BitScanReverse64(&r, x); # else // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 ^ (r + 32); + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 ^ static_cast(r + 32); // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); # endif FMT_ASSERT(x != 0, ""); - FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. return 63 ^ static_cast(r); } # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) -inline int ctz(uint32_t x) { +inline auto ctz(uint32_t x) -> int { unsigned long r = 0; _BitScanForward(&r, x); FMT_ASSERT(x != 0, ""); - FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. return static_cast(r); } # define FMT_BUILTIN_CTZ(n) detail::ctz(n) -inline int ctzll(uint64_t x) { +inline auto ctzll(uint64_t x) -> int { unsigned long r = 0; FMT_ASSERT(x != 0, ""); - FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. # ifdef _WIN64 _BitScanForward64(&r, x); # else @@ -267,75 +265,276 @@ inline int ctzll(uint64_t x) { FMT_END_NAMESPACE #endif -// Enable the deprecated numeric alignment. -#ifndef FMT_DEPRECATED_NUMERIC_ALIGN -# define FMT_DEPRECATED_NUMERIC_ALIGN 0 -#endif - FMT_BEGIN_NAMESPACE + +template struct disjunction : std::false_type {}; +template struct disjunction

: P {}; +template +struct disjunction + : conditional_t> {}; + +template struct conjunction : std::true_type {}; +template struct conjunction

: P {}; +template +struct conjunction + : conditional_t, P1> {}; + namespace detail { -// An equivalent of `*reinterpret_cast(&source)` that doesn't have -// undefined behavior (e.g. due to type aliasing). -// Example: uint64_t d = bit_cast(2.718); -template -inline Dest bit_cast(const Source& source) { - static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); - Dest dest; - std::memcpy(&dest, &source, sizeof(dest)); - return dest; +FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { + ignore_unused(condition); +#ifdef FMT_FUZZ + if (condition) throw std::runtime_error("fuzzing limit reached"); +#endif +} + +template struct string_literal { + static constexpr CharT value[sizeof...(C)] = {C...}; + constexpr operator basic_string_view() const { + return {value, sizeof...(C)}; + } +}; + +#if FMT_CPLUSPLUS < 201703L +template +constexpr CharT string_literal::value[sizeof...(C)]; +#endif + +template class formatbuf : public Streambuf { + private: + using char_type = typename Streambuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename Streambuf::int_type; + using traits_type = typename Streambuf::traits_type; + + buffer& buffer_; + + public: + explicit formatbuf(buffer& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + +// Implementation of std::bit_cast for pre-C++20. +template +FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { +#ifdef __cpp_lib_bit_cast + if (is_constant_evaluated()) return std::bit_cast(from); +#endif + auto to = To(); + // The cast suppresses a bogus -Wclass-memaccess on GCC. + std::memcpy(static_cast(&to), &from, sizeof(to)); + return to; } -inline bool is_big_endian() { - const auto u = 1u; +inline auto is_big_endian() -> bool { +#ifdef _WIN32 + return false; +#elif defined(__BIG_ENDIAN__) + return true; +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) + return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; +#else struct bytes { - char data[sizeof(u)]; + char data[sizeof(int)]; }; - return bit_cast(u).data[0] == 0; + return bit_cast(1).data[0] == 0; +#endif } -// A fallback implementation of uintptr_t for systems that lack it. -struct fallback_uintptr { - unsigned char value[sizeof(void*)]; +class uint128_fallback { + private: + uint64_t lo_, hi_; + + friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept; - fallback_uintptr() = default; - explicit fallback_uintptr(const void* p) { - *this = bit_cast(p); - if (is_big_endian()) { - for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) - std::swap(value[i], value[j]); + public: + constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} + constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} + + constexpr uint64_t high() const noexcept { return hi_; } + constexpr uint64_t low() const noexcept { return lo_; } + + template ::value)> + constexpr explicit operator T() const { + return static_cast(lo_); + } + + friend constexpr auto operator==(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_; + } + friend constexpr auto operator!=(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return !(lhs == rhs); + } + friend constexpr auto operator>(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_; + } + friend constexpr auto operator|(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { + return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_}; + } + friend constexpr auto operator&(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { + return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_}; + } + friend constexpr auto operator~(const uint128_fallback& n) + -> uint128_fallback { + return {~n.hi_, ~n.lo_}; + } + friend auto operator+(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> uint128_fallback { + auto result = uint128_fallback(lhs); + result += rhs; + return result; + } + friend auto operator*(const uint128_fallback& lhs, uint32_t rhs) + -> uint128_fallback { + FMT_ASSERT(lhs.hi_ == 0, ""); + uint64_t hi = (lhs.lo_ >> 32) * rhs; + uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs; + uint64_t new_lo = (hi << 32) + lo; + return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; + } + friend auto operator-(const uint128_fallback& lhs, uint64_t rhs) + -> uint128_fallback { + return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; + } + FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback { + if (shift == 64) return {0, hi_}; + if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64); + return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; + } + FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback { + if (shift == 64) return {lo_, 0}; + if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64); + return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)}; + } + FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& { + return *this = *this >> shift; + } + FMT_CONSTEXPR void operator+=(uint128_fallback n) { + uint64_t new_lo = lo_ + n.lo_; + uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0); + FMT_ASSERT(new_hi >= hi_, ""); + lo_ = new_lo; + hi_ = new_hi; + } + FMT_CONSTEXPR void operator&=(uint128_fallback n) { + lo_ &= n.lo_; + hi_ &= n.hi_; + } + + FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept { + if (is_constant_evaluated()) { + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); + return *this; } +#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__) + unsigned long long carry; + lo_ = __builtin_addcll(lo_, n, 0, &carry); + hi_ += carry; +#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__) + unsigned long long result; + auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result); + lo_ = result; + hi_ += carry; +#elif defined(_MSC_VER) && defined(_M_X64) + auto carry = _addcarry_u64(0, lo_, n, &lo_); + _addcarry_u64(carry, hi_, 0, &hi_); +#else + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); +#endif + return *this; } }; + +using uint128_t = conditional_t; + #ifdef UINTPTR_MAX using uintptr_t = ::uintptr_t; -inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } #else -using uintptr_t = fallback_uintptr; -inline fallback_uintptr to_uintptr(const void* p) { - return fallback_uintptr(p); -} +using uintptr_t = uint128_t; #endif // Returns the largest possible value for type T. Same as // std::numeric_limits::max() but shorter and not affected by the max macro. -template constexpr T max_value() { +template constexpr auto max_value() -> T { return (std::numeric_limits::max)(); } -template constexpr int num_bits() { +template constexpr auto num_bits() -> int { return std::numeric_limits::digits; } // std::numeric_limits::digits may return 0 for 128-bit ints. -template <> constexpr int num_bits() { return 128; } -template <> constexpr int num_bits() { return 128; } -template <> constexpr int num_bits() { - return static_cast(sizeof(void*) * - std::numeric_limits::digits); +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } + +// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t +// and 128-bit pointers to uint128_fallback. +template sizeof(From))> +inline auto bit_cast(const From& from) -> To { + constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned)); + struct data_t { + unsigned value[static_cast(size)]; + } data = bit_cast(from); + auto result = To(); + if (const_check(is_big_endian())) { + for (int i = 0; i < size; ++i) + result = (result << num_bits()) | data.value[i]; + } else { + for (int i = size - 1; i >= 0; --i) + result = (result << num_bits()) | data.value[i]; + } + return result; +} + +template +FMT_CONSTEXPR20 inline auto countl_zero_fallback(UInt n) -> int { + int lz = 0; + constexpr UInt msb_mask = static_cast(1) << (num_bits() - 1); + for (; (n & msb_mask) == 0; n <<= 1) lz++; + return lz; +} + +FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n); +#endif + return countl_zero_fallback(n); +} + +FMT_CONSTEXPR20 inline auto countl_zero(uint64_t n) -> int { +#ifdef FMT_BUILTIN_CLZLL + if (!is_constant_evaluated()) return FMT_BUILTIN_CLZLL(n); +#endif + return countl_zero_fallback(n); } FMT_INLINE void assume(bool condition) { (void)condition; -#if FMT_HAS_BUILTIN(__builtin_assume) +#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION __builtin_assume(condition); #endif } @@ -346,31 +545,38 @@ using iterator_t = decltype(std::begin(std::declval())); template using sentinel_t = decltype(std::end(std::declval())); // A workaround for std::string not having mutable data() until C++17. -template inline Char* get_data(std::basic_string& s) { +template +inline auto get_data(std::basic_string& s) -> Char* { return &s[0]; } template -inline typename Container::value_type* get_data(Container& c) { +inline auto get_data(Container& c) -> typename Container::value_type* { return c.data(); } #if defined(_SECURE_SCL) && _SECURE_SCL // Make a checked iterator to avoid MSVC warnings. template using checked_ptr = stdext::checked_array_iterator; -template checked_ptr make_checked(T* p, size_t size) { +template +constexpr auto make_checked(T* p, size_t size) -> checked_ptr { return {p, size}; } #else template using checked_ptr = T*; -template inline T* make_checked(T* p, size_t) { return p; } +template constexpr auto make_checked(T* p, size_t) -> T* { + return p; +} #endif +// Attempts to reserve space for n extra characters in the output range. +// Returns a pointer to the reserved range or a reference to it. template ::value)> -#if FMT_CLANG_VERSION +#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif -inline checked_ptr -reserve(std::back_insert_iterator it, size_t n) { +inline auto +reserve(std::back_insert_iterator it, size_t n) + -> checked_ptr { Container& c = get_container(it); size_t size = c.size(); c.resize(size + n); @@ -378,21 +584,26 @@ reserve(std::back_insert_iterator it, size_t n) { } template -inline buffer_appender reserve(buffer_appender it, size_t n) { +inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { buffer& buf = get_container(it); buf.try_reserve(buf.size() + n); return it; } -template inline Iterator& reserve(Iterator& it, size_t) { +template +constexpr auto reserve(Iterator& it, size_t) -> Iterator& { return it; } +template +using reserve_iterator = + remove_reference_t(), 0))>; + template -constexpr T* to_pointer(OutputIt, size_t) { +constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } -template T* to_pointer(buffer_appender it, size_t n) { +template auto to_pointer(buffer_appender it, size_t n) -> T* { buffer& buf = get_container(it); auto size = buf.size(); if (buf.capacity() < size + n) return nullptr; @@ -401,195 +612,262 @@ template T* to_pointer(buffer_appender it, size_t n) { } template ::value)> -inline std::back_insert_iterator base_iterator( - std::back_insert_iterator& it, - checked_ptr) { +inline auto base_iterator(std::back_insert_iterator& it, + checked_ptr) + -> std::back_insert_iterator { return it; } template -inline Iterator base_iterator(Iterator, Iterator it) { +constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { return it; } -// An output iterator that counts the number of objects written to it and -// discards them. -class counting_iterator { - private: - size_t count_; - - public: - using iterator_category = std::output_iterator_tag; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - using _Unchecked_type = counting_iterator; // Mark iterator as checked. - - struct value_type { - template void operator=(const T&) {} - }; - - counting_iterator() : count_(0) {} - - size_t count() const { return count_; } - - counting_iterator& operator++() { - ++count_; - return *this; - } - counting_iterator operator++(int) { - auto it = *this; - ++*this; - return it; - } - - friend counting_iterator operator+(counting_iterator it, difference_type n) { - it.count_ += static_cast(n); - return it; - } - - value_type operator*() const { return {}; } -}; - -template class truncating_iterator_base { - protected: - OutputIt out_; - size_t limit_; - size_t count_; - - truncating_iterator_base(OutputIt out, size_t limit) - : out_(out), limit_(limit), count_(0) {} - - public: - using iterator_category = std::output_iterator_tag; - using value_type = typename std::iterator_traits::value_type; - using difference_type = void; - using pointer = void; - using reference = void; - using _Unchecked_type = - truncating_iterator_base; // Mark iterator as checked. - - OutputIt base() const { return out_; } - size_t count() const { return count_; } -}; - -// An output iterator that truncates the output and counts the number of objects -// written to it. -template ::value_type>::type> -class truncating_iterator; - -template -class truncating_iterator - : public truncating_iterator_base { - mutable typename truncating_iterator_base::value_type blackhole_; - - public: - using value_type = typename truncating_iterator_base::value_type; - - truncating_iterator(OutputIt out, size_t limit) - : truncating_iterator_base(out, limit) {} - - truncating_iterator& operator++() { - if (this->count_++ < this->limit_) ++this->out_; - return *this; +// is spectacularly slow to compile in C++20 so use a simple fill_n +// instead (#1998). +template +FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) + -> OutputIt { + for (Size i = 0; i < count; ++i) *out++ = value; + return out; +} +template +FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { + if (is_constant_evaluated()) { + return fill_n(out, count, value); } + std::memset(out, value, to_unsigned(count)); + return out + count; +} - truncating_iterator operator++(int) { - auto it = *this; - ++*this; - return it; - } +#ifdef __cpp_char8_t +using char8_type = char8_t; +#else +enum char8_type : unsigned char {}; +#endif - value_type& operator*() const { - return this->count_ < this->limit_ ? *this->out_ : blackhole_; +template +FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy_str(begin, end, out); +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from s, reporting errors in e. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in e, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) + -> const char* { + constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + constexpr const int shiftc[] = {0, 18, 12, 6, 0}; + constexpr const int shifte[] = {0, 6, 4, 2, 0}; + + int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" + [static_cast(*s) >> 3]; + // Compute the pointer to the next character early so that the next + // iteration can start working on the next character. Neither Clang + // nor GCC figure out this reordering on their own. + const char* next = s + len + !len; + + using uchar = unsigned char; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + *c = uint32_t(uchar(s[0]) & masks[len]) << 18; + *c |= uint32_t(uchar(s[1]) & 0x3f) << 12; + *c |= uint32_t(uchar(s[2]) & 0x3f) << 6; + *c |= uint32_t(uchar(s[3]) & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (uchar(s[1]) & 0xc0) >> 2; + *e |= (uchar(s[2]) & 0xc0) >> 4; + *e |= uchar(s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} + +constexpr FMT_INLINE_VARIABLE uint32_t invalid_code_point = ~uint32_t(); + +// Invokes f(cp, sv) for every code point cp in s with sv being the string view +// corresponding to the code point. cp is invalid_code_point on error. +template +FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { + auto decode = [f](const char* buf_ptr, const char* ptr) { + auto cp = uint32_t(); + auto error = 0; + auto end = utf8_decode(buf_ptr, &cp, &error); + bool result = f(error ? invalid_code_point : cp, + string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); + return result ? (error ? buf_ptr + 1 : end) : nullptr; + }; + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) { + p = decode(p, p); + if (!p) return; + } } -}; - -template -class truncating_iterator - : public truncating_iterator_base { - public: - truncating_iterator(OutputIt out, size_t limit) - : truncating_iterator_base(out, limit) {} - - template truncating_iterator& operator=(T val) { - if (this->count_++ < this->limit_) *this->out_++ = val; - return *this; + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + copy_str(p, p + num_chars_left, buf); + const char* buf_ptr = buf; + do { + auto end = decode(buf_ptr, p); + if (!end) return; + p += end - buf_ptr; + buf_ptr = end; + } while (buf_ptr - buf < num_chars_left); } - - truncating_iterator& operator++() { return *this; } - truncating_iterator& operator++(int) { return *this; } - truncating_iterator& operator*() { return *this; } -}; +} template -inline size_t count_code_points(basic_string_view s) { +inline auto compute_width(basic_string_view s) -> size_t { return s.size(); } -// Counts the number of code points in a UTF-8 string. -inline size_t count_code_points(basic_string_view s) { - const char* data = s.data(); +// Computes approximate display width of a UTF-8 string. +FMT_CONSTEXPR inline size_t compute_width(string_view s) { size_t num_code_points = 0; - for (size_t i = 0, size = s.size(); i != size; ++i) { - if ((data[i] & 0xc0) != 0x80) ++num_code_points; - } + // It is not a lambda for compatibility with C++14. + struct count_code_points { + size_t* count; + FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { + *count += detail::to_unsigned( + 1 + + (cp >= 0x1100 && + (cp <= 0x115f || // Hangul Jamo init. consonants + cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET + cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET + // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE: + (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || + (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables + (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs + (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms + (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms + (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms + (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms + (cp >= 0x20000 && cp <= 0x2fffd) || // CJK + (cp >= 0x30000 && cp <= 0x3fffd) || + // Miscellaneous Symbols and Pictographs + Emoticons: + (cp >= 0x1f300 && cp <= 0x1f64f) || + // Supplemental Symbols and Pictographs: + (cp >= 0x1f900 && cp <= 0x1f9ff)))); + return true; + } + }; + // We could avoid branches by using utf8_decode directly. + for_each_codepoint(s, count_code_points{&num_code_points}); return num_code_points; } -inline size_t count_code_points(basic_string_view s) { - return count_code_points(basic_string_view( - reinterpret_cast(s.data()), s.size())); +inline auto compute_width(basic_string_view s) -> size_t { + return compute_width( + string_view(reinterpret_cast(s.data()), s.size())); } template -inline size_t code_point_index(basic_string_view s, size_t n) { +inline auto code_point_index(basic_string_view s, size_t n) -> size_t { size_t size = s.size(); return n < size ? n : size; } // Calculates the index of the nth code point in a UTF-8 string. -inline size_t code_point_index(basic_string_view s, size_t n) { - const char8_type* data = s.data(); +inline auto code_point_index(string_view s, size_t n) -> size_t { + const char* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { - if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) { - return i; - } + if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i; } return s.size(); } -template -using needs_conversion = bool_constant< - std::is_same::value_type, - char>::value && - std::is_same::value>; - -template ::value)> -OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - return std::copy(begin, end, it); +inline auto code_point_index(basic_string_view s, size_t n) + -> size_t { + return code_point_index( + string_view(reinterpret_cast(s.data()), s.size()), n); } -template ::value)> -OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { - return std::transform(begin, end, it, - [](char c) { return static_cast(c); }); -} +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; -template -inline counting_iterator copy_str(InputIt begin, InputIt end, - counting_iterator it) { - return it + (end - begin); -} +template +using is_signed = + std::integral_constant::is_signed || + std::is_same::value>; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + +#ifndef FMT_USE_FLOAT +# define FMT_USE_FLOAT 1 +#endif +#ifndef FMT_USE_DOUBLE +# define FMT_USE_DOUBLE 1 +#endif +#ifndef FMT_USE_LONG_DOUBLE +# define FMT_USE_LONG_DOUBLE 1 +#endif + +#ifndef FMT_USE_FLOAT128 +# ifdef __clang__ +// Clang emulates GCC, so it has to appear early. +# if FMT_HAS_INCLUDE() +# define FMT_USE_FLOAT128 1 +# endif +# elif defined(__GNUC__) +// GNU C++: +# if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) +# define FMT_USE_FLOAT128 1 +# endif +# endif +# ifndef FMT_USE_FLOAT128 +# define FMT_USE_FLOAT128 0 +# endif +#endif + +#if FMT_USE_FLOAT128 +using float128 = __float128; +#else +using float128 = void; +#endif +template using is_float128 = std::is_same; + +template +using is_floating_point = + bool_constant::value || is_float128::value>; + +template ::value> +struct is_fast_float : bool_constant::is_iec559 && + sizeof(T) <= sizeof(double)> {}; +template struct is_fast_float : std::false_type {}; template -using is_fast_float = bool_constant::is_iec559 && - sizeof(T) <= sizeof(double)>; +using is_double_double = bool_constant::digits == 106>; #ifndef FMT_USE_FULL_CACHE_DRAGONBOX # define FMT_USE_FULL_CACHE_DRAGONBOX 0 @@ -598,7 +876,7 @@ using is_fast_float = bool_constant::is_iec559 && template template void buffer::append(const U* begin, const U* end) { - do { + while (begin != end) { auto count = to_unsigned(end - begin); try_reserve(size_ + count); auto free_cap = capacity_ - size_; @@ -606,16 +884,17 @@ void buffer::append(const U* begin, const U* end) { std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); size_ += count; begin += count; - } while (begin != end); + } } -template -void iterator_buffer::flush() { - out_ = std::copy_n(data_, this->limit(this->size()), out_); - this->clear(); -} +template +struct is_locale : std::false_type {}; +template +struct is_locale> : std::true_type {}; } // namespace detail +FMT_BEGIN_EXPORT + // The number of characters to store in the basic_memory_buffer object itself // to avoid dynamic memory allocation. enum { inline_buffer_size = 500 }; @@ -625,20 +904,12 @@ enum { inline_buffer_size = 500 }; A dynamically growing memory buffer for trivially copyable/constructible types with the first ``SIZE`` elements stored in the object itself. - You can use one of the following type aliases for common character types: - - +----------------+------------------------------+ - | Type | Definition | - +================+==============================+ - | memory_buffer | basic_memory_buffer | - +----------------+------------------------------+ - | wmemory_buffer | basic_memory_buffer | - +----------------+------------------------------+ + You can use the ``memory_buffer`` type alias for ``char`` instead. **Example**:: - fmt::memory_buffer out; - format_to(out, "The answer is {}.", 42); + auto out = fmt::memory_buffer(); + format_to(std::back_inserter(out), "The answer is {}.", 42); This will append the following output to the ``out`` object: @@ -659,39 +930,62 @@ class basic_memory_buffer final : public detail::buffer { Allocator alloc_; // Deallocate memory allocated by the buffer. - void deallocate() { + FMT_CONSTEXPR20 void deallocate() { T* data = this->data(); if (data != store_) alloc_.deallocate(data, this->capacity()); } protected: - void grow(size_t size) final FMT_OVERRIDE; + FMT_CONSTEXPR20 void grow(size_t size) override { + detail::abort_fuzzing_if(size > 5000); + const size_t max_size = std::allocator_traits::max_size(alloc_); + size_t old_capacity = this->capacity(); + size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) + new_capacity = size; + else if (new_capacity > max_size) + new_capacity = size > max_size ? size : max_size; + T* old_data = this->data(); + T* new_data = + std::allocator_traits::allocate(alloc_, new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::uninitialized_copy(old_data, old_data + this->size(), + detail::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) alloc_.deallocate(old_data, old_capacity); + } public: using value_type = T; using const_reference = const T&; - explicit basic_memory_buffer(const Allocator& alloc = Allocator()) + FMT_CONSTEXPR20 explicit basic_memory_buffer( + const Allocator& alloc = Allocator()) : alloc_(alloc) { this->set(store_, SIZE); + if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); } - ~basic_memory_buffer() { deallocate(); } + FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } private: // Move data from other to this buffer. - void move(basic_memory_buffer& other) { + FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { alloc_ = std::move(other.alloc_); T* data = other.data(); size_t size = other.size(), capacity = other.capacity(); if (data == other.store_) { this->set(store_, capacity); - std::uninitialized_copy(other.store_, other.store_ + size, - detail::make_checked(store_, capacity)); + detail::copy_str(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); } else { this->set(data, capacity); // Set pointer to the inline array so that delete is not called // when deallocating. other.set(other.store_, 0); + other.clear(); } this->resize(size); } @@ -703,14 +997,16 @@ class basic_memory_buffer final : public detail::buffer { of the other object to it. \endrst */ - basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); } + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { + move(other); + } /** \rst Moves the content of the other ``basic_memory_buffer`` object to this one. \endrst */ - basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT { + auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); move(other); @@ -718,13 +1014,13 @@ class basic_memory_buffer final : public detail::buffer { } // Returns a copy of the allocator associated with this buffer. - Allocator get_allocator() const { return alloc_; } + auto get_allocator() const -> Allocator { return alloc_; } /** Resizes the buffer to contain *count* elements. If T is a POD type new elements may not be initialized. */ - void resize(size_t count) { this->try_resize(count); } + FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } /** Increases the buffer capacity to *new_capacity*. */ void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } @@ -737,71 +1033,120 @@ class basic_memory_buffer final : public detail::buffer { } }; -template -void basic_memory_buffer::grow(size_t size) { -#ifdef FMT_FUZZ - if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); -#endif - size_t old_capacity = this->capacity(); - size_t new_capacity = old_capacity + old_capacity / 2; - if (size > new_capacity) new_capacity = size; - T* old_data = this->data(); - T* new_data = - std::allocator_traits::allocate(alloc_, new_capacity); - // The following code doesn't throw, so the raw pointer above doesn't leak. - std::uninitialized_copy(old_data, old_data + this->size(), - detail::make_checked(new_data, new_capacity)); - this->set(new_data, new_capacity); - // deallocate must not throw according to the standard, but even if it does, - // the buffer already uses the new storage and will deallocate it in - // destructor. - if (old_data != store_) alloc_.deallocate(old_data, old_capacity); -} - using memory_buffer = basic_memory_buffer; -using wmemory_buffer = basic_memory_buffer; template struct is_contiguous> : std::true_type { }; -/** A formatting error such as invalid format string. */ -FMT_CLASS_API -class FMT_API format_error : public std::runtime_error { - public: - explicit format_error(const char* message) : std::runtime_error(message) {} - explicit format_error(const std::string& message) - : std::runtime_error(message) {} - format_error(const format_error&) = default; - format_error& operator=(const format_error&) = default; - format_error(format_error&&) = default; - format_error& operator=(format_error&&) = default; - ~format_error() FMT_NOEXCEPT FMT_OVERRIDE; -}; - +FMT_END_EXPORT namespace detail { +FMT_API bool write_console(std::FILE* f, string_view text); +FMT_API void print(std::FILE*, string_view); +} // namespace detail +FMT_BEGIN_EXPORT -template -using is_signed = - std::integral_constant::is_signed || - std::is_same::value>; +// Suppress a misleading warning in older versions of clang. +#if FMT_CLANG_VERSION +# pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +/** An error reported from a formatting function. */ +class FMT_API format_error : public std::runtime_error { + public: + using std::runtime_error::runtime_error; +}; + +namespace detail_exported { +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template struct fixed_string { + constexpr fixed_string(const Char (&str)[N]) { + detail::copy_str(static_cast(str), + str + N, data); + } + Char data[N] = {}; +}; +#endif + +// Converts a compile-time string to basic_string_view. +template +constexpr auto compile_string_to_view(const Char (&s)[N]) + -> basic_string_view { + // Remove trailing NUL character if needed. Won't be present if this is used + // with a raw character array (i.e. not defined as a string). + return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; +} +template +constexpr auto compile_string_to_view(detail::std_string_view s) + -> basic_string_view { + return {s.data(), s.size()}; +} +} // namespace detail_exported + +class loc_value { + private: + basic_format_arg value_; + + public: + template ::value)> + loc_value(T value) : value_(detail::make_arg(value)) {} + + template ::value)> + loc_value(T) {} + + template auto visit(Visitor&& vis) -> decltype(vis(0)) { + return visit_format_arg(vis, value_); + } +}; + +// A locale facet that formats values in UTF-8. +// It is parameterized on the locale to avoid the heavy include. +template class format_facet : public Locale::facet { + private: + std::string separator_; + std::string grouping_; + std::string decimal_point_; + + protected: + virtual auto do_put(appender out, loc_value val, + const format_specs<>& specs) const -> bool; + + public: + static FMT_API typename Locale::id id; + + explicit format_facet(Locale& loc); + explicit format_facet(string_view sep = "", + std::initializer_list g = {3}, + std::string decimal_point = ".") + : separator_(sep.data(), sep.size()), + grouping_(g.begin(), g.end()), + decimal_point_(decimal_point) {} + + auto put(appender out, loc_value val, const format_specs<>& specs) const + -> bool { + return do_put(out, val, specs); + } +}; + +FMT_BEGIN_DETAIL_NAMESPACE // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. template ::value)> -FMT_CONSTEXPR bool is_negative(T value) { +constexpr auto is_negative(T value) -> bool { return value < 0; } template ::value)> -FMT_CONSTEXPR bool is_negative(T) { +constexpr auto is_negative(T) -> bool { return false; } -template ::value)> -FMT_CONSTEXPR bool is_supported_floating_point(T) { - return (std::is_same::value && FMT_USE_FLOAT) || - (std::is_same::value && FMT_USE_DOUBLE) || - (std::is_same::value && FMT_USE_LONG_DOUBLE); +template +FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { + if (std::is_same()) return FMT_USE_FLOAT; + if (std::is_same()) return FMT_USE_DOUBLE; + if (std::is_same()) return FMT_USE_LONG_DOUBLE; + return true; } // Smallest of uint32_t, uint64_t, uint128_t that is large enough to @@ -811,121 +1156,33 @@ using uint32_or_64_or_128_t = conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, uint32_t, conditional_t() <= 64, uint64_t, uint128_t>>; - -// 128-bit integer type used internally -struct FMT_EXTERN_TEMPLATE_API uint128_wrapper { - uint128_wrapper() = default; - -#if FMT_USE_INT128 - uint128_t internal_; - - uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT - : internal_{static_cast(low) | - (static_cast(high) << 64)} {} - - uint128_wrapper(uint128_t u) : internal_{u} {} - - uint64_t high() const FMT_NOEXCEPT { return uint64_t(internal_ >> 64); } - uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); } - - uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { - internal_ += n; - return *this; - } -#else - uint64_t high_; - uint64_t low_; - - uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT : high_{high}, - low_{low} {} - - uint64_t high() const FMT_NOEXCEPT { return high_; } - uint64_t low() const FMT_NOEXCEPT { return low_; } - - uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { -# if defined(_MSC_VER) && defined(_M_X64) - unsigned char carry = _addcarry_u64(0, low_, n, &low_); - _addcarry_u64(carry, high_, 0, &high_); - return *this; -# else - uint64_t sum = low_ + n; - high_ += (sum < low_ ? 1 : 0); - low_ = sum; - return *this; -# endif - } -#endif -}; - -// Table entry type for divisibility test used internally -template struct FMT_EXTERN_TEMPLATE_API divtest_table_entry { - T mod_inv; - T max_quotient; -}; - -// Static data is placed in this class template for the header-only config. -template struct FMT_EXTERN_TEMPLATE_API basic_data { - static const uint64_t powers_of_10_64[]; - static const uint32_t zero_or_powers_of_10_32_new[]; - static const uint64_t zero_or_powers_of_10_64_new[]; - static const uint64_t grisu_pow10_significands[]; - static const int16_t grisu_pow10_exponents[]; - static const divtest_table_entry divtest_table_for_pow5_32[]; - static const divtest_table_entry divtest_table_for_pow5_64[]; - static const uint64_t dragonbox_pow10_significands_64[]; - static const uint128_wrapper dragonbox_pow10_significands_128[]; - // log10(2) = 0x0.4d104d427de7fbcc... - static const uint64_t log10_2_significand = 0x4d104d427de7fbcc; -#if !FMT_USE_FULL_CACHE_DRAGONBOX - static const uint64_t powers_of_5_64[]; - static const uint32_t dragonbox_pow10_recovery_errors[]; +template +using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ + (factor)*1000000, (factor)*10000000, (factor)*100000000, \ + (factor)*1000000000 + +// Converts value in the range [0, 100) to a string. +constexpr const char* digits2(size_t value) { + // GCC generates slightly better code when value is pointer-size. + return &"0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"[value * 2]; +} + +// Sign is a template parameter to workaround a bug in gcc 4.8. +template constexpr Char sign(Sign s) { +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 + static_assert(std::is_same::value, ""); #endif - // GCC generates slightly better code for pairs than chars. - using digit_pair = char[2]; - static const digit_pair digits[]; - static const char hex_digits[]; - static const char foreground_color[]; - static const char background_color[]; - static const char reset_color[5]; - static const wchar_t wreset_color[5]; - static const char signs[]; - static const char left_padding_shifts[5]; - static const char right_padding_shifts[5]; - - // DEPRECATED! These are for ABI compatibility. - static const uint32_t zero_or_powers_of_10_32[]; - static const uint64_t zero_or_powers_of_10_64[]; -}; - -// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). -// This is a function instead of an array to workaround a bug in GCC10 (#1810). -FMT_INLINE uint16_t bsr2log10(int bsr) { - static constexpr uint16_t data[] = { - 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, - 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, - 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, - 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; - return data[bsr]; + return static_cast("\0-+ "[s]); } -#ifndef FMT_EXPORTED -FMT_EXTERN template struct basic_data; -#endif - -// This is a struct rather than an alias to avoid shadowing warnings in gcc. -struct data : basic_data<> {}; - -#ifdef FMT_BUILTIN_CLZLL -// Returns the number of decimal digits in n. Leading zeros are not counted -// except for n == 0 in which case count_digits returns 1. -inline int count_digits(uint64_t n) { - // https://github.com/fmtlib/format-benchmark/blob/master/digits10 - auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63); - return t - (n < data::zero_or_powers_of_10_64_new[t]); -} -#else -// Fallback version of count_digits used when __builtin_clz is not available. -inline int count_digits(uint64_t n) { +template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { int count = 1; for (;;) { // Integer division is slow so do it for a group of four digits instead @@ -939,103 +1196,146 @@ inline int count_digits(uint64_t n) { count += 4; } } -#endif - #if FMT_USE_INT128 -inline int count_digits(uint128_t n) { - int count = 1; - for (;;) { - // Integer division is slow so do it for a group of four digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - if (n < 10) return count; - if (n < 100) return count + 1; - if (n < 1000) return count + 2; - if (n < 10000) return count + 3; - n /= 10000U; - count += 4; - } +FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int { + return count_digits_fallback(n); } #endif -// Counts the number of digits in n. BITS = log2(radix). -template inline int count_digits(UInt n) { - int num_digits = 0; - do { - ++num_digits; - } while ((n >>= BITS) != 0); - return num_digits; +#ifdef FMT_BUILTIN_CLZLL +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +inline auto do_count_digits(uint64_t n) -> int { + // This has comparable performance to the version by Kendall Willets + // (https://github.com/fmtlib/format-benchmark/blob/master/digits10) + // but uses smaller tables. + // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). + static constexpr uint8_t bsr2log10[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; + static constexpr const uint64_t zero_or_powers_of_10[] = { + 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + return t - (n < zero_or_powers_of_10[t]); } +#endif -template <> int count_digits<4>(detail::fallback_uintptr n); - -#if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) -#elif FMT_MSC_VER -# define FMT_ALWAYS_INLINE __forceinline -#else -# define FMT_ALWAYS_INLINE inline +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { +#ifdef FMT_BUILTIN_CLZLL + if (!is_constant_evaluated()) { + return do_count_digits(n); + } #endif + return count_digits_fallback(n); +} -// To suppress unnecessary security cookie checks -#if FMT_MSC_VER && !FMT_CLANG_VERSION -# define FMT_SAFEBUFFERS __declspec(safebuffers) -#else -# define FMT_SAFEBUFFERS +// Counts the number of digits in n. BITS = log2(radix). +template +FMT_CONSTEXPR auto count_digits(UInt n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated() && num_bits() == 32) + return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; #endif + // Lambda avoids unreachable code warnings from NVHPC. + return [](UInt m) { + int num_digits = 0; + do { + ++num_digits; + } while ((m >>= BITS) != 0); + return num_digits; + }(n); +} #ifdef FMT_BUILTIN_CLZ -// Optional version of count_digits for better performance on 32-bit platforms. -inline int count_digits(uint32_t n) { - auto t = bsr2log10(FMT_BUILTIN_CLZ(n | 1) ^ 31); - return t - (n < data::zero_or_powers_of_10_32_new[t]); +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +FMT_INLINE auto do_count_digits(uint32_t n) -> int { +// An optimization by Kendall Willets from https://bit.ly/3uOIQrB. +// This increments the upper 32 bits (log10(T) - 1) when >= T is added. +# define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T) + static constexpr uint64_t table[] = { + FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 + FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 + FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512 + FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096 + FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k + FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k + FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k + FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M + FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M + FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M + FMT_INC(1000000000), FMT_INC(1000000000) // 4B + }; + auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31]; + return static_cast((n + inc) >> 32); } #endif -template constexpr int digits10() FMT_NOEXCEPT { - return std::numeric_limits::digits10; +// Optional version of count_digits for better performance on 32-bit platforms. +FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) { + return do_count_digits(n); + } +#endif + return count_digits_fallback(n); } -template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } -template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } -template FMT_API std::string grouping_impl(locale_ref loc); -template inline std::string grouping(locale_ref loc) { - return grouping_impl(loc); -} -template <> inline std::string grouping(locale_ref loc) { - return grouping_impl(loc); +template constexpr auto digits10() noexcept -> int { + return std::numeric_limits::digits10; } +template <> constexpr auto digits10() noexcept -> int { return 38; } +template <> constexpr auto digits10() noexcept -> int { return 38; } -template FMT_API Char thousands_sep_impl(locale_ref loc); -template inline Char thousands_sep(locale_ref loc) { - return Char(thousands_sep_impl(loc)); +template struct thousands_sep_result { + std::string grouping; + Char thousands_sep; +}; + +template +FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; +template +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { + auto result = thousands_sep_impl(loc); + return {result.grouping, Char(result.thousands_sep)}; } -template <> inline wchar_t thousands_sep(locale_ref loc) { +template <> +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { return thousands_sep_impl(loc); } -template FMT_API Char decimal_point_impl(locale_ref loc); -template inline Char decimal_point(locale_ref loc) { +template +FMT_API auto decimal_point_impl(locale_ref loc) -> Char; +template inline auto decimal_point(locale_ref loc) -> Char { return Char(decimal_point_impl(loc)); } -template <> inline wchar_t decimal_point(locale_ref loc) { +template <> inline auto decimal_point(locale_ref loc) -> wchar_t { return decimal_point_impl(loc); } // Compares two characters for equality. -template bool equal2(const Char* lhs, const char* rhs) { - return lhs[0] == rhs[0] && lhs[1] == rhs[1]; +template auto equal2(const Char* lhs, const char* rhs) -> bool { + return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); } -inline bool equal2(const char* lhs, const char* rhs) { +inline auto equal2(const char* lhs, const char* rhs) -> bool { return memcmp(lhs, rhs, 2) == 0; } // Copies two characters from src to dst. -template void copy2(Char* dst, const char* src) { +template +FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { + if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { + memcpy(dst, src, 2); + return; + } *dst++ = static_cast(*src++); *dst = static_cast(*src); } -FMT_INLINE void copy2(char* dst, const char* src) { memcpy(dst, src, 2); } template struct format_decimal_result { Iterator begin; @@ -1046,8 +1346,8 @@ template struct format_decimal_result { // buffer of specified size. The caller must ensure that the buffer is large // enough. template -inline format_decimal_result format_decimal(Char* out, UInt value, - int size) { +FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) + -> format_decimal_result { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); out += size; Char* end = out; @@ -1056,7 +1356,7 @@ inline format_decimal_result format_decimal(Char* out, UInt value, // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. out -= 2; - copy2(out, data::digits[value % 100]); + copy2(out, digits2(static_cast(value % 100))); value /= 100; } if (value < 10) { @@ -1064,58 +1364,37 @@ inline format_decimal_result format_decimal(Char* out, UInt value, return {out, end}; } out -= 2; - copy2(out, data::digits[value]); + copy2(out, digits2(static_cast(value))); return {out, end}; } template >::value)> -inline format_decimal_result format_decimal(Iterator out, UInt value, - int size) { +FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) + -> format_decimal_result { // Buffer is large enough to hold all digits (digits10 + 1). - Char buffer[digits10() + 1]; + Char buffer[digits10() + 1] = {}; auto end = format_decimal(buffer, value, size).end; - return {out, detail::copy_str(buffer, end, out)}; + return {out, detail::copy_str_noinline(buffer, end, out)}; } template -inline Char* format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) { +FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) -> Char* { buffer += num_digits; Char* end = buffer; do { - const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; - unsigned digit = (value & ((1 << BASE_BITS) - 1)); + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = static_cast(value & ((1 << BASE_BITS) - 1)); *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]); } while ((value >>= BASE_BITS) != 0); return end; } -template -Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, - bool = false) { - auto char_digits = std::numeric_limits::digits / 4; - int start = (num_digits + char_digits - 1) / char_digits - 1; - if (int start_digits = num_digits % char_digits) { - unsigned value = n.value[start--]; - buffer = format_uint(buffer, value, start_digits); - } - for (; start >= 0; --start) { - unsigned value = n.value[start]; - buffer += char_digits; - auto p = buffer; - for (int i = 0; i < char_digits; ++i) { - unsigned digit = (value & ((1 << BASE_BITS) - 1)); - *--p = static_cast(data::hex_digits[digit]); - value >>= BASE_BITS; - } - } - return buffer; -} - template -inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { +inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) + -> It { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { format_uint(ptr, value, num_digits, upper); return out; @@ -1123,141 +1402,190 @@ inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). char buffer[num_bits() / BASE_BITS + 1]; format_uint(buffer, value, num_digits, upper); - return detail::copy_str(buffer, buffer + num_digits, out); + return detail::copy_str_noinline(buffer, buffer + num_digits, out); } // A converter from UTF-8 to UTF-16. class utf8_to_utf16 { private: - wmemory_buffer buffer_; + basic_memory_buffer buffer_; public: FMT_API explicit utf8_to_utf16(string_view s); - operator wstring_view() const { return {&buffer_[0], size()}; } - size_t size() const { return buffer_.size() - 1; } - const wchar_t* c_str() const { return &buffer_[0]; } - std::wstring str() const { return {&buffer_[0], size()}; } + operator basic_string_view() const { return {&buffer_[0], size()}; } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const wchar_t* { return &buffer_[0]; } + auto str() const -> std::wstring { return {&buffer_[0], size()}; } }; -template struct null {}; - -// Workaround an array initialization issue in gcc 4.8. -template struct fill_t { +// A converter from UTF-16/UTF-32 (host endian) to UTF-8. +template +class unicode_to_utf8 { private: - enum { max_size = 4 }; - Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; - unsigned char size_ = 1; + Buffer buffer_; public: - FMT_CONSTEXPR void operator=(basic_string_view s) { - auto size = s.size(); - if (size > max_size) { - FMT_THROW(format_error("invalid fill")); - return; + unicode_to_utf8() {} + explicit unicode_to_utf8(basic_string_view s) { + static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, + "Expect utf16 or utf32"); + + if (!convert(s)) + FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" + : "invalid utf32")); + } + operator string_view() const { return string_view(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const char* c_str() const { return &buffer_[0]; } + std::string str() const { return std::string(&buffer_[0], size()); } + + // Performs conversion returning a bool instead of throwing exception on + // conversion error. This method may still throw in case of memory allocation + // error. + bool convert(basic_string_view s) { + if (!convert(buffer_, s)) return false; + buffer_.push_back(0); + return true; + } + static bool convert(Buffer& buf, basic_string_view s) { + for (auto p = s.begin(); p != s.end(); ++p) { + uint32_t c = static_cast(*p); + if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { + return false; + } + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + if (c < 0x80) { + buf.push_back(static_cast(c)); + } else if (c < 0x800) { + buf.push_back(static_cast(0xc0 | (c >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + buf.push_back(static_cast(0xe0 | (c >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + buf.push_back(static_cast(0xf0 | (c >> 18))); + buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else { + return false; + } } - for (size_t i = 0; i < size; ++i) data_[i] = s[i]; - size_ = static_cast(size); + return true; } +}; - size_t size() const { return size_; } - const Char* data() const { return data_; } +// Computes 128-bit result of multiplication of two 64-bit unsigned integers. +inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return {static_cast(p >> 64), static_cast(p)}; +#elif defined(_MSC_VER) && defined(_M_X64) + auto result = uint128_fallback(); + result.lo_ = _umul128(x, y, &result.hi_); + return result; +#else + const uint64_t mask = static_cast(max_value()); - FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } - FMT_CONSTEXPR const Char& operator[](size_t index) const { - return data_[index]; - } -}; -} // namespace detail + uint64_t a = x >> 32; + uint64_t b = x & mask; + uint64_t c = y >> 32; + uint64_t d = y & mask; + + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; -// We cannot use enum classes as bit fields because of a gcc bug -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. -namespace align { -enum type { none, left, right, center, numeric }; + uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); + + return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + (bd & mask)}; +#endif } -using align_t = align::type; -namespace sign { -enum type { none, minus, plus, space }; +namespace dragonbox { +// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from +// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. +inline int floor_log10_pow2(int e) noexcept { + FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); + static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); + return (e * 315653) >> 20; } -using sign_t = sign::type; -// Format specifiers for built-in and string types. -template struct basic_format_specs { - int width; - int precision; - char type; - align_t align : 4; - sign_t sign : 3; - bool alt : 1; // Alternate form ('#'). - detail::fill_t fill; - - constexpr basic_format_specs() - : width(0), - precision(-1), - type(0), - align(align::none), - sign(sign::none), - alt(false) {} -}; +inline int floor_log2_pow10(int e) noexcept { + FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); + return (e * 1741647) >> 19; +} -using format_specs = basic_format_specs; +// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. +inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return static_cast(p >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + return __umulh(x, y); +#else + return umul128(x, y).high(); +#endif +} -namespace detail { -namespace dragonbox { +// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline uint128_fallback umul192_upper128(uint64_t x, + uint128_fallback y) noexcept { + uint128_fallback r = umul128(x, y.high()); + r += umul128_upper64(x, y.low()); + return r; +} + +FMT_API uint128_fallback get_cached_power(int k) noexcept; // Type-specific information that Dragonbox uses. -template struct float_info; +template struct float_info; template <> struct float_info { using carrier_uint = uint32_t; - static const int significand_bits = 23; static const int exponent_bits = 8; - static const int min_exponent = -126; - static const int max_exponent = 127; - static const int exponent_bias = -127; - static const int decimal_digits = 9; static const int kappa = 1; static const int big_divisor = 100; static const int small_divisor = 10; static const int min_k = -31; static const int max_k = 46; - static const int cache_bits = 64; - static const int divisibility_check_by_5_threshold = 39; - static const int case_fc_pm_half_lower_threshold = -1; - static const int case_fc_pm_half_upper_threshold = 6; - static const int case_fc_lower_threshold = -2; - static const int case_fc_upper_threshold = 6; - static const int case_shorter_interval_left_endpoint_lower_threshold = 2; - static const int case_shorter_interval_left_endpoint_upper_threshold = 3; static const int shorter_interval_tie_lower_threshold = -35; static const int shorter_interval_tie_upper_threshold = -35; - static const int max_trailing_zeros = 7; }; template <> struct float_info { using carrier_uint = uint64_t; - static const int significand_bits = 52; static const int exponent_bits = 11; - static const int min_exponent = -1022; - static const int max_exponent = 1023; - static const int exponent_bias = -1023; - static const int decimal_digits = 17; static const int kappa = 2; static const int big_divisor = 1000; static const int small_divisor = 100; static const int min_k = -292; - static const int max_k = 326; - static const int cache_bits = 128; - static const int divisibility_check_by_5_threshold = 86; - static const int case_fc_pm_half_lower_threshold = -2; - static const int case_fc_pm_half_upper_threshold = 9; - static const int case_fc_lower_threshold = -4; - static const int case_fc_upper_threshold = 9; - static const int case_shorter_interval_left_endpoint_lower_threshold = 2; - static const int case_shorter_interval_left_endpoint_upper_threshold = 3; + static const int max_k = 341; static const int shorter_interval_tie_lower_threshold = -77; static const int shorter_interval_tie_upper_threshold = -77; - static const int max_trailing_zeros = 16; +}; + +// An 80- or 128-bit floating point number. +template +struct float_info::digits == 64 || + std::numeric_limits::digits == 113 || + is_float128::value>> { + using carrier_uint = detail::uint128_t; + static const int exponent_bits = 15; +}; + +// A double-double floating point number. +template +struct float_info::value>> { + using carrier_uint = detail::uint128_t; }; template struct decimal_fp { @@ -1266,37 +1594,40 @@ template struct decimal_fp { int exponent; }; -template FMT_API decimal_fp to_decimal(T x) FMT_NOEXCEPT; +template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; } // namespace dragonbox -template -constexpr typename dragonbox::float_info::carrier_uint exponent_mask() { - using uint = typename dragonbox::float_info::carrier_uint; - return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) - << dragonbox::float_info::significand_bits; +// Returns true iff Float has the implicit bit which is not stored. +template constexpr bool has_implicit_bit() { + // An 80-bit FP number has a 64-bit significand an no implicit bit. + return std::numeric_limits::digits != 64; } -// A floating-point presentation format. -enum class float_format : unsigned char { - general, // General: exponent notation or fixed point based on magnitude. - exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. - fixed, // Fixed point with the default precision of 6, e.g. 0.0012. - hex -}; +// Returns the number of significand bits stored in Float. The implicit bit is +// not counted since it is not stored. +template constexpr int num_significand_bits() { + // std::numeric_limits may not support __float128. + return is_float128() ? 112 + : (std::numeric_limits::digits - + (has_implicit_bit() ? 1 : 0)); +} -struct float_specs { - int precision; - float_format format : 8; - sign_t sign : 8; - bool upper : 1; - bool locale : 1; - bool binary32 : 1; - bool use_grisu : 1; - bool showpoint : 1; -}; +template +constexpr auto exponent_mask() -> + typename dragonbox::float_info::carrier_uint { + using float_uint = typename dragonbox::float_info::carrier_uint; + return ((float_uint(1) << dragonbox::float_info::exponent_bits) - 1) + << num_significand_bits(); +} +template constexpr auto exponent_bias() -> int { + // std::numeric_limits may not support __float128. + return is_float128() ? 16383 + : std::numeric_limits::max_exponent - 1; +} // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. -template It write_exponent(int exp, It it) { +template +FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { *it++ = static_cast('-'); @@ -1305,185 +1636,234 @@ template It write_exponent(int exp, It it) { *it++ = static_cast('+'); } if (exp >= 100) { - const char* top = data::digits[exp / 100]; + const char* top = digits2(to_unsigned(exp / 100)); if (exp >= 1000) *it++ = static_cast(top[0]); *it++ = static_cast(top[1]); exp %= 100; } - const char* d = data::digits[exp]; + const char* d = digits2(to_unsigned(exp)); *it++ = static_cast(d[0]); *it++ = static_cast(d[1]); return it; } -template -int format_float(T value, int precision, float_specs specs, buffer& buf); - -// Formats a floating-point number with snprintf. -template -int snprintf_float(T value, int precision, float_specs specs, - buffer& buf); - -template T promote_float(T value) { return value; } -inline double promote_float(float value) { return static_cast(value); } - -template -FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { - switch (spec) { - case 0: - case 'd': - handler.on_dec(); - break; - case 'x': - case 'X': - handler.on_hex(); - break; - case 'b': - case 'B': - handler.on_bin(); - break; - case 'o': - handler.on_oct(); - break; -#ifdef FMT_DEPRECATED_N_SPECIFIER - case 'n': -#endif - case 'L': - handler.on_num(); - break; - case 'c': - handler.on_chr(); - break; - default: - handler.on_error(); +// A floating-point number f * pow(2, e) where F is an unsigned type. +template struct basic_fp { + F f; + int e; + + static constexpr const int num_significand_bits = + static_cast(sizeof(F) * num_bits()); + + constexpr basic_fp() : f(0), e(0) {} + constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 floating-point number. + template FMT_CONSTEXPR basic_fp(Float n) { assign(n); } + + // Assigns n to this and return true iff predecessor is closer than successor. + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::digits <= 113, "unsupported FP"); + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename dragonbox::float_info::carrier_uint; + const auto num_float_significand_bits = + detail::num_significand_bits(); + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + const auto significand_mask = implicit_bit - 1; + auto u = bit_cast(n); + f = static_cast(u & significand_mask); + auto biased_e = static_cast((u & exponent_mask()) >> + num_float_significand_bits); + // The predecessor is closer if n is a normalized power of 2 (f == 0) + // other than the smallest normalized number (biased_e > 1). + auto is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e == 0) + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + else if (has_implicit_bit()) + f += static_cast(implicit_bit); + e = biased_e - exponent_bias() - num_float_significand_bits; + if (!has_implicit_bit()) ++e; + return is_predecessor_closer; + } + + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::is_iec559, "unsupported FP"); + return assign(static_cast(n)); } +}; + +using fp = basic_fp; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template +FMT_CONSTEXPR basic_fp normalize(basic_fp value) { + // Handle subnormals. + const auto implicit_bit = F(1) << num_significand_bits(); + const auto shifted_implicit_bit = implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = basic_fp::num_significand_bits - + num_significand_bits() - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; } -template -FMT_CONSTEXPR float_specs parse_float_type_spec( - const basic_format_specs& specs, ErrorHandler&& eh = {}) { - auto result = float_specs(); - result.showpoint = specs.alt; - switch (specs.type) { - case 0: - result.format = float_format::general; - result.showpoint |= specs.precision > 0; - break; - case 'G': - result.upper = true; - FMT_FALLTHROUGH; - case 'g': - result.format = float_format::general; - break; - case 'E': - result.upper = true; - FMT_FALLTHROUGH; - case 'e': - result.format = float_format::exp; - result.showpoint |= specs.precision != 0; - break; - case 'F': - result.upper = true; - FMT_FALLTHROUGH; - case 'f': - result.format = float_format::fixed; - result.showpoint |= specs.precision != 0; - break; - case 'A': - result.upper = true; - FMT_FALLTHROUGH; - case 'a': - result.format = float_format::hex; - break; -#ifdef FMT_DEPRECATED_N_SPECIFIER - case 'n': +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +#if FMT_USE_INT128 + auto product = static_cast<__uint128_t>(lhs) * rhs; + auto f = static_cast(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); #endif - case 'L': - result.locale = true; - break; - default: - eh.on_error("invalid type specifier"); - break; - } - return result; } -template -FMT_CONSTEXPR void handle_char_specs(const basic_format_specs* specs, - Handler&& handler) { - if (!specs) return handler.on_char(); - if (specs->type && specs->type != 'c') return handler.on_int(); - if (specs->align == align::numeric || specs->sign != sign::none || specs->alt) - handler.on_error("invalid format specifier for char"); - handler.on_char(); -} +FMT_CONSTEXPR inline fp operator*(fp x, fp y) { + return {multiply(x.f, y.f), x.e + y.e + 64}; +} + +template struct basic_data { + // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. + // These are generated by support/compute-powers.py. + static constexpr uint64_t pow10_significands[87] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, + }; -template -FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) { - if (spec == 0 || spec == 's') - handler.on_string(); - else if (spec == 'p') - handler.on_pointer(); - else - handler.on_error("invalid type specifier"); -} +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnarrowing" +#endif + // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding + // to significands above. + static constexpr int16_t pow10_exponents[87] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic pop +#endif -template -FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) { - if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); -} + static constexpr uint64_t power_of_10_64[20] = { + 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + + // For checking rounding thresholds. + // The kth entry is chosen to be the smallest integer such that the + // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. + static constexpr uint32_t fractional_part_rounding_thresholds[8] = { + 2576980378, // ceil(2^31 + 2^32/10^1) + 2190433321, // ceil(2^31 + 2^32/10^2) + 2151778616, // ceil(2^31 + 2^32/10^3) + 2147913145, // ceil(2^31 + 2^32/10^4) + 2147526598, // ceil(2^31 + 2^32/10^5) + 2147487943, // ceil(2^31 + 2^32/10^6) + 2147484078, // ceil(2^31 + 2^32/10^7) + 2147483691 // ceil(2^31 + 2^32/10^8) + }; +}; -template -FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { - if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); -} +#if FMT_CPLUSPLUS < 201703L +template constexpr uint64_t basic_data::pow10_significands[]; +template constexpr int16_t basic_data::pow10_exponents[]; +template constexpr uint64_t basic_data::power_of_10_64[]; +template +constexpr uint32_t basic_data::fractional_part_rounding_thresholds[]; +#endif -template class int_type_checker : private ErrorHandler { - public: - FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {} +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct data : basic_data<> {}; - FMT_CONSTEXPR void on_dec() {} - FMT_CONSTEXPR void on_hex() {} - FMT_CONSTEXPR void on_bin() {} - FMT_CONSTEXPR void on_oct() {} - FMT_CONSTEXPR void on_num() {} - FMT_CONSTEXPR void on_chr() {} +// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its +// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. +FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, + int& pow10_exponent) { + const int shift = 32; + // log10(2) = 0x0.4d104d427de7fbcc... + const int64_t significand = 0x4d104d427de7fbcc; + int index = static_cast( + ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + + ((int64_t(1) << shift) - 1)) // ceil + >> 32 // arithmetic shift + ); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between 2 consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + // Using *(x + index) instead of x[index] avoids an issue with some compilers + // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode). + return {*(data::pow10_significands + index), + *(data::pow10_exponents + index)}; +} - FMT_CONSTEXPR void on_error() { - ErrorHandler::on_error("invalid type specifier"); - } -}; - -template -class char_specs_checker : public ErrorHandler { - private: - char type_; - - public: - FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh) - : ErrorHandler(eh), type_(type) {} - - FMT_CONSTEXPR void on_int() { - handle_int_type_spec(type_, int_type_checker(*this)); - } - FMT_CONSTEXPR void on_char() {} -}; - -template -class cstring_type_checker : public ErrorHandler { - public: - FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) - : ErrorHandler(eh) {} +template +using convert_float_result = + conditional_t::value || + std::numeric_limits::digits == + std::numeric_limits::digits, + double, T>; - FMT_CONSTEXPR void on_string() {} - FMT_CONSTEXPR void on_pointer() {} -}; +template +constexpr auto convert_float(T value) -> convert_float_result { + return static_cast>(value); +} template -FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { +FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, + const fill_t& fill) -> OutputIt { auto fill_size = fill.size(); - if (fill_size == 1) return std::fill_n(it, n, fill[0]); - for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it); + if (fill_size == 1) return detail::fill_n(it, n, fill[0]); + auto data = fill.data(); + for (size_t i = 0; i < n; ++i) + it = copy_str(data, data + fill_size, it); return it; } @@ -1492,39 +1872,232 @@ FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { // width: output display width in (terminal) column positions. template -inline OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, size_t size, - size_t width, F&& f) { +FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, + size_t size, size_t width, F&& f) -> OutputIt { static_assert(align == align::left || align == align::right, ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; - auto* shifts = align == align::left ? data::left_padding_shifts - : data::right_padding_shifts; + // Shifts are encoded as string literals because static constexpr is not + // supported in constexpr functions. + auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; size_t left_padding = padding >> shifts[specs.align]; + size_t right_padding = padding - left_padding; auto it = reserve(out, size + padding * specs.fill.size()); - it = fill(it, left_padding, specs.fill); + if (left_padding != 0) it = fill(it, left_padding, specs.fill); it = f(it); - it = fill(it, padding - left_padding, specs.fill); + if (right_padding != 0) it = fill(it, right_padding, specs.fill); return base_iterator(out, it); } template -inline OutputIt write_padded(OutputIt out, - const basic_format_specs& specs, size_t size, - F&& f) { +constexpr auto write_padded(OutputIt out, const format_specs& specs, + size_t size, F&& f) -> OutputIt { return write_padded(out, specs, size, size, f); } +template +FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, + const format_specs& specs) -> OutputIt { + return write_padded( + out, specs, bytes.size(), [bytes](reserve_iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); +} + +template +auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) + -> OutputIt { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + auto write = [=](reserve_iterator it) { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + return format_uint<4, Char>(it, value, num_digits); + }; + return specs ? write_padded(out, *specs, size, write) + : base_iterator(out, write(reserve(out, size))); +} + +// Returns true iff the code point cp is printable. +FMT_API auto is_printable(uint32_t cp) -> bool; + +inline auto needs_escape(uint32_t cp) -> bool { + return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || + !is_printable(cp); +} + +template struct find_escape_result { + const Char* begin; + const Char* end; + uint32_t cp; +}; + +template +using make_unsigned_char = + typename conditional_t::value, + std::make_unsigned, + type_identity>::type; + +template +auto find_escape(const Char* begin, const Char* end) + -> find_escape_result { + for (; begin != end; ++begin) { + uint32_t cp = static_cast>(*begin); + if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; + if (needs_escape(cp)) return {begin, begin + 1, cp}; + } + return {begin, nullptr, 0}; +} + +inline auto find_escape(const char* begin, const char* end) + -> find_escape_result { + if (!is_utf8()) return find_escape(begin, end); + auto result = find_escape_result{end, nullptr, 0}; + for_each_codepoint(string_view(begin, to_unsigned(end - begin)), + [&](uint32_t cp, string_view sv) { + if (needs_escape(cp)) { + result = {sv.begin(), sv.end(), cp}; + return false; + } + return true; + }); + return result; +} + +#define FMT_STRING_IMPL(s, base, explicit) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ + using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t; \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ + operator fmt::basic_string_view() const { \ + return fmt::detail_exported::compile_string_to_view(s); \ + } \ + }; \ + return FMT_COMPILE_STRING(); \ + }() + +/** + \rst + Constructs a compile-time format string from a string literal *s*. + + **Example**:: + + // A compile-time error because 'd' is an invalid specifier for strings. + std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); + \endrst + */ +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) + +template +auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { + *out++ = static_cast('\\'); + *out++ = static_cast(prefix); + Char buf[width]; + fill_n(buf, width, static_cast('0')); + format_uint<4>(buf, cp, width); + return copy_str(buf, buf + width, out); +} + +template +auto write_escaped_cp(OutputIt out, const find_escape_result& escape) + -> OutputIt { + auto c = static_cast(escape.cp); + switch (escape.cp) { + case '\n': + *out++ = static_cast('\\'); + c = static_cast('n'); + break; + case '\r': + *out++ = static_cast('\\'); + c = static_cast('r'); + break; + case '\t': + *out++ = static_cast('\\'); + c = static_cast('t'); + break; + case '"': + FMT_FALLTHROUGH; + case '\'': + FMT_FALLTHROUGH; + case '\\': + *out++ = static_cast('\\'); + break; + default: + if (escape.cp < 0x100) { + return write_codepoint<2, Char>(out, 'x', escape.cp); + } + if (escape.cp < 0x10000) { + return write_codepoint<4, Char>(out, 'u', escape.cp); + } + if (escape.cp < 0x110000) { + return write_codepoint<8, Char>(out, 'U', escape.cp); + } + for (Char escape_char : basic_string_view( + escape.begin, to_unsigned(escape.end - escape.begin))) { + out = write_codepoint<2, Char>(out, 'x', + static_cast(escape_char) & 0xFF); + } + return out; + } + *out++ = c; + return out; +} + +template +auto write_escaped_string(OutputIt out, basic_string_view str) + -> OutputIt { + *out++ = static_cast('"'); + auto begin = str.begin(), end = str.end(); + do { + auto escape = find_escape(begin, end); + out = copy_str(begin, escape.begin, out); + begin = escape.end; + if (!begin) break; + out = write_escaped_cp(out, escape); + } while (begin != end); + *out++ = static_cast('"'); + return out; +} + +template +auto write_escaped_char(OutputIt out, Char v) -> OutputIt { + *out++ = static_cast('\''); + if ((needs_escape(static_cast(v)) && v != static_cast('"')) || + v == static_cast('\'')) { + out = write_escaped_cp( + out, find_escape_result{&v, &v + 1, static_cast(v)}); + } else { + *out++ = v; + } + *out++ = static_cast('\''); + return out; +} + template -OutputIt write_bytes(OutputIt out, string_view bytes, - const basic_format_specs& specs) { - using iterator = remove_reference_t; - return write_padded(out, specs, bytes.size(), [bytes](iterator it) { - const char* data = bytes.data(); - return copy_str(data, data + bytes.size(), it); +FMT_CONSTEXPR auto write_char(OutputIt out, Char value, + const format_specs& specs) -> OutputIt { + bool is_debug = specs.type == presentation_type::debug; + return write_padded(out, specs, 1, [=](reserve_iterator it) { + if (is_debug) return write_escaped_char(it, value); + *it++ = value; + return it; }); } +template +FMT_CONSTEXPR auto write(OutputIt out, Char value, + const format_specs& specs, locale_ref loc = {}) + -> OutputIt { + // char is formatted as unsigned char for consistency across platforms. + using unsigned_type = + conditional_t::value, unsigned char, unsigned>; + return check_char_specs(specs) + ? write_char(out, value, specs) + : write(out, static_cast(value), specs, loc); +} // Data for write_int that doesn't depend on output iterator type. It is used to // avoid template code bloat. @@ -1532,9 +2105,9 @@ template struct write_int_data { size_t size; size_t padding; - write_int_data(int num_digits, string_view prefix, - const basic_format_specs& specs) - : size(prefix.size() + to_unsigned(num_digits)), padding(0) { + FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, + const format_specs& specs) + : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { if (specs.align == align::numeric) { auto width = to_unsigned(specs.width); if (width > size) { @@ -1542,7 +2115,7 @@ template struct write_int_data { size = width; } } else if (specs.precision > num_digits) { - size = prefix.size() + to_unsigned(specs.precision); + size = (prefix >> 24) + to_unsigned(specs.precision); padding = to_unsigned(specs.precision - num_digits); } } @@ -1550,264 +2123,560 @@ template struct write_int_data { // Writes an integer in the format // -// where are written by f(it). -template -OutputIt write_int(OutputIt out, int num_digits, string_view prefix, - const basic_format_specs& specs, F f) { +// where are written by write_digits(it). +// prefix contains chars in three lower bytes and the size in the fourth byte. +template +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, + unsigned prefix, + const format_specs& specs, + W write_digits) -> OutputIt { + // Slightly faster check for specs.width == 0 && specs.precision == -1. + if ((specs.width | (specs.precision + 1)) == 0) { + auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); + if (prefix != 0) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + } + return base_iterator(out, write_digits(it)); + } auto data = write_int_data(num_digits, prefix, specs); - using iterator = remove_reference_t; - return write_padded(out, specs, data.size, [=](iterator it) { - if (prefix.size() != 0) - it = copy_str(prefix.begin(), prefix.end(), it); - it = std::fill_n(it, data.padding, static_cast('0')); - return f(it); - }); + return write_padded( + out, specs, data.size, [=](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + it = detail::fill_n(it, data.padding, static_cast('0')); + return write_digits(it); + }); } -template -OutputIt write(OutputIt out, basic_string_view s, - const basic_format_specs& specs) { - auto data = s.data(); - auto size = s.size(); - if (specs.precision >= 0 && to_unsigned(specs.precision) < size) - size = code_point_index(s, to_unsigned(specs.precision)); - auto width = specs.width != 0 - ? count_code_points(basic_string_view(data, size)) - : 0; - using iterator = remove_reference_t; - return write_padded(out, specs, size, width, [=](iterator it) { - return copy_str(data, data + size, it); - }); +template class digit_grouping { + private: + std::string grouping_; + std::basic_string thousands_sep_; + + struct next_state { + std::string::const_iterator group; + int pos; + }; + next_state initial_state() const { return {grouping_.begin(), 0}; } + + // Returns the next digit group separator position. + int next(next_state& state) const { + if (thousands_sep_.empty()) return max_value(); + if (state.group == grouping_.end()) return state.pos += grouping_.back(); + if (*state.group <= 0 || *state.group == max_value()) + return max_value(); + state.pos += *state.group++; + return state.pos; + } + + public: + explicit digit_grouping(locale_ref loc, bool localized = true) { + if (!localized) return; + auto sep = thousands_sep(loc); + grouping_ = sep.grouping; + if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep); + } + digit_grouping(std::string grouping, std::basic_string sep) + : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} + + bool has_separator() const { return !thousands_sep_.empty(); } + + int count_separators(int num_digits) const { + int count = 0; + auto state = initial_state(); + while (num_digits > next(state)) ++count; + return count; + } + + // Applies grouping to digits and write the output to out. + template + Out apply(Out out, basic_string_view digits) const { + auto num_digits = static_cast(digits.size()); + auto separators = basic_memory_buffer(); + separators.push_back(0); + auto state = initial_state(); + while (int i = next(state)) { + if (i >= num_digits) break; + separators.push_back(i); + } + for (int i = 0, sep_index = static_cast(separators.size() - 1); + i < num_digits; ++i) { + if (num_digits - i == separators[sep_index]) { + out = + copy_str(thousands_sep_.data(), + thousands_sep_.data() + thousands_sep_.size(), out); + --sep_index; + } + *out++ = static_cast(digits[to_unsigned(i)]); + } + return out; + } +}; + +// Writes a decimal integer with digit grouping. +template +auto write_int(OutputIt out, UInt value, unsigned prefix, + const format_specs& specs, + const digit_grouping& grouping) -> OutputIt { + static_assert(std::is_same, UInt>::value, ""); + int num_digits = count_digits(value); + char digits[40]; + format_decimal(digits, value, num_digits); + unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + + grouping.count_separators(num_digits)); + return write_padded( + out, specs, size, size, [&](reserve_iterator it) { + if (prefix != 0) { + char sign = static_cast(prefix); + *it++ = static_cast(sign); + } + return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); + }); +} + +// Writes a localized value. +FMT_API auto write_loc(appender out, loc_value value, + const format_specs<>& specs, locale_ref loc) -> bool; +template +inline auto write_loc(OutputIt, loc_value, const format_specs&, + locale_ref) -> bool { + return false; } -// The handle_int_type_spec handler that writes an integer. -template struct int_writer { - OutputIt out; - locale_ref locale; - const basic_format_specs& specs; +FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { + prefix |= prefix != 0 ? value << 8 : value; + prefix += (1u + (value > 0xff ? 1 : 0)) << 24; +} + +template struct write_int_arg { UInt abs_value; - char prefix[4]; - unsigned prefix_size; - - using iterator = - remove_reference_t(), 0))>; - - string_view get_prefix() const { return string_view(prefix, prefix_size); } - - template - int_writer(OutputIt output, locale_ref loc, Int value, - const basic_format_specs& s) - : out(output), - locale(loc), - specs(s), - abs_value(static_cast(value)), - prefix_size(0) { - static_assert(std::is_same, UInt>::value, ""); - if (is_negative(value)) { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } else if (specs.sign != sign::none && specs.sign != sign::minus) { - prefix[0] = specs.sign == sign::plus ? '+' : ' '; - ++prefix_size; - } + unsigned prefix; +}; + +template +FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) + -> write_int_arg> { + auto prefix = 0u; + auto abs_value = static_cast>(value); + if (is_negative(value)) { + prefix = 0x01000000 | '-'; + abs_value = 0 - abs_value; + } else { + constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', + 0x1000000u | ' '}; + prefix = prefixes[sign]; + } + return {abs_value, prefix}; +} + +template struct loc_writer { + buffer_appender out; + const format_specs& specs; + std::basic_string sep; + std::string grouping; + std::basic_string decimal_point; + + template ::value)> + auto operator()(T value) -> bool { + auto arg = make_write_int_arg(value, specs.sign); + write_int(out, static_cast>(arg.abs_value), arg.prefix, + specs, digit_grouping(grouping, sep)); + return true; } - void on_dec() { + template ::value)> + auto operator()(T) -> bool { + return false; + } +}; + +template +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, + const format_specs& specs, + locale_ref) -> OutputIt { + static_assert(std::is_same>::value, ""); + auto abs_value = arg.abs_value; + auto prefix = arg.prefix; + switch (specs.type) { + case presentation_type::none: + case presentation_type::dec: { auto num_digits = count_digits(abs_value); - out = write_int( - out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) { + return write_int( + out, num_digits, prefix, specs, [=](reserve_iterator it) { return format_decimal(it, abs_value, num_digits).end; }); } - - void on_hex() { - if (specs.alt) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = specs.type; - } + case presentation_type::hex_lower: + case presentation_type::hex_upper: { + bool upper = specs.type == presentation_type::hex_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); int num_digits = count_digits<4>(abs_value); - out = write_int(out, num_digits, get_prefix(), specs, - [this, num_digits](iterator it) { - return format_uint<4, Char>(it, abs_value, num_digits, - specs.type != 'x'); - }); + return write_int( + out, num_digits, prefix, specs, [=](reserve_iterator it) { + return format_uint<4, Char>(it, abs_value, num_digits, upper); + }); } - - void on_bin() { - if (specs.alt) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = static_cast(specs.type); - } + case presentation_type::bin_lower: + case presentation_type::bin_upper: { + bool upper = specs.type == presentation_type::bin_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); int num_digits = count_digits<1>(abs_value); - out = write_int(out, num_digits, get_prefix(), specs, - [this, num_digits](iterator it) { - return format_uint<1, Char>(it, abs_value, num_digits); - }); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); } - - void on_oct() { + case presentation_type::oct: { int num_digits = count_digits<3>(abs_value); - if (specs.alt && specs.precision <= num_digits && abs_value != 0) { - // Octal prefix '0' is counted as a digit, so only add it if precision - // is not greater than the number of digits. - prefix[prefix_size++] = '0'; - } - out = write_int(out, num_digits, get_prefix(), specs, - [this, num_digits](iterator it) { - return format_uint<3, Char>(it, abs_value, num_digits); - }); - } - - enum { sep_size = 1 }; - - void on_num() { - std::string groups = grouping(locale); - if (groups.empty()) return on_dec(); - auto sep = thousands_sep(locale); - if (!sep) return on_dec(); - int num_digits = count_digits(abs_value); - int size = num_digits, n = num_digits; - std::string::const_iterator group = groups.cbegin(); - while (group != groups.cend() && n > *group && *group > 0 && - *group != max_value()) { - size += sep_size; - n -= *group; - ++group; - } - if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); - char digits[40]; - format_decimal(digits, abs_value, num_digits); - basic_memory_buffer buffer; - size += static_cast(prefix_size); - const auto usize = to_unsigned(size); - buffer.resize(usize); - basic_string_view s(&sep, sep_size); - // Index of a decimal digit with the least significant digit having index 0. - int digit_index = 0; - group = groups.cbegin(); - auto p = buffer.data() + size - 1; - for (int i = num_digits - 1; i > 0; --i) { - *p-- = static_cast(digits[i]); - if (*group <= 0 || ++digit_index % *group != 0 || - *group == max_value()) - continue; - if (group + 1 != groups.cend()) { - digit_index = 0; - ++group; - } - std::uninitialized_copy(s.data(), s.data() + s.size(), - make_checked(p, s.size())); - p -= s.size(); - } - *p-- = static_cast(*digits); - if (prefix_size != 0) *p = static_cast('-'); - auto data = buffer.data(); - out = write_padded( - out, specs, usize, usize, - [=](iterator it) { return copy_str(data, data + size, it); }); + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + if (specs.alt && specs.precision <= num_digits && abs_value != 0) + prefix_append(prefix, '0'); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_uint<3, Char>(it, abs_value, num_digits); + }); + } + case presentation_type::chr: + return write_char(out, static_cast(abs_value), specs); + default: + throw_format_error("invalid format specifier"); } + return out; +} +template +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( + OutputIt out, write_int_arg arg, const format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int(out, arg, specs, loc); +} +template ::value && + !std::is_same::value && + std::is_same>::value)> +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const format_specs& specs, + locale_ref loc) -> OutputIt { + if (specs.localized && write_loc(out, value, specs, loc)) return out; + return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, + loc); +} +// An inlined version of write used in format string compilation. +template ::value && + !std::is_same::value && + !std::is_same>::value)> +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const format_specs& specs, + locale_ref loc) -> OutputIt { + if (specs.localized && write_loc(out, value, specs, loc)) return out; + return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); +} + +// An output iterator that counts the number of objects written to it and +// discards them. +class counting_iterator { + private: + size_t count_; + + public: + using iterator_category = std::output_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + FMT_UNCHECKED_ITERATOR(counting_iterator); + + struct value_type { + template FMT_CONSTEXPR void operator=(const T&) {} + }; + + FMT_CONSTEXPR counting_iterator() : count_(0) {} - void on_chr() { *out++ = static_cast(abs_value); } + FMT_CONSTEXPR size_t count() const { return count_; } - FMT_NORETURN void on_error() { - FMT_THROW(format_error("invalid type specifier")); + FMT_CONSTEXPR counting_iterator& operator++() { + ++count_; + return *this; + } + FMT_CONSTEXPR counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; } -}; -template -OutputIt write_nonfinite(OutputIt out, bool isinf, - const basic_format_specs& specs, - const float_specs& fspecs) { - auto str = - isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); - constexpr size_t str_size = 3; - auto sign = fspecs.sign; - auto size = str_size + (sign ? 1 : 0); - using iterator = remove_reference_t; - return write_padded(out, specs, size, [=](iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); - return copy_str(str, str + str_size, it); - }); -} + FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, + difference_type n) { + it.count_ += static_cast(n); + return it; + } -// A decimal floating-point number significand * pow(10, exp). -struct big_decimal_fp { - const char* significand; - int significand_size; - int exponent; + FMT_CONSTEXPR value_type operator*() const { return {}; } }; -inline int get_significand_size(const big_decimal_fp& fp) { - return fp.significand_size; -} -template -inline int get_significand_size(const dragonbox::decimal_fp& fp) { - return count_digits(fp.significand); +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const format_specs& specs) -> OutputIt { + auto data = s.data(); + auto size = s.size(); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) + size = code_point_index(s, to_unsigned(specs.precision)); + bool is_debug = specs.type == presentation_type::debug; + size_t width = 0; + if (specs.width != 0) { + if (is_debug) + width = write_escaped_string(counting_iterator{}, s).count(); + else + width = compute_width(basic_string_view(data, size)); + } + return write_padded(out, specs, size, width, + [=](reserve_iterator it) { + if (is_debug) return write_escaped_string(it, s); + return copy_str(data, data + size, it); + }); } - template -inline OutputIt write_significand(OutputIt out, const char* significand, - int& significand_size) { - return copy_str(significand, significand + significand_size, out); +FMT_CONSTEXPR auto write(OutputIt out, + basic_string_view> s, + const format_specs& specs, locale_ref) + -> OutputIt { + return write(out, s, specs); } -template -inline OutputIt write_significand(OutputIt out, UInt significand, - int significand_size) { - return format_decimal(out, significand, significand_size).end; +template +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, + const format_specs& specs, locale_ref) + -> OutputIt { + return specs.type != presentation_type::pointer + ? write(out, basic_string_view(s), specs, {}) + : write_ptr(out, bit_cast(s), &specs); } -template ::value)> -inline Char* write_significand(Char* out, UInt significand, - int significand_size, int integral_size, - Char decimal_point) { - if (!decimal_point) - return format_decimal(out, significand, significand_size).end; - auto end = format_decimal(out + 1, significand, significand_size).end; - if (integral_size == 1) - out[0] = out[1]; - else - std::copy_n(out + 1, integral_size, out); - out[integral_size] = decimal_point; - return end; +template ::value && + !std::is_same::value && + !std::is_same::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto size = (negative ? 1 : 0) + static_cast(num_digits); + auto it = reserve(out, size); + if (auto ptr = to_pointer(it, size)) { + if (negative) *ptr++ = static_cast('-'); + format_decimal(ptr, abs_value, num_digits); + return out; + } + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits).end; + return base_iterator(out, it); } -template >::value)> -inline OutputIt write_significand(OutputIt out, UInt significand, - int significand_size, int integral_size, - Char decimal_point) { - // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. - Char buffer[digits10() + 2]; - auto end = write_significand(buffer, significand, significand_size, - integral_size, decimal_point); - return detail::copy_str(buffer, end, out); -} +// A floating-point presentation format. +enum class float_format : unsigned char { + general, // General: exponent notation or fixed point based on magnitude. + exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. + fixed, // Fixed point with the default precision of 6, e.g. 0.0012. + hex +}; -template -inline OutputIt write_significand(OutputIt out, const char* significand, - int significand_size, int integral_size, - Char decimal_point) { - out = detail::copy_str(significand, significand + integral_size, out); - if (!decimal_point) return out; - *out++ = decimal_point; - return detail::copy_str(significand + integral_size, - significand + significand_size, out); -} +struct float_specs { + int precision; + float_format format : 8; + sign_t sign : 8; + bool upper : 1; + bool locale : 1; + bool binary32 : 1; + bool showpoint : 1; +}; -template -OutputIt write_float(OutputIt out, const DecimalFP& fp, - const basic_format_specs& specs, float_specs fspecs, - Char decimal_point) { - auto significand = fp.significand; - int significand_size = get_significand_size(fp); - static const Char zero = static_cast('0'); +template +FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs, + ErrorHandler&& eh = {}) + -> float_specs { + auto result = float_specs(); + result.showpoint = specs.alt; + result.locale = specs.localized; + switch (specs.type) { + case presentation_type::none: + result.format = float_format::general; + break; + case presentation_type::general_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::general_lower: + result.format = float_format::general; + break; + case presentation_type::exp_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::exp_lower: + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::fixed_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::fixed_lower: + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::hexfloat_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::hexfloat_lower: + result.format = float_format::hex; + break; + default: + eh.on_error("invalid format specifier"); + break; + } + return result; +} + +template +FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, + format_specs specs, + const float_specs& fspecs) -> OutputIt { + auto str = + isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); + constexpr size_t str_size = 3; + auto sign = fspecs.sign; + auto size = str_size + (sign ? 1 : 0); + // Replace '0'-padding with space for non-finite values. + const bool is_zero_fill = + specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); + if (is_zero_fill) specs.fill[0] = static_cast(' '); + return write_padded(out, specs, size, [=](reserve_iterator it) { + if (sign) *it++ = detail::sign(sign); + return copy_str(str, str + str_size, it); + }); +} + +// A decimal floating-point number significand * pow(10, exp). +struct big_decimal_fp { + const char* significand; + int significand_size; + int exponent; +}; + +constexpr auto get_significand_size(const big_decimal_fp& f) -> int { + return f.significand_size; +} +template +inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { + return count_digits(f.significand); +} + +template +constexpr auto write_significand(OutputIt out, const char* significand, + int significand_size) -> OutputIt { + return copy_str(significand, significand + significand_size, out); +} +template +inline auto write_significand(OutputIt out, UInt significand, + int significand_size) -> OutputIt { + return format_decimal(out, significand, significand_size).end; +} +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int exponent, + const Grouping& grouping) -> OutputIt { + if (!grouping.has_separator()) { + out = write_significand(out, significand, significand_size); + return detail::fill_n(out, exponent, static_cast('0')); + } + auto buffer = memory_buffer(); + write_significand(appender(buffer), significand, significand_size); + detail::fill_n(appender(buffer), exponent, '0'); + return grouping.apply(out, string_view(buffer.data(), buffer.size())); +} + +template ::value)> +inline auto write_significand(Char* out, UInt significand, int significand_size, + int integral_size, Char decimal_point) -> Char* { + if (!decimal_point) + return format_decimal(out, significand, significand_size).end; + out += significand_size + 1; + Char* end = out; + int floating_size = significand_size - integral_size; + for (int i = floating_size / 2; i > 0; --i) { + out -= 2; + copy2(out, digits2(static_cast(significand % 100))); + significand /= 100; + } + if (floating_size % 2 != 0) { + *--out = static_cast('0' + significand % 10); + significand /= 10; + } + *--out = decimal_point; + format_decimal(out - integral_size, significand, integral_size); + return end; +} + +template >::value)> +inline auto write_significand(OutputIt out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. + Char buffer[digits10() + 2]; + auto end = write_significand(buffer, significand, significand_size, + integral_size, decimal_point); + return detail::copy_str_noinline(buffer, end, out); +} + +template +FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + out = detail::copy_str_noinline(significand, + significand + integral_size, out); + if (!decimal_point) return out; + *out++ = decimal_point; + return detail::copy_str_noinline(significand + integral_size, + significand + significand_size, out); +} + +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int integral_size, + Char decimal_point, + const Grouping& grouping) -> OutputIt { + if (!grouping.has_separator()) { + return write_significand(out, significand, significand_size, integral_size, + decimal_point); + } + auto buffer = basic_memory_buffer(); + write_significand(buffer_appender(buffer), significand, + significand_size, integral_size, decimal_point); + grouping.apply( + out, basic_string_view(buffer.data(), to_unsigned(integral_size))); + return detail::copy_str_noinline(buffer.data() + integral_size, + buffer.end(), out); +} + +template > +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { + auto significand = f.significand; + int significand_size = get_significand_size(f); + const Char zero = static_cast('0'); auto sign = fspecs.sign; size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); - using iterator = remove_reference_t; + using iterator = reserve_iterator; + + Char decimal_point = + fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); - int output_exp = fp.exponent + significand_size - 1; + int output_exp = f.exponent + significand_size - 1; auto use_exp_format = [=]() { if (fspecs.format == float_format::exp) return true; if (fspecs.format != float_format::general) return false; @@ -1820,7 +2689,8 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, if (use_exp_format()) { int num_zeros = 0; if (fspecs.showpoint) { - num_zeros = (std::max)(fspecs.precision - significand_size, 0); + num_zeros = fspecs.precision - significand_size; + if (num_zeros < 0) num_zeros = 0; size += to_unsigned(num_zeros); } else if (significand_size == 1) { decimal_point = Char(); @@ -1832,11 +2702,11 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); char exp_char = fspecs.upper ? 'E' : 'e'; auto write = [=](iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); + if (sign) *it++ = detail::sign(sign); // Insert a decimal point after the first digit and add an exponent. it = write_significand(it, significand, significand_size, 1, decimal_point); - if (num_zeros > 0) it = std::fill_n(it, num_zeros, zero); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); *it++ = static_cast(exp_char); return write_exponent(output_exp, it); }; @@ -1844,36 +2714,38 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, : base_iterator(out, write(reserve(out, size))); } - int exp = fp.exponent + significand_size; - if (fp.exponent >= 0) { + int exp = f.exponent + significand_size; + if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] - size += to_unsigned(fp.exponent); + size += to_unsigned(f.exponent); int num_zeros = fspecs.precision - exp; -#ifdef FMT_FUZZ - if (num_zeros > 5000) - throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); -#endif + abort_fuzzing_if(num_zeros > 5000); if (fspecs.showpoint) { - if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; + ++size; + if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0; if (num_zeros > 0) size += to_unsigned(num_zeros); } + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(exp)); return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); - it = write_significand(it, significand, significand_size); - it = std::fill_n(it, fp.exponent, zero); + if (sign) *it++ = detail::sign(sign); + it = write_significand(it, significand, significand_size, + f.exponent, grouping); if (!fspecs.showpoint) return it; *it++ = decimal_point; - return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; }); } else if (exp > 0) { // 1234e-2 -> 12.34[0+] int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(exp)); return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); + if (sign) *it++ = detail::sign(sign); it = write_significand(it, significand, significand_size, exp, - decimal_point); - return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + decimal_point, grouping); + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; }); } // 1234e-6 -> 0.001234 @@ -1882,1453 +2754,1481 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp, fspecs.precision < num_zeros) { num_zeros = fspecs.precision; } - size += 2 + to_unsigned(num_zeros); + bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; + size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); + if (sign) *it++ = detail::sign(sign); *it++ = zero; - if (num_zeros == 0 && significand_size == 0 && !fspecs.showpoint) return it; + if (!pointy) return it; *it++ = decimal_point; - it = std::fill_n(it, num_zeros, zero); + it = detail::fill_n(it, num_zeros, zero); return write_significand(it, significand, significand_size); }); } -template ::value)> -OutputIt write(OutputIt out, T value, basic_format_specs specs, - locale_ref loc = {}) { - if (const_check(!is_supported_floating_point(value))) return out; - float_specs fspecs = parse_float_type_spec(specs); - fspecs.sign = specs.sign; - if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. - fspecs.sign = sign::minus; - value = -value; - } else if (fspecs.sign == sign::minus) { - fspecs.sign = sign::none; - } - - if (!std::isfinite(value)) - return write_nonfinite(out, std::isinf(value), specs, fspecs); - - if (specs.align == align::numeric && fspecs.sign) { - auto it = reserve(out, 1); - *it++ = static_cast(data::signs[fspecs.sign]); - out = base_iterator(out, it); - fspecs.sign = sign::none; - if (specs.width != 0) --specs.width; - } - - memory_buffer buffer; - if (fspecs.format == float_format::hex) { - if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); - snprintf_float(promote_float(value), specs.precision, fspecs, buffer); - return write_bytes(out, {buffer.data(), buffer.size()}, specs); - } - int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; - if (fspecs.format == float_format::exp) { - if (precision == max_value()) - FMT_THROW(format_error("number is too big")); - else - ++precision; - } - if (const_check(std::is_same())) fspecs.binary32 = true; - fspecs.use_grisu = is_fast_float(); - int exp = format_float(promote_float(value), precision, fspecs, buffer); - fspecs.precision = precision; - Char point = - fspecs.locale ? decimal_point(loc) : static_cast('.'); - auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, fp, specs, fspecs, point); -} - -template ::value)> -OutputIt write(OutputIt out, T value) { - if (const_check(!is_supported_floating_point(value))) return out; - - using floaty = conditional_t::value, double, T>; - using uint = typename dragonbox::float_info::carrier_uint; - auto bits = bit_cast(value); - - auto fspecs = float_specs(); - auto sign_bit = bits & (uint(1) << (num_bits() - 1)); - if (sign_bit != 0) { - fspecs.sign = sign::minus; - value = -value; - } - - static const auto specs = basic_format_specs(); - uint mask = exponent_mask(); - if ((bits & mask) == mask) - return write_nonfinite(out, std::isinf(value), specs, fspecs); - - auto dec = dragonbox::to_decimal(static_cast(value)); - return write_float(out, dec, specs, fspecs, static_cast('.')); -} - -template ::value && - !is_fast_float::value)> -inline OutputIt write(OutputIt out, T value) { - return write(out, value, basic_format_specs()); -} - -template -OutputIt write_char(OutputIt out, Char value, - const basic_format_specs& specs) { - using iterator = remove_reference_t; - return write_padded(out, specs, 1, [=](iterator it) { - *it++ = value; - return it; - }); -} - -template -OutputIt write_ptr(OutputIt out, UIntPtr value, - const basic_format_specs* specs) { - int num_digits = count_digits<4>(value); - auto size = to_unsigned(num_digits) + size_t(2); - using iterator = remove_reference_t; - auto write = [=](iterator it) { - *it++ = static_cast('0'); - *it++ = static_cast('x'); - return format_uint<4, Char>(it, value, num_digits); - }; - return specs ? write_padded(out, *specs, size, write) - : base_iterator(out, write(reserve(out, size))); -} - -template struct is_integral : std::is_integral {}; -template <> struct is_integral : std::true_type {}; -template <> struct is_integral : std::true_type {}; - -template -OutputIt write(OutputIt out, monostate) { - FMT_ASSERT(false, ""); - return out; -} - -template ::value)> -OutputIt write(OutputIt out, string_view value) { - auto it = reserve(out, value.size()); - it = copy_str(value.begin(), value.end(), it); - return base_iterator(out, it); -} +template class fallback_digit_grouping { + public: + constexpr fallback_digit_grouping(locale_ref, bool) {} -template -OutputIt write(OutputIt out, basic_string_view value) { - auto it = reserve(out, value.size()); - it = std::copy(value.begin(), value.end(), it); - return base_iterator(out, it); -} + constexpr bool has_separator() const { return false; } -template -buffer_appender write(buffer_appender out, - basic_string_view value) { - get_container(out).append(value.begin(), value.end()); - return out; -} + constexpr int count_separators(int) const { return 0; } -template ::value && - !std::is_same::value && - !std::is_same::value)> -OutputIt write(OutputIt out, T value) { - auto abs_value = static_cast>(value); - bool negative = is_negative(value); - // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. - if (negative) abs_value = ~abs_value + 1; - int num_digits = count_digits(abs_value); - auto size = (negative ? 1 : 0) + static_cast(num_digits); - auto it = reserve(out, size); - if (auto ptr = to_pointer(it, size)) { - if (negative) *ptr++ = static_cast('-'); - format_decimal(ptr, abs_value, num_digits); + template + constexpr Out apply(Out out, basic_string_view) const { return out; } - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits).end; - return base_iterator(out, it); -} - -template -OutputIt write(OutputIt out, bool value) { - return write(out, string_view(value ? "true" : "false")); -} - -template -OutputIt write(OutputIt out, Char value) { - auto it = reserve(out, 1); - *it++ = value; - return base_iterator(out, it); -} +}; -template -OutputIt write(OutputIt out, const Char* value) { - if (!value) { - FMT_THROW(format_error("string pointer is null")); +template +FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { + if (is_constant_evaluated()) { + return do_write_float>(out, f, specs, fspecs, + loc); } else { - auto length = std::char_traits::length(value); - out = write(out, basic_string_view(value, length)); + return do_write_float(out, f, specs, fspecs, loc); } - return out; } -template -OutputIt write(OutputIt out, const void* value) { - return write_ptr(out, to_uintptr(value), nullptr); -} - -template -auto write(OutputIt out, const T& value) -> typename std::enable_if< - mapped_type_constant>::value == - type::custom_type, - OutputIt>::type { - using context_type = basic_format_context; - using formatter_type = - conditional_t::value, - typename context_type::template formatter_type, - fallback_formatter>; - context_type ctx(out, {}, {}); - return formatter_type().format(value, ctx); +template constexpr bool isnan(T value) { + return !(value >= value); // std::isnan doesn't support __float128. } -// An argument visitor that formats the argument and writes it via the output -// iterator. It's a class and not a generic lambda for compatibility with C++11. -template struct default_arg_formatter { - using context = basic_format_context; - - OutputIt out; - basic_format_args args; - locale_ref loc; - - template OutputIt operator()(T value) { - return write(out, value); - } - - OutputIt operator()(typename basic_format_arg::handle handle) { - basic_format_parse_context parse_ctx({}); - basic_format_context format_ctx(out, args, loc); - handle.format(parse_ctx, format_ctx); - return format_ctx.out(); - } -}; - -template -class arg_formatter_base { - public: - using iterator = OutputIt; - using char_type = Char; - using format_specs = basic_format_specs; - - private: - iterator out_; - locale_ref locale_; - format_specs* specs_; - - // Attempts to reserve space for n extra characters in the output range. - // Returns a pointer to the reserved range or a reference to out_. - auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) { - return detail::reserve(out_, n); - } - - using reserve_iterator = remove_reference_t(), 0))>; - - template void write_int(T value, const format_specs& spec) { - using uint_type = uint32_or_64_or_128_t; - int_writer w(out_, locale_, value, spec); - handle_int_type_spec(spec.type, w); - out_ = w.out; - } - - void write(char value) { - auto&& it = reserve(1); - *it++ = value; - } - - template ::value)> - void write(Ch value) { - out_ = detail::write(out_, value); - } - - void write(string_view value) { - auto&& it = reserve(value.size()); - it = copy_str(value.begin(), value.end(), it); - } - void write(wstring_view value) { - static_assert(std::is_same::value, ""); - auto&& it = reserve(value.size()); - it = std::copy(value.begin(), value.end(), it); - } - - template - void write(const Ch* s, size_t size, const format_specs& specs) { - auto width = specs.width != 0 - ? count_code_points(basic_string_view(s, size)) - : 0; - out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) { - return copy_str(s, s + size, it); - }); - } - - template - void write(basic_string_view s, const format_specs& specs = {}) { - out_ = detail::write(out_, s, specs); - } - - void write_pointer(const void* p) { - out_ = write_ptr(out_, to_uintptr(p), specs_); - } - - struct char_spec_handler : ErrorHandler { - arg_formatter_base& formatter; - Char value; - - char_spec_handler(arg_formatter_base& f, Char val) - : formatter(f), value(val) {} +template +struct has_isfinite : std::false_type {}; - void on_int() { - // char is only formatted as int if there are specs. - formatter.write_int(static_cast(value), *formatter.specs_); - } - void on_char() { - if (formatter.specs_) - formatter.out_ = write_char(formatter.out_, value, *formatter.specs_); - else - formatter.write(value); +template +struct has_isfinite> + : std::true_type {}; + +template ::value&& + has_isfinite::value)> +FMT_CONSTEXPR20 bool isfinite(T value) { + constexpr T inf = T(std::numeric_limits::infinity()); + if (is_constant_evaluated()) + return !detail::isnan(value) && value < inf && value > -inf; + return std::isfinite(value); +} +template ::value)> +FMT_CONSTEXPR bool isfinite(T value) { + T inf = T(std::numeric_limits::infinity()); + // std::isfinite doesn't support __float128. + return !detail::isnan(value) && value < inf && value > -inf; +} + +template ::value)> +FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { + if (is_constant_evaluated()) { +#ifdef __cpp_if_constexpr + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + return (bits >> (num_bits() - 1)) != 0; } - }; - - struct cstring_spec_handler : error_handler { - arg_formatter_base& formatter; - const Char* value; - - cstring_spec_handler(arg_formatter_base& f, const Char* val) - : formatter(f), value(val) {} - - void on_string() { formatter.write(value); } - void on_pointer() { formatter.write_pointer(value); } - }; - - protected: - iterator out() { return out_; } - format_specs* specs() { return specs_; } - - void write(bool value) { - if (specs_) - write(string_view(value ? "true" : "false"), *specs_); - else - out_ = detail::write(out_, value); +#endif } + return std::signbit(static_cast(value)); +} + +enum class round_direction { unknown, up, down }; + +// Given the divisor (normally a power of 10), the remainder = v % divisor for +// some number v and the error, returns whether v should be rounded up, down, or +// whether the rounding direction can't be determined due to error. +// error should be less than divisor / 2. +FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, + uint64_t remainder, + uint64_t error) { + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. + // Round down if (remainder + error) * 2 <= divisor. + if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) + return round_direction::down; + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + return round_direction::up; + } + return round_direction::unknown; +} + +namespace digits { +enum result { + more, // Generate more digits. + done, // Done generating digits. + error // Digit generation cancelled due to an error. +}; +} - void write(const Char* value) { - if (!value) { - FMT_THROW(format_error("string pointer is null")); +struct gen_digits_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, + uint64_t remainder, uint64_t error, + bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (!integral && error >= remainder) return digits::error; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; } else { - auto length = std::char_traits::length(value); - basic_string_view sv(value, length); - specs_ ? write(sv, *specs_) : write(sv); + FMT_ASSERT(error == 1 && divisor > 2, ""); } - } - - public: - arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc) - : out_(out), locale_(loc), specs_(s) {} - - iterator operator()(monostate) { - FMT_ASSERT(false, "invalid argument type"); - return out_; - } - - template ::value)> - FMT_INLINE iterator operator()(T value) { - if (specs_) - write_int(value, *specs_); - else - out_ = detail::write(out_, value); - return out_; - } - - iterator operator()(Char value) { - handle_char_specs(specs_, - char_spec_handler(*this, static_cast(value))); - return out_; - } - - iterator operator()(bool value) { - if (specs_ && specs_->type) return (*this)(value ? 1 : 0); - write(value != 0); - return out_; - } - - template ::value)> - iterator operator()(T value) { - auto specs = specs_ ? *specs_ : format_specs(); - if (const_check(is_supported_floating_point(value))) - out_ = detail::write(out_, value, specs, locale_); - else - FMT_ASSERT(false, "unsupported float argument type"); - return out_; - } - - iterator operator()(const Char* value) { - if (!specs_) return write(value), out_; - handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value)); - return out_; - } - - iterator operator()(basic_string_view value) { - if (specs_) { - check_string_type_spec(specs_->type, error_handler()); - write(value, *specs_); - } else { - write(value); + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; } - return out_; - } - - iterator operator()(const void* value) { - if (specs_) check_pointer_type_spec(specs_->type, error_handler()); - write_pointer(value); - return out_; + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[size++] = '0'; + else + ++exp10; + } + return digits::done; } }; -/** The default argument formatter. */ -template -class arg_formatter : public arg_formatter_base { - private: - using char_type = Char; - using base = arg_formatter_base; - using context_type = basic_format_context; - - context_type& ctx_; - basic_format_parse_context* parse_ctx_; - const Char* ptr_; - - public: - using iterator = typename base::iterator; - using format_specs = typename base::format_specs; - - /** - \rst - Constructs an argument formatter object. - *ctx* is a reference to the formatting context, - *specs* contains format specifier information for standard argument types. - \endrst - */ - explicit arg_formatter( - context_type& ctx, - basic_format_parse_context* parse_ctx = nullptr, - format_specs* specs = nullptr, const Char* ptr = nullptr) - : base(ctx.out(), specs, ctx.locale()), - ctx_(ctx), - parse_ctx_(parse_ctx), - ptr_(ptr) {} - - using base::operator(); - - /** Formats an argument of a user-defined type. */ - iterator operator()(typename basic_format_arg::handle handle) { - if (ptr_) advance_to(*parse_ctx_, ptr_); - handle.format(*parse_ctx_, ctx_); - return ctx_.out(); +inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { + // Adjust fixed precision by exponent because it is relative to decimal + // point. + if (exp10 > 0 && precision > max_value() - exp10) + FMT_THROW(format_error("number is too big")); + precision += exp10; +} + +// Generates output using the Grisu digit-gen algorithm. +// error: the size of the region (lower, upper) outside of which numbers +// definitely do not round to value (Delta in Grisu3). +FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error, + int& exp, + gen_digits_handler& handler) + -> digits::result { + const fp one(1ULL << -value.e, value.e); + // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be + // zero because it contains a product of two 64-bit numbers with MSB set (due + // to normalization) - 1, shifted right by at most 60 bits. + auto integral = static_cast(value.f >> -one.e); + FMT_ASSERT(integral != 0, ""); + FMT_ASSERT(integral == value.f >> -one.e, ""); + // The fractional part of scaled value (p2 in Grisu) c = value % one. + uint64_t fractional = value.f & (one.f - 1); + exp = count_digits(integral); // kappa in Grisu. + // Non-fixed formats require at least one digit and no precision adjustment. + if (handler.fixed) { + adjust_precision(handler.precision, exp + handler.exp10); + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (handler.precision <= 0) { + if (handler.precision < 0) return digits::done; + // Divide by 10 to prevent overflow. + uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e; + auto dir = get_round_direction(divisor, value.f / 10, error * 10); + if (dir == round_direction::unknown) return digits::error; + handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } } -}; - -template FMT_CONSTEXPR bool is_name_start(Char c) { - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; -} - -// Parses the range [begin, end) as an unsigned integer. This function assumes -// that the range is non-empty and the first character is a digit. -template -FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, - ErrorHandler&& eh) { - FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); - unsigned value = 0; - // Convert to unsigned to prevent a warning. - constexpr unsigned max_int = max_value(); - unsigned big = max_int / 10; + // Generate digits for the integral part. This can produce up to 10 digits. do { - // Check for overflow. - if (value > big) { - value = max_int + 1; + uint32_t digit = 0; + auto divmod_integral = [&](uint32_t divisor) { + digit = integral / divisor; + integral %= divisor; + }; + // This optimization by Milo Yip reduces the number of integer divisions by + // one per iteration. + switch (exp) { + case 10: + divmod_integral(1000000000); break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); } - value = value * 10 + unsigned(*begin - '0'); - ++begin; - } while (begin != end && '0' <= *begin && *begin <= '9'); - if (value > max_int) eh.on_error("number is too big"); - return static_cast(value); -} - -template class custom_formatter { - private: - using char_type = typename Context::char_type; - - basic_format_parse_context& parse_ctx_; - Context& ctx_; - - public: - explicit custom_formatter(basic_format_parse_context& parse_ctx, - Context& ctx) - : parse_ctx_(parse_ctx), ctx_(ctx) {} - - void operator()(typename basic_format_arg::handle h) const { - h.format(parse_ctx_, ctx_); - } - - template void operator()(T) const {} -}; - -template -using is_integer = - bool_constant::value && !std::is_same::value && - !std::is_same::value && - !std::is_same::value>; - -template class width_checker { - public: - explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} - - template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T value) { - if (is_negative(value)) handler_.on_error("negative width"); - return static_cast(value); - } - - template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T) { - handler_.on_error("width is not integer"); - return 0; - } - - private: - ErrorHandler& handler_; -}; - -template class precision_checker { - public: - explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} - - template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T value) { - if (is_negative(value)) handler_.on_error("negative precision"); - return static_cast(value); - } - - template ::value)> - FMT_CONSTEXPR unsigned long long operator()(T) { - handler_.on_error("precision is not integer"); - return 0; - } - - private: - ErrorHandler& handler_; -}; - -// A format specifier handler that sets fields in basic_format_specs. -template class specs_setter { - public: - explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) - : specs_(specs) {} - - FMT_CONSTEXPR specs_setter(const specs_setter& other) - : specs_(other.specs_) {} - - FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } - FMT_CONSTEXPR void on_fill(basic_string_view fill) { - specs_.fill = fill; - } - FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } - FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } - FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } - FMT_CONSTEXPR void on_hash() { specs_.alt = true; } - - FMT_CONSTEXPR void on_zero() { - specs_.align = align::numeric; - specs_.fill[0] = Char('0'); - } - - FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } - FMT_CONSTEXPR void on_precision(int precision) { - specs_.precision = precision; - } - FMT_CONSTEXPR void end_precision() {} - - FMT_CONSTEXPR void on_type(Char type) { - specs_.type = static_cast(type); - } - - protected: - basic_format_specs& specs_; -}; - -template class numeric_specs_checker { - public: - FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type) - : error_handler_(eh), arg_type_(arg_type) {} - - FMT_CONSTEXPR void require_numeric_argument() { - if (!is_arithmetic_type(arg_type_)) - error_handler_.on_error("format specifier requires numeric argument"); - } - - FMT_CONSTEXPR void check_sign() { - require_numeric_argument(); - if (is_integral_type(arg_type_) && arg_type_ != type::int_type && - arg_type_ != type::long_long_type && arg_type_ != type::char_type) { - error_handler_.on_error("format specifier requires signed argument"); - } - } - - FMT_CONSTEXPR void check_precision() { - if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) - error_handler_.on_error("precision not allowed for this argument type"); + --exp; + auto remainder = (static_cast(integral) << -one.e) + fractional; + auto result = handler.on_digit(static_cast('0' + digit), + data::power_of_10_64[exp] << -one.e, + remainder, error, true); + if (result != digits::more) return result; + } while (exp > 0); + // Generate digits for the fractional part. + for (;;) { + fractional *= 10; + error *= 10; + char digit = static_cast('0' + (fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + auto result = handler.on_digit(digit, one.f, fractional, error, false); + if (result != digits::more) return result; } +} - private: - ErrorHandler& error_handler_; - detail::type arg_type_; -}; - -// A format specifier handler that checks if specifiers are consistent with the -// argument type. -template class specs_checker : public Handler { - private: - numeric_specs_checker checker_; - - // Suppress an MSVC warning about using this in initializer list. - FMT_CONSTEXPR Handler& error_handler() { return *this; } - - public: - FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) - : Handler(handler), checker_(error_handler(), arg_type) {} - - FMT_CONSTEXPR specs_checker(const specs_checker& other) - : Handler(other), checker_(error_handler(), other.arg_type_) {} +class bigint { + private: + // A bigint is stored as an array of bigits (big digits), with bigit at index + // 0 being the least significant one. + using bigit = uint32_t; + using double_bigit = uint64_t; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; - FMT_CONSTEXPR void on_align(align_t align) { - if (align == align::numeric) checker_.require_numeric_argument(); - Handler::on_align(align); + FMT_CONSTEXPR20 bigit operator[](int index) const { + return bigits_[to_unsigned(index)]; } - - FMT_CONSTEXPR void on_plus() { - checker_.check_sign(); - Handler::on_plus(); + FMT_CONSTEXPR20 bigit& operator[](int index) { + return bigits_[to_unsigned(index)]; } - FMT_CONSTEXPR void on_minus() { - checker_.check_sign(); - Handler::on_minus(); - } + static constexpr const int bigit_bits = num_bits(); - FMT_CONSTEXPR void on_space() { - checker_.check_sign(); - Handler::on_space(); - } + friend struct formatter; - FMT_CONSTEXPR void on_hash() { - checker_.require_numeric_argument(); - Handler::on_hash(); + FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); } - FMT_CONSTEXPR void on_zero() { - checker_.require_numeric_argument(); - Handler::on_zero(); + FMT_CONSTEXPR20 void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); } - FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } -}; - -template