/
zOMGdev
/
cppcms
Обзор
Документация
Войти
/
zOMGdev
/
cppcms
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v1.0.5
src/json.cpp
1 077 строк
22 KB
Artyom Beilis
Fixing #124 -
04 июн 2014, 15:03
04 июн 2014, 15:03
a50fe48
Код
Авторство
О чём код?
/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #define CPPCMS_SOURCE #include <cppcms/json.h> #include <sstream> #include <stdio.h> #include <typeinfo> #include <algorithm> #include <iostream> #include <locale> #include <limits> #include <iomanip> #include <stdexcept> #include <stack> #include "utf_iterator.h" #include <cppcms/config.h> #ifdef CPPCMS_USE_EXTERNAL_BOOST # include <boost/variant.hpp> #else // Internal Boost # include <cppcms_boost/variant.hpp> namespace boost = cppcms_boost; #endif namespace cppcms { namespace json { 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 "<<actual; msg_ +=msg.str(); } bad_value_cast::bad_value_cast(std::string const &s,json_type expected, json_type actual) : msg_("cppcms::json::bad_cast: " + s) { std::ostringstream msg; msg<<" error converting from "<<actual<<" to "<<expected; msg_ +=msg.str(); } bad_value_cast::~bad_value_cast() throw() { } const char* bad_value_cast::what() const throw() { return msg_.c_str(); } typedef boost::variant< undefined, null, bool, double, std::string, json::object, json::array > 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 const &value::copyable::operator=(value::copyable const &r) { if(&r!=this) d=r.d; return *this; } template<typename T> T &get_variant_value(variant_type &v) { try { return boost::get<T>(v); } catch(boost::bad_get const &e) { throw bad_value_cast("",json_type(v.which())); } } template<typename T> T const &get_variant_value(variant_type const &v) { try { return boost::get<T>(v); } catch(boost::bad_get const &e) { throw bad_value_cast("",json_type(v.which())); } } double const &value::number() const { return get_variant_value<double>(d->value()); } bool const &value::boolean() const { return get_variant_value<bool>(d->value()); } std::string const &value::str() const { return get_variant_value<std::string>(d->value()); } json::object const &value::object() const { return get_variant_value<json::object>(d->value()); } json::array const &value::array() const { return get_variant_value<json::array>(d->value()); } double &value::number() { return get_variant_value<double>(d->value()); } bool &value::boolean() { return get_variant_value<bool>(d->value()); } std::string &value::str() { return get_variant_value<std::string>(d->value()); } json::object &value::object() { return get_variant_value<json::object>(d->value()); } json::array &value::array() { return get_variant_value<json::array>(d->value()); } 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 json_type(d->value().which()); } namespace { std::string escape(std::string const &input) { std::string result; result.reserve(input.size()); result+='"'; std::string::const_iterator i,end=input.end(); for(i=input.begin();i!=end;i++) { switch(*i) { case 0x22: case 0x5C: result+='\\'; result+=*i; break; case '\b': result+="\\b"; break; case '\f': result+="\\f"; break; case '\n': result+="\\n"; break; case '\r': result+="\\r"; break; case '\t': result+="\\t"; break; default: if(0x00<=*i && *i<=0x1F) { char buf[8]; #ifdef CPPCMS_HAVE_SNPRINTF snprintf(buf,sizeof(buf),"\\u%04X",*i); #else sprintf(buf,"\\u%04X",*i); #endif result+=buf; } else { result+=*i; } } } result+='"'; return result; } 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<<c; return; } switch(c) { case '{': case '[': out<<c<<'\n'; tabs++; pad(out,tabs); break; case ',': out<<c<<'\n'; pad(out,tabs); break; case ':': out<<" :\t"; break; case '}': case ']': out<<'\n'; tabs--; pad(out,tabs); out<<c<<'\n'; pad(out,tabs); break; } } } // namespace anonymous void value::write(std::ostream &out,int tabs) const { std::locale original(out.getloc()); out.imbue(std::locale("C")); try { write_value(out,tabs); } catch(...) { out.imbue(original); throw; } out.imbue(original); } void value::write_value(std::ostream &out,int tabs) const { switch(type()) { case json::is_undefined: throw bad_value_cast("Can't write undefined value to stream"); case json::is_null: out<<"null"; break; case json::is_number: out<<std::setprecision(std::numeric_limits<double>::digits10+1)<<number(); break; case json::is_string: out<<escape(str()); break; case json::is_boolean: out<< (boolean() ? "true" : "false") ; break; case json::is_array: { json::array const &a=array(); unsigned i; indent(out,'[',tabs); for(i=0;i<a.size();) { a[i].write_value(out,tabs); i++; if(i<a.size()) indent(out,',',tabs); } indent(out,']',tabs); } break; case json::is_object: { json::object const &obj=object(); object::const_iterator p,end; p=obj.begin(); end=obj.end(); indent(out,'{',tabs); while(p!=end) { out<<escape(p->first); 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<<name[c - 255]; } else { ss<<char(c); } if(c==tock_str) ss<<" "<<str; else if(c==tock_number) ss<<" "<<real; return ss.str(); } #endif int next() { for(;;) { char c; if(!is_.get(c)) return tock_eof; switch(c) { case '[': case '{': case ':': case ',': case '}': case ']': return c; case ' ': case '\t': case '\r': break; case '\n': line++; break; case '"': is_.unget(); 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': is_.unget(); if(parse_number()) return tock_number; return tock_err; case '/': if(check("/")) { while(is_.get(c) && 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) { char c; while(*s && is_.get(c) && c==*s) s++; return *s==0; } bool parse_string() { char c; str.clear(); if(!is_.get(c) || c!='"') return false; bool second_surragate_expected=false; uint16_t first_surragate = 0; for(;;) { if(!is_.get(c)) return false; if(second_surragate_expected && c!='\\') return false; if(0<= c && c <= 0x1F) return false; if(c=='"') break; if(c=='\\') { if(!is_.get(c)) 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<<names[t]; return out; } #endif 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<std::pair<state_type,value *> > stack; stack.push(std::make_pair(st_done,&result)); while(!stack.empty() && state !=st_error && state!=st_done) { int c=tock.next(); #ifdef DEBUG_PARSER std::cout<<state<<" "<<tock.write_tocken(c)<<std::endl; #endif switch(state) { case st_object_or_array_or_value_expected: if(c=='[') { *stack.top().second=json::array(); state=st_array_value_or_close_expected; } else if(c=='{') { *stack.top().second=json::object(); state=st_object_key_or_close_expected; } else if(c==tock_str) { *stack.top().second=tock.str; state=stack.top().first; stack.pop(); } else if(c==tock_true) { *stack.top().second=true; state=stack.top().first; stack.pop(); } else if(c==tock_false) { *stack.top().second=false; state=stack.top().first; stack.pop(); } else if(c==tock_null) { *stack.top().second=null(); state=stack.top().first; stack.pop(); } else if(c==tock_number) { *stack.top().second=tock.real; state=stack.top().first; stack.pop(); } else state = st_error; break; case st_object_key_or_close_expected: if(c=='}') { state=stack.top().first; stack.pop(); } else if (c==tock_str) { key=tock.str; state = st_object_colon_expected; } else state = st_error; break; case st_object_colon_expected: if(c!=':') state=st_error; else state=st_object_value_expected; break; case st_object_value_expected: { json::object &obj = stack.top().second->object(); std::pair<json::object::iterator,bool> 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; value v; if(!parse_stream(in,*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