Browse Source

Performance improvements in json parsing

master
Artyom Beilis 8 years ago
parent
commit
7eaa3f2b78
3 changed files with 73 additions and 13 deletions
  1. +13
    -0
      cppcms/json.h
  2. +46
    -13
      src/json.cpp
  3. +14
    -0
      tests/json_test.cpp

+ 13
- 0
cppcms/json.h View File

@@ -486,6 +486,19 @@ namespace json {
bool load(std::istream &in,bool full,int *line_number=0);

///
/// Read a value from character range
///
/// Note: only JSON object and JSON array are considered valid values
///
/// \param begin - begin of the text range, at the end points to the end of parsed range
/// \param end - end of the text range
/// \param full require EOF once the object is read, otherwise consider it as syntax error
/// \param line_number return a number of the line where syntax error occurred
/// \result returns true if the value was read successfully, otherwise returns false to indicate a syntax error.
///
bool load(char const *&begin,char const *end,bool full,int *line_number=0);

///
/// Compare two values objects, return true if they are same
///
bool operator==(value const &other) const;


+ 46
- 13
src/json.cpp View File

@@ -840,11 +840,11 @@ namespace json {

int next()
{
std::streambuf *buf = is_.rdbuf();
for(;;) {
char c;
if(!is_.get(c))
int c;
if((c=buf->sbumpc())==-1)
return tock_eof;
switch(c) {
case '[':
case '{':
@@ -861,7 +861,7 @@ namespace json {
line++;
break;
case '"':
is_.unget();
buf->sungetc();
if(parse_string())
return tock_str;
return tock_err;
@@ -888,13 +888,13 @@ namespace json {
case '7':
case '8':
case '9':
is_.unget();
buf->sungetc();
if(parse_number())
return tock_number;
return tock_err;
case '/':
if(check("/")) {
while(is_.get(c) && c!='\n')
while((c=buf->sbumpc())!=-1 && c!='\n')
;
if(c=='\n')
break;
@@ -912,21 +912,22 @@ namespace json {

bool check(char const *s)
{
char c;
while(*s && is_.get(c) && c==*s)
std::streambuf *buf = is_.rdbuf();
while(*s && buf->sbumpc()==std::char_traits<char>::to_int_type(*s))
s++;
return *s==0;
}
bool parse_string()
{
char c;
int c;
std::streambuf *buf=is_.rdbuf();
str.clear();
if(!is_.get(c) || c!='"')
if((c=buf->sbumpc())==-1 || c!='"')
return false;
bool second_surragate_expected=false;
uint16_t first_surragate = 0;
for(;;) {
if(!is_.get(c))
if((c=buf->sbumpc())==-1)
return false;
if(second_surragate_expected && c!='\\')
return false;
@@ -935,7 +936,7 @@ namespace json {
if(c=='"')
break;
if(c=='\\') {
if(!is_.get(c))
if((c=buf->sbumpc())==-1)
return false;
if(second_surragate_expected && c!='u')
return false;
@@ -1056,6 +1057,28 @@ namespace json {
}
#endif

class charbuf : public std::streambuf {
public:
charbuf(char const *begin,char const *end)
{
setg(const_cast<char *>(begin),const_cast<char *>(begin),const_cast<char *>(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);
@@ -1270,7 +1293,6 @@ namespace json {
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;
@@ -1279,6 +1301,17 @@ namespace json {
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)
{


+ 14
- 0
tests/json_test.cpp View File

@@ -34,12 +34,21 @@ public:
json::value Parse(std::string s,int line)
{
std::istringstream ss(s);
json::value v2;
char const *begin,*end;
begin = s.c_str();
end = begin + s.size();
bool r=v2.load(begin,end,true);
json::value v;
if(!v.load(ss,true)) {
TEST(!r);
std::ostringstream tmp;
tmp << "Parsing error of " << s << " in line " << line;
throw parsing_error(tmp.str());
}
TEST(ss.get()==-1);
TEST(begin == end);
TEST(v==v2);
return v;
}

@@ -212,6 +221,11 @@ int main()
long double val = std::numeric_limits<long double>::max() / 100;
THROWS(v["x"]=val);
}
char const *part="{}[]";
TEST(v.load(part,part+4,false));
TEST(*part=='[');
TEST(v.type() == cppcms::json::is_object);
TEST(!v.load(part,part+4,true));

}
catch(std::exception const &e)


Loading…
Cancel
Save