From 9a567d9d2d2bce7e13e805748dca1e1e7db0682b Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Thu, 11 Jul 2013 14:19:11 +0200
Subject: [PATCH] - tidied code - added more documentation

---
 README.md   |  51 ++++++++++++-
 src/JSON.cc | 205 +++++++++++++++++++++++++++++++++++++---------------
 src/JSON.h  |  68 ++++++++++++-----
 3 files changed, 243 insertions(+), 81 deletions(-)

diff --git a/README.md b/README.md
index 38ad5812..c7e7986f 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,9 @@ There are myriads of [JSON](http://json.org) libraries out there, and each may e
 
 Other aspects were not so important to us:
 
-- **Memory efficiency**. Each JSON object has an overhead of one pointer and one enumeration element (1 byte). We use the following C++ data types: `std::string` for strings, `int` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. We know that there are more efficient ways to store the values, but we are happy enough right now. And by the way: [Valgrind](http://valgrind.org) says our code is free of leaks.
+- **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). We use the following C++ data types: `std::string` for strings, `int` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. We know that there are more efficient ways to store the values, but we are happy enough right now. And by the way: [Valgrind](http://valgrind.org) says our code is free of leaks.
 
-- **Speed**. We currently implement the parser as naive [recursive descent parser](http://en.wikipedia.org/wiki/Recursive_descent_parser) with hand coded string handling. It is fast enough, but a [LALR-parser](http://en.wikipedia.org/wiki/LALR_parser) with a decent regular expression processor should be even faster.
+- **Speed**. We currently implement the parser as naive [recursive descent parser](http://en.wikipedia.org/wiki/Recursive_descent_parser) with hand coded string handling. It is fast enough, but a [LALR-parser](http://en.wikipedia.org/wiki/LALR_parser) with a decent regular expression processor should be even faster (but would consist of more files which makes the integration harder).
 
 - **Rigourous standard compliance**. We followed the [specification](http://json.org) as close as possible, but did not invest too much in a 100% compliance with respect to Unicode support. As a result, there might be edge cases of false positives and false negatives, but as long as we have a hand-written parser, we won't invest too much to be fully compliant.
 
@@ -48,7 +48,7 @@ j["name"] = "Niels";
 // add an object inside the object
 j["further"]["entry"] = 42;
 
-// add an array that is stored as std::vector
+// add an array that is stored as std::vector (C++11)
 j["list"] = { 1, 0, 2 };
 ```
 
@@ -63,6 +63,8 @@ j << "{ \"pi\": 3.141, \"happy\": true }";
 std::cout << j;
 ```
 
+These operators work for any subclases of `std::istream` or `std::ostream`.
+
 ### STL-like access
 
 ```cpp
@@ -77,6 +79,11 @@ for (JSON::iterator it = j.begin(); it != j.end(); ++it) {
   std::cout << *it << '\n';
 }
 
+// C++11 style
+for (auto element : j) {
+  std::cout << element << '\n';
+}
+
 // getter/setter
 std::string tmp = j[0];
 j[1] = 42;
@@ -85,4 +92,42 @@ j[1] = 42;
 j.size();     // 3
 j.empty();    // false
 j.type();     // JSON::array
+
+// create an object
+JSON o;
+o["foo"] = 23;
+o["bar"] = false;
+o["baz"] = 3.141;
+
+// find an entry
+if (o.find("foo") != o.end()) {
+  // there is an entry with key "foo"
+}
+
+// iterate the object
+for (JSON::iterator it = o.begin(); it != o.end(); ++it) {
+  std::cout << it.key() << ':' << it.value() << '\n';
+}
 ```
+
+### Implicit conversions
+
+The type of the JSON object is determined automatically by the expression to store. Likewise, the stored value is implicitly converted 
+
+```cpp
+/// strings
+std::string s1 = "Hello, world!";
+JSON js = s;
+std::string s2 = j;
+
+// Booleans
+bool b1 = true;
+JSON jb = b1;
+bool b2 = jb;
+
+// numbers
+int i = 42;
+JSON jn = i;
+double f = jn;
+```
+
diff --git a/src/JSON.cc b/src/JSON.cc
index a0afea2b..130cfb2a 100644
--- a/src/JSON.cc
+++ b/src/JSON.cc
@@ -10,10 +10,20 @@
 #include <cstring>
 #include <cstdlib>
 
+// allow us to use "nullptr" everywhere
+#include <cstddef>
+#ifndef nullptr
+#define nullptr NULL
+#endif
+
+
+/////////////////////
+// HELPER FUNCTION //
+/////////////////////
+
 #ifdef __cplusplus11
 using std::to_string;
 #else
-
 inline std::string to_string(double f) {
     std::stringstream s;
     s << f;
@@ -21,18 +31,20 @@ inline std::string to_string(double f) {
 }
 #endif
 
-/******************
- * STATIC MEMBERS *
- ******************/
+
+////////////////////
+// STATIC MEMBERS //
+////////////////////
 
 #ifdef __cplusplus11
 /// a mutex to ensure thread safety
 std::mutex JSON::_token;
 #endif
 
-/*******************************
- * CONSTRUCTORS AND DESTRUCTOR *
- *******************************/
+
+/////////////////////////////////
+// CONSTRUCTORS AND DESTRUCTOR //
+/////////////////////////////////
 
 JSON::JSON() : _type(null) {}
 
@@ -133,6 +145,7 @@ JSON& JSON::operator=(const JSON& o) {
         return *this;
     }
 
+    // first delete original value
     switch (_type) {
         case (array): {
             delete _value.array;
@@ -160,6 +173,7 @@ JSON& JSON::operator=(const JSON& o) {
         }
     }
 
+    // then copy given value from o
     _type = o._type;
     switch (_type) {
         case (array): {
@@ -226,9 +240,9 @@ JSON::~JSON() {
 }
 
 
-/*****************************
- * OPERATORS AND CONVERSIONS *
- *****************************/
+///////////////////////////////
+// OPERATORS AND CONVERSIONS //
+///////////////////////////////
 
 JSON::operator const std::string() const {
     switch (_type) {
@@ -271,6 +285,27 @@ JSON::operator bool() const {
     }
 }
 
+JSON::operator std::vector<JSON>() const {
+    if (_type == array) {
+        return *_value.array;
+    }
+    if (_type == object) {
+        throw std::runtime_error("cannot cast " + _typename() + " to JSON array");
+    }
+
+    std::vector<JSON> result;
+    result.push_back(*this);
+    return result;
+}
+
+JSON::operator std::map<std::string, JSON>() const {
+    if (_type == object) {
+        return *_value.object;
+    } else {
+        throw std::runtime_error("cannot cast " + _typename() + " to JSON object");
+    }
+}
+
 const std::string JSON::toString() const {
     switch (_type) {
         case (null): {
@@ -322,9 +357,9 @@ const std::string JSON::toString() const {
 }
 
 
-/*****************************************
- * ADDING ELEMENTS TO OBJECTS AND ARRAYS *
- *****************************************/
+///////////////////////////////////////////
+// ADDING ELEMENTS TO OBJECTS AND ARRAYS //
+///////////////////////////////////////////
 
 JSON& JSON::operator+=(const JSON& o) {
     push_back(o);
@@ -483,40 +518,54 @@ const JSON& JSON::operator[](const std::string& key) const {
 /// return the number of stored values
 size_t JSON::size() const {
     switch (_type) {
-        case (array):
+        case (array): {
             return _value.array->size();
-        case (object):
+        }
+        case (object): {
             return _value.object->size();
-        case (null):
+        }
+        case (null): {
             return 0;
-        case (string):
+        }
+        case (string): {
             return 1;
-        case (boolean):
+        }
+        case (boolean): {
             return 1;
-        case (number):
+        }
+        case (number): {
             return 1;
-        case (number_float):
+        }
+        case (number_float): {
             return 1;
+        }
     }
 }
 
 /// checks whether object is empty
 bool JSON::empty() const {
     switch (_type) {
-        case (array):
+        case (array): {
             return _value.array->empty();
-        case (object):
+        }
+        case (object): {
             return _value.object->empty();
-        case (null):
+        }
+        case (null): {
             return true;
-        case (string):
+        }
+        case (string): {
             return false;
-        case (boolean):
+        }
+        case (boolean): {
             return false;
-        case (number):
+        }
+        case (number): {
             return false;
-        case (number_float):
+        }
+        case (number_float): {
             return false;
+        }
     }
 }
 
@@ -624,25 +673,36 @@ bool JSON::operator==(const JSON& o) const {
     return false;
 }
 
+/// lexicographically compares the values
+bool JSON::operator!=(const JSON& o) const {
+    return not operator==(o);
+}
 
 
 /// return the type as string
 std::string JSON::_typename() const {
     switch (_type) {
-        case (array):
+        case (array): {
             return "array";
-        case (object):
+        }
+        case (object): {
             return "object";
-        case (null):
+        }
+        case (null): {
             return "null";
-        case (string):
+        }
+        case (string): {
             return "string";
-        case (boolean):
+        }
+        case (boolean): {
             return "boolean";
-        case (number):
+        }
+        case (number): {
             return "number";
-        case (number_float):
+        }
+        case (number_float): {
             return "number";
+        }
     }
 }
 
@@ -683,7 +743,7 @@ JSON::parser::~parser() {
     delete [] _buffer;
 }
 
-void JSON::parser::error(std::string msg) {
+void JSON::parser::error(std::string msg = "") {
     throw std::runtime_error("parse error at position " + to_string(_pos) + ": " + msg + ", last read: '" + _current + "'");
 }
 
@@ -917,8 +977,9 @@ JSON::iterator::iterator(JSON* j) : _object(j), _vi(nullptr), _oi(nullptr) {
                 _oi = new object_t::iterator(_object->_value.object->begin());
                 break;
             }
-            default:
+            default: {
                 break;
+            }
         }
 }
 
@@ -933,8 +994,9 @@ JSON::iterator::iterator(const JSON::iterator& o) : _object(o._object), _vi(null
                 _oi = new object_t::iterator(*(o._oi));
                 break;
             }
-            default:
+            default: {
                 break;
+            }
         }
 }
 
@@ -955,8 +1017,9 @@ JSON::iterator& JSON::iterator::operator=(const JSON::iterator& o) {
                 _oi = new object_t::iterator(*(o._oi));
                 break;
             }
-            default:
+            default: {
                 break;
+            }
         }
     return *this;
 }
@@ -1002,12 +1065,15 @@ JSON& JSON::iterator::operator*() const {
     }
 
     switch (_object->_type) {
-        case (array):
+        case (array): {
             return **_vi;
-        case (object):
+        }
+        case (object): {
             return (*_oi)->second;
-        default:
+        }
+        default: {
             return *_object;
+        }
     }
 }
 
@@ -1017,12 +1083,15 @@ JSON* JSON::iterator::operator->() const {
     }
 
     switch (_object->_type) {
-        case (array):
+        case (array): {
             return &(**_vi);
-        case (object):
+        }
+        case (object): {
             return &((*_oi)->second);
-        default:
+        }
+        default: {
             return _object;
+        }
     }
 }
 
@@ -1040,12 +1109,15 @@ JSON& JSON::iterator::value() const {
     }
 
     switch (_object->_type) {
-        case (array):
+        case (array): {
             return **_vi;
-        case (object):
+        }
+        case (object): {
             return (*_oi)->second;
-        default:
+        }
+        default: {
             return *_object;
+        }
     }
 }
 
@@ -1079,8 +1151,9 @@ JSON::const_iterator::const_iterator(const JSON* j) : _object(j), _vi(nullptr),
                 _oi = new object_t::const_iterator(_object->_value.object->begin());
                 break;
             }
-            default:
+            default: {
                 break;
+            }
         }
 }
 
@@ -1095,8 +1168,9 @@ JSON::const_iterator::const_iterator(const JSON::const_iterator& o) : _object(o.
                 _oi = new object_t::const_iterator(*(o._oi));
                 break;
             }
-            default:
+            default: {
                 break;
+            }
         }
 }
 
@@ -1111,8 +1185,9 @@ JSON::const_iterator::const_iterator(const JSON::iterator& o) : _object(o._objec
                 _oi = new object_t::const_iterator(*(o._oi));
                 break;
             }
-            default:
+            default: {
                 break;
+            }
         }
 }
 
