From d908a2657e67f436f8158436d69c496aab94bdaa Mon Sep 17 00:00:00 2001 From: ipknHama Date: Thu, 17 Apr 2014 22:32:21 +0900 Subject: preparing JSON library for flask++; complete writing feature --- Makefile | 7 +- json.h | 474 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ unittest.cpp | 14 ++ 3 files changed, 491 insertions(+), 4 deletions(-) create mode 100644 json.h diff --git a/Makefile b/Makefile index ad6dcf2..15f7bda 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ all: covtest example -example: example.cpp flask.h http_server.h http_connection.h parser.h http_response.h routing.h common.h utility.h +example: example.cpp flask.h http_server.h http_connection.h parser.h http_response.h routing.h common.h utility.h json.h g++ -Wall -g -O3 -std=c++11 -o example example.cpp http-parser/http_parser.c -pthread -lboost_system -lboost_thread -I http-parser/ test: covtest @@ -14,9 +14,8 @@ unittest: unittest.cpp routing.h g++ -Wall -g -std=c++11 -o unittest unittest.cpp ./unittest -covtest: unittest.cpp routing.h utility.h flask.h http_server.h http_connection.h parser.h http_response.h common.h - g++ -O2 -Wall -g -std=c++11 -fno-default-inline -fno-inline-small-functions --coverage -o covtest unittest.cpp http-parser/http_parser.c -pthread -lboost_system -lboost_thread -I http-parser/ - #clang++ -O2 -Wall -g -std=c++11 -o covtest unittest.cpp http-parser/http_parser.c -pthread -lboost_system -lboost_thread -I http-parser/ +covtest: unittest.cpp routing.h utility.h flask.h http_server.h http_connection.h parser.h http_response.h common.h json.h + g++ -O2 -Wall -g -std=c++11 -o covtest unittest.cpp http-parser/http_parser.c -pthread -lboost_system -lboost_thread -I http-parser/ ./covtest gcov -r unittest.cpp diff --git a/json.h b/json.h new file mode 100644 index 0000000..e060a42 --- /dev/null +++ b/json.h @@ -0,0 +1,474 @@ +#pragma once +#include + +#include +#include + +namespace flask +{ + namespace json + { + enum class type : intptr_t + { + Null, + False, + True, + Number, + String, + List, + Object, + }; + + const char* json_type_str = + "\0\0\0\0\0\0\0\0" + "\1\0\0\0\0\0\0\0" + "\2\0\0\0\0\0\0\0" + "\3\0\0\0\0\0\0\0" + "\4\0\0\0\0\0\0\0" + "\5\0\0\0\0\0\0\0" + "\6\0\0\0\0\0\0\0"; + + struct r_string + { + operator std::string () + { + return std::string(s_, e_); + } + + char* s_; + char* e_; + }; + + struct rvalue + { + rvalue() {} + rvalue(type t) + : start_{json_type_str + 8*(int)t}, + end_{json_type_str+8*(int)t+8} + {} + ~rvalue() + { + deallocate(); + } + void deallocate() + { + if (mem_) + { + delete[] mem_; + mem_ = nullptr; + } + } + type t() const + { + return *reinterpret_cast(start_); + } + const void* data() const + { + return reinterpret_cast(start_ + sizeof(intptr_t)); + } + + int i() const + { + if (t() != type::Number) + throw std::runtime_error("value is not number"); + return static_cast(*(double*)data()); + } + + double d() const + { + if (t() != type::Number) + throw std::runtime_error("value is not number"); + return *(double*)data(); + } + + r_string s() const + { + if (t() != type::Number) + throw std::runtime_error("value is not number"); + return {(char*)data(),(char*)data() + sizeof(char*)}; + } + + const char* start_{}; + const char* end_{}; + char* mem_{}; + }; + + //inline rvalue decode(const std::string& s) + //{ + //} + + /*inline boost::optional decode(const char* data, size_t size, bool partial = false) + { + static char* escaped = "\"\\\/\b\f\n\r\t" + const char* e = data + size; + auto ws_skip = [&] + { + while(data != e) + { + while(data != e && *data == ' ') ++data; + while(data != e && *data == '\t') ++data; + while(data != e && *data == '\r') ++data; + while(data != e && *data == '\n') ++data; + } + }; + auto decode_string = [&]->boost::optional + { + ws_skip(); + if (data == e || *data != '"') + return {}; + while(data != e) + { + if (*data == '"') + { + } + else if (*data == '\\') + { + // TODO + //if (data+1 == e) + //return {}; + //data += 2; + } + else + { + data ++; + } + } + }; + auto decode_list = [&]->boost::optional + { + // TODO + }; + auto decode_number = [&]->boost::optional + { + // TODO + }; + auto decode_value = [&]->boost::optional + { + if (data == e) + return {}; + switch(*data) + { + case '"': + emit_str_begin(); + case '{': + case 't': + if (e-data >= 4 && + data[1] == 'r' && + data[2] == 'u' && + data[3] == 'e') + return rvalue(type::True); + else + return {}; + case 'f': + case 'n': + } + // TODO + return {}; + }; + auto decode_object =[&]->boost::optional + { + ws_skip(); + if (data == e || *data != '{') + return {}; + + ws_skip(); + if (data == e) + return {}; + if (*data == '}') + return rvalue(type::Object); + while(1) + { + auto t = decode_string(); + ws_skip(); + if (data == e) + return {}; + if (data != ':') + return {}; + auto v = decode_value(); + ws_skip(); + if (data == e) + return {}; + if (data == '}') + break; + if (data != ',') + return {}; + } + // TODO build up object + throw std::runtime_error("Not implemented"); + return rvalue(type::Object); + }; + + return decode_object(); + + + } + } + } + }*/ + + class wvalue + { + public: + type t{type::Null}; + private: + double d; + std::string s; + std::unique_ptr> l; + std::unique_ptr> o; + public: + + wvalue() {} + + wvalue(wvalue&& r) + : + t(r.t), + d(r.d), + s{std::move(r.s)}, + l{std::move(r.l)}, + o{std::move(r.o)} + { + } + + void clear() + { + t = type::Null; + l.reset(); + o.reset(); + } + + void reset() + { + t = type::Null; + l.reset(); + o.reset(); + } + + wvalue& operator = (std::nullptr_t) + { + reset(); + return *this; + } + wvalue& operator = (bool value) + { + reset(); + if (value) + t = type::True; + else + t = type::False; + return *this; + } + + wvalue& operator = (double value) + { + reset(); + t = type::Number; + d = value; + return *this; + } + + wvalue& operator = (uint16_t value) + { + reset(); + t = type::Number; + d = (double)value; + return *this; + } + + wvalue& operator = (int16_t value) + { + reset(); + t = type::Number; + d = (double)value; + return *this; + } + + wvalue& operator = (uint32_t value) + { + reset(); + t = type::Number; + d = (double)value; + return *this; + } + + wvalue& operator = (int32_t value) + { + reset(); + t = type::Number; + d = (double)value; + return *this; + } + + wvalue& operator = (uint64_t value) + { + reset(); + t = type::Number; + d = (double)value; + return *this; + } + + wvalue& operator = (int64_t value) + { + reset(); + t = type::Number; + d = (double)value; + return *this; + } + + wvalue& operator=(const char* str) + { + reset(); + t = type::String; + s = str; + return *this; + } + + wvalue& operator=(const std::string& str) + { + reset(); + t = type::String; + s = str; + return *this; + } + + wvalue& operator[](unsigned index) + { + if (t != type::List) + reset(); + t = type::List; + if (!l) + l = std::move(std::unique_ptr>(new std::vector{})); + l->resize(index+1); + return (*l)[index]; + } + + wvalue& operator[](const std::string& str) + { + if (t != type::Object) + reset(); + t = type::Object; + if (!o) + o = std::move( + std::unique_ptr< + std::unordered_map + >( + new std::unordered_map{})); + return (*o)[str]; + } + + size_t estimate_length() const + { + switch(t) + { + case type::Null: return 4; + case type::False: return 5; + case type::True: return 4; + case type::Number: return 30; + case type::String: return 2+s.size()+s.size()/2; + case type::List: + { + size_t sum{}; + if (l) + { + for(auto& x:*l) + { + sum += 1; + sum += x.estimate_length(); + } + } + return sum+2; + } + case type::Object: + { + size_t sum{}; + if (o) + { + for(auto& kv:*o) + { + sum += 2; + sum += 2+kv.first.size()+kv.first.size()/2; + sum += kv.second.estimate_length(); + } + } + return sum+2; + } + } + return 1; + } + + + friend void encode_internal(const wvalue& v, std::string& out); + friend std::string encode(const wvalue& v); + }; + + void encode_string(const std::string& str, std::string& out) + { + // TODO escaping + out.push_back('"'); + out += str; + out.push_back('"'); + } + void encode_internal(const wvalue& v, std::string& out) + { + switch(v.t) + { + case type::Null: out += "null"; break; + case type::False: out += "false"; break; + case type::True: out += "true"; break; + case type::Number: out += boost::lexical_cast(v.d); break; + case type::String: encode_string(v.s, out); break; + case type::List: + { + out.push_back('['); + if (v.l) + { + bool first = true; + for(auto& x:*v.l) + { + if (!first) + { + out.push_back(','); + } + first = false; + encode_internal(x, out); + } + } + out.push_back(']'); + } + break; + case type::Object: + { + out.push_back('{'); + if (v.o) + { + bool first = true; + for(auto& kv:*v.o) + { + if (!first) + { + out.push_back(','); + } + first = false; + encode_string(kv.first, out); + out.push_back(':'); + encode_internal(kv.second, out); + } + } + out.push_back('}'); + } + break; + } + } + + std::string encode(const wvalue& v) + { + std::string ret; + ret.reserve(v.estimate_length()); + encode_internal(v, ret); + return ret; + } + + //std::vector encode_ref(wvalue& v) + //{ + //} + } +} diff --git a/unittest.cpp b/unittest.cpp index c0a45b7..48798d0 100644 --- a/unittest.cpp +++ b/unittest.cpp @@ -3,6 +3,7 @@ #include "routing.h" #include "utility.h" #include "flask.h" +#include "json.h" using namespace std; using namespace flask; @@ -235,6 +236,19 @@ TEST(multi_server) server2.stop(); } +TEST(json_write) +{ + json::wvalue x; + x["message"] = "hello world"; + ASSERT_EQUAL(R"({"message":"hello world"})", json::encode(x)); + + json::wvalue y; + y["scores"][0] = 1; + y["scores"][1] = "king"; + y["scores"][2] = 3.5; + ASSERT_EQUAL(R"({"scores":[1,"king",3.5]})", json::encode(y)); +} + int testmain() { bool failed = false; -- cgit v1.2.3-54-g00ecf