提问人:Danny Cohen 提问时间:10/10/2023 最后编辑:Danny Cohen 更新时间:10/10/2023 访问量:113
JsonCpp 不防止 uint64 溢出,并且有奇怪的行为
JsonCpp do not protect from uint64 overflow and have weird behavior
问:
我正在玩 JsonCpp,并在到达 uint64 范围结束时注意到奇怪的行为。
范围内的数字工作正常。
高于范围但介于 0 之间的数字返回 0。
上面的数字会引发异常。[2**64,2**64+2**11]
2**64+2**11
这是意料之中的吗?我可以配置 JsonCpp 为我执行这些验证吗?
提前致谢!
下面是演示该问题的代码片段。
#include <iostream>
#include <jsoncpp/json/json.h>
#include <jsoncpp/json/reader.h>
#include <jsoncpp/json/value.h>
int main() {
//std::string uint64Str = "1234"; // any number in range [0, 2**64-1] works correctly (prints uint64Str)
//std::string uint64Str = "18446744073709551615"; // any number in range [0, 2**64-1] works correctly, including 2**64-1 (prints uint64Str)
//std::string uint64Str = "18446744073709551616"; //max uint64 + 1 print 0
//std::string uint64Str = "18446744073709551617"; //max uint64 + 2 print 0
// ...
//std::string uint64Str = "18446744073709553663"; //max uint64 + 2048 print 0
//std::string uint64Str = "18446744073709553664"; //max uint64 + 2049 print 0
std::string uint64Str = "18446744073709553665"; //max uint64 + 2050 (or more) throws instance of 'Json::LogicError' what(): double out of UInt64 range
Json::Value root;
Json::Reader reader;
bool parsingResult = reader.parse("{\"key\":"+uint64Str+"}", root);
std::cout<<root["key"].asUInt64()<<std::endl;
return 0;
}
答:
1赞
Ted Lyngmo
10/10/2023
#1
问题是非常大的数字被存储为实数(它不适合 )。你也需要把它当作一个真实的来阅读:uint64_t
std::cout << root["key"].asDouble() << std::endl;
// ^^^^^^^^^^
实际的异常来自内部,其中检查 a 是否可以适合请求的范围:switch
Value::UInt64 Value::asUInt64() const
realValue
case realValue:
JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
"double out of UInt64 range");
return UInt64( value_.real_ );
评论
0赞
Severin Pappadeux
10/10/2023
这是Javascript约定,但我认为它不适用于C++
0赞
Ted Lyngmo
10/10/2023
@SeverinPappadeux 什么是 javascript 约定?我对javascript几乎一无所知,所以我说不出来。
0赞
Severin Pappadeux
10/10/2023
我相信 Javascript 没有整数。整数是使用 53 位尾数的双精度
1赞
Danny Cohen
10/10/2023
@TedLyngmo我想在通过这个范围时出现例外。我想我会使用你以前的建议。谢谢!
2赞
n. m. could be an AI
10/10/2023
@DannyCohen我认为您应该向 JsonCPP 提交错误报告。为这些数字返回 0 是完全不合理的。
1赞
Danny Cohen
10/10/2023
#2
感谢 TedLyngmo 的指导。
在他的回答和额外的研究之后。 大于 max 的数字存储在实数(双精度)中。
然后运行以下代码:
case realValue:
JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
"double out of UInt64 range");
return UInt64( value_.real_ );
我希望该函数在超出范围的数字上失败。
但是,它的实现是:InRange
template <typename T, typename U>
static inline bool InRange(double d, T min, U max) {
// The casts can lose precision, but we are looking only for
// an approximate range. Might fail on edge cases though. ~cdunn
return d >= static_cast<double>(min) && d <= static_cast<double>(max);
}
它将 max 转换为 double,并将 max 向上舍入 (to ),如果 d 介于 is 之间,则也舍入 (to )。
所以它认为它在范围内。
将 d 转换为 uint64_t 时,您将得到 max+1,即 0。2**64+2
max<uint64> to 2**62+2**11
2**64+2
该功能确实提到,这并不确切......然而,我认为这是库中的一个错误。InRange
我将使用的解决方案是:
uint64_t getUint64(const Json::Value& shouldBeUInt64) {
uint64_t res = shouldBeUInt64.asUInt64();
if (res == 0 and shouldBeUInt64.asDouble()>0){
throw Json::LogicError("double out of UInt64 range");
}
return res;
}
更新(2023年11月25日): 请参阅此处的建议修复:https://github.com/open-source-parsers/jsoncpp/pull/1519
评论
parsingResult
uint64_t
asUInt64()