@@ -1133,8 +1208,9 @@ JSON::const_iterator& JSON::const_iterator::operator=(const JSON::const_iterator
                 _oi = new object_t::const_iterator(*(o._oi));
                 break;
             }
-            default:
+            default: {
                 break;
+            }
         }
     return *this;
 }
@@ -1180,12 +1256,15 @@ const JSON& JSON::const_iterator::operator*() const {
     }
 
     switch (_object->_type) {
-        case (array):
+        case (array): {
             return **_vi;
-        case (object):
+        }
+        case (object): {
             return (*_oi)->second;
-        default:
+        }
+        default: {
             return *_object;
+        }
     }
 }
 
@@ -1195,12 +1274,15 @@ const JSON* JSON::const_iterator::operator->() const {
     }
 
     switch (_object->_type) {
-        case (array):
+        case (array): {
             return &(**_vi);
-        case (object):
+        }
+        case (object): {
             return &((*_oi)->second);
-        default:
+        }
+        default: {
             return _object;
+        }
     }
 }
 
@@ -1218,11 +1300,14 @@ const JSON& JSON::const_iterator::value() const {
     }
 
     switch (_object->_type) {
-        case (array):
+        case (array): {
             return **_vi;
-        case (object):
+        }
+        case (object): {
             return (*_oi)->second;
-        default:
+        }
+        default: {
             return *_object;
+        }
     }
 }
