diff --git a/sql/rpl_info_file.h b/sql/rpl_info_file.h index 1b16330e0bc2a..f862382d15d39 100644 --- a/sql/rpl_info_file.h +++ b/sql/rpl_info_file.h @@ -19,7 +19,6 @@ #define RPL_INFO_FILE_H #include // uintN_t -#include // std::from/to_chars and other helpers #include // superclass of Info_file::Mem_fn #include // IO_CACHE, FN_REFLEN, ... @@ -36,7 +35,6 @@ namespace Int_IO_CACHE */ template static constexpr size_t BUF_SIZE= std::numeric_limits::digits10 + 1 + std::numeric_limits::is_signed; - static constexpr auto ERRC_OK= std::errc(); /** @ref IO_CACHE (reading one line with the `\n`) version of std::from_chars() @@ -45,19 +43,38 @@ namespace Int_IO_CACHE */ template static bool from_chars(IO_CACHE *file, I &value) { + int error; /** - +2 for the terminating `\n\0` (They are ignored by - std::from_chars(), but my_b_gets() includes them.) + +2 for the terminating `\n\0` + (They are ignored, but my_b_gets() includes them.) */ char buf[BUF_SIZE + 2]; /// includes the `\n` but excludes the `\0` size_t length= my_b_gets(file, buf, sizeof(buf)); if (!length) // EOF return true; - // SFINAE if `I` is not a numeric type - std::from_chars_result result= std::from_chars(buf, &(buf[length]), value); - // Return `true` if the conversion failed or if the number ended early - return result.ec != ERRC_OK || *(result.ptr) != '\n'; + char *end= &(buf[length]); + longlong val= my_strtoll10(buf, &end, &error); + switch (error) { + case -1: + if (!std::numeric_limits::is_signed) + return true; + [[fallthrough]]; + case 0: + /*TODO + This upper range check is not needed when using type- + specific variants of a safe string-to-integer converter + (e.g., std::from_chars() when all platforms support it). + */ + if (*end == '\n' && value <= std::numeric_limits::max()) + { + value= static_cast(val); + return false; + } + [[fallthrough]]; + default: + return true; + } } /** Convenience overload of from_chars(IO_CACHE *, I &) for `operator=` types @@ -79,14 +96,19 @@ namespace Int_IO_CACHE */ template static void to_chars(IO_CACHE *file, I value) { - /** - my_b_printf() uses a buffer too, - so we might as well save on format parsing and buffer resizing - */ char buf[BUF_SIZE]; - std::to_chars_result result= std::to_chars(buf, &buf[sizeof(buf)], value); - DBUG_ASSERT(result.ec == ERRC_OK); - my_b_write(file, reinterpret_cast(buf), result.ptr - buf); + /*TODO: + * my_b_printf() needs updates and so doesn't + support `long long`s at the moment. + * We can avoid format parsing by expanding + int10_to_str() if not supporting std::to_chars(). + */ + int len= std::numeric_limits::is_signed ? + snprintf(buf, BUF_SIZE, "%lld", static_cast(value)) : + snprintf(buf, BUF_SIZE, "%llu", static_cast(value)) + ; + DBUG_ASSERT(len > 0); + my_b_write(file, reinterpret_cast(buf), len); } }; @@ -209,7 +231,6 @@ struct Info_file */ struct Mem_fn: std::function { - using List= const std::initializer_list; /// Null Constructor Mem_fn(std::nullptr_t null= nullptr): std::function(null) {} @@ -242,36 +263,55 @@ struct Info_file (either contains a `.` or is entirely empty) rather than an integer. @return `false` if the file has parsed successfully or `true` if error */ - bool load_from_file(Mem_fn::List value_list, size_t default_line_count) + template bool load_from_file( + const Mem_fn (&value_list)[size], + size_t default_line_count= 0 + ) { return load_from_file(value_list, size, default_line_count); } + /** + Flush the MySQL line-based section to the @ref file + @param value_list List of wrapped member pointers to values. + @param total_line_count + The number of lines to describe the file as on the first line of the file. + If this is larger than `value_list.size()`, suffix the file with empty + lines until the line count (including the line count line) is this many. + This reservation provides compatibility with MySQL, + who has added more old-style lines while MariaDB innovated. + */ + template void save_to_file( + const Mem_fn (&value_list)[size], + size_t total_line_count= size + /* line count line */ 1 + ) { return save_to_file(value_list, size, total_line_count); } + +private: + bool + load_from_file(const Mem_fn *values, size_t size, size_t default_line_count) { + long val; /** The first row is temporarily stored in the first value. If it is a line count and not a log name (new format), the second row will overwrite it. */ - auto &line1= dynamic_cast &>((*(value_list.begin()))(this)); + auto &line1= dynamic_cast &>(values[0](this)); if (line1.load_from(&file)) return true; - size_t line_count; - std::from_chars_result result= std::from_chars( - line1.buf, &line1.buf[sizeof(line1.buf)], line_count); + char *end= str2int(line1.buf, 10, 0, INT32_MAX, &val); /** If this first line was not a number - the line count, then it was the first value for real, so the for loop should then skip over it, the index 0 of the list. */ - size_t i= result.ec != Int_IO_CACHE::ERRC_OK || *(result.ptr) != '\0'; + size_t i= !end || *end != '\0'; /* Set the default after parsing: While std::from_chars() does not replace the output if it failed, it does replace if the line is not fully spent. */ - if (i) - line_count= default_line_count; + size_t line_count= i ? static_cast(val) : default_line_count; for (; i < line_count; ++i) { int c; - if (i < value_list.size()) // line known in the `value_list` + if (i < size) // line known in the `value_list` { - const Mem_fn &pm= value_list.begin()[i]; + const Mem_fn &pm= values[i]; if (pm) { if (pm(this).load_from(&file)) @@ -294,19 +334,9 @@ struct Info_file return false; } - /** - Flush the MySQL line-based section to the @ref file - @param value_list List of wrapped member pointers to values. - @param total_line_count - The number of lines to describe the file as on the first line of the file. - If this is larger than `value_list.size()`, suffix the file with empty - lines until the line count (including the line count line) is this many. - This reservation provides compatibility with MySQL, - who has added more old-style lines while MariaDB innovated. - */ - void save_to_file(Mem_fn::List value_list, size_t total_line_count) + void save_to_file(const Mem_fn *values, size_t size, size_t total_line_count) { - DBUG_ASSERT(total_line_count > value_list.size()); + DBUG_ASSERT(total_line_count > size); my_b_seek(&file, 0); /* If the new contents take less space than the previous file contents, @@ -316,8 +346,9 @@ struct Info_file */ Int_IO_CACHE::to_chars(&file, total_line_count); my_b_write_byte(&file, '\n'); - for (const Mem_fn &pm: value_list) + for (size_t i= 0; i < size; ++i) { + const Mem_fn &pm= values[i]; if (pm) pm(this).save_to(&file); my_b_write_byte(&file, '\n'); @@ -327,7 +358,7 @@ struct Info_file (1 for the line count line + line count) inclusive -> max line inclusive = line count exclusive <- max line inclusive */ - for (; total_line_count > value_list.size(); --total_line_count) + for (; total_line_count > size; --total_line_count) my_b_write_byte(&file, '\n'); } diff --git a/sql/rpl_master_info_file.h b/sql/rpl_master_info_file.h index e84ff7badb64e..dfa7d5dcffd0e 100644 --- a/sql/rpl_master_info_file.h +++ b/sql/rpl_master_info_file.h @@ -244,7 +244,7 @@ struct Master_info_file: Info_file bool load_from(IO_CACHE *file) override { - uint32_t count; + long count; size_t i; /// +1 for the terminating delimiter char buf[Int_IO_CACHE::BUF_SIZE + 1]; @@ -257,22 +257,18 @@ struct Master_info_file: Info_file if (c == /* End of Line */ '\n' || c == /* End of Count */ ' ') break; } - /* - * std::from_chars() fails if `count` will overflow in any way. - * exclusive end index of the string = size - */ - std::from_chars_result result= std::from_chars(buf, &buf[i], count); + char *end= str2int(buf, 10, 1, INT32_MAX, &count); // Reserve enough elements ahead of time. - if (result.ec != Int_IO_CACHE::ERRC_OK || allocate_dynamic(&array, count)) + if (!end || allocate_dynamic(&array, count)) return true; while (count--) { - uint32_t value; + long value; /* Check that the previous number ended with a ` `, not `\n` or anything else. */ - if (*(result.ptr) != ' ') + if (*end != ' ') return true; for (i= 0; i < sizeof(buf); ++i) { @@ -287,8 +283,8 @@ struct Master_info_file: Info_file if (c == /* End of Count */ ' ' || c == /* End of Line */ '\n') break; } - result= std::from_chars(buf, &buf[i], value); - if (result.ec != Int_IO_CACHE::ERRC_OK) + end= str2int(buf, 10, 1, INT32_MAX, &value); + if (!end) return true; ulong id= value; bool oom= insert_dynamic(&array, (uchar *)&id); @@ -301,7 +297,7 @@ struct Master_info_file: Info_file return true; } // Check that the last number ended with a `\n`, not ` ` or anything else. - if (*(result.ptr) != '\n') + if (*end != '\n') return true; sort_dynamic(&array, change_master_id_cmp); // to be safe return false; @@ -417,8 +413,8 @@ struct Master_info_file: Info_file } void save_to(IO_CACHE *file) override { - my_b_write_byte(file, - '0' + static_cast(operator enum_master_use_gtid())); + my_b_write_byte(file, static_cast( + '0' + static_cast(operator enum_master_use_gtid()))); } } /// Using_Gtid @@ -485,9 +481,9 @@ struct Master_info_file: Info_file overprecise= decimal.frac > 3; // decomposed from my_decimal2int() to reduce a bit of computations auto rounded= my_decimal(), product= my_decimal(); - int unexpected_error= - decimal_round(&decimal, &rounded, 3, HALF_UP) | - decimal_mul(&rounded, &THOUSAND, &product) | + bool unexpected_error= + decimal_round(&decimal, &rounded, 3, HALF_UP) || + decimal_mul(&rounded, &THOUSAND, &product) || decimal2ulonglong(&product, &decimal_out); DBUG_ASSERT(!unexpected_error); result= static_cast(decimal_out); @@ -532,18 +528,8 @@ struct Master_info_file: Info_file full advantage of the non-negative `DECIMAL(10,3)` format. */ void save_to(IO_CACHE *file) override { - char buf[Int_IO_CACHE::BUF_SIZE - /* decimal part */ 3]; auto[integer_part, decimal_part]= div(operator uint32_t(), 1000); - std::to_chars_result result= - std::to_chars(buf, &buf[sizeof(buf)], integer_part); - DBUG_ASSERT(result.ec == Int_IO_CACHE::ERRC_OK); - my_b_write(file, reinterpret_cast(buf), result.ptr - buf); - my_b_write_byte(file, '.'); - result= std::to_chars(buf, &buf[sizeof(buf)], decimal_part); - DBUG_ASSERT(result.ec == Int_IO_CACHE::ERRC_OK); - for (ptrdiff_t digits= result.ptr - buf; digits < 3; ++digits) - my_b_write_byte(file, '0'); - my_b_write(file, reinterpret_cast(buf), result.ptr - buf); + my_b_printf(file, "%u.%03u", integer_part, decimal_part); } } /// `Slave_heartbeat_period` of SHOW ALL SLAVES STATUS @@ -552,7 +538,7 @@ struct Master_info_file: Info_file /// }@ - inline static Mem_fn::List VALUE_LIST= { + inline static const Mem_fn VALUE_LIST[] { &Master_info_file::master_log_file, &Master_info_file::master_log_pos, &Master_info_file::master_host, @@ -587,7 +573,8 @@ struct Master_info_file: Info_file keys should match the corresponding old property name in @ref Master_info. */ // C++ default allocator to match that `mysql_execute_command()` uses `new` - inline static const std::unordered_map VALUE_MAP= { + inline static + const std::unordered_map VALUE_MAP= { /* These are here to annotate whether they are `DEFAULT`. They are repeated from @ref VALUE_LIST to enable bidirectional diff --git a/sql/rpl_relay_log_info_file.h b/sql/rpl_relay_log_info_file.h index fd2f530cc608f..3dbfb2028d14a 100644 --- a/sql/rpl_relay_log_info_file.h +++ b/sql/rpl_relay_log_info_file.h @@ -36,7 +36,7 @@ struct Relay_log_info_file: Info_file Int_value sql_delay; /// }@ - inline static const Mem_fn::List VALUE_LIST= { + inline static const Mem_fn VALUE_LIST[] { &Relay_log_info_file::relay_log_file, &Relay_log_info_file::relay_log_pos, &Relay_log_info_file::read_master_log_file, @@ -50,8 +50,7 @@ struct Relay_log_info_file: Info_file } void save_to_file() override { - return Info_file::save_to_file(VALUE_LIST, - VALUE_LIST.size() + /* line count line */ 1); + return Info_file::save_to_file(VALUE_LIST); } };