mirror of
https://github.com/jbeder/yaml-cpp.git
synced 2025-09-09 04:41:16 +00:00
patch: added more defensive programming techniques
This commit is contained in:

committed by
Jesse Beder

parent
06ffaf3104
commit
ebfbf27115
@@ -15,10 +15,10 @@ namespace fp_formatting {
|
|||||||
/**
|
/**
|
||||||
* Converts a integer into its ASCII digits.
|
* Converts a integer into its ASCII digits.
|
||||||
*
|
*
|
||||||
* @param begin/end - a buffer, must be at least 20bytes long
|
* @param begin/end - a buffer, must be at least 20bytes long.
|
||||||
* @param value - input value
|
* @param value - input value.
|
||||||
* @param width - minimum number of digits, fill with '0' to the left. Must be equal or smaller than the buffer size.
|
* @param width - minimum number of digits, fill with '0' to the left. Must be equal or smaller than the buffer size.
|
||||||
* @return - number of digits filled into the buffer.
|
* @return - number of digits filled into the buffer (or -1 if preconditions are not meet)
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* std::array<char, 20> buffer;
|
* std::array<char, 20> buffer;
|
||||||
@@ -29,10 +29,26 @@ namespace fp_formatting {
|
|||||||
* assert(buffer[2] == '3');
|
* assert(buffer[2] == '3');
|
||||||
*/
|
*/
|
||||||
int ConvertToChars(char* begin, char* end, size_t value, int width=1) {
|
int ConvertToChars(char* begin, char* end, size_t value, int width=1) {
|
||||||
|
// precondition of this function (will trigger in debug build)
|
||||||
assert(width >= 1);
|
assert(width >= 1);
|
||||||
assert(end >= begin); // end must be after begin
|
assert(end >= begin); // end must be after begin
|
||||||
assert(end-begin >= width); // Buffer must be large enough
|
assert(end-begin >= width); // Buffer must be large enough
|
||||||
assert(end-begin >= 20); // 2^64 has 20digits, so at least 20 digits must be available
|
assert(end-begin >= 20); // 2^64 has 20digits, so at least 20 digits must be available
|
||||||
|
|
||||||
|
// defensive programming, abort if precondition are not met (will trigger in release build)
|
||||||
|
if (width < 1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (end < begin) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (end-begin < width) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (end-begin < 20) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// count number of digits, and fill digits array accordingly
|
// count number of digits, and fill digits array accordingly
|
||||||
int digits_ct{};
|
int digits_ct{};
|
||||||
@@ -59,8 +75,7 @@ int ConvertToChars(char* begin, char* end, size_t value, int width=1) {
|
|||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::string FpToString(T v, int precision = 0) {
|
std::string FpToString(T v, int precision = 0) {
|
||||||
// assert(precision > 0);
|
// hard coded constant, at which exponent should switch to a scientific notation
|
||||||
// hardcoded constant, at which exponent should switch to a scientific notation
|
|
||||||
int const lowerExponentThreshold = -5;
|
int const lowerExponentThreshold = -5;
|
||||||
int const upperExponentThreshold = (precision==0)?6:precision;
|
int const upperExponentThreshold = (precision==0)?6:precision;
|
||||||
if (precision == 0) {
|
if (precision == 0) {
|
||||||
@@ -70,6 +85,7 @@ std::string FpToString(T v, int precision = 0) {
|
|||||||
// dragonbox/to_decimal does not handle value 0, inf, NaN
|
// dragonbox/to_decimal does not handle value 0, inf, NaN
|
||||||
if (v == 0 || std::isinf(v) || std::isnan(v)) {
|
if (v == 0 || std::isinf(v) || std::isnan(v)) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
ss.imbue(std::locale("C"));
|
||||||
ss << v;
|
ss << v;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
@@ -79,6 +95,14 @@ std::string FpToString(T v, int precision = 0) {
|
|||||||
auto digits = std::array<char, 20>{}; // max digits of size_t is 20.
|
auto digits = std::array<char, 20>{}; // max digits of size_t is 20.
|
||||||
auto digits_ct = ConvertToChars(digits.data(), digits.data() + digits.size(), r.significand);
|
auto digits_ct = ConvertToChars(digits.data(), digits.data() + digits.size(), r.significand);
|
||||||
|
|
||||||
|
// defensive programming, ConvertToChars arguments are invalid
|
||||||
|
if (digits_ct == -1) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(std::locale("C"));
|
||||||
|
ss << v;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
// check if requested precision is lower than
|
// check if requested precision is lower than
|
||||||
// required digits for exact representation
|
// required digits for exact representation
|
||||||
if (digits_ct > precision) {
|
if (digits_ct > precision) {
|
||||||
@@ -133,6 +157,15 @@ std::string FpToString(T v, int precision = 0) {
|
|||||||
*(output_ptr++) = (exponent>=0)?'+':'-';
|
*(output_ptr++) = (exponent>=0)?'+':'-';
|
||||||
auto exp_digits = std::array<char, 20>{};
|
auto exp_digits = std::array<char, 20>{};
|
||||||
auto exp_digits_ct = ConvertToChars(exp_digits.data(), exp_digits.data() + exp_digits.size(), std::abs(exponent), /*.precision=*/ 2);
|
auto exp_digits_ct = ConvertToChars(exp_digits.data(), exp_digits.data() + exp_digits.size(), std::abs(exponent), /*.precision=*/ 2);
|
||||||
|
|
||||||
|
// defensive programming, ConvertToChars arguments are invalid
|
||||||
|
if (exp_digits_ct == -1) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(std::locale("C"));
|
||||||
|
ss << v;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
for (int i{0}; i < exp_digits_ct; ++i) {
|
for (int i{0}; i < exp_digits_ct; ++i) {
|
||||||
*(output_ptr++) = exp_digits[i];
|
*(output_ptr++) = exp_digits[i];
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user