diff --git a/src/JSON.h b/src/JSON.h
index e6a3ee99..4871807a 100644
--- a/src/JSON.h
+++ b/src/JSON.h
@@ -5,30 +5,25 @@
 #define __cplusplus11
 #endif
 
-// allow us to use "nullptr" everywhere
-#include <cstddef>
-#ifndef nullptr
-#define nullptr NULL
-#endif
-
+// STL containers
 #include <string>
 #include <vector>
 #include <map>
 
-// additional C++11 header
+// additional C++11 headers
 #ifdef __cplusplus11
 #include <mutex>
 #include <initializer_list>
 #endif
 
 class JSON {
-        // forward declaration to friend this class
+    // forward declaration to friend this class
     public:
         class iterator;
         class const_iterator;
 
-    private:
 #ifdef __cplusplus11
+    private:
         /// mutex to guard payload
         static std::mutex _token;
 #endif
@@ -55,19 +50,32 @@ class JSON {
 
         /// a JSON value
         union value {
+            /// array as pointer to array_t
             array_t* array;
+            /// object as pointer to object_t
             object_t* object;
+            /// string as pointer to string_t
             string_t* string;
+            /// Boolean
             boolean_t boolean;
+            /// number (integer)
             number_t number;
+            /// number (float)
             number_float_t number_float;
 
+            /// default constructor
             value() {}
+            /// constructor for arrays
             value(array_t* array): array(array) {}
+            /// constructor for objects
             value(object_t* object): object(object) {}
+            /// constructor for strings
             value(string_t* string): string(string) {}
+            /// constructor for Booleans
             value(boolean_t boolean) : boolean(boolean) {}
+            /// constructor for numbers (integer)
             value(number_t number) : number(number) {}
+            /// constructor for numbers (float)
             value(number_float_t number_float) : number_float(number_float) {}
         };
 
@@ -77,22 +85,22 @@ class JSON {
 
         /// the payload
         value _value;
-
+        
 #ifdef __cplusplus11
         /// a type for array initialization
         typedef std::initializer_list<JSON> array_init_t;
 #endif
 
     public:
-        /// create an empty (null) object
+        /// create a null object
         JSON();
-        /// create an empty object according to given type
+        /// create an object according to given type
         JSON(json_t);
-        /// create a string object from C++ string
+        /// create a string object from a C++ string
         JSON(const std::string&);
-        /// create a string object from C string
+        /// create a string object from a C string
         JSON(char*);
-        /// create a string object from C string
+        /// create a string object from a C string
         JSON(const char*);
         /// create a Boolean object
         JSON(const bool);
@@ -135,13 +143,16 @@ class JSON {
         operator double() const;
         /// implicit conversion to Boolean (only for Booleans)
         operator bool() const;
+        /// implicit conversion to JSON vector (not for objects)
+        operator std::vector<JSON>() const;
+        /// implicit conversion to JSON map (only for objects)
+        operator std::map<std::string, JSON>() const;
 
         /// write to stream
         friend std::ostream& operator<<(std::ostream& o, const JSON& j) {
             o << j.toString();
             return o;
         }
-
         /// write to stream
         friend std::ostream& operator>>(const JSON& j, std::ostream& o) {
             o << j.toString();
@@ -153,7 +164,6 @@ class JSON {
             parser(i).parse(j);
             return i;
         }
-
         /// read from stream
         friend std::istream& operator<<(JSON& j, std::istream& i) {
             parser(i).parse(j);
@@ -211,8 +221,11 @@ class JSON {
 
         /// find an element in an object (returns end() iterator otherwise)
         iterator find(const std::string&);
+        /// find an element in an object (returns end() iterator otherwise)
         const_iterator find(const std::string&) const;
+        /// find an element in an object (returns end() iterator otherwise)
         iterator find(const char*);
+        /// find an element in an object (returns end() iterator otherwise)
         const_iterator find(const char*) const;
 
         /// direct access to the underlying payload
@@ -222,6 +235,8 @@ class JSON {
 
         /// lexicographically compares the values
         bool operator==(const JSON&) const;
+        /// lexicographically compares the values
+        bool operator!=(const JSON&) const;
 
     private:
         /// return the type as string
@@ -299,26 +314,43 @@ class JSON {
         const_iterator cend() const;
 
     private:
+        /// a helper class to parse a JSON object
         class parser {
             public:
+                /// a parser reading from a C string
                 parser(char*);
+                /// a parser reading from a C++ string
                 parser(std::string&);
+                /// a parser reading from an input stream
                 parser(std::istream&);
+                /// destructor of the parser
                 ~parser();
+                /// parse into a given JSON object
                 void parse(JSON&);
 
             private:
+                /// read the next character, stripping whitespace
                 bool next();
-                void error(std::string = "") __attribute__((noreturn));
+                /// raise an exception with an error message
+                void error(std::string) __attribute__((noreturn));
+                /// parse a quoted string
                 std::string parseString();
+                /// parse a Boolean "true"
                 void parseTrue();
+                /// parse a Boolean "false"
                 void parseFalse();
+                /// parse a null object
                 void parseNull();
+                /// a helper function to expect a certain character
                 void expect(char);
 
+                /// the current character
                 char _current;
+                /// a buffer of the input
                 char* _buffer;
+                /// the position inside the input buffer
                 size_t _pos;
+                /// the length of the input buffer
                 size_t _length;
         };
 };