Przeglądaj źródła

[Fix #20] add null primitive type
[Refactor] optimized / moved stream reading code

isundil 9 lat temu
rodzic
commit
e019e07f76

+ 2 - 0
include/jsonPrimitive.hh

@@ -8,6 +8,8 @@
 
 #include "jsonElement.hh"
 
+class Null {}; //Null primitive
+
 class AJSonPrimitive
 {
     public:

+ 1 - 0
include/outputFlag.hh

@@ -51,6 +51,7 @@ class OutputFlag
         static const char TYPE_OBJ;
         static const char TYPE_OBJKEY;
         static const char TYPE_ARR;
+        static const char TYPE_NULL;
 
         static const char SPECIAL_NONE;
         static const char SPECIAL_SEARCH;

+ 7 - 2
include/streamConsumer.hh

@@ -49,7 +49,12 @@ class StreamConsumer
         /**
          * @return non-null on successfully read JSonElement, or null if token (',', '[', ...)
         **/
-        JSonElement *consumeToken(JSonContainer *parent, std::string &buf);
+        JSonElement *consumeToken(JSonContainer *parent, std::stringstream &buf);
+        JSonElement *consumeString(JSonContainer *parent, std::stringstream &buf);
+        JSonElement *consumeBool(JSonContainer *parent, std::stringstream &buf, char c);
+        JSonElement *consumeNumber(JSonContainer *parent, std::stringstream &buf, char c);
+        JSonElement *consumeNull(JSonContainer *parent, std::stringstream &buf);
+
         /**
          * read next item, fill object or array if found
         **/
@@ -70,7 +75,7 @@ class StreamConsumer
         /**
          * compute unicode value and append it to buffer
         **/
-        static void appendUnicode(const char [4], std::string &);
+        static void appendUnicode(const char [4], std::stringstream &);
 
         /**
          * input stream

+ 4 - 0
src/curseOutput.cpp

@@ -361,6 +361,8 @@ const OutputFlag CurseOutput::getFlag(const JSonElement *item) const
         res.type(OutputFlag::TYPE_STRING);
     else if (dynamic_cast<const JSonPrimitive<bool> *>(i))
         res.type(OutputFlag::TYPE_BOOL);
+    else if (dynamic_cast<const JSonPrimitive<Null> *>(i))
+        res.type(OutputFlag::TYPE_NULL);
     else if (dynamic_cast<const AJSonPrimitive *>(i))
         res.type(OutputFlag::TYPE_NUMBER);
     else if (dynamic_cast<const JSonObject*>(i))
@@ -695,6 +697,7 @@ void CurseOutput::init()
         start_color();
         init_pair(OutputFlag::TYPE_NUMBER, COLOR_GREEN, COLOR_BLACK);
         init_pair(OutputFlag::TYPE_BOOL, COLOR_RED, COLOR_BLACK);
+        init_pair(OutputFlag::TYPE_NULL, COLOR_RED, COLOR_BLACK);
         init_pair(OutputFlag::TYPE_STRING, COLOR_CYAN, COLOR_BLACK);
         init_pair(OutputFlag::TYPE_OBJKEY, COLOR_CYAN, COLOR_BLACK);
         init_pair(OutputFlag::SPECIAL_SEARCH, COLOR_WHITE, COLOR_BLUE);
@@ -703,6 +706,7 @@ void CurseOutput::init()
         colors.insert(OutputFlag::TYPE_BOOL);
         colors.insert(OutputFlag::TYPE_STRING);
         colors.insert(OutputFlag::TYPE_OBJKEY);
+        colors.insert(OutputFlag::TYPE_NULL);
     }
 
     signal(SIGWINCH, _resizeFnc);

+ 4 - 0
src/jsonPrimitive.cpp

@@ -9,6 +9,7 @@
 AJSonPrimitive::~AJSonPrimitive()
 {}
 
+template<> JSonPrimitive<Null>::~JSonPrimitive() {}
 template<> JSonPrimitive<double>::~JSonPrimitive() {}
 template<> JSonPrimitive<bool>::~JSonPrimitive() {}
 template<> JSonPrimitive<int>::~JSonPrimitive() {}
@@ -18,6 +19,9 @@ template<> JSonPrimitive<std::string>::~JSonPrimitive() {}
 template<> std::string JSonPrimitive<std::string>::toString() const
 { return value; }
 
+template<> std::string JSonPrimitive<Null>::toString() const
+{ return "null"; }
+
 template<> std::string JSonPrimitive<double>::toString() const
 {
     return std::to_string(value);

+ 1 - 0
src/outputFlag.cpp

@@ -13,6 +13,7 @@ const char OutputFlag::TYPE_BOOL    =3;
 const char OutputFlag::TYPE_OBJ     =4;
 const char OutputFlag::TYPE_OBJKEY  =5;
 const char OutputFlag::TYPE_ARR     =6;
+const char OutputFlag::TYPE_NULL    =7;
 
 const char OutputFlag::SPECIAL_NONE     =50;
 const char OutputFlag::SPECIAL_SEARCH   =51;

+ 184 - 120
src/streamConsumer.cpp

@@ -34,8 +34,9 @@ StreamConsumer *StreamConsumer::read()
 
 JSonElement *StreamConsumer::readNext(JSonContainer *parent)
 {
-    std::string buf;
-    JSonElement *root = consumeToken(parent, buf);
+    std::stringstream sbuf;
+    JSonElement *root = consumeToken(parent, sbuf);
+    const std::string buf = sbuf.str();
 
     if (root == nullptr)
     {
@@ -63,17 +64,17 @@ JSonObject *StreamConsumer::readObject(JSonContainer *parent)
 {
     JSonElement *keyObj;
     JSonObject *result = nullptr;
-    std::string buf;
+    std::stringstream buf;
 
     do
     {
         keyObj = consumeToken(result, buf);
-        if (result == nullptr && keyObj == nullptr && buf == "}")
+        if (result == nullptr && keyObj == nullptr && buf.str() == "}")
             return new JSonObject(parent);
         JSonPrimitive<std::string> *key = dynamic_cast<JSonPrimitive<std::string> *>(keyObj);
         if (key == nullptr)
             throw JSonObject::NotAKeyException(stream.tellg(), history);
-        if (consumeToken(parent, buf) != nullptr || buf != ":")
+        if (consumeToken(parent, buf) != nullptr || buf.str() != ":")
             throw JsonUnexpectedException(':', stream.tellg(), history);
         if (result == nullptr)
             result = new JSonObject(parent);
@@ -91,15 +92,16 @@ JSonObject *StreamConsumer::readObject(JSonContainer *parent)
         result->push(key->getValue(), child);
         delete keyObj;
         keyObj = consumeToken(result, buf);
-    } while (!keyObj && buf != "}");
+    } while (!keyObj && buf.str() != "}");
     return result;
 }
 
 JSonArray *StreamConsumer::readArray(JSonContainer *parent)
 {
     JSonArray *result = nullptr;
-    std::string buf;
-    JSonElement *child = consumeToken(parent, buf);
+    std::stringstream sbuf;
+    JSonElement *child = consumeToken(parent, sbuf);
+    std::string buf = sbuf.str();
 
     if (child == nullptr && buf == "]")
         return new JSonArray(parent); //Empty object
@@ -115,159 +117,221 @@ JSonArray *StreamConsumer::readArray(JSonContainer *parent)
             result = new JSonArray(parent);
         child->setParent(result);
         result->push_back(child);
-        child = consumeToken(result, buf);
+        child = consumeToken(result, sbuf);
+        buf = sbuf.str();
         if (child != nullptr)
             throw JsonUnexpectedException(']', stream.tellg(), history);
         else if (buf == "]")
             break;
         else if (buf != ",")
             throw JsonUnexpectedException(']', stream.tellg(), history);
-        child = consumeToken(result, buf);
+        child = consumeToken(result, sbuf);
+        buf = sbuf.str();
     } while (true);
     return result;
 }
 
-JSonElement *StreamConsumer::consumeToken(JSonContainer *parent, std::string &buf)
+JSonElement *StreamConsumer::consumeString(JSonContainer *parent, std::stringstream &buf)
 {
     bool escaped = false;
-    bool inString = false;
-    bool inBool = false;
-    bool inNumber = false;
-    bool numberIsDouble = false;
+
+    buf.str("");
+    buf.clear();
 
     while (stream.good())
     {
         char c = stream.get();
         history.put(c);
 
-        if (inString)
+        if (!escaped)
         {
-            if (!escaped)
-            {
-                if (c == '"')
-                    return new JSonPrimitive<std::string>(parent, buf);
-                else if (c == '\\')
-                    escaped = true;
-                else
-                    buf += c;
-            }
+            if (c == '"')
+                return new JSonPrimitive<std::string>(parent, buf.str());
+            else if (c == '\\')
+                escaped = true;
             else
+                buf.write(&c, 1);
+        }
+        else
+        {
+            if (c == '\\' || c == '"')
+                buf.write("\"", 1);
+            else if (c == 'u')
             {
-                if (c == '\\' || c == '"')
-                {
-                    buf += c;
-                    escaped = false;
-                }
-                else if (c == 'u')
+                if (params && params->isIgnoringUnicode())
+                    buf.write("\\u", 2);
+                else
                 {
-                    if (params && params->isIgnoringUnicode())
-                    {
-                        buf += "\\u";
-                        escaped = false;
+                    char unicodeBuf[4];
+                    stream.read(unicodeBuf, 4);
+                    std::streamsize gcount = stream.gcount();
+                    history.put(unicodeBuf, gcount);
+                    if (gcount != 4)
+                        break;
+                    try {
+                        appendUnicode(unicodeBuf, buf);
                     }
-                    else
+                    catch (std::invalid_argument &e)
                     {
-                        char unicodeBuf[4];
-                        stream.read(unicodeBuf, 4);
-                        std::streamsize gcount = stream.gcount();
-                        history.put(unicodeBuf, gcount);
-                        if (gcount != 4)
-                            break;
-                        try {
-                            appendUnicode(unicodeBuf, buf);
-                        }
-                        catch (std::invalid_argument &e)
-                        {
-                            throw JsonHexvalueException(e.what(), stream.tellg(), history);
-                        }
-                        escaped = false;
+                        throw JsonHexvalueException(e.what(), stream.tellg(), history);
                     }
                 }
-                else
-                    throw JsonEscapedException(c, stream.tellg(), history);
-            }
-        }
-        else if (inBool)
-        {
-            if (c == 'a' || c == 'e' || c == 'l' || c == 'r' || c == 's' || c == 'u')
-                buf += c;
-            else if (buf == "true")
-            {
-                history.pop_back();
-                stream.unget();
-                return new JSonPrimitive<bool>(parent, true);
             }
-            else if (buf == "false")
+            else if (params->isStrict())
+                throw JsonEscapedException(c, stream.tellg(), history);
+            else
             {
-                history.pop_back();
-                stream.unget();
-                return new JSonPrimitive<bool>(parent, false);
+                buf.write("\\", 1).write(&c, 1);
+                warnings.push_back(Warning(JsonEscapedException(c, stream.tellg(), history)));
             }
-            else if (ignoreChar(c))
-                ;
-            else
+            escaped = false;
+        }
+    }
+    buf.str("");
+    buf.clear();
+    return nullptr;
+}
+
+JSonElement *StreamConsumer::consumeBool(JSonContainer *parent, std::stringstream &buf, char firstChar)
+{
+    size_t read =1;
+
+    buf.str("");
+    buf.clear();
+    buf.write(&firstChar, 1);
+
+    //TODO batch-get 3 char, then do that
+    while (stream.good())
+    {
+        char c = stream.get();
+        history.put(c);
+
+        if (c == 'a' || c == 'e' || c == 'l' || c == 'r' || c == 's' || c == 'u')
+        {
+            if ((read >= 5 && firstChar == 'f') || (read >= 4 && firstChar == 't'))
                 throw JsonFormatException(stream.tellg(), history);
+            buf.write(&c, 1);
+            read++;
         }
-        else if (inNumber)
+        else if (buf.str() == "true")
         {
-            if (c >= '0' && c <= '9')
-                buf += c;
-            else if (c == '.' && !numberIsDouble)
-            {
-                numberIsDouble = true;
-                buf += c;
-            }
-            else
-            {
-                history.pop_back();
-                stream.unget();
-                if (numberIsDouble)
-                {
-                    try {
-                        return new JSonPrimitive<double>(parent, atof(buf.c_str()));
-                    } catch (std::runtime_error &e)
-                    {
-                        throw JsonFormatException(stream.tellg(), history);
-                    }
-                }
-                try
-                {
-                    return new JSonPrimitive<int>(parent, std::stoi(buf));
-                }
-                catch(std::out_of_range e)
-                {
-                    return new JSonPrimitive<long long>(parent, std::stol(buf));
-                }
-            }
+            history.pop_back();
+            stream.unget();
+            return new JSonPrimitive<bool>(parent, true);
+        }
+        else if (buf.str() == "false")
+        {
+            history.pop_back();
+            stream.unget();
+            return new JSonPrimitive<bool>(parent, false);
         }
+        else if (ignoreChar(c))
+            ;
         else
+            throw JsonFormatException(stream.tellg(), history);
+    }
+    buf.str("");
+    buf.clear();
+    return nullptr;
+}
+
+JSonElement *StreamConsumer::consumeNumber(JSonContainer *parent, std::stringstream &buf, char firstChar)
+{
+    bool numberIsDouble = false;
+
+    buf.str("");
+    buf.clear();
+    buf.write(&firstChar, 1);
+
+    while (stream.good())
+    {
+        char c = stream.get();
+        history.put(c);
+
+        if (c >= '0' && c <= '9')
+            buf.write(&c, 1);
+        else if (c == '.' && !numberIsDouble)
         {
-            //!InString, !inbool
-            if (c == '"')
-            {
-                buf = "";
-                inString = true;
-            }
-            else if (c == 't' || c == 'f')
+            numberIsDouble = true;
+            buf.write(&c, 1);
+        }
+        else
+        {
+            history.pop_back();
+            stream.unget();
+            if (numberIsDouble)
             {
-                buf = c;
-                inBool = true;
+                try {
+                    return new JSonPrimitive<double>(parent, atof(buf.str().c_str()));
+                } catch (std::runtime_error &e)
+                {
+                    throw JsonFormatException(stream.tellg(), history);
+                }
             }
-            else if (c == '{' || c == '[' || c == '}' || c == ']' || c == ':' || c == ',')
+            try
             {
-                buf = c;
-                return nullptr;
+                return new JSonPrimitive<int>(parent, std::stoi(buf.str()));
             }
-            else if ((c >= '0' && c <= '9') || c == '.' || c == '-')
+            catch(std::out_of_range e)
             {
-                buf = c;
-                inNumber = true;
+                return new JSonPrimitive<long long>(parent, std::stol(buf.str()));
             }
-            else if (!ignoreChar(c))
-                throw JsonFormatException(stream.tellg(), history);
         }
     }
-    buf = "";
+    buf.str("");
+    buf.clear();
+    return nullptr;
+}
+
+JSonElement *StreamConsumer::consumeNull(JSonContainer *parent, std::stringstream &buf)
+{
+    char _buf[5] = { 'n', '\0', '\0', '\0', '\0' };
+
+    buf.str("");
+    buf.clear();
+
+    stream.read(&_buf[1], 3);
+    buf.write(_buf, 4);
+    history.put(&_buf[1], 3);
+    if (!stream.good())
+    {
+        buf.str("");
+        buf.clear();
+        return nullptr;
+    }
+    if (std::string("null") == _buf)
+        return new JSonPrimitive<Null>(parent, Null());
+    throw JsonFormatException(stream.tellg(), history);
+}
+
+JSonElement *StreamConsumer::consumeToken(JSonContainer *parent, std::stringstream &buf)
+{
+    while (stream.good())
+    {
+        char c = stream.get();
+        history.put(c);
+
+        //!InString, !inbool
+        if (c == '"')
+            return consumeString(parent, buf);
+        else if (c == 't' || c == 'f')
+            return consumeBool(parent, buf, c);
+        else if (c == 'n')
+            return consumeNull(parent, buf);
+        else if ((c >= '0' && c <= '9') || c == '.' || c == '-')
+            return consumeNumber(parent, buf, c);
+        else if (c == '{' || c == '[' || c == '}' || c == ']' || c == ':' || c == ',')
+        {
+            buf.str("");
+            buf.clear();
+            buf.write(&c, 1);
+            return nullptr;
+        }
+        else if (!ignoreChar(c))
+            throw JsonFormatException(stream.tellg(), history);
+    }
+    buf.str("");
+    buf.clear();
     return nullptr;
 }
 
@@ -291,13 +355,13 @@ static T hexbyte(const char str[], unsigned int len)
     return result;
 }
 
-void StreamConsumer::appendUnicode(const char unicode[4], std::string &buf)
+void StreamConsumer::appendUnicode(const char unicode[4], std::stringstream &buf)
 {
     unsigned short uni = hexbyte<unsigned short>(unicode, 4);
     char test[5];
     bzero(test, sizeof(*test) *5);
     snprintf(test, 4, "%lc", uni);
-    buf += test;
+    buf.write(test, 4);
 }
 
 bool StreamConsumer::ignoreChar(char c) const noexcept