See More

/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #define CPPCMS_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "utf_iterator.h" namespace cppcms { namespace json { static const size_t json_max_depth = 512; bad_value_cast::bad_value_cast() : msg_("cppcms::json::bad_cast") { } bad_value_cast::bad_value_cast(std::string const &s) : msg_("cppcms::json::bad_cast: "+s ) { } bad_value_cast::bad_value_cast(std::string const &s,json_type actual) : msg_("cppcms::json::bad_cast: " + s) { std::ostringstream msg; msg<<" error converting from "< struct json_type_traits; template<> struct json_type_traits { static const json_type type=is_undefined; }; template<> struct json_type_traits { static const json_type type=is_null; }; template<> struct json_type_traits { static const json_type type=is_boolean; }; template<> struct json_type_traits { static const json_type type=is_number; }; template<> struct json_type_traits { static const json_type type=is_array; }; template<> struct json_type_traits { static const json_type type=is_object; }; template<> struct json_type_traits<:string> { static const json_type type=is_string; }; template struct cpp_type_traits; template<> struct cpp_type_traits { typedef bool type; }; template<> struct cpp_type_traits { typedef double type; }; template<> struct cpp_type_traits { typedef std::string type; }; template<> struct cpp_type_traits { typedef array type; }; template<> struct cpp_type_traits { typedef object type; }; class variant { public: union memory { void *p; bool bv; double dv; char s[sizeof(std::string)]; char a[sizeof(json::array)]; char o[sizeof(json::object)]; } m; void *ptr() { return static_cast(&m); } void const *ptr() const { return static_cast(&m); } json_type which() const { return type; } json_type type; bool operator==(variant const &other) const { if(type != other.type) return false; switch(type) { case is_boolean: return get() == other.get(); case is_number: return get() == other.get(); case is_string: return get<:string>() == other.get<:string>(); case is_object: return get() == other.get(); case is_array: return get() == other.get(); default: return true; } } variant() { create(is_undefined); } ~variant() { destroy(); } variant(variant const &other) { create(other.type,other.m); } variant &operator=(variant const &other) { if(this!=&other) { destroy(); create(other.type,other.m); } return *this; } template T &get() { json_type expected = json_type_traits::type; if(expected != type) throw bad_value_cast("invalid type",expected,type); switch(type) { case is_undefined: case is_null: throw bad_value_cast("non-fetchable type",type); default: return *static_cast(ptr()); } } template T const &get() const { json_type expected = json_type_traits::type; if(expected != type) throw bad_value_cast("invalid type",expected,type); switch(type) { case is_undefined: case is_null: throw bad_value_cast("non-fetchable type",type); default: return *static_cast(ptr()); } } template variant(T const &v) { create(v); } template variant &operator=(T const &other) { destroy(); create(other); return *this; } template void create(T const &v) { void *p=ptr(); type = json_type_traits::type; switch(type) { case is_undefined: case is_null: case is_boolean: case is_number: memcpy(&m,&v,sizeof(T)); break; case is_string: new(p) std::string(reinterpret_cast<:string const>(v)); break; case is_object: new(p) object(reinterpret_cast(v)); break; case is_array: new(p) array(reinterpret_cast(v)); break; } } void create(json_type t) { type = t; memset(ptr(),0,sizeof(m)); switch(type) { case is_undefined: case is_null: case is_boolean: case is_number: break; case is_string: new(ptr()) std::string(); break; case is_object: new(ptr()) object(); break; case is_array: new(ptr()) array(); break; } } void create(json_type t,memory const &other) { void *p=&m; type = t; switch(t) { case is_undefined: case is_null: case is_boolean: case is_number: memcpy(&m,&other,sizeof(m)); break; case is_string: new(p) std::string(reinterpret_cast<:string const>(other)); break; case is_object: new(p) object(reinterpret_cast(other)); break; case is_array: new(p) array(reinterpret_cast(other)); break; } } void destroy() { typedef std::string string_type; switch(type) { case is_undefined: case is_null: case is_boolean: case is_number: break; case is_string: static_cast(ptr())->~string_type(); break; case is_object: static_cast(ptr())->~object(); break; case is_array: static_cast(ptr())->~array(); break; } memset(&m,0,sizeof(m)); } }; typedef variant variant_type; struct value::_data { public: variant_type &value() { return value_; } variant_type const &value() const { return value_; } private: variant_type value_; }; using namespace std; value::copyable::copyable() : d(new value::_data()) { } value::copyable::~copyable() { } value::copyable::copyable(copyable const &r) : d(r.d) { } value::copyable &value::copyable::operator=(value::copyable &&r) { d.swap(r.d); return *this; } value::copyable::copyable(value::copyable &&r) : d(new value::_data()) { d.swap(r.d); } value::copyable const &value::copyable::operator=(value::copyable const &r) { if(&r!=this) d=r.d; return *this; } double const &value::number() const { return d->value().get(); } bool const &value::boolean() const { return d->value().get(); } std::string const &value::str() const { return d->value().get<:string>(); } json::object const &value::object() const { return d->value().get<:object>(); } json::array const &value::array() const { return d->value().get<:array>(); } double &value::number() { return d->value().get(); } bool &value::boolean() { return d->value().get(); } std::string &value::str() { return d->value().get<:string>(); } json::object &value::object() { return d->value().get<:object>(); } json::array &value::array() { return d->value().get<:array>(); } bool value::is_undefined() const { return d->value().which()==json::is_undefined; } bool value::is_null() const { return d->value().which()==json::is_null; } void value::undefined() { d->value()=json::undefined(); } void value::null() { d->value()=json::null(); } void value::boolean(bool x) { d->value()=x; } void value::number(double x) { d->value()=x; } void value::str(std::string const &x) { d->value()=x; } void value::object(json::object const &x) { d->value()=x; } void value::array(json::array const &x) { d->value()=x; } json_type value::type() const { return d->value().which(); } namespace details { template void generic_append(char const *begin,char const *end,Appender &a) { a.append('"'); char const *i,*last; char buf[8] = "\\u00"; for(i=begin,last = begin;i!=end;) { char const *addon = 0; unsigned char c=*i; switch(c) { case 0x22: addon = "\\\""; break; case 0x5C: addon = "\\\\"; break; case '\b': addon = "\\b"; break; case '\f': addon = "\\f"; break; case '\n': addon = "\\n"; break; case '\r': addon = "\\r"; break; case '\t': addon = "\\t"; break; default: if(c<=0x1F) { static char const tohex[]="0123456789abcdef"; buf[4]=tohex[c >> 4]; buf[5]=tohex[c & 0xF]; buf[6]=0; addon = buf; } }; if(addon) { a.append(last,i-last); a.append(addon); i++; last = i; } else { i++; } } a.append(last,i-last); a.append('"'); } struct string_append { std::string *out; void append(char c) { *out += c; } void append(char const *s,size_t n) { out->append(s,n); } void append(char const *s) { out->append(s); } }; struct stream_append { std::ostream *out; void append(char c) { *out << c; } void append(char const *s,size_t n) { out->write(s,n); } void append(char const *s) { *out << s; } }; } std::string CPPCMS_API to_json(char const *begin,char const *end) { std::string result; result.reserve(end-begin + 2); details::string_append ap = { &result }; details::generic_append(begin,end,ap); return result; } std::string CPPCMS_API to_json(std::string const &s) { return to_json(s.c_str(),s.c_str()+s.size()); } void CPPCMS_API to_json(char const *begin,char const *end,std::ostream &out) { details::stream_append ap = { &out }; details::generic_append(begin,end,ap); } void CPPCMS_API to_json(std::string const &str,std::ostream &out) { to_json(str.c_str(),str.c_str()+str.size(),out); } namespace { void pad(std::ostream &out,int tb) { for(;tb > 0;tb--) out<<'\t'; } void indent(std::ostream &out,char c,int &tabs) { if(tabs < 0) { out<::digits10+1)<first.begin(),p->first.end(),out); indent(out,':',tabs); p->second.write_value(out,tabs); ++p; if(p!=end) indent(out,',',tabs); } indent(out,'}',tabs); } break; default: throw bad_value_cast("Unknown type found: internal error"); } } void value::save(std::ostream &out,int how) const { int tabs=(how & readable) ? 0 : -1; write(out,tabs); } std::string value::save(int how) const { std::ostringstream ss; int tabs=(how & readable) ? 0 : -1; write(ss,tabs); return ss.str(); } std::ostream &operator<<(std::ostream &out,value const &v) { v.save(out); return out; } bool value::operator==(value const &other) const { return d->value()==other.d->value(); } bool value::operator!=(value const &other) const { return !(*this==other); } // returns empty if not found value const &value::find(char const *cpath) const { string_key path=string_key::unowned(cpath); static value const empty; value const *ptr=this; size_t pos=0; size_t new_pos; do { new_pos=path.find('.',pos); string_key const part=path.unowned_substr(pos,new_pos - pos); if(new_pos!=std::string::npos) new_pos++; if(part.empty()) return empty; if(ptr->type()!=json::is_object) return empty; json::object const &obj=ptr->object(); json::object::const_iterator p; if((p=obj.find(part))==obj.end()) return empty; ptr=&p->second; pos=new_pos; } while(new_pos < path.size()); return *ptr; } // returns empty if not found value const &value::find(std::string const &path) const { return find(path.c_str()); } // throws if not found value const &value::at(std::string const &path) const { return at(path.c_str()); } value const &value::at(char const *path) const { value const &v=find(path); if(v.is_undefined()) throw bad_value_cast(std::string("Value not found at ")+path ); return v; } value &value::at(std::string const &path) { return at(path.c_str()); } value &value::at(char const *cpath) { string_key path=string_key::unowned(cpath); value *ptr=this; size_t pos=0; size_t new_pos; do { new_pos=path.find('.',pos); string_key part=path.unowned_substr(pos,new_pos - pos); if(new_pos!=std::string::npos) new_pos++; if(part.empty()) throw bad_value_cast("Invalid path provided"); if(ptr->type()!=json::is_object) throw bad_value_cast("",ptr->type(),json::is_object); json::object &obj=ptr->object(); json::object::iterator p; if((p=obj.find(part))==obj.end()) throw bad_value_cast("Member "+part.str()+" not found"); ptr=&p->second; pos=new_pos; } while(new_pos < path.size()); return *ptr; } void value::at(std::string const &path,value const &v) { at(path.c_str(),v); } void value::at(char const *cpath,value const &v) { string_key path=string_key::unowned(cpath); value *ptr=this; size_t pos=0; size_t new_pos; do { new_pos=path.find('.',pos); string_key part=path.unowned_substr(pos,new_pos - pos); if(new_pos!=std::string::npos) new_pos++; if(part.empty()) throw bad_value_cast("Invalid path provided"); if(ptr->type()!=json::is_object) { *ptr=json::object(); } json::object &obj=ptr->object(); json::object::iterator p; if((p=obj.find(part))==obj.end()) { ptr=&obj.insert(std::make_pair(part.str(),json::value())).first->second; } else ptr=&p->second; pos=new_pos; } while(new_pos < path.size()); *ptr=v; } value &value::operator[](std::string const &name) { if(type()!=json::is_object) set_value(json::object()); json::object &self=object(); json::object::iterator p=self.find(string_key::unowned(name)); if(p==self.end()) return self.insert(std::make_pair(name,value())).first->second; return p->second; } value const &value::operator[](std::string const &name) const { if(type()!=json::is_object) throw bad_value_cast("",type(),json::is_object); json::object const &self=object(); json::object::const_iterator p=self.find(string_key::unowned(name)); if(p==self.end()) throw bad_value_cast("Member "+name+" not found"); return p->second; } value &value::operator[](size_t n) { if(type()!=json::is_array) set_value(json::array()); json::array &self=array(); if(n>=self.size()) self.resize(n+1,json::null()); return self[n]; } value const &value::operator[](size_t n) const { if(type()!=json::is_array) throw bad_value_cast("",type(),json::is_array); if(n >= array().size()) throw bad_value_cast("Index out of range"); return array()[n]; } //#define DEBUG_PARSER namespace { enum { tock_eof = 255, tock_err, tock_str, tock_number, tock_true, tock_false, tock_null }; struct tockenizer { public: tockenizer(std::istream &in) : real(0), line(1), is_(in), locale_(in.getloc()) { is_.imbue(std::locale::classic()); } ~tockenizer() { is_.imbue(locale_); } std::string str; double real; int line; #ifdef DEBUG_PARSER std::string write_tocken(int c) { std::ostringstream ss; if(c>=255) { static char const *name[] = { "eof", "err", "str", "number", "true", "false", "null" }; ss<sbumpc())==-1) return tock_eof; switch(c) { case '[': case '{': case ':': case ',': case '}': case ']': return c; case ' ': case '\t': case '\r': break; case '\n': line++; break; case '"': buf->sungetc(); if(parse_string()) return tock_str; return tock_err; case 't': if(check("rue")) return tock_true; return tock_err; case 'n': if(check("ull")) return tock_null; return tock_err; case 'f': if(check("alse")) return tock_false; return tock_err; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': buf->sungetc(); if(parse_number()) return tock_number; return tock_err; case '/': if(check("/")) { while((c=buf->sbumpc())!=-1 && c!='\n') ; if(c=='\n') break; return tock_eof; } return tock_err; default: return tock_err; } } } private: std::istream &is_; std::locale locale_; bool check(char const *s) { std::streambuf *buf = is_.rdbuf(); while(*s && buf->sbumpc()==std::char_traits::to_int_type(*s)) s++; return *s==0; } bool parse_string() { int c; std::streambuf *buf=is_.rdbuf(); str.clear(); if((c=buf->sbumpc())==-1 || c!='"') return false; bool second_surragate_expected=false; uint16_t first_surragate = 0; for(;;) { if((c=buf->sbumpc())==-1) return false; if(second_surragate_expected && c!='\\') return false; if(0<= c && c <= 0x1F) return false; if(c=='"') break; if(c=='\\') { if((c=buf->sbumpc())==-1) return false; if(second_surragate_expected && c!='u') return false; switch(c) { case '"': case '\\': case '/': str+=char(c); break; case 'b': str+='\b'; break; case 'f': str+='\f'; break; case 'n': str+='\n'; break; case 'r': str+='\r'; break; case 't': str+='\t'; break; case 'u': { uint16_t x; if(!read_4_digits(x)) return false; if(second_surragate_expected) { if(!utf16::is_second_surrogate(x)) return false; append(utf16::combine_surrogate(first_surragate,x)); second_surragate_expected=false; } else if(utf16::is_first_surrogate(x)) { second_surragate_expected=true; first_surragate=x; } else { append(x); } } break; default: return false; } } else { str+=char(c); } } if(!utf8::validate(str.begin(),str.end())) return false; return true; } bool read_4_digits(uint16_t &x) { char buf[5]={0}; if(!is_.get(buf,5)) return false; for(unsigned i=0;i<4;i++) { char c=buf[i]; if( ('0'<= c && c<='9') || ('A'<= c && c<='F') || ('a'<= c && c<='f') ) { continue; } return false; } unsigned v; sscanf(buf,"%x",&v); x=v; return true; } void append(uint32_t x) { utf8::seq s=utf8::encode(x); str.append(s.c,s.len); } bool parse_number() { is_ >> real; return !is_.fail();; } }; typedef enum { st_init = 0, st_object_or_array_or_value_expected = 0 , st_object_key_or_close_expected, st_object_colon_expected, st_object_value_expected, st_object_close_or_comma_expected, st_array_value_or_close_expected, st_array_close_or_comma_expected, st_error, st_done } state_type; #ifdef DEBUG_PARSER std::ostream &operator<<(std::ostream &out,state_type t) { static char const *names[] = { "st_object_or_array_or_value_expected", "st_object_key_or_close_expected", "st_object_colon_expected", "st_object_value_expected", "st_object_close_or_comma_expected", "st_array_value_or_close_expected", "st_array_close_or_comma_expected", "st_error", "st_done" }; out<(begin),const_cast(begin),const_cast(end)); } char const *position() { return gptr(); } }; bool parse_stream(std::istream &in,value &out,bool force_eof,int &error_at_line); bool parse_stream(char const *&begin,char const *end,value &out,bool force_eof,int &error_at_line) { charbuf b(begin,end); std::istream is(&b); bool r=parse_stream(is,out,force_eof,error_at_line); begin = b.position(); return r; } bool parse_stream(std::istream &in,value &out,bool force_eof,int &error_at_line) { tockenizer tock(in); value result; state_type state=st_init; std::string key; std::stack<:pair> > stack; stack.push(std::make_pair(st_done,&result)); while(!stack.empty() && state !=st_error && state!=st_done && stack.size() <= json_max_depth) { int c=tock.next(); #ifdef DEBUG_PARSER std::cout<object(); std::pair<:object::iterator> res= obj.insert(std::make_pair(key,json::value())); if(res.second==false) { state=st_error; break; } json::value &val=res.first->second; if(c==tock_str) { val=tock.str; state=st_object_close_or_comma_expected; } else if(c==tock_true) { val=true; state=st_object_close_or_comma_expected; } else if(c==tock_false) { val=false; state=st_object_close_or_comma_expected; } else if(c==tock_null) { val=null(); state=st_object_close_or_comma_expected; } else if(c==tock_number) { val=tock.real; state=st_object_close_or_comma_expected; } else if(c=='[') { val=json::array(); state=st_array_value_or_close_expected; stack.push(std::make_pair(st_object_close_or_comma_expected,&val)); } else if(c=='{') { val=json::object(); state=st_object_key_or_close_expected; stack.push(std::make_pair(st_object_close_or_comma_expected,&val)); } else state=st_error; } break; case st_object_close_or_comma_expected: if(c==',') state=st_object_key_or_close_expected; else if(c=='}') { state=stack.top().first; stack.pop(); } else state=st_error; break; case st_array_value_or_close_expected: { if(c==']') { state=stack.top().first; stack.pop(); break; } json::array &ar = stack.top().second->array(); ar.push_back(json::value()); json::value &val=ar.back(); if(c==tock_str) { val=tock.str; state=st_array_close_or_comma_expected; } else if(c==tock_true) { val=true; state=st_array_close_or_comma_expected; } else if(c==tock_false) { val=false; state=st_array_close_or_comma_expected; } else if(c==tock_null) { val=null(); state=st_array_close_or_comma_expected; } else if(c==tock_number) { val=tock.real; state=st_array_close_or_comma_expected; } else if(c=='[') { val=json::array(); state=st_array_value_or_close_expected; stack.push(std::make_pair(st_array_close_or_comma_expected,&val)); } else if(c=='{') { val=json::object(); state=st_object_key_or_close_expected; stack.push(std::make_pair(st_array_close_or_comma_expected,&val)); } else state=st_error; break; } case st_array_close_or_comma_expected: if(c==']') { state=stack.top().first; stack.pop(); } else if(c==',') state=st_array_value_or_close_expected; else state=st_error; break; case st_done: case st_error: break; }; } if(state==st_done) { if(force_eof) { if(tock.next()!=tock_eof) { error_at_line=tock.line; return false; } } out.swap(result); return true; } error_at_line=tock.line; return false; } } // anonymous bool value::load(std::istream &in,bool full,int *line_number) { int err_line; if(!parse_stream(in,*this,full,err_line)) { if(line_number) *line_number=err_line; return false; } return true; } bool value::load(char const *&begin,char const *end,bool full,int *line_number) { int err_line; if(!parse_stream(begin,end,*this,full,err_line)) { if(line_number) *line_number=err_line; return false; } return true; } std::istream &operator>>(std::istream &in,value &v) { int line_no; if(!parse_stream(in,v,false,line_no)) in.setstate( std::istream::failbit ); return in; } std::ostream &operator<<(std::ostream &out,json_type t) { switch(t) { case is_undefined: out<<"undefined"; break; case is_null: out<<"null"; break; case is_boolean: out<<"boolean"; break; case is_number: out<<"number"; break; case is_string: out<<"string"; break; case is_object: out<<"object"; break; case is_array: out<<"array"; break; default: out<<"Illegal"; } return out; } } // json } // cppcms