diff options
author | ipknHama <ipknhama@gmail.com> | 2014-04-19 07:12:56 +0900 |
---|---|---|
committer | ipknHama <ipknhama@gmail.com> | 2014-04-19 07:12:56 +0900 |
commit | de7a70de347c33ece5b25379c366b2c49418df08 (patch) | |
tree | 66ac3757b35d5dbafe3b4ec736c43a1f33e6ca0c /json.h | |
parent | 86433135f3428cfd3f0184f10411bc169e2ae9b9 (diff) | |
download | crow-de7a70de347c33ece5b25379c366b2c49418df08.tar.gz crow-de7a70de347c33ece5b25379c366b2c49418df08.zip |
add json reading feature; optimization
Diffstat (limited to 'json.h')
-rw-r--r-- | json.h | 1015 |
1 files changed, 891 insertions, 124 deletions
@@ -1,14 +1,47 @@ #pragma once -#include <iostream> + +//#define FLASKPP_JSON_NO_ERROR_CHECK #include <string> #include <unordered_map> +#include <iostream> +#include <algorithm> +#include <memory> +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/operators.hpp> + +#ifdef __GNUG__ +#define flask_json_likely(x) __builtin_expect(x, 1) +#define flask_json_unlikely(x) __builtin_expect(x, 0) +#else +#ifdef __clang__ +#define flask_json_likely(x) __builtin_expect(x, 1) +#define flask_json_unlikely(x) __builtin_expect(x, 0) +#else +#define flask_json_likely(x) x +#define flask_json_unlikely(x) x +#endif +#endif + namespace flask { namespace json { - enum class type : intptr_t + std::string escape(const std::string& str) + { + // TODO + return str; + } + std::string unescape(const std::string& str) + { + // TODO + return str; + } + + + enum class type : char { Null, False, @@ -19,192 +52,904 @@ namespace flask 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"; + class rvalue; + rvalue load_copy(const char* data, size_t size); - struct r_string + namespace detail { - operator std::string () + + struct r_string + : boost::less_than_comparable<r_string>, + boost::less_than_comparable<r_string, std::string>, + boost::equality_comparable<r_string>, + boost::equality_comparable<r_string, std::string> + { + r_string() {}; + r_string(const char* s, uint32_t length, uint8_t has_escaping) + : s_(s), length_(length), has_escaping_(has_escaping) + {}; + ~r_string() + { + if (owned_) + delete[] s_; + } + + r_string(const r_string& r) + { + *this = r; + } + + r_string(r_string&& r) + { + *this = r; + } + + r_string& operator = (r_string&& r) + { + s_ = r.s_; + length_ = r.length_; + has_escaping_ = r.has_escaping_; + owned_ = r.owned_; + return *this; + } + + r_string& operator = (const r_string& r) + { + s_ = r.s_; + length_ = r.length_; + has_escaping_ = r.has_escaping_; + owned_ = 0; + return *this; + } + + operator std::string () const + { + return unescape(); + } + + std::string unescape() const + { + // TODO + return std::string(begin(), end()); + } + + const char* begin() const { return s_; } + const char* end() const { return s_+length_; } + + using iterator = const char*; + using const_iterator = const char*; + + const char* s_; + uint32_t length_; + uint8_t has_escaping_; + uint8_t owned_{0}; + friend std::ostream& operator << (std::ostream& os, const r_string& s) + { + os << (std::string)s; + return os; + } + private: + void force(const char* s, uint32_t length) + { + s_ = s; + length_ = length; + owned_ = 1; + } + friend rvalue flask::json::load_copy(const char* data, size_t size); + }; + + bool operator < (const r_string& l, const r_string& r) { - return std::string(s_, e_); + return boost::lexicographical_compare(l,r); } - char* s_; - char* e_; - }; + bool operator < (const r_string& l, const std::string& r) + { + return boost::lexicographical_compare(l,r); + } + + bool operator > (const r_string& l, const std::string& r) + { + return boost::lexicographical_compare(r,l); + } - struct rvalue + bool operator == (const r_string& l, const r_string& r) + { + return boost::equals(l,r); + } + + bool operator == (const r_string& l, const std::string& r) + { + return boost::equals(l,r); + } + } + + class rvalue { - rvalue() {} - rvalue(type t) - : start_{json_type_str + 8*(int)t}, - end_{json_type_str+8*(int)t+8} + static const int error_bit = 4; + public: + rvalue() noexcept : option_{error_bit} + {} + rvalue(type t) noexcept + : lsize_{}, lremain_{}, t_{t} + {} + rvalue(type t, const char* s, const char* e) noexcept + : start_{s}, + end_{e}, + t_{t} + {} + rvalue(type t, const char* s, const char* e, uint8_t option) noexcept + : start_{s}, + end_{e}, + t_{t}, + option_{option} {} - ~rvalue() + + rvalue(const rvalue& r) + : start_(r.start_), + end_(r.end_), + key_(r.key_), + t_(r.t_), + option_(r.option_) { - deallocate(); + copy_l(r); } - void deallocate() + + rvalue(rvalue&& r) noexcept { - if (mem_) - { - delete[] mem_; - mem_ = nullptr; - } + *this = std::move(r); } - type t() const + + rvalue& operator = (const rvalue& r) { - return *reinterpret_cast<const type*>(start_); + start_ = r.start_; + end_ = r.end_; + key_ = r.key_; + copy_l(r); + t_ = r.t_; + option_ = r.option_; + return *this; } - const void* data() const + rvalue& operator = (rvalue&& r) noexcept { - return reinterpret_cast<const void*>(start_ + sizeof(intptr_t)); + start_ = r.start_; + end_ = r.end_; + key_ = std::move(r.key_); + l_ = std::move(r.l_); + lsize_ = r.lsize_; + lremain_ = r.lremain_; + t_ = r.t_; + option_ = r.option_; + return *this; } - int i() const + explicit operator bool() const noexcept { + return (option_ & error_bit) == 0; + } + + explicit operator int64_t() const + { + return i(); + } + + explicit operator int() const + { + return i(); + } + + type t() const + { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + if (option_ & error_bit) + { + throw std::runtime_error("invalid json object"); + } +#endif + return t_; + } + + int64_t i() const + { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK if (t() != type::Number) throw std::runtime_error("value is not number"); - return static_cast<int>(*(double*)data()); +#endif + return boost::lexical_cast<int64_t>(start_, end_-start_); } double d() const { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK if (t() != type::Number) throw std::runtime_error("value is not number"); - return *(double*)data(); +#endif + return boost::lexical_cast<double>(start_, end_-start_); } - r_string s() const + detail::r_string s_raw() const { - if (t() != type::Number) - throw std::runtime_error("value is not number"); - return {(char*)data(),(char*)data() + sizeof(char*)}; +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + if (t() != type::String) + throw std::runtime_error("value is not string"); +#endif + return detail::r_string{start_, (uint32_t)(end_-start_), has_escaping()}; } - const char* start_{}; - const char* end_{}; - char* mem_{}; + detail::r_string s() const + { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + if (t() != type::String) + throw std::runtime_error("value is not string"); +#endif + return detail::r_string{start_, (uint32_t)(end_-start_), has_escaping()}; + } + + bool has(const char* str) const + { + return has(std::string(str)); + } + + bool has(const std::string& str) const + { + struct Pred + { + bool operator()(const rvalue& l, const rvalue& r) const + { + return l.key_ < r.key_; + }; + bool operator()(const rvalue& l, const std::string& r) const + { + return l.key_ < r; + }; + bool operator()(const std::string& l, const rvalue& r) const + { + return l < r.key_; + }; + }; + if (!is_cached()) + { + std::sort(begin(), end(), Pred()); + set_cached(); + } + auto it = lower_bound(begin(), end(), str, Pred()); + return it != end() && it->key_ == str; + } + + rvalue* begin() const + { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + if (t() != type::Object && t() != type::List) + throw std::runtime_error("value is not a container"); +#endif + return l_.get(); + } + rvalue* end() const + { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + if (t() != type::Object && t() != type::List) + throw std::runtime_error("value is not a container"); +#endif + return l_.get()+lsize_; + } + + const detail::r_string& key() const + { + return key_; + } + + size_t size() const + { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + if (t() != type::Object && t() != type::List) + throw std::runtime_error("value is not a container"); +#endif + return lsize_; + } + + const rvalue& operator[](int index) const + { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + if (t() != type::List) + throw std::runtime_error("value is not a list"); + if (index >= (int)lsize_ || index < 0) + throw std::runtime_error("list out of bound"); +#endif + return l_[index]; + } + + const rvalue& operator[](size_t index) const + { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + if (t() != type::List) + throw std::runtime_error("value is not a list"); + if (index >= lsize_) + throw std::runtime_error("list out of bound"); +#endif + return l_[index]; + } + + const rvalue& operator[](const char* str) const + { + return this->operator[](std::string(str)); + } + + const rvalue& operator[](const std::string& str) const + { +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + if (t() != type::Object) + throw std::runtime_error("value is not an object"); +#endif + struct Pred + { + bool operator()(const rvalue& l, const rvalue& r) const + { + return l.key_ < r.key_; + }; + bool operator()(const rvalue& l, const std::string& r) const + { + return l.key_ < r; + }; + bool operator()(const std::string& l, const rvalue& r) const + { + return l < r.key_; + }; + }; + if (!is_cached()) + { + std::sort(begin(), end(), Pred()); + set_cached(); + } + auto it = lower_bound(begin(), end(), str, Pred()); + if (it != end() && it->key_ == str) + return *it; +#ifndef FLASKPP_JSON_NO_ERROR_CHECK + throw std::runtime_error("cannot find key"); +#else + static rvalue nullValue; + return nullValue; +#endif + } + + void set_error() + { + option_|=error_bit; + } + + bool error() const + { + return (option_&error_bit)!=0; + } + private: + bool has_escaping() const + { + return (option_&1)!=0; + } + bool is_cached() const + { + return (option_&2)!=0; + } + void set_cached() const + { + option_ |= 2; + } + void copy_l(const rvalue& r) + { + lsize_ = r.lsize_; + lremain_ = 0; + l_.reset(new rvalue[lsize_]); + std::copy(r.begin(), r.end(), begin()); + } + + void emplace_back(rvalue&& v) + { + if (!lremain_) + { + int new_size = lsize_ + lsize_; + if (new_size - lsize_ > 60000) + new_size = lsize_ + 60000; + if (new_size < 4) + new_size = 4; + rvalue* p = new rvalue[new_size]; + rvalue* p2 = p; + for(auto& x : *this) + *p2++ = std::move(x); + l_.reset(p); + lremain_ = new_size - lsize_; + } + l_[lsize_++] = std::move(v); + lremain_ --; + } + + const char* start_; + const char* end_; + detail::r_string key_; + std::unique_ptr<rvalue[]> l_; + uint32_t lsize_; + uint16_t lremain_; + type t_; + mutable uint8_t option_{0}; + + friend rvalue load_nocopy(const char* data, size_t size); + friend rvalue load_copy(const char* data, size_t size); + friend std::ostream& operator <<(std::ostream& os, const rvalue& r) + { + switch(r.t_) + { + + 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::String: os << '"' << r.s_raw() << '"'; break; + case type::List: + { + os << '['; + bool first = true; + for(auto& x : r) + { + if (!first) + os << ','; + first = false; + os << x; + } + os << ']'; + } + break; + case type::Object: + { + os << '{'; + bool first = true; + for(auto& x : r) + { + if (!first) + os << ','; + os << '"' << escape(r.key_) << '"'; + first = false; + os << x; + } + os << '}'; + } + break; + } + return os; + } }; + bool operator == (const rvalue& l, const std::string& r) + { + return l.s() == r; + } + + bool operator == (const std::string& l, const rvalue& r) + { + return l == r.s(); + } + + bool operator != (const rvalue& l, const std::string& r) + { + return l.s() != r; + } + + bool operator != (const std::string& l, const rvalue& r) + { + return l != r.s(); + } + + bool operator == (const rvalue& l, double r) + { + return l.d() == r; + } + + bool operator == (double l, const rvalue& r) + { + return l == r.d(); + } + + bool operator != (const rvalue& l, double r) + { + return l.d() != r; + } + + bool operator != (double l, const rvalue& r) + { + return l != r.d(); + } + + //inline rvalue decode(const std::string& s) //{ //} - - /*inline boost::optional<const rvalue> decode(const char* data, size_t size, bool partial = false) + inline rvalue load_nocopy(const char* data, size_t size) { - static char* escaped = "\"\\\/\b\f\n\r\t" - const char* e = data + size; - auto ws_skip = [&] + //static const char* escaped = "\"\\/\b\f\n\r\t"; + struct Parser + { + Parser(const char* data, size_t size) + : data(data) + { + } + + bool consume(char c) { - 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; - } + if (flask_json_unlikely(*data != c)) + return false; + data++; + return true; + } + + void ws_skip() + { + while(*data == ' ' || *data == '\t' || *data == '\r' || *data == '\n') ++data; }; - auto decode_string = [&]->boost::optional<rvalue> + + rvalue decode_string() { - ws_skip(); - if (data == e || *data != '"') + if (flask_json_unlikely(!consume('"'))) return {}; - while(data != e) + const char* start = data; + uint8_t has_escaping = 0; + while(1) { - if (*data == '"') + if (flask_json_likely(*data != '"' && *data != '\\')) { + data ++; } - else if (*data == '\\') + else if (*data == '"') { - // TODO - //if (data+1 == e) - //return {}; - //data += 2; + data++; + return {type::String, start, data-1, has_escaping}; } - else + else if (*data == '\\') { - data ++; + has_escaping = 1; + // TODO + data++; + switch(*data) + { + case 'u': + data += 4; + // TODO + break; + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + data ++; + break; + default: + return {}; + } } } - }; - auto decode_list = [&]->boost::optional<rvalue> + return {}; + } + + rvalue decode_list() { - // TODO - }; - auto decode_number = [&]->boost::optional<rvalue> + rvalue ret(type::List); + if (flask_json_unlikely(!consume('['))) + { + ret.set_error(); + return ret; + } + ws_skip(); + if (flask_json_unlikely(*data == ']')) + { + data++; + return ret; + } + + while(1) + { + auto v = decode_value(); + if (flask_json_unlikely(!v)) + { + ret.set_error(); + break; + } + ws_skip(); + ret.emplace_back(std::move(v)); + if (*data == ']') + { + data++; + break; + } + if (flask_json_unlikely(!consume(','))) + { + ret.set_error(); + break; + } + ws_skip(); + } + return ret; + } + + rvalue decode_number() { - // TODO - }; - auto decode_value = [&]->boost::optional<rvalue> + const char* start = data; + + enum NumberParsingState + { + Minus, + AfterMinus, + ZeroFirst, + Digits, + DigitsAfterPoints, + E, + DigitsAfterE, + Invalid, + } state{Minus}; + while(flask_json_likely(state != Invalid)) + { + switch(*data) + { + case '0': + state = (NumberParsingState)"\2\2\7\3\4\6\6"[state]; + /*if (state == NumberParsingState::Minus || state == NumberParsingState::AfterMinus) + { + state = NumberParsingState::ZeroFirst; + } + else if (state == NumberParsingState::Digits || + state == NumberParsingState::DigitsAfterE || + state == NumberParsingState::DigitsAfterPoints) + { + // ok; pass + } + else if (state == NumberParsingState::E) + { + state = NumberParsingState::DigitsAfterE; + } + else + return {};*/ + break; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + state = (NumberParsingState)"\3\3\7\3\4\6\6"[state]; + while(*(data+1) >= '0' && *(data+1) <= '9') data++; + /*if (state == NumberParsingState::Minus || state == NumberParsingState::AfterMinus) + { + state = NumberParsingState::Digits; + } + else if (state == NumberParsingState::Digits || + state == NumberParsingState::DigitsAfterE || + state == NumberParsingState::DigitsAfterPoints) + { + // ok; pass + } + else if (state == NumberParsingState::E) + { + state = NumberParsingState::DigitsAfterE; + } + else + return {};*/ + break; + case '.': + state = (NumberParsingState)"\7\7\7\4\7\7\7"[state]; + /* + if (state == NumberParsingState::Digits) + { + state = NumberParsingState::DigitsAfterPoints; + } + else + return {}; + */ + break; + case '-': + state = (NumberParsingState)"\1\7\7\7\7\6\7"[state]; + /*if (state == NumberParsingState::Minus) + { + state = NumberParsingState::AfterMinus; + } + else if (state == NumberParsingState::E) + { + state = NumberParsingState::DigitsAfterE; + } + else + return {};*/ + break; + case '+': + state = (NumberParsingState)"\7\7\7\7\7\6\7"[state]; + /*if (state == NumberParsingState::E) + { + state = NumberParsingState::DigitsAfterE; + } + else + return {};*/ + break; + case 'e': case 'E': + state = (NumberParsingState)"\7\7\7\5\5\7\7"[state]; + /*if (state == NumberParsingState::Digits || + state == NumberParsingState::DigitsAfterPoints) + { + state = NumberParsingState::E; + } + else + return {};*/ + break; + default: + if (flask_json_likely(state == NumberParsingState::ZeroFirst || + state == NumberParsingState::Digits || + state == NumberParsingState::DigitsAfterPoints || + state == NumberParsingState::DigitsAfterE)) + return {type::Number, start, data}; + else + return {}; + } + data++; + } + + return {}; + } + + rvalue decode_value() { - if (data == e) - return {}; switch(*data) { - case '"': - emit_str_begin(); + case '[': + return decode_list(); case '{': + return decode_object(); + case '"': + return decode_string(); case 't': - if (e-data >= 4 && + if (//e-data >= 4 && data[1] == 'r' && data[2] == 'u' && data[3] == 'e') - return rvalue(type::True); + { + data += 4; + return {type::True}; + } else return {}; case 'f': + if (//e-data >= 5 && + data[1] == 'a' && + data[2] == 'l' && + data[3] == 's' && + data[4] == 'e') + { + data += 5; + return {type::False}; + } + else + return {}; case 'n': + if (//e-data >= 4 && + data[1] == 'u' && + data[2] == 'l' && + data[3] == 'l') + { + data += 4; + return {type::Null}; + } + else + return {}; + //case '1': case '2': case '3': + //case '4': case '5': case '6': + //case '7': case '8': case '9': + //case '0': case '-': + default: + return decode_number(); } - // TODO return {}; - }; - auto decode_object =[&]->boost::optional<rvalue> + } + + rvalue decode_object() { - ws_skip(); - if (data == e || *data != '{') - return {}; + rvalue ret(type::Object); + if (flask_json_unlikely(!consume('{'))) + { + ret.set_error(); + return ret; + } + + ws_skip(); + + if (flask_json_unlikely(*data == '}')) + { + data++; + return ret; + } - 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 == '}') + if (flask_json_unlikely(!t)) + { + ret.set_error(); break; - if (data != ',') - return {}; - } - // TODO build up object - throw std::runtime_error("Not implemented"); - return rvalue(type::Object); - }; + } - return decode_object(); + ws_skip(); + if (flask_json_unlikely(!consume(':'))) + { + ret.set_error(); + break; + } + // TODO caching key to speed up (flyweight?) + auto key = t.s(); + ws_skip(); + auto v = decode_value(); + if (flask_json_unlikely(!v)) + { + ret.set_error(); + break; } + ws_skip(); + + v.key_ = std::move(key); + ret.emplace_back(std::move(v)); + if (flask_json_unlikely(*data == '}')) + { + data++; + break; + } + if (flask_json_unlikely(!consume(','))) + { + ret.set_error(); + break; + } + ws_skip(); + } + return ret; } - } - }*/ + + rvalue parse() + { + //auto ret = decode_object(); + ws_skip(); + auto ret = decode_value(); + ws_skip(); + if (*data != '\0') + ret.set_error(); + return ret; + } + + const char* data; + }; + return Parser(data, size).parse(); + } + inline rvalue load_copy(const char* data, size_t size) + { + char* s = new char[size+1]; + memcpy(s, data, size+1); + auto ret = load_nocopy(s, size); + if (ret) + ret.key_.force(s, size); + else + delete[] s; + return ret; + } + + inline rvalue load_copy(const char* data) + { + return load_copy(data, strlen(data)); + } + + inline rvalue load_copy(const std::string& str) + { + return load_copy(str.data(), str.size()); + } + + inline rvalue load_nocopy(const char* data) + { + return load_nocopy(data, strlen(data)); + } + + inline rvalue load_nocopy(const std::string& str) + { + return load_nocopy(str.data(), str.size()); + } + class wvalue { @@ -335,6 +1080,24 @@ namespace flask return *this; } + template <typename T> + wvalue& operator[](const std::vector<T>& v) + { + if (t != type::List) + reset(); + t = type::List; + if (!l) + l = std::move(std::unique_ptr<std::vector<wvalue>>(new std::vector<wvalue>{})); + l->clear(); + l->resize(v.size()); + size_t idx = 0; + for(auto& x:v) + { + (*l)[idx++] = x; + } + return *this; + } + wvalue& operator[](unsigned index) { if (t != type::List) @@ -342,7 +1105,8 @@ namespace flask t = type::List; if (!l) l = std::move(std::unique_ptr<std::vector<wvalue>>(new std::vector<wvalue>{})); - l->resize(index+1); + if (l->size() < index+1) + l->resize(index+1); return (*l)[index]; } @@ -401,18 +1165,18 @@ namespace flask } - friend void encode_internal(const wvalue& v, std::string& out); - friend std::string encode(const wvalue& v); + friend void dump_internal(const wvalue& v, std::string& out); + friend std::string dump(const wvalue& v); }; - void encode_string(const std::string& str, std::string& out) + void dump_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) + void dump_internal(const wvalue& v, std::string& out) { switch(v.t) { @@ -420,7 +1184,7 @@ namespace flask case type::False: out += "false"; break; case type::True: out += "true"; break; case type::Number: out += boost::lexical_cast<std::string>(v.d); break; - case type::String: encode_string(v.s, out); break; + case type::String: dump_string(v.s, out); break; case type::List: { out.push_back('['); @@ -434,7 +1198,7 @@ namespace flask out.push_back(','); } first = false; - encode_internal(x, out); + dump_internal(x, out); } } out.push_back(']'); @@ -453,9 +1217,9 @@ namespace flask out.push_back(','); } first = false; - encode_string(kv.first, out); + dump_string(kv.first, out); out.push_back(':'); - encode_internal(kv.second, out); + dump_internal(kv.second, out); } } out.push_back('}'); @@ -464,16 +1228,19 @@ namespace flask } } - std::string encode(const wvalue& v) + std::string dump(const wvalue& v) { std::string ret; ret.reserve(v.estimate_length()); - encode_internal(v, ret); + dump_internal(v, ret); return ret; } - //std::vector<boost::asio::const_buffer> encode_ref(wvalue& v) + //std::vector<boost::asio::const_buffer> dump_ref(wvalue& v) //{ //} } } + +#undef flask_json_likely +#undef flask_json_unlikely |