1304 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1304 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /// \file json.cpp Holds all JSON-related code.
 | |
| 
 | |
| #include "json.h"
 | |
| #include "defines.h"
 | |
| #include <sstream>
 | |
| #include <fstream>
 | |
| #include <stdlib.h>
 | |
| #include <stdint.h> //for uint64_t
 | |
| #include <string.h> //for memcpy
 | |
| #include <arpa/inet.h> //for htonl
 | |
| 
 | |
| /// Construct from a root Value to iterate over.
 | |
| JSON::Iter::Iter(Value & root){
 | |
|   myType = root.myType;
 | |
|   i = 0;
 | |
|   r = &root;
 | |
|   if (!root.size()){myType = JSON::EMPTY;}
 | |
|   if (myType == JSON::ARRAY){
 | |
|     aIt = root.arrVal.begin();
 | |
|   }
 | |
|   if (myType == JSON::OBJECT){
 | |
|     oIt = root.objVal.begin();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// Dereferences into a Value reference.
 | |
| /// If invalid iterator, returns an empty reference and prints a warning message.
 | |
| JSON::Value & JSON::Iter::operator*() const{
 | |
|   if (myType == JSON::ARRAY && aIt != r->arrVal.end()){
 | |
|     return **aIt;
 | |
|   }
 | |
|   if (myType == JSON::OBJECT && oIt != r->objVal.end()){
 | |
|     return *(oIt->second);
 | |
|   }
 | |
|   static JSON::Value error;
 | |
|   WARN_MSG("Dereferenced invalid JSON iterator");
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| /// Dereferences into a Value reference.
 | |
| /// If invalid iterator, returns an empty reference and prints a warning message.
 | |
| JSON::Value * JSON::Iter::operator->() const{
 | |
|   return &(operator*());
 | |
| }
 | |
| 
 | |
| /// True if not done iterating.
 | |
| JSON::Iter::operator bool() const{
 | |
|   return ((myType == JSON::ARRAY && aIt != r->arrVal.end()) || (myType == JSON::OBJECT && oIt != r->objVal.end()));
 | |
| }
 | |
| 
 | |
| /// Go to next iteration.
 | |
| JSON::Iter & JSON::Iter::operator++(){
 | |
|   if (*this){
 | |
|     ++i;
 | |
|     if (myType == JSON::ARRAY){
 | |
|       ++aIt;
 | |
|     }
 | |
|     if (myType == JSON::OBJECT){
 | |
|       ++oIt;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// Return the name of the current indice.
 | |
| const std::string & JSON::Iter::key() const{
 | |
|   if (myType == JSON::OBJECT && *this){
 | |
|     return oIt->first;
 | |
|   }
 | |
|   static const std::string empty;
 | |
|   WARN_MSG("Got key from invalid JSON iterator");
 | |
|   return empty;
 | |
| }
 | |
| 
 | |
| /// Return the number of the current indice.
 | |
| unsigned int JSON::Iter::num() const{
 | |
|   return i;
 | |
| }
 | |
| 
 | |
| /// Construct from a root Value to iterate over.
 | |
| JSON::ConstIter::ConstIter(const Value & root){
 | |
|   myType = root.myType;
 | |
|   i = 0;
 | |
|   r = &root;
 | |
|   if (!root.size()){myType = JSON::EMPTY;}
 | |
|   if (myType == JSON::ARRAY){
 | |
|     aIt = root.arrVal.begin();
 | |
|   }
 | |
|   if (myType == JSON::OBJECT){
 | |
|     oIt = root.objVal.begin();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// Dereferences into a Value reference.
 | |
| /// If invalid iterator, returns an empty reference and prints a warning message.
 | |
| const JSON::Value & JSON::ConstIter::operator*() const{
 | |
|   if (myType == JSON::ARRAY && aIt != r->arrVal.end()){
 | |
|     return **aIt;
 | |
|   }
 | |
|   if (myType == JSON::OBJECT && oIt != r->objVal.end()){
 | |
|     return *(oIt->second);
 | |
|   }
 | |
|   static JSON::Value error;
 | |
|   WARN_MSG("Dereferenced invalid JSON iterator");
 | |
|   return error;
 | |
| }
 | |
| 
 | |
| /// Dereferences into a Value reference.
 | |
| /// If invalid iterator, returns an empty reference and prints a warning message.
 | |
| const JSON::Value * JSON::ConstIter::operator->() const{
 | |
|   return &(operator*());
 | |
| }
 | |
| 
 | |
| /// True if not done iterating.
 | |
| JSON::ConstIter::operator bool() const{
 | |
|   return ((myType == JSON::ARRAY && aIt != r->arrVal.end()) || (myType == JSON::OBJECT && oIt != r->objVal.end()));
 | |
| }
 | |
| 
 | |
| /// Go to next iteration.
 | |
| JSON::ConstIter & JSON::ConstIter::operator++(){
 | |
|   if (*this){
 | |
|     ++i;
 | |
|     if (myType == JSON::ARRAY){
 | |
|       ++aIt;
 | |
|     }
 | |
|     if (myType == JSON::OBJECT){
 | |
|       ++oIt;
 | |
|     }
 | |
|   }
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| /// Return the name of the current indice.
 | |
| const std::string & JSON::ConstIter::key() const{
 | |
|   if (myType == JSON::OBJECT && *this){
 | |
|     return oIt->first;
 | |
|   }
 | |
|   static const std::string empty;
 | |
|   WARN_MSG("Got key from invalid JSON iterator");
 | |
|   return empty;
 | |
| }
 | |
| 
 | |
| /// Return the number of the current indice.
 | |
| unsigned int JSON::ConstIter::num() const{
 | |
|   return i;
 | |
| }
 | |
| 
 | |
| static inline char c2hex(char c) {
 | |
|   if (c >= '0' && c <= '9') return c - '0';
 | |
|   if (c >= 'a' && c <= 'f') return c - 'a' + 10;
 | |
|   if (c >= 'A' && c <= 'F') return c - 'A' + 10;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static inline char hex2c(char c) {
 | |
|   if (c < 10) {
 | |
|     return '0' + c;
 | |
|   }
 | |
|   if (c < 16) {
 | |
|     return 'A' + (c - 10);
 | |
|   }
 | |
|   return '0';
 | |
| }
 | |
| 
 | |
| static std::string read_string(int separator, std::istream & fromstream) {
 | |
|   std::string out;
 | |
|   bool escaped = false;
 | |
|   while (fromstream.good()) {
 | |
|     char c;
 | |
|     fromstream.get(c);
 | |
|     if (c == '\\') {
 | |
|       escaped = true;
 | |
|       continue;
 | |
|     }
 | |
|     if (escaped) {
 | |
|       switch (c) {
 | |
|         case 'b':
 | |
|           out += '\b';
 | |
|           break;
 | |
|         case 'f':
 | |
|           out += '\f';
 | |
|           break;
 | |
|         case 'n':
 | |
|           out += '\n';
 | |
|           break;
 | |
|         case 'r':
 | |
|           out += '\r';
 | |
|           break;
 | |
|         case 't':
 | |
|           out += '\t';
 | |
|           break;
 | |
|         case 'u': {
 | |
|             char d1, d2, d3, d4;
 | |
|             fromstream.get(d1);
 | |
|             fromstream.get(d2);
 | |
|             fromstream.get(d3);
 | |
|             fromstream.get(d4);
 | |
|             out.append(1, (c2hex(d4) + (c2hex(d3) << 4)));
 | |
|             //We ignore the upper two characters.
 | |
|             // + (c2hex(d2) << 8) + (c2hex(d1) << 16)
 | |
|             break;
 | |
|           }
 | |
|         default:
 | |
|           out.append(1, c);
 | |
|           break;
 | |
|       }
 | |
|       escaped = false;
 | |
|     } else {
 | |
|       if (c == separator) {
 | |
|         return out;
 | |
|       } else {
 | |
|         out.append(1, c);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return out;
 | |
| }
 | |
| 
 | |
| static std::string string_escape(const std::string val) {
 | |
|   std::string out = "\"";
 | |
|   for (unsigned int i = 0; i < val.size(); ++i) {
 | |
|     switch (val.data()[i]) {
 | |
|       case '"':
 | |
|         out += "\\\"";
 | |
|         break;
 | |
|       case '\\':
 | |
|         out += "\\\\";
 | |
|         break;
 | |
|       case '\n':
 | |
|         out += "\\n";
 | |
|         break;
 | |
|       case '\b':
 | |
|         out += "\\b";
 | |
|         break;
 | |
|       case '\f':
 | |
|         out += "\\f";
 | |
|         break;
 | |
|       case '\r':
 | |
|         out += "\\r";
 | |
|         break;
 | |
|       case '\t':
 | |
|         out += "\\t";
 | |
|         break;
 | |
|       default:
 | |
|         if (val.data()[i] < 32 || val.data()[i] > 126) {
 | |
|           out += "\\u00";
 | |
|           out += hex2c((val.data()[i] >> 4) & 0xf);
 | |
|           out += hex2c(val.data()[i] & 0xf);
 | |
|         } else {
 | |
|           out += val.data()[i];
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
|   out += "\"";
 | |
|   return out;
 | |
| }
 | |
| 
 | |
| /// Skips an std::istream forward until any of the following characters is seen: ,]}
 | |
| static void skipToEnd(std::istream & fromstream) {
 | |
|   while (fromstream.good()) {
 | |
|     char peek = fromstream.peek();
 | |
|     if (peek == ',') {
 | |
|       return;
 | |
|     }
 | |
|     if (peek == ']') {
 | |
|       return;
 | |
|     }
 | |
|     if (peek == '}') {
 | |
|       return;
 | |
|     }
 | |
|     peek = fromstream.get();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to null;
 | |
| JSON::Value::Value() {
 | |
|   null();
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to null
 | |
| JSON::Value::~Value() {
 | |
|   null();
 | |
| }
 | |
| 
 | |
| JSON::Value::Value(const Value & rhs) {
 | |
|   null();
 | |
|   *this = rhs;
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to read from this position in the std::istream
 | |
| JSON::Value::Value(std::istream & fromstream) {
 | |
|   null();
 | |
|   bool reading_object = false;
 | |
|   bool reading_array = false;
 | |
|   bool negative = false;
 | |
|   bool stop = false;
 | |
|   while (!stop && fromstream.good()) {
 | |
|     int c = fromstream.peek();
 | |
|     switch (c) {
 | |
|       case '{':
 | |
|         reading_object = true;
 | |
|         c = fromstream.get();
 | |
|         myType = OBJECT;
 | |
|         break;
 | |
|       case '[': {
 | |
|           reading_array = true;
 | |
|           c = fromstream.get();
 | |
|           myType = ARRAY;
 | |
|           Value tmp = JSON::Value(fromstream);
 | |
|           if (tmp.myType != EMPTY) {
 | |
|             append(tmp);
 | |
|           }
 | |
|           break;
 | |
|         }
 | |
|       case '\'':
 | |
|       case '"':
 | |
|         c = fromstream.get();
 | |
|         if (!reading_object) {
 | |
|           myType = STRING;
 | |
|           strVal = read_string(c, fromstream);
 | |
|           stop = true;
 | |
|         } else {
 | |
|           std::string tmpstr = read_string(c, fromstream);
 | |
|           (*this)[tmpstr] = JSON::Value(fromstream);
 | |
|         }
 | |
|         break;
 | |
|       case '-':
 | |
|         c = fromstream.get();
 | |
|         negative = true;
 | |
|         break;
 | |
|       case '0':
 | |
|       case '1':
 | |
|       case '2':
 | |
|       case '3':
 | |
|       case '4':
 | |
|       case '5':
 | |
|       case '6':
 | |
|       case '7':
 | |
|       case '8':
 | |
|       case '9':
 | |
|         c = fromstream.get();
 | |
|         myType = INTEGER;
 | |
|         intVal *= 10;
 | |
|         intVal += c - '0';
 | |
|         break;
 | |
|       case ',':
 | |
|         if (!reading_object && !reading_array) {
 | |
|           stop = true;
 | |
|           break;
 | |
|         }
 | |
|         c = fromstream.get();
 | |
|         if (reading_array) {
 | |
|           append(JSON::Value(fromstream));
 | |
|         }
 | |
|         break;
 | |
|       case '}':
 | |
|         if (reading_object) {
 | |
|           c = fromstream.get();
 | |
|         }
 | |
|         stop = true;
 | |
|         break;
 | |
|       case ']':
 | |
|         if (reading_array) {
 | |
|           c = fromstream.get();
 | |
|         }
 | |
|         stop = true;
 | |
|         break;
 | |
|       case 't':
 | |
|       case 'T':
 | |
|         skipToEnd(fromstream);
 | |
|         myType = BOOL;
 | |
|         intVal = 1;
 | |
|         stop = true;
 | |
|         break;
 | |
|       case 'f':
 | |
|       case 'F':
 | |
|         skipToEnd(fromstream);
 | |
|         myType = BOOL;
 | |
|         intVal = 0;
 | |
|         stop = true;
 | |
|         break;
 | |
|       case 'n':
 | |
|       case 'N':
 | |
|         skipToEnd(fromstream);
 | |
|         myType = EMPTY;
 | |
|         stop = true;
 | |
|         break;
 | |
|       default:
 | |
|         c = fromstream.get(); //ignore this character
 | |
|         continue;
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
|   if (negative) {
 | |
|     intVal *= -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given string.
 | |
| JSON::Value::Value(const std::string & val) {
 | |
|   myType = STRING;
 | |
|   strVal = val;
 | |
|   intVal = 0;
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given string.
 | |
| JSON::Value::Value(const char * val) {
 | |
|   myType = STRING;
 | |
|   strVal = val;
 | |
|   intVal = 0;
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given integer.
 | |
| JSON::Value::Value(long long int val) {
 | |
|   myType = INTEGER;
 | |
|   intVal = val;
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given integer.
 | |
| JSON::Value::Value(bool val) {
 | |
|   myType = BOOL;
 | |
|   if (val){
 | |
|     intVal = 1;
 | |
|   }else{
 | |
|     intVal = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// Compares a JSON::Value to another for equality.
 | |
| bool JSON::Value::operator==(const JSON::Value & rhs) const {
 | |
|   if (myType != rhs.myType){
 | |
|     return false;
 | |
|   }
 | |
|   if (myType == INTEGER || myType == BOOL) {
 | |
|     return intVal == rhs.intVal;
 | |
|   }
 | |
|   if (myType == STRING) {
 | |
|     return strVal == rhs.strVal;
 | |
|   }
 | |
|   if (myType == EMPTY) {
 | |
|     return true;
 | |
|   }
 | |
|   if (size() != rhs.size()){
 | |
|     return false;
 | |
|   }
 | |
|   if (myType == OBJECT) {
 | |
|     jsonForEachConst(*this, it){
 | |
|       if (!rhs.isMember(it.key()) || *it != rhs[it.key()]) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   if (myType == ARRAY) {
 | |
|     jsonForEachConst(*this, it){
 | |
|       if (*it != rhs[it.num()]) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /// Compares a JSON::Value to another for equality.
 | |
| bool JSON::Value::operator!=(const JSON::Value & rhs) const {
 | |
|   return !((*this) == rhs);
 | |
| }
 | |
| 
 | |
| /// Completely clears the contents of this value,
 | |
| /// changing its type to NULL in the process.
 | |
| void JSON::Value::null() {
 | |
|   shrink(0);
 | |
|   strVal.clear();
 | |
|   intVal = 0;
 | |
|   myType = EMPTY;
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to be equal to the given JSON::Value.
 | |
| JSON::Value & JSON::Value::operator=(const JSON::Value & rhs) {
 | |
|   null();
 | |
|   myType = rhs.myType;
 | |
|   if (myType == STRING){
 | |
|     strVal = rhs.strVal;
 | |
|   }
 | |
|   if (myType == BOOL || myType == INTEGER){
 | |
|     intVal = rhs.intVal;
 | |
|   }
 | |
|   if (myType == OBJECT){
 | |
|     jsonForEachConst(rhs, i){
 | |
|       (*this)[i.key()] = *i;
 | |
|     }
 | |
|   }
 | |
|   if (myType == ARRAY){
 | |
|     jsonForEachConst(rhs, i){
 | |
|       append(*i);
 | |
|     }
 | |
|   }
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given boolean.
 | |
| JSON::Value & JSON::Value::operator=(const bool & rhs) {
 | |
|   null();
 | |
|   myType = BOOL;
 | |
|   if (rhs) intVal = 1;
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given string.
 | |
| JSON::Value & JSON::Value::operator=(const std::string & rhs) {
 | |
|   null();
 | |
|   myType = STRING;
 | |
|   strVal = rhs;
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given string.
 | |
| JSON::Value & JSON::Value::operator=(const char * rhs) {
 | |
|   return ((*this) = (std::string)rhs);
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given integer.
 | |
| JSON::Value & JSON::Value::operator=(const long long int & rhs) {
 | |
|   null();
 | |
|   myType = INTEGER;
 | |
|   intVal = rhs;
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given integer.
 | |
| JSON::Value & JSON::Value::operator=(const int & rhs) {
 | |
|   return ((*this) = (long long int)rhs);
 | |
| }
 | |
| 
 | |
| /// Sets this JSON::Value to the given integer.
 | |
| JSON::Value & JSON::Value::operator=(const unsigned int & rhs) {
 | |
|   return ((*this) = (long long int)rhs);
 | |
| }
 | |
| 
 | |
| /// Automatic conversion to long long int - returns 0 if not convertable.
 | |
| JSON::Value::operator long long int() const {
 | |
|   if (myType == INTEGER) {
 | |
|     return intVal;
 | |
|   }
 | |
|   if (myType == STRING) {
 | |
|     return atoll(strVal.c_str());
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /// Automatic conversion to std::string.
 | |
| /// Returns the raw string value if available, otherwise calls toString().
 | |
| JSON::Value::operator std::string() const {
 | |
|   if (myType == STRING) {
 | |
|     return strVal;
 | |
|   } else {
 | |
|     if (myType == EMPTY) {
 | |
|       return "";
 | |
|     } else {
 | |
|       return toString();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// Automatic conversion to bool.
 | |
| /// Returns true if there is anything meaningful stored into this value.
 | |
| JSON::Value::operator bool() const {
 | |
|   if (myType == STRING) {
 | |
|     return strVal != "";
 | |
|   }
 | |
|   if (myType == INTEGER) {
 | |
|     return intVal != 0;
 | |
|   }
 | |
|   if (myType == BOOL) {
 | |
|     return intVal != 0;
 | |
|   }
 | |
|   if (myType == OBJECT) {
 | |
|     return size() > 0;
 | |
|   }
 | |
|   if (myType == ARRAY) {
 | |
|     return size() > 0;
 | |
|   }
 | |
|   if (myType == EMPTY) {
 | |
|     return false;
 | |
|   }
 | |
|   return false; //unimplemented? should never happen...
 | |
| }
 | |
| 
 | |
| /// Explicit conversion to std::string.
 | |
| const std::string JSON::Value::asString() const {
 | |
|   return (std::string) * this;
 | |
| }
 | |
| /// Explicit conversion to long long int.
 | |
| const long long int JSON::Value::asInt() const {
 | |
|   return (long long int) * this;
 | |
| }
 | |
| /// Explicit conversion to bool.
 | |
| const bool JSON::Value::asBool() const {
 | |
|   return (bool) * this;
 | |
| }
 | |
| 
 | |
| /// Explicit conversion to std::string reference.
 | |
| /// Returns a direct reference for string type JSON::Value objects,
 | |
| /// but a reference to a static empty string otherwise.
 | |
| /// \warning Only save to use when the JSON::Value is a string type!
 | |
| const std::string & JSON::Value::asStringRef() const {
 | |
|   static std::string ugly_buffer;
 | |
|   if (myType == STRING) {
 | |
|     return strVal;
 | |
|   }
 | |
|   return ugly_buffer;
 | |
| }
 | |
| 
 | |
| /// Explicit conversion to c-string.
 | |
| /// Returns a direct reference for string type JSON::Value objects,
 | |
| /// a reference to an empty string otherwise.
 | |
| /// \warning Only save to use when the JSON::Value is a string type!
 | |
| const char * JSON::Value::c_str() const {
 | |
|   if (myType == STRING) {
 | |
|     return strVal.c_str();
 | |
|   }
 | |
|   return "";
 | |
| }
 | |
| 
 | |
| /// Retrieves or sets the JSON::Value at this position in the object.
 | |
| /// Converts destructively to object if not already an object.
 | |
| JSON::Value & JSON::Value::operator[](const std::string i) {
 | |
|   if (myType != OBJECT) {
 | |
|     null();
 | |
|     myType = OBJECT;
 | |
|   }
 | |
|   Value * pntr = objVal[i];
 | |
|   if (!pntr){
 | |
|     objVal[i] = new JSON::Value();
 | |
|     pntr = objVal[i];
 | |
|   }
 | |
|   return *pntr;
 | |
| }
 | |
| 
 | |
| /// Retrieves or sets the JSON::Value at this position in the object.
 | |
| /// Converts destructively to object if not already an object.
 | |
| JSON::Value & JSON::Value::operator[](const char * i) {
 | |
|   if (myType != OBJECT) {
 | |
|     null();
 | |
|     myType = OBJECT;
 | |
|   }
 | |
|   Value * pntr = objVal[i];
 | |
|   if (!pntr){
 | |
|     objVal[i] = new JSON::Value();
 | |
|     pntr = objVal[i];
 | |
|   }
 | |
|   return *pntr;
 | |
| }
 | |
| 
 | |
| /// Retrieves or sets the JSON::Value at this position in the array.
 | |
| /// Converts destructively to array if not already an array.
 | |
| JSON::Value & JSON::Value::operator[](unsigned int i) {
 | |
|   if (myType != ARRAY) {
 | |
|     null();
 | |
|     myType = ARRAY;
 | |
|   }
 | |
|   while (i >= arrVal.size()) {
 | |
|     append(new JSON::Value());
 | |
|   }
 | |
|   return *arrVal[i];
 | |
| }
 | |
| 
 | |
| /// Retrieves the JSON::Value at this position in the object.
 | |
| /// Fails horribly if that values does not exist.
 | |
| const JSON::Value & JSON::Value::operator[](const std::string i) const {
 | |
|   return *objVal.find(i)->second;
 | |
| }
 | |
| 
 | |
| /// Retrieves the JSON::Value at this position in the object.
 | |
| /// Fails horribly if that values does not exist.
 | |
| const JSON::Value & JSON::Value::operator[](const char * i) const {
 | |
|   return *objVal.find(i)->second;
 | |
| }
 | |
| 
 | |
| /// Retrieves the JSON::Value at this position in the array.
 | |
| /// Fails horribly if that values does not exist.
 | |
| const JSON::Value & JSON::Value::operator[](unsigned int i) const {
 | |
|   return *arrVal[i];
 | |
| }
 | |
| 
 | |
| /// Packs to a std::string for transfer over the network.
 | |
| /// If the object is a container type, this function will call itself recursively and contain all contents.
 | |
| /// As a side effect, this function clear the internal buffer of any object-types.
 | |
| std::string JSON::Value::toPacked() const {
 | |
|   std::string r;
 | |
|   if (isInt() || isNull() || isBool()) {
 | |
|     r += 0x01;
 | |
|     uint64_t numval = intVal;
 | |
|     r += *(((char *) &numval) + 7);
 | |
|     r += *(((char *) &numval) + 6);
 | |
|     r += *(((char *) &numval) + 5);
 | |
|     r += *(((char *) &numval) + 4);
 | |
|     r += *(((char *) &numval) + 3);
 | |
|     r += *(((char *) &numval) + 2);
 | |
|     r += *(((char *) &numval) + 1);
 | |
|     r += *(((char *) &numval));
 | |
|   }
 | |
|   if (isString()) {
 | |
|     r += 0x02;
 | |
|     r += strVal.size() / (256 * 256 * 256);
 | |
|     r += strVal.size() / (256 * 256);
 | |
|     r += strVal.size() / 256;
 | |
|     r += strVal.size() % 256;
 | |
|     r += strVal;
 | |
|   }
 | |
|   if (isObject()) {
 | |
|     r += 0xE0;
 | |
|     if (objVal.size() > 0) {
 | |
|       jsonForEachConst(*this, i){
 | |
|         if (i.key().size() > 0) {
 | |
|           r += i.key().size() / 256;
 | |
|           r += i.key().size() % 256;
 | |
|           r += i.key();
 | |
|           r += i->toPacked();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     r += (char)0x0;
 | |
|     r += (char)0x0;
 | |
|     r += (char)0xEE;
 | |
|   }
 | |
|   if (isArray()) {
 | |
|     r += 0x0A;
 | |
|     jsonForEachConst(*this, i){
 | |
|       r += i->toPacked();
 | |
|     }
 | |
|     r += (char)0x0;
 | |
|     r += (char)0x0;
 | |
|     r += (char)0xEE;
 | |
|   }
 | |
|   return r;
 | |
| }
 | |
| //toPacked
 | |
| 
 | |
| /// Packs and transfers over the network.
 | |
| /// If the object is a container type, this function will call itself recursively for all contents.
 | |
| void JSON::Value::sendTo(Socket::Connection & socket) const {
 | |
|   if (isInt() || isNull() || isBool()) {
 | |
|     socket.SendNow("\001", 1);
 | |
|     int tmpHalf = htonl((int)(intVal >> 32));
 | |
|     socket.SendNow((char *)&tmpHalf, 4);
 | |
|     tmpHalf = htonl((int)(intVal & 0xFFFFFFFF));
 | |
|     socket.SendNow((char *)&tmpHalf, 4);
 | |
|     return;
 | |
|   }
 | |
|   if (isString()) {
 | |
|     socket.SendNow("\002", 1);
 | |
|     int tmpVal = htonl((int)strVal.size());
 | |
|     socket.SendNow((char *)&tmpVal, 4);
 | |
|     socket.SendNow(strVal);
 | |
|     return;
 | |
|   }
 | |
|   if (isObject()) {
 | |
|     if (isMember("trackid") && isMember("time")) {
 | |
|       unsigned int trackid = objVal.find("trackid")->second->asInt();
 | |
|       long long time = objVal.find("time")->second->asInt();
 | |
|       unsigned int size = 16;
 | |
|       if (objVal.size() > 0) {
 | |
|         jsonForEachConst(*this, i){
 | |
|           if (i.key().size() > 0 && i.key() != "trackid" && i.key() != "time" && i.key() != "datatype") {
 | |
|             size += 2 + i.key().size() + i->packedSize();
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       socket.SendNow("DTP2", 4);
 | |
|       size = htonl(size);
 | |
|       socket.SendNow((char *)&size, 4);
 | |
|       trackid = htonl(trackid);
 | |
|       socket.SendNow((char *)&trackid, 4);
 | |
|       int tmpHalf = htonl((int)(time >> 32));
 | |
|       socket.SendNow((char *)&tmpHalf, 4);
 | |
|       tmpHalf = htonl((int)(time & 0xFFFFFFFF));
 | |
|       socket.SendNow((char *)&tmpHalf, 4);
 | |
|       socket.SendNow("\340", 1);
 | |
|       if (objVal.size() > 0) {
 | |
|         jsonForEachConst(*this, i){
 | |
|           if (i.key().size() > 0 && i.key() != "trackid" && i.key() != "time" && i.key() != "datatype") {
 | |
|             char sizebuffer[2] = {0, 0};
 | |
|             sizebuffer[0] = (i.key().size() >> 8) & 0xFF;
 | |
|             sizebuffer[1] = i.key().size() & 0xFF;
 | |
|             socket.SendNow(sizebuffer, 2);
 | |
|             socket.SendNow(i.key());
 | |
|             i->sendTo(socket);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       socket.SendNow("\000\000\356", 3);
 | |
|       return;
 | |
|     }
 | |
|     if (isMember("tracks")) {
 | |
|       socket.SendNow("DTSC", 4);
 | |
|       unsigned int size = htonl(packedSize());
 | |
|       socket.SendNow((char *)&size, 4);
 | |
|     }
 | |
|     socket.SendNow("\340", 1);
 | |
|     if (objVal.size() > 0) {
 | |
|       jsonForEachConst(*this, i){
 | |
|         if (i.key().size() > 0) {
 | |
|           char sizebuffer[2] = {0, 0};
 | |
|           sizebuffer[0] = (i.key().size() >> 8) & 0xFF;
 | |
|           sizebuffer[1] = i.key().size() & 0xFF;
 | |
|           socket.SendNow(sizebuffer, 2);
 | |
|           socket.SendNow(i.key());
 | |
|           i->sendTo(socket);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     socket.SendNow("\000\000\356", 3);
 | |
|     return;
 | |
|   }
 | |
|   if (isArray()) {
 | |
|     socket.SendNow("\012", 1);
 | |
|     jsonForEachConst(*this, i){
 | |
|       i->sendTo(socket);
 | |
|     }
 | |
|     socket.SendNow("\000\000\356", 3);
 | |
|     return;
 | |
|   }
 | |
| }//sendTo
 | |
| 
 | |
| /// Returns the packed size of this Value.
 | |
| unsigned int JSON::Value::packedSize() const {
 | |
|   if (isInt() || isNull() || isBool()) {
 | |
|     return 9;
 | |
|   }
 | |
|   if (isString()) {
 | |
|     return 5 + strVal.size();
 | |
|   }
 | |
|   if (isObject()) {
 | |
|     unsigned int ret = 4;
 | |
|     if (objVal.size() > 0) {
 | |
|       jsonForEachConst(*this, i){
 | |
|         if (i.key().size() > 0) {
 | |
|           ret += 2 + i.key().size() + i->packedSize();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     return ret;
 | |
|   }
 | |
|   if (isArray()) {
 | |
|     unsigned int ret = 4;
 | |
|     jsonForEachConst(*this, i){
 | |
|       ret += i->packedSize();
 | |
|     }
 | |
|     return ret;
 | |
|   }
 | |
|   return 0;
 | |
| }//packedSize
 | |
| 
 | |
| /// Pre-packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header.
 | |
| /// Non-object-types will print an error.
 | |
| /// The internal buffer is guaranteed to be up-to-date after this function is called.
 | |
| void JSON::Value::netPrepare() {
 | |
|   if (myType != OBJECT) {
 | |
|     DEBUG_MSG(DLVL_ERROR, "Only objects may be netpacked!");
 | |
|     return;
 | |
|   }
 | |
|   std::string packed = toPacked();
 | |
|   //insert proper header for this type of data
 | |
|   int packID = -1;
 | |
|   long long unsigned int time = (*this)["time"].asInt();
 | |
|   std::string dataType;
 | |
|   if (isMember("datatype") || isMember("trackid")) {
 | |
|     dataType = (*this)["datatype"].asString();
 | |
|     if (isMember("trackid")) {
 | |
|       packID = (*this)["trackid"].asInt();
 | |
|     } else {
 | |
|       if ((*this)["datatype"].asString() == "video") {
 | |
|         packID = 1;
 | |
|       }
 | |
|       if ((*this)["datatype"].asString() == "audio") {
 | |
|         packID = 2;
 | |
|       }
 | |
|       if ((*this)["datatype"].asString() == "meta") {
 | |
|         packID = 3;
 | |
|       }
 | |
|       //endmark and the likes...
 | |
|       if (packID == -1) {
 | |
|         packID = 0;
 | |
|       }
 | |
|     }
 | |
|     removeMember("time");
 | |
|     if (packID != 0) {
 | |
|       removeMember("datatype");
 | |
|     }
 | |
|     removeMember("trackid");
 | |
|     packed = toPacked();
 | |
|     (*this)["time"] = (long long int)time;
 | |
|     (*this)["datatype"] = dataType;
 | |
|     (*this)["trackid"] = packID;
 | |
|     strVal.resize(packed.size() + 20);
 | |
|     memcpy((void *)strVal.c_str(), "DTP2", 4);
 | |
|   } else {
 | |
|     packID = -1;
 | |
|     strVal.resize(packed.size() + 8);
 | |
|     memcpy((void *)strVal.c_str(), "DTSC", 4);
 | |
|   }
 | |
|   //insert the packet length at bytes 4-7
 | |
|   unsigned int size = packed.size();
 | |
|   if (packID != -1) {
 | |
|     size += 12;
 | |
|   }
 | |
|   size = htonl(size);
 | |
|   memcpy((void *)(strVal.c_str() + 4), (void *) &size, 4);
 | |
|   //copy the rest of the string
 | |
|   if (packID == -1) {
 | |
|     memcpy((void *)(strVal.c_str() + 8), packed.c_str(), packed.size());
 | |
|     return;
 | |
|   }
 | |
|   packID = htonl(packID);
 | |
|   memcpy((void *)(strVal.c_str() + 8), (void *) &packID, 4);
 | |
|   int tmpHalf = htonl((int)(time >> 32));
 | |
|   memcpy((void *)(strVal.c_str() + 12), (void *) &tmpHalf, 4);
 | |
|   tmpHalf = htonl((int)(time & 0xFFFFFFFF));
 | |
|   memcpy((void *)(strVal.c_str() + 16), (void *) &tmpHalf, 4);
 | |
|   memcpy((void *)(strVal.c_str() + 20), packed.c_str(), packed.size());
 | |
| }
 | |
| 
 | |
| /// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header.
 | |
| /// Non-object-types will print an error and return an empty string.
 | |
| /// This function returns a reference to an internal buffer where the prepared data is kept.
 | |
| /// The internal buffer is *not* made stale if any changes occur inside the object - subsequent calls to toPacked() will clear the buffer,
 | |
| /// calls to netPrepare will guarantee it is up-to-date.
 | |
| std::string & JSON::Value::toNetPacked() {
 | |
|   static std::string emptystring;
 | |
|   //check if this is legal
 | |
|   if (myType != OBJECT) {
 | |
|     INFO_MSG("Ignored attempt to netpack a non-object.");
 | |
|     return emptystring;
 | |
|   }
 | |
|   //if sneaky storage doesn't contain correct data, re-calculate it
 | |
|   if (strVal.size() == 0 || strVal[0] != 'D' || strVal[1] != 'T') {
 | |
|     netPrepare();
 | |
|   }
 | |
|   return strVal;
 | |
| }
 | |
| 
 | |
| /// Converts this JSON::Value to valid JSON notation and returns it.
 | |
| /// Makes absolutely no attempts to pretty-print anything. :-)
 | |
| std::string JSON::Value::toString() const {
 | |
|   switch (myType) {
 | |
|     case INTEGER: {
 | |
|         std::stringstream st;
 | |
|         st << intVal;
 | |
|         return st.str();
 | |
|         break;
 | |
|       }
 | |
|     case BOOL: {
 | |
|         if (intVal != 0){
 | |
|           return "true";
 | |
|         }else{
 | |
|           return "false";
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     case STRING: {
 | |
|         return string_escape(strVal);
 | |
|         break;
 | |
|       }
 | |
|     case ARRAY: {
 | |
|         std::string tmp = "[";
 | |
|         if (arrVal.size() > 0) {
 | |
|           jsonForEachConst(*this, i){
 | |
|             tmp += i->toString();
 | |
|             if (i.num()+1 != arrVal.size()) {
 | |
|               tmp += ",";
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         tmp += "]";
 | |
|         return tmp;
 | |
|         break;
 | |
|       }
 | |
|     case OBJECT: {
 | |
|         std::string tmp2 = "{";
 | |
|         if (objVal.size() > 0) {
 | |
|           jsonForEachConst(*this, i){
 | |
|             tmp2 += string_escape(i.key()) + ":";
 | |
|             tmp2 += i->toString();
 | |
|             if (i.num()+1 != objVal.size()) {
 | |
|               tmp2 += ",";
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         tmp2 += "}";
 | |
|         return tmp2;
 | |
|         break;
 | |
|       }
 | |
|     case EMPTY:
 | |
|     default:
 | |
|       return "null";
 | |
|   }
 | |
|   return "null"; //should never get here...
 | |
| }
 | |
| 
 | |
| /// Converts this JSON::Value to valid JSON notation and returns it.
 | |
| /// Makes an attempt at pretty-printing.
 | |
| std::string JSON::Value::toPrettyString(int indentation) const {
 | |
|   switch (myType) {
 | |
|     case INTEGER: {
 | |
|         std::stringstream st;
 | |
|         st << intVal;
 | |
|         return st.str();
 | |
|         break;
 | |
|       }
 | |
|     case STRING: {
 | |
|         for (unsigned int i = 0; i < 201 && i < strVal.size(); ++i) {
 | |
|           if (strVal[i] < 32 || strVal[i] > 126 || strVal.size() > 200) {
 | |
|             return "\"" + JSON::Value((long long int)strVal.size()).asString() + " bytes of data\"";
 | |
|           }
 | |
|         }
 | |
|         return string_escape(strVal);
 | |
|         break;
 | |
|       }
 | |
|     case ARRAY: {
 | |
|         if (arrVal.size() > 0) {
 | |
|           std::string tmp = "[\n" + std::string(indentation + 2, ' ');
 | |
|           jsonForEachConst(*this, i){
 | |
|             tmp += i->toPrettyString(indentation + 2);
 | |
|             if (i.num() + 1 != arrVal.size()) {
 | |
|               tmp += ", ";
 | |
|             }
 | |
|           }
 | |
|           tmp += "\n" + std::string(indentation, ' ') + "]";
 | |
|           return tmp;
 | |
|         } else {
 | |
|           return "[]";
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     case OBJECT: {
 | |
|         if (objVal.size() > 0) {
 | |
|           bool shortMode = false;
 | |
|           if (size() <= 3 && isMember("len")) {
 | |
|             shortMode = true;
 | |
|           }
 | |
|           std::string tmp2 = "{" + std::string((shortMode ? "" : "\n"));
 | |
|           jsonForEachConst(*this, i){
 | |
|             tmp2 += (shortMode ? std::string("") : std::string(indentation + 2, ' ')) + string_escape(i.key()) + ":";
 | |
|             tmp2 += i->toPrettyString(indentation + 2);
 | |
|             if (i.num() + 1 != objVal.size()) {
 | |
|               tmp2 += "," + std::string((shortMode ? " " : "\n"));
 | |
|             }
 | |
|           }
 | |
|           tmp2 += (shortMode ? std::string("") : "\n" + std::string(indentation, ' ')) + "}";
 | |
|           return tmp2;
 | |
|         } else {
 | |
|           return "{}";
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     case EMPTY:
 | |
|     default:
 | |
|       return "null";
 | |
|   }
 | |
|   return "null"; //should never get here...
 | |
| }
 | |
| 
 | |
| /// Appends the given value to the end of this JSON::Value array.
 | |
| /// Turns this value into an array if it is not already one.
 | |
| void JSON::Value::append(const JSON::Value & rhs) {
 | |
|   if (myType != ARRAY) {
 | |
|     null();
 | |
|     myType = ARRAY;
 | |
|   }
 | |
|   arrVal.push_back(new JSON::Value(rhs));
 | |
| }
 | |
| 
 | |
| /// Prepends the given value to the beginning of this JSON::Value array.
 | |
| /// Turns this value into an array if it is not already one.
 | |
| void JSON::Value::prepend(const JSON::Value & rhs) {
 | |
|   if (myType != ARRAY) {
 | |
|     null();
 | |
|     myType = ARRAY;
 | |
|   }
 | |
|   arrVal.push_front(new JSON::Value(rhs));
 | |
| }
 | |
| 
 | |
| /// For array and object JSON::Value objects, reduces them
 | |
| /// so they contain at most size elements, throwing away
 | |
| /// the first elements and keeping the last ones.
 | |
| /// Does nothing for other JSON::Value types, nor does it
 | |
| /// do anything if the size is already lower or equal to the
 | |
| /// given size.
 | |
| void JSON::Value::shrink(unsigned int size) {
 | |
|   while (arrVal.size() > size) {
 | |
|     delete arrVal.front();
 | |
|     arrVal.pop_front();
 | |
|   }
 | |
|   while (objVal.size() > size) {
 | |
|     delete objVal.begin()->second;
 | |
|     objVal.erase(objVal.begin());
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// For object JSON::Value objects, removes the member with
 | |
| /// the given name, if it exists. Has no effect otherwise.
 | |
| void JSON::Value::removeMember(const std::string & name) {
 | |
|   if (objVal.count(name)){
 | |
|     delete objVal[name];
 | |
|     objVal.erase(name);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// For object JSON::Value objects, returns true if the
 | |
| /// given name is a member. Returns false otherwise.
 | |
| bool JSON::Value::isMember(const std::string & name) const {
 | |
|   return objVal.count(name) > 0;
 | |
| }
 | |
| 
 | |
| /// Returns true if this object is an integer.
 | |
| bool JSON::Value::isInt() const {
 | |
|   return (myType == INTEGER);
 | |
| }
 | |
| 
 | |
| /// Returns true if this object is a string.
 | |
| bool JSON::Value::isString() const {
 | |
|   return (myType == STRING);
 | |
| }
 | |
| 
 | |
| /// Returns true if this object is a bool.
 | |
| bool JSON::Value::isBool() const {
 | |
|   return (myType == BOOL);
 | |
| }
 | |
| 
 | |
| /// Returns true if this object is an object.
 | |
| bool JSON::Value::isObject() const {
 | |
|   return (myType == OBJECT);
 | |
| }
 | |
| 
 | |
| /// Returns true if this object is an array.
 | |
| bool JSON::Value::isArray() const {
 | |
|   return (myType == ARRAY);
 | |
| }
 | |
| 
 | |
| /// Returns true if this object is null.
 | |
| bool JSON::Value::isNull() const {
 | |
|   return (myType == EMPTY);
 | |
| }
 | |
| 
 | |
| /// Returns the total of the objects and array size combined.
 | |
| unsigned int JSON::Value::size() const {
 | |
|   return objVal.size() + arrVal.size();
 | |
| }
 | |
| 
 | |
| /// Converts a std::string to a JSON::Value.
 | |
| JSON::Value JSON::fromString(std::string json) {
 | |
|   std::istringstream is(json);
 | |
|   return JSON::Value(is);
 | |
| }
 | |
| 
 | |
| /// Converts a file to a JSON::Value.
 | |
| JSON::Value JSON::fromFile(std::string filename) {
 | |
|   std::ifstream File;
 | |
|   File.open(filename.c_str());
 | |
|   JSON::Value ret(File);
 | |
|   File.close();
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /// Parses a single DTMI type - used recursively by the JSON::fromDTMI functions.
 | |
| /// This function updates i every call with the new position in the data.
 | |
| /// \param data The raw data to parse.
 | |
| /// \param len The size of the raw data.
 | |
| /// \param i Current parsing position in the raw data (defaults to 0).
 | |
| /// \returns A single JSON::Value, parsed from the raw data.
 | |
| JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int & i) {
 | |
|   JSON::Value ret;
 | |
|   fromDTMI(data, len, i, ret);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /// Parses a single DTMI type - used recursively by the JSON::fromDTMI functions.
 | |
| /// This function updates i every call with the new position in the data.
 | |
| /// \param data The raw data to parse.
 | |
| /// \param len The size of the raw data.
 | |
| /// \param i Current parsing position in the raw data (defaults to 0).
 | |
| /// \param ret Will be set to JSON::Value, parsed from the raw data.
 | |
| void JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int & i, JSON::Value & ret) {
 | |
|   ret.null();
 | |
|   if (i >= len) {
 | |
|     return;
 | |
|   }
 | |
|   switch (data[i]) {
 | |
|     case 0x01: { //integer
 | |
|         if (i + 8 >= len) {
 | |
|           return;
 | |
|         }
 | |
|         unsigned char tmpdbl[8];
 | |
|         tmpdbl[7] = data[i + 1];
 | |
|         tmpdbl[6] = data[i + 2];
 | |
|         tmpdbl[5] = data[i + 3];
 | |
|         tmpdbl[4] = data[i + 4];
 | |
|         tmpdbl[3] = data[i + 5];
 | |
|         tmpdbl[2] = data[i + 6];
 | |
|         tmpdbl[1] = data[i + 7];
 | |
|         tmpdbl[0] = data[i + 8];
 | |
|         i += 9; //skip 8(an uint64_t)+1 forwards
 | |
|         uint64_t * d = (uint64_t *)tmpdbl;
 | |
|         ret = (long long int) * d;
 | |
|         return;
 | |
|         break;
 | |
|       }
 | |
|     case 0x02: { //string
 | |
|         if (i + 4 >= len) {
 | |
|           return;
 | |
|         }
 | |
|         unsigned int tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to UTF-8-long length
 | |
|         std::string tmpstr = std::string((const char *)data + i + 5, (size_t)tmpi); //set the string data
 | |
|         if (i + 4 + tmpi >= len) {
 | |
|           return;
 | |
|         }
 | |
|         i += tmpi + 5; //skip length+size+1 forwards
 | |
|         ret = tmpstr;
 | |
|         return;
 | |
|         break;
 | |
|       }
 | |
|     case 0xFF: //also object
 | |
|     case 0xE0: { //object
 | |
|         ++i;
 | |
|         while (data[i] + data[i + 1] != 0 && i < len) { //while not encountering 0x0000 (we assume 0x0000EE)
 | |
|           if (i + 2 >= len) {
 | |
|             return;
 | |
|           }
 | |
|           unsigned int tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length
 | |
|           std::string tmpstr = std::string((const char *)data + i + 2, (size_t)tmpi); //set the string data
 | |
|           i += tmpi + 2; //skip length+size forwards
 | |
|           ret[tmpstr].null();
 | |
|           fromDTMI(data, len, i, ret[tmpstr]); //add content, recursively parsed, updating i, setting indice to tmpstr
 | |
|         }
 | |
|         i += 3; //skip 0x0000EE
 | |
|         return;
 | |
|         break;
 | |
|       }
 | |
|     case 0x0A: { //array
 | |
|         ++i;
 | |
|         while (data[i] + data[i + 1] != 0 && i < len) { //while not encountering 0x0000 (we assume 0x0000EE)
 | |
|           JSON::Value tval;
 | |
|           fromDTMI(data, len, i, tval); //add content, recursively parsed, updating i
 | |
|           ret.append(tval);
 | |
|         }
 | |
|         i += 3; //skip 0x0000EE
 | |
|         return;
 | |
|         break;
 | |
|       }
 | |
|   }
 | |
|   DEBUG_MSG(DLVL_FAIL, "Unimplemented DTMI type %hhx, @ %i / %i - returning.", data[i], i, len);
 | |
|   i += 1;
 | |
|   return;
 | |
| } //fromOneDTMI
 | |
| 
 | |
| /// Parses a std::string to a valid JSON::Value.
 | |
| /// This function will find one DTMI object in the string and return it.
 | |
| void JSON::fromDTMI(std::string & data, JSON::Value & ret) {
 | |
|   unsigned int i = 0;
 | |
|   return fromDTMI((const unsigned char *)data.c_str(), data.size(), i, ret);
 | |
| } //fromDTMI
 | |
| 
 | |
| /// Parses a std::string to a valid JSON::Value.
 | |
| /// This function will find one DTMI object in the string and return it.
 | |
| JSON::Value JSON::fromDTMI(std::string & data) {
 | |
|   unsigned int i = 0;
 | |
|   return fromDTMI((const unsigned char *)data.c_str(), data.size(), i);
 | |
| } //fromDTMI
 | |
| 
 | |
| void JSON::fromDTMI2(std::string & data, JSON::Value & ret) {
 | |
|   unsigned int i = 0;
 | |
|   fromDTMI2((const unsigned char *)data.c_str(), data.size(), i, ret);
 | |
|   return;
 | |
| }
 | |
| 
 | |
| JSON::Value JSON::fromDTMI2(std::string & data) {
 | |
|   JSON::Value ret;
 | |
|   unsigned int i = 0;
 | |
|   fromDTMI2((const unsigned char *)data.c_str(), data.size(), i, ret);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| void JSON::fromDTMI2(const unsigned char * data, unsigned int len, unsigned int & i, JSON::Value & ret) {
 | |
|   if (len < 13) {
 | |
|     return;
 | |
|   }
 | |
|   long long int tmpTrackID = ntohl(((int *)(data + i))[0]);
 | |
|   long long int tmpTime = ntohl(((int *)(data + i))[1]);
 | |
|   tmpTime <<= 32;
 | |
|   tmpTime += ntohl(((int *)(data + i))[2]);
 | |
|   i += 12;
 | |
|   fromDTMI(data, len, i, ret);
 | |
|   ret["time"] = tmpTime;
 | |
|   ret["trackid"] = tmpTrackID;
 | |
|   return;
 | |
| }
 | |
| 
 | |
| JSON::Value JSON::fromDTMI2(const unsigned char * data, unsigned int len, unsigned int & i) {
 | |
|   JSON::Value ret;
 | |
|   fromDTMI2(data, len, i, ret);
 | |
|   return ret;
 | |
| }
 | 
