diff options
-rw-r--r-- | include/crow/json.h | 126 | ||||
-rw-r--r-- | include/crow/websocket.h | 9 | ||||
-rw-r--r-- | tests/unittest.cpp | 33 |
3 files changed, 143 insertions, 25 deletions
diff --git a/include/crow/json.h b/include/crow/json.h index 1aa303e..85344fd 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -96,6 +96,13 @@ namespace crow } } + enum class num_type : char { + Signed_integer, + Unsigned_integer, + Floating_point, + Null + }; + class rvalue; rvalue load(const char* data, size_t size); @@ -217,13 +224,16 @@ namespace crow : start_{s}, end_{e}, t_{t} - {} + { + determine_num_type(); + } rvalue(const rvalue& r) : start_(r.start_), end_(r.end_), key_(r.key_), t_(r.t_), + nt_(r.nt_), option_(r.option_) { copy_l(r); @@ -241,6 +251,7 @@ namespace crow key_ = r.key_; copy_l(r); t_ = r.t_; + nt_ = r.nt_; option_ = r.option_; return *this; } @@ -253,6 +264,7 @@ namespace crow lsize_ = r.lsize_; lremain_ = r.lremain_; t_ = r.t_; + nt_ = r.nt_; option_ = r.option_; return *this; } @@ -288,6 +300,17 @@ namespace crow 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 { #ifndef CROW_JSON_NO_ERROR_CHECK @@ -592,6 +615,28 @@ namespace crow 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* end_; detail::r_string key_; @@ -599,6 +644,7 @@ namespace crow uint32_t lsize_; uint16_t lremain_; type t_; + num_type nt_{num_type::Null}; mutable uint8_t option_{0}; friend rvalue load_nocopy_internal(char* data, size_t size); @@ -611,7 +657,17 @@ namespace crow case type::Null: os << "null"; break; case type::False: os << "false"; break; case type::True: os << "true"; break; - case type::Number: os << r.d(); break; + case type::Number: + { + switch (r.nt()) + { + case num_type::Floating_point: os << r.d(); break; + case num_type::Signed_integer: os << r.i(); break; + case num_type::Unsigned_integer: os << r.u(); break; + case num_type::Null: throw std::runtime_error("Number with num_type Null"); + } + } + break; case type::String: os << '"' << r.s() << '"'; break; case type::List: { @@ -1093,7 +1149,12 @@ namespace crow type t() const { return t_; } private: 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::unique_ptr<std::vector<wvalue>> l; std::unique_ptr<std::unordered_map<std::string, wvalue>> o; @@ -1111,7 +1172,13 @@ namespace crow case type::True: return; 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; case type::String: s = r.s(); @@ -1141,7 +1208,7 @@ namespace crow wvalue& operator = (wvalue&& r) { t_ = r.t_; - d = r.d; + num = r.num; s = std::move(r.s); l = std::move(r.l); o = std::move(r.o); @@ -1150,9 +1217,7 @@ namespace crow void clear() { - t_ = type::Null; - l.reset(); - o.reset(); + reset(); } void reset() @@ -1181,7 +1246,8 @@ namespace crow { reset(); t_ = type::Number; - d = value; + num.d = value; + nt = num_type::Floating_point; return *this; } @@ -1189,7 +1255,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.ui = value; + nt = num_type::Unsigned_integer; return *this; } @@ -1197,7 +1264,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.si = value; + nt = num_type::Signed_integer; return *this; } @@ -1205,7 +1273,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.si = value; + nt = num_type::Signed_integer; return *this; } @@ -1213,7 +1282,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.si = value; + nt = num_type::Signed_integer; return *this; } @@ -1221,7 +1291,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.si = value; + nt = num_type::Signed_integer; return *this; } @@ -1229,7 +1300,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.ui = value; + nt = num_type::Unsigned_integer; return *this; } @@ -1237,7 +1309,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.ui = value; + nt = num_type::Unsigned_integer; return *this; } @@ -1245,7 +1318,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.ui = value; + nt = num_type::Unsigned_integer; return *this; } @@ -1407,11 +1481,23 @@ namespace crow { char outbuf[128]; #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 - sprintf(outbuf, "%g", v.d); +#define MSC_COMPATIBLE_SPRINTF(BUFFER_PTR, FORMAT_PTR, VALUE) sprintf((BUFFER_PTR), (FORMAT_PTR), (VALUE)) #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; } break; diff --git a/include/crow/websocket.h b/include/crow/websocket.h index ee0885f..c2dc3f8 100644 --- a/include/crow/websocket.h +++ b/include/crow/websocket.h @@ -152,7 +152,7 @@ namespace crow else { buf[1] += 127; - *(uint64_t*)(buf+2) = ((1==htonl(1)) ? (uint64_t)size : ((uint64_t)htonl((size) & 0xFFFFFFFF) << 32) | htonl((size) >> 32)); + *reinterpret_cast<uint64_t*>(buf+2) = ((1==htonl(1)) ? static_cast<uint64_t>(size) : (static_cast<uint64_t>(htonl((size) & 0xFFFFFFFF)) << 32) | htonl(static_cast<uint64_t>(size) >> 32)); return {buf, buf+10}; } } @@ -191,7 +191,7 @@ namespace crow { is_reading = false; - mini_header_ = htons(mini_header_); + mini_header_ = ntohs(mini_header_); #ifdef CROW_ENABLE_DEBUG if (!ec && bytes_transferred != 2) @@ -231,9 +231,9 @@ namespace crow case WebSocketReadState::Len16: { remaining_length_ = 0; - uint16_t remaining_length16_ = 0; + remaining_length16_ = 0; boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length16_, 2), - [this,&remaining_length16_](const boost::system::error_code& ec, std::size_t + [this](const boost::system::error_code& ec, std::size_t #ifdef CROW_ENABLE_DEBUG bytes_transferred #endif @@ -502,6 +502,7 @@ namespace crow std::string message_; std::string fragment_; WebSocketReadState state_{WebSocketReadState::MiniHeader}; + uint16_t remaining_length16_{0}; uint64_t remaining_length_{0}; bool close_connection_{false}; bool is_reading{false}; diff --git a/tests/unittest.cpp b/tests/unittest.cpp index bffdd60..6fe1bbd 100644 --- a/tests/unittest.cpp +++ b/tests/unittest.cpp @@ -501,7 +501,7 @@ TEST(json_read) //ASSERT_THROW(3 == x["message"]); 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); ASSERT_EQUAL(3, y["int"]); ASSERT_EQUAL(3.0, y["int"]); @@ -519,6 +519,7 @@ TEST(json_read) ASSERT_EQUAL(2, q); q = y["ints"][2].i(); ASSERT_EQUAL(3, q); + ASSERT_EQUAL(1234567890, y["bigint"]); std::string s2 = R"({"bools":[true, false], "doubles":[1.2, -3.4]})"; auto z = json::load(s2); @@ -532,6 +533,10 @@ TEST(json_read) std::string s3 = R"({"uint64": 18446744073709551615})"; auto z1 = json::load(s3); ASSERT_EQUAL(18446744073709551615ull, z1["uint64"].u()); + + std::ostringstream os; + os << z1["uint64"]; + ASSERT_EQUAL("18446744073709551615", os.str()); } TEST(json_read_real) @@ -596,6 +601,8 @@ TEST(json_write) ASSERT_TRUE(R"({"message":{"x":3,"y":5}})" == json::dump(x) || R"({"message":{"y":5,"x":3}})" == json::dump(x)); x["message"] = 5.5; ASSERT_EQUAL(R"({"message":5.5})", json::dump(x)); + x["message"] = 1234567890; + ASSERT_EQUAL(R"({"message":1234567890})", json::dump(x)); json::wvalue y; y["scores"][0] = 1; @@ -616,6 +623,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) { auto t = crow::mustache::compile(R"---(attack of {{name}})---"); |