Handle big integers in json::wvalue

Handled by adding an enum num_type in both rvalue and wvalue (to separate
between signed/unsigned ints, and floating point values) and a union for
the number value in wvalue.
This commit is contained in:
Erik Åldstedt Sund 2017-10-22 13:31:17 +02:00
parent 7f3f72441c
commit 4fe7dd171a
2 changed files with 123 additions and 20 deletions

View File

@ -96,6 +96,13 @@ namespace crow
} }
} }
enum class num_type : char {
Signed_integer,
Unsigned_integer,
Floating_point,
Null
};
class rvalue; class rvalue;
rvalue load(const char* data, size_t size); rvalue load(const char* data, size_t size);
@ -216,13 +223,16 @@ namespace crow
: start_{s}, : start_{s},
end_{e}, end_{e},
t_{t} t_{t}
{} {
determine_num_type();
}
rvalue(const rvalue& r) rvalue(const rvalue& r)
: start_(r.start_), : start_(r.start_),
end_(r.end_), end_(r.end_),
key_(r.key_), key_(r.key_),
t_(r.t_), t_(r.t_),
nt_(r.nt_),
option_(r.option_) option_(r.option_)
{ {
copy_l(r); copy_l(r);
@ -240,6 +250,7 @@ namespace crow
key_ = r.key_; key_ = r.key_;
copy_l(r); copy_l(r);
t_ = r.t_; t_ = r.t_;
nt_ = r.nt_;
option_ = r.option_; option_ = r.option_;
return *this; return *this;
} }
@ -252,6 +263,7 @@ namespace crow
lsize_ = r.lsize_; lsize_ = r.lsize_;
lremain_ = r.lremain_; lremain_ = r.lremain_;
t_ = r.t_; t_ = r.t_;
nt_ = r.nt_;
option_ = r.option_; option_ = r.option_;
return *this; return *this;
} }
@ -287,6 +299,17 @@ namespace crow
return t_; return t_;
} }
num_type nt() const
{
#ifndef CROW_JSON_NO_ERROR_CHECK
if (option_ & error_bit)
{
throw std::runtime_error("invalid json object");
}
#endif
return nt_;
}
int64_t i() const int64_t i() const
{ {
#ifndef CROW_JSON_NO_ERROR_CHECK #ifndef CROW_JSON_NO_ERROR_CHECK
@ -591,6 +614,28 @@ namespace crow
lremain_ --; lremain_ --;
} }
// determines num_type from the string
void determine_num_type()
{
if (t_ != type::Number)
{
nt_ = num_type::Null;
return;
}
const std::size_t len = end_ - start_;
const bool has_minus = std::memchr(start_, '-', len) != nullptr;
const bool has_e = std::memchr(start_, 'e', len) != nullptr
|| std::memchr(start_, 'E', len) != nullptr;
const bool has_dec_sep = std::memchr(start_, '.', len) != nullptr;
if (has_dec_sep || has_e)
nt_ = num_type::Floating_point;
else if (has_minus)
nt_ = num_type::Signed_integer;
else
nt_ = num_type::Unsigned_integer;
}
mutable char* start_; mutable char* start_;
mutable char* end_; mutable char* end_;
detail::r_string key_; detail::r_string key_;
@ -598,6 +643,7 @@ namespace crow
uint32_t lsize_; uint32_t lsize_;
uint16_t lremain_; uint16_t lremain_;
type t_; type t_;
num_type nt_{num_type::Null};
mutable uint8_t option_{0}; mutable uint8_t option_{0};
friend rvalue load_nocopy_internal(char* data, size_t size); friend rvalue load_nocopy_internal(char* data, size_t size);
@ -1092,7 +1138,12 @@ namespace crow
type t() const { return t_; } type t() const { return t_; }
private: private:
type t_{type::Null}; type t_{type::Null};
double d {}; num_type nt{num_type::Null};
union {
double d;
int64_t si;
uint64_t ui {};
} num;
std::string s; std::string s;
std::unique_ptr<std::vector<wvalue>> l; std::unique_ptr<std::vector<wvalue>> l;
std::unique_ptr<std::unordered_map<std::string, wvalue>> o; std::unique_ptr<std::unordered_map<std::string, wvalue>> o;
@ -1110,7 +1161,13 @@ namespace crow
case type::True: case type::True:
return; return;
case type::Number: case type::Number:
d = r.d(); nt = r.nt();
if (nt == num_type::Floating_point)
num.d = r.d();
else if (nt == num_type::Signed_integer)
num.si = r.i();
else
num.ui = r.u();
return; return;
case type::String: case type::String:
s = r.s(); s = r.s();
@ -1140,7 +1197,7 @@ namespace crow
wvalue& operator = (wvalue&& r) wvalue& operator = (wvalue&& r)
{ {
t_ = r.t_; t_ = r.t_;
d = r.d; num = r.num;
s = std::move(r.s); s = std::move(r.s);
l = std::move(r.l); l = std::move(r.l);
o = std::move(r.o); o = std::move(r.o);
@ -1149,9 +1206,7 @@ namespace crow
void clear() void clear()
{ {
t_ = type::Null; reset();
l.reset();
o.reset();
} }
void reset() void reset()
@ -1180,7 +1235,8 @@ namespace crow
{ {
reset(); reset();
t_ = type::Number; t_ = type::Number;
d = value; num.d = value;
nt = num_type::Floating_point;
return *this; return *this;
} }
@ -1188,7 +1244,8 @@ namespace crow
{ {
reset(); reset();
t_ = type::Number; t_ = type::Number;
d = (double)value; num.ui = value;
nt = num_type::Unsigned_integer;
return *this; return *this;
} }
@ -1196,7 +1253,8 @@ namespace crow
{ {
reset(); reset();
t_ = type::Number; t_ = type::Number;
d = (double)value; num.si = value;
nt = num_type::Signed_integer;
return *this; return *this;
} }
@ -1204,7 +1262,8 @@ namespace crow
{ {
reset(); reset();
t_ = type::Number; t_ = type::Number;
d = (double)value; num.si = value;
nt = num_type::Signed_integer;
return *this; return *this;
} }
@ -1212,7 +1271,8 @@ namespace crow
{ {
reset(); reset();
t_ = type::Number; t_ = type::Number;
d = (double)value; num.si = value;
nt = num_type::Signed_integer;
return *this; return *this;
} }
@ -1220,7 +1280,8 @@ namespace crow
{ {
reset(); reset();
t_ = type::Number; t_ = type::Number;
d = (double)value; num.si = value;
nt = num_type::Signed_integer;
return *this; return *this;
} }
@ -1228,7 +1289,8 @@ namespace crow
{ {
reset(); reset();
t_ = type::Number; t_ = type::Number;
d = (double)value; num.ui = value;
nt = num_type::Unsigned_integer;
return *this; return *this;
} }
@ -1236,7 +1298,8 @@ namespace crow
{ {
reset(); reset();
t_ = type::Number; t_ = type::Number;
d = (double)value; num.ui = value;
nt = num_type::Unsigned_integer;
return *this; return *this;
} }
@ -1244,7 +1307,8 @@ namespace crow
{ {
reset(); reset();
t_ = type::Number; t_ = type::Number;
d = (double)value; num.ui = value;
nt = num_type::Unsigned_integer;
return *this; return *this;
} }
@ -1406,11 +1470,23 @@ namespace crow
{ {
char outbuf[128]; char outbuf[128];
#ifdef _MSC_VER #ifdef _MSC_VER
sprintf_s(outbuf, 128, "%g", v.d); #define MSC_COMPATIBLE_SPRINTF(BUFFER_PTR, FORMAT_PTR, VALUE) sprintf_s((BUFFER_PTR), 128, (FORMAT_PTR), (VALUE))
#else #else
sprintf(outbuf, "%g", v.d); #define MSC_COMPATIBLE_SPRINTF(BUFFER_PTR, FORMAT_PTR, VALUE) sprintf((BUFFER_PTR), (FORMAT_PTR), (VALUE))
#endif #endif
if (v.nt == num_type::Floating_point)
{
MSC_COMPATIBLE_SPRINTF(outbuf, "%g", v.num.d);
}
else if (v.nt == num_type::Signed_integer)
{
MSC_COMPATIBLE_SPRINTF(outbuf, "%lld", v.num.si);
}
else
{
MSC_COMPATIBLE_SPRINTF(outbuf, "%llu", v.num.ui);
}
#undef MSC_COMPATIBLE_SPRINTF
out += outbuf; out += outbuf;
} }
break; break;

View File

@ -501,7 +501,7 @@ TEST(json_read)
//ASSERT_THROW(3 == x["message"]); //ASSERT_THROW(3 == x["message"]);
ASSERT_EQUAL(12, x["message"].size()); ASSERT_EQUAL(12, x["message"].size());
std::string s = R"({"int":3, "ints" :[1,2,3,4,5] })"; std::string s = R"({"int":3, "ints" :[1,2,3,4,5], "bigint":1234567890 })";
auto y = json::load(s); auto y = json::load(s);
ASSERT_EQUAL(3, y["int"]); ASSERT_EQUAL(3, y["int"]);
ASSERT_EQUAL(3.0, y["int"]); ASSERT_EQUAL(3.0, y["int"]);
@ -519,6 +519,7 @@ TEST(json_read)
ASSERT_EQUAL(2, q); ASSERT_EQUAL(2, q);
q = y["ints"][2].i(); q = y["ints"][2].i();
ASSERT_EQUAL(3, q); ASSERT_EQUAL(3, q);
ASSERT_EQUAL(1234567890, y["bigint"]);
std::string s2 = R"({"bools":[true, false], "doubles":[1.2, -3.4]})"; std::string s2 = R"({"bools":[true, false], "doubles":[1.2, -3.4]})";
auto z = json::load(s2); auto z = json::load(s2);
@ -596,6 +597,8 @@ TEST(json_write)
ASSERT_TRUE(R"({"message":{"x":3,"y":5}})" == json::dump(x) || R"({"message":{"y":5,"x":3}})" == json::dump(x)); ASSERT_TRUE(R"({"message":{"x":3,"y":5}})" == json::dump(x) || R"({"message":{"y":5,"x":3}})" == json::dump(x));
x["message"] = 5.5; x["message"] = 5.5;
ASSERT_EQUAL(R"({"message":5.5})", json::dump(x)); ASSERT_EQUAL(R"({"message":5.5})", json::dump(x));
x["message"] = 1234567890;
ASSERT_EQUAL(R"({"message":1234567890})", json::dump(x));
json::wvalue y; json::wvalue y;
y["scores"][0] = 1; y["scores"][0] = 1;
@ -616,6 +619,30 @@ TEST(json_write)
} }
TEST(json_copy_r_to_w_to_r)
{
json::rvalue r = json::load(R"({"smallint":2,"bigint":2147483647,"fp":23.43,"fpsc":2.343e1,"str":"a string","trueval":true,"falseval":false,"nullval":null,"listval":[1,2,"foo","bar"],"obj":{"member":23,"other":"baz"}})");
json::wvalue w{r};
json::rvalue x = json::load(json::dump(w)); // why no copy-ctor wvalue -> rvalue?
ASSERT_EQUAL(2, x["smallint"]);
ASSERT_EQUAL(2147483647, x["bigint"]);
ASSERT_EQUAL(23.43, x["fp"]);
ASSERT_EQUAL(23.43, x["fpsc"]);
ASSERT_EQUAL("a string", x["str"]);
ASSERT_TRUE(true == x["trueval"].b());
ASSERT_TRUE(false == x["falseval"].b());
ASSERT_TRUE(json::type::Null == x["nullval"].t());
ASSERT_EQUAL(4u, x["listval"].size());
ASSERT_EQUAL(1, x["listval"][0]);
ASSERT_EQUAL(2, x["listval"][1]);
ASSERT_EQUAL("foo", x["listval"][2]);
ASSERT_EQUAL("bar", x["listval"][3]);
ASSERT_EQUAL(23, x["obj"]["member"]);
ASSERT_EQUAL("member", x["obj"]["member"].key());
ASSERT_EQUAL("baz", x["obj"]["other"]);
ASSERT_EQUAL("other", x["obj"]["other"].key());
}
TEST(template_basic) TEST(template_basic)
{ {
auto t = crow::mustache::compile(R"---(attack of {{name}})---"); auto t = crow::mustache::compile(R"---(attack of {{name}})---");