RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
soa/jsoncpp/json_writer.cpp
00001 // Copyright 2007-2010 Baptiste Lepilleur
00002 // Distributed under MIT license, or public domain if desired and
00003 // recognized in your jurisdiction.
00004 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
00005 #include "soa/jsoncpp/writer.h"
00006 #include <utility>
00007 #include <stdio.h>
00008 #include <string.h>
00009 #include <iostream>
00010 #include <sstream>
00011 #include <iomanip>
00012 
00013 #if _MSC_VER >= 1400 // VC++ 8.0
00014 #pragma warning( disable : 4996 )   // disable warning about strdup being deprecated.
00015 #endif
00016 
00017 namespace Json {
00018 
00019 static bool isControlCharacter(char ch)
00020 {
00021    return ch > 0 && ch <= 0x1F;
00022 }
00023 
00024 static bool containsControlCharacter( const char* str )
00025 {
00026    while ( *str )
00027    {
00028       if ( isControlCharacter( *(str++) ) )
00029          return true;
00030    }
00031    return false;
00032 }
00033 
00034 static void uintToString( unsigned long long value,
00035                           char *&current )
00036 {
00037    *--current = 0;
00038    do
00039    {
00040       *--current = (value % 10) + '0';
00041       value /= 10;
00042    }
00043    while ( value != 0 );
00044 }
00045 
00046 std::string valueToString( Int value )
00047 {
00048    char buffer[32];
00049    char *current = buffer + sizeof(buffer);
00050    bool isNegative = value < 0;
00051    if ( isNegative )
00052       value = -value;
00053    uintToString( UInt(value), current );
00054    if ( isNegative )
00055       *--current = '-';
00056    JSON_ASSERT( current >= buffer );
00057    return current;
00058 }
00059 
00060 
00061 std::string valueToString( UInt value )
00062 {
00063    char buffer[32];
00064    char *current = buffer + sizeof(buffer);
00065    uintToString( value, current );
00066    JSON_ASSERT( current >= buffer );
00067    return current;
00068 }
00069 
00070 std::string valueToString( double value )
00071 {
00072    char buffer[32];
00073 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
00074    sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
00075 #else
00076    sprintf(buffer, "%#.16g", value);
00077 #endif
00078    char* ch = buffer + strlen(buffer) - 1;
00079    if (*ch != '0') return buffer; // nothing to truncate, so save time
00080    while(ch > buffer && *ch == '0'){
00081      --ch;
00082    }
00083    char* last_nonzero = ch;
00084    while(ch >= buffer){
00085      switch(*ch){
00086      case '0':
00087      case '1':
00088      case '2':
00089      case '3':
00090      case '4':
00091      case '5':
00092      case '6':
00093      case '7':
00094      case '8':
00095      case '9':
00096        --ch;
00097        continue;
00098      case '.':
00099        // Truncate zeroes to save bytes in output, but keep one.
00100        *(last_nonzero+2) = '\0';
00101        return buffer;
00102      default:
00103        return buffer;
00104      }
00105    }
00106    return buffer;
00107 }
00108 
00109 
00110 std::string valueToString( bool value )
00111 {
00112    return value ? "true" : "false";
00113 }
00114 
00115 std::string valueToQuotedString( const char *value )
00116 {
00117    // Not sure how to handle unicode...
00118    if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
00119       return std::string("\"") + value + "\"";
00120    // We have to walk value and escape any special characters.
00121    // Appending to std::string is not efficient, but this should be rare.
00122    // (Note: forward slashes are *not* rare, but I am not escaping them.)
00123    unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
00124    std::string result;
00125    result.reserve(maxsize); // to avoid lots of mallocs
00126    result += "\"";
00127    for (const char* c=value; *c != 0; ++c)
00128    {
00129       switch(*c)
00130       {
00131          case '\"':
00132             result += "\\\"";
00133             break;
00134          case '\\':
00135             result += "\\\\";
00136             break;
00137          case '\b':
00138             result += "\\b";
00139             break;
00140          case '\f':
00141             result += "\\f";
00142             break;
00143          case '\n':
00144             result += "\\n";
00145             break;
00146          case '\r':
00147             result += "\\r";
00148             break;
00149          case '\t':
00150             result += "\\t";
00151             break;
00152          //case '/':
00153             // Even though \/ is considered a legal escape in JSON, a bare
00154             // slash is also legal, so I see no reason to escape it.
00155             // (I hope I am not misunderstanding something.
00156             // blep notes: actually escaping \/ may be useful in javascript to avoid </
00157             // sequence.
00158             // Should add a flag to allow this compatibility mode and prevent this
00159             // sequence from occurring.
00160          default:
00161             if ( isControlCharacter( *c ) )
00162             {
00163                std::ostringstream oss;
00164                oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
00165                result += oss.str();
00166             }
00167             else
00168             {
00169                result += *c;
00170             }
00171             break;
00172       }
00173    }
00174    result += "\"";
00175    return result;
00176 }
00177 
00178 // Class Writer
00179 // //////////////////////////////////////////////////////////////////
00180 Writer::~Writer()
00181 {
00182 }
00183 
00184 
00185 // Class FastWriter
00186 // //////////////////////////////////////////////////////////////////
00187 
00188 FastWriter::FastWriter()
00189    : yamlCompatiblityEnabled_( false )
00190 {
00191 }
00192 
00193 
00194 void
00195 FastWriter::enableYAMLCompatibility()
00196 {
00197    yamlCompatiblityEnabled_ = true;
00198 }
00199 
00200 
00201 std::string
00202 FastWriter::write( const Value &root )
00203 {
00204    document_ = "";
00205    writeValue( root );
00206    document_ += "\n";
00207    return document_;
00208 }
00209 
00210 
00211 void
00212 FastWriter::writeValue( const Value &value )
00213 {
00214    switch ( value.type() )
00215    {
00216    case nullValue:
00217       document_ += "null";
00218       break;
00219    case intValue:
00220       document_ += valueToString( value.asInt() );
00221       break;
00222    case uintValue:
00223       document_ += valueToString( value.asUInt() );
00224       break;
00225    case realValue:
00226       document_ += valueToString( value.asDouble() );
00227       break;
00228    case stringValue:
00229       document_ += valueToQuotedString( value.asCString() );
00230       break;
00231    case booleanValue:
00232       document_ += valueToString( value.asBool() );
00233       break;
00234    case arrayValue:
00235       {
00236          document_ += "[";
00237          int size = value.size();
00238          for ( int index =0; index < size; ++index )
00239          {
00240             if ( index > 0 )
00241                document_ += ",";
00242             writeValue( value[index] );
00243          }
00244          document_ += "]";
00245       }
00246       break;
00247    case objectValue:
00248       {
00249          Value::Members members( value.getMemberNames() );
00250          document_ += "{";
00251          for ( Value::Members::iterator it = members.begin();
00252                it != members.end();
00253                ++it )
00254          {
00255             const std::string &name = *it;
00256             if ( it != members.begin() )
00257                document_ += ",";
00258             document_ += valueToQuotedString( name.c_str() );
00259             document_ += yamlCompatiblityEnabled_ ? ": "
00260                                                   : ":";
00261             writeValue( value[name] );
00262          }
00263          document_ += "}";
00264       }
00265       break;
00266    }
00267 }
00268 
00269 
00270 // Class StyledWriter
00271 // //////////////////////////////////////////////////////////////////
00272 
00273 StyledWriter::StyledWriter()
00274    : rightMargin_( 74 )
00275    , indentSize_( 3 )
00276 {
00277 }
00278 
00279 
00280 std::string
00281 StyledWriter::write( const Value &root )
00282 {
00283    document_ = "";
00284    addChildValues_ = false;
00285    indentString_ = "";
00286    writeCommentBeforeValue( root );
00287    writeValue( root );
00288    writeCommentAfterValueOnSameLine( root );
00289    document_ += "\n";
00290    return document_;
00291 }
00292 
00293 
00294 void
00295 StyledWriter::writeValue( const Value &value )
00296 {
00297    switch ( value.type() )
00298    {
00299    case nullValue:
00300       pushValue( "null" );
00301       break;
00302    case intValue:
00303       pushValue( valueToString( value.asInt() ) );
00304       break;
00305    case uintValue:
00306       pushValue( valueToString( value.asUInt() ) );
00307       break;
00308    case realValue:
00309       pushValue( valueToString( value.asDouble() ) );
00310       break;
00311    case stringValue:
00312       pushValue( valueToQuotedString( value.asCString() ) );
00313       break;
00314    case booleanValue:
00315       pushValue( valueToString( value.asBool() ) );
00316       break;
00317    case arrayValue:
00318       writeArrayValue( value);
00319       break;
00320    case objectValue:
00321       {
00322          Value::Members members( value.getMemberNames() );
00323          if ( members.empty() )
00324             pushValue( "{}" );
00325          else
00326          {
00327             writeWithIndent( "{" );
00328             indent();
00329             Value::Members::iterator it = members.begin();
00330             while ( true )
00331             {
00332                const std::string &name = *it;
00333                const Value &childValue = value[name];
00334                writeCommentBeforeValue( childValue );
00335                writeWithIndent( valueToQuotedString( name.c_str() ) );
00336                document_ += " : ";
00337                writeValue( childValue );
00338                if ( ++it == members.end() )
00339                {
00340                   writeCommentAfterValueOnSameLine( childValue );
00341                   break;
00342                }
00343                document_ += ",";
00344                writeCommentAfterValueOnSameLine( childValue );
00345             }
00346             unindent();
00347             writeWithIndent( "}" );
00348          }
00349       }
00350       break;
00351    }
00352 }
00353 
00354 
00355 void
00356 StyledWriter::writeArrayValue( const Value &value )
00357 {
00358    unsigned size = value.size();
00359    if ( size == 0 )
00360       pushValue( "[]" );
00361    else
00362    {
00363       bool isArrayMultiLine = isMultineArray( value );
00364       if ( isArrayMultiLine )
00365       {
00366          writeWithIndent( "[" );
00367          indent();
00368          bool hasChildValue = !childValues_.empty();
00369          unsigned index =0;
00370          while ( true )
00371          {
00372             const Value &childValue = value[index];
00373             writeCommentBeforeValue( childValue );
00374             if ( hasChildValue )
00375                writeWithIndent( childValues_[index] );
00376             else
00377             {
00378                writeIndent();
00379                writeValue( childValue );
00380             }
00381             if ( ++index == size )
00382             {
00383                writeCommentAfterValueOnSameLine( childValue );
00384                break;
00385             }
00386             document_ += ",";
00387             writeCommentAfterValueOnSameLine( childValue );
00388          }
00389          unindent();
00390          writeWithIndent( "]" );
00391       }
00392       else // output on a single line
00393       {
00394          JSON_ASSERT( childValues_.size() == size );
00395          document_ += "[ ";
00396          for ( unsigned index =0; index < size; ++index )
00397          {
00398             if ( index > 0 )
00399                document_ += ", ";
00400             document_ += childValues_[index];
00401          }
00402          document_ += " ]";
00403       }
00404    }
00405 }
00406 
00407 
00408 bool
00409 StyledWriter::isMultineArray( const Value &value )
00410 {
00411    int size = value.size();
00412    bool isMultiLine = size*3 >= rightMargin_ ;
00413    childValues_.clear();
00414    for ( int index =0; index < size  &&  !isMultiLine; ++index )
00415    {
00416       const Value &childValue = value[index];
00417       isMultiLine = isMultiLine  ||
00418                      ( (childValue.isArray()  ||  childValue.isObject())  &&
00419                         childValue.size() > 0 );
00420    }
00421    if ( !isMultiLine ) // check if line length > max line length
00422    {
00423       childValues_.reserve( size );
00424       addChildValues_ = true;
00425       int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
00426       for ( int index =0; index < size  &&  !isMultiLine; ++index )
00427       {
00428          writeValue( value[index] );
00429          lineLength += int( childValues_[index].length() );
00430          isMultiLine = isMultiLine  &&  hasCommentForValue( value[index] );
00431       }
00432       addChildValues_ = false;
00433       isMultiLine = isMultiLine  ||  lineLength >= rightMargin_;
00434    }
00435    return isMultiLine;
00436 }
00437 
00438 
00439 void
00440 StyledWriter::pushValue( const std::string &value )
00441 {
00442    if ( addChildValues_ )
00443       childValues_.push_back( value );
00444    else
00445       document_ += value;
00446 }
00447 
00448 
00449 void
00450 StyledWriter::writeIndent()
00451 {
00452    if ( !document_.empty() )
00453    {
00454       char last = document_[document_.length()-1];
00455       if ( last == ' ' )     // already indented
00456          return;
00457       if ( last != '\n' )    // Comments may add new-line
00458          document_ += '\n';
00459    }
00460    document_ += indentString_;
00461 }
00462 
00463 
00464 void
00465 StyledWriter::writeWithIndent( const std::string &value )
00466 {
00467    writeIndent();
00468    document_ += value;
00469 }
00470 
00471 
00472 void
00473 StyledWriter::indent()
00474 {
00475    indentString_ += std::string( indentSize_, ' ' );
00476 }
00477 
00478 
00479 void
00480 StyledWriter::unindent()
00481 {
00482    JSON_ASSERT( int(indentString_.size()) >= indentSize_ );
00483    indentString_.resize( indentString_.size() - indentSize_ );
00484 }
00485 
00486 
00487 void
00488 StyledWriter::writeCommentBeforeValue( const Value &root )
00489 {
00490    if ( !root.hasComment( commentBefore ) )
00491       return;
00492    document_ += normalizeEOL( root.getComment( commentBefore ) );
00493    document_ += "\n";
00494 }
00495 
00496 
00497 void
00498 StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
00499 {
00500    if ( root.hasComment( commentAfterOnSameLine ) )
00501       document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
00502 
00503    if ( root.hasComment( commentAfter ) )
00504    {
00505       document_ += "\n";
00506       document_ += normalizeEOL( root.getComment( commentAfter ) );
00507       document_ += "\n";
00508    }
00509 }
00510 
00511 
00512 bool
00513 StyledWriter::hasCommentForValue( const Value &value )
00514 {
00515    return value.hasComment( commentBefore )
00516           ||  value.hasComment( commentAfterOnSameLine )
00517           ||  value.hasComment( commentAfter );
00518 }
00519 
00520 
00521 std::string
00522 StyledWriter::normalizeEOL( const std::string &text )
00523 {
00524    std::string normalized;
00525    normalized.reserve( text.length() );
00526    const char *begin = text.c_str();
00527    const char *end = begin + text.length();
00528    const char *current = begin;
00529    while ( current != end )
00530    {
00531       char c = *current++;
00532       if ( c == '\r' ) // mac or dos EOL
00533       {
00534          if ( *current == '\n' ) // convert dos EOL
00535             ++current;
00536          normalized += '\n';
00537       }
00538       else // handle unix EOL & other char
00539          normalized += c;
00540    }
00541    return normalized;
00542 }
00543 
00544 
00545 // Class StyledStreamWriter
00546 // //////////////////////////////////////////////////////////////////
00547 
00548 StyledStreamWriter::StyledStreamWriter( std::string indentation )
00549    : document_(NULL)
00550    , rightMargin_( 74 )
00551    , indentation_( indentation )
00552 {
00553 }
00554 
00555 
00556 void
00557 StyledStreamWriter::write( std::ostream &out, const Value &root )
00558 {
00559    document_ = &out;
00560    addChildValues_ = false;
00561    indentString_ = "";
00562    writeCommentBeforeValue( root );
00563    writeValue( root );
00564    writeCommentAfterValueOnSameLine( root );
00565    *document_ << "\n";
00566    document_ = NULL; // Forget the stream, for safety.
00567 }
00568 
00569 
00570 void
00571 StyledStreamWriter::writeValue( const Value &value )
00572 {
00573    switch ( value.type() )
00574    {
00575    case nullValue:
00576       pushValue( "null" );
00577       break;
00578    case intValue:
00579       pushValue( valueToString( value.asInt() ) );
00580       break;
00581    case uintValue:
00582       pushValue( valueToString( value.asUInt() ) );
00583       break;
00584    case realValue:
00585       pushValue( valueToString( value.asDouble() ) );
00586       break;
00587    case stringValue:
00588       pushValue( valueToQuotedString( value.asCString() ) );
00589       break;
00590    case booleanValue:
00591       pushValue( valueToString( value.asBool() ) );
00592       break;
00593    case arrayValue:
00594       writeArrayValue( value);
00595       break;
00596    case objectValue:
00597       {
00598          Value::Members members( value.getMemberNames() );
00599          if ( members.empty() )
00600             pushValue( "{}" );
00601          else
00602          {
00603             writeWithIndent( "{" );
00604             indent();
00605             Value::Members::iterator it = members.begin();
00606             while ( true )
00607             {
00608                const std::string &name = *it;
00609                const Value &childValue = value[name];
00610                writeCommentBeforeValue( childValue );
00611                writeWithIndent( valueToQuotedString( name.c_str() ) );
00612                *document_ << " : ";
00613                writeValue( childValue );
00614                if ( ++it == members.end() )
00615                {
00616                   writeCommentAfterValueOnSameLine( childValue );
00617                   break;
00618                }
00619                *document_ << ",";
00620                writeCommentAfterValueOnSameLine( childValue );
00621             }
00622             unindent();
00623             writeWithIndent( "}" );
00624          }
00625       }
00626       break;
00627    }
00628 }
00629 
00630 
00631 void
00632 StyledStreamWriter::writeArrayValue( const Value &value )
00633 {
00634    unsigned size = value.size();
00635    if ( size == 0 )
00636       pushValue( "[]" );
00637    else
00638    {
00639       bool isArrayMultiLine = isMultineArray( value );
00640       if ( isArrayMultiLine )
00641       {
00642          writeWithIndent( "[" );
00643          indent();
00644          bool hasChildValue = !childValues_.empty();
00645          unsigned index =0;
00646          while ( true )
00647          {
00648             const Value &childValue = value[index];
00649             writeCommentBeforeValue( childValue );
00650             if ( hasChildValue )
00651                writeWithIndent( childValues_[index] );
00652             else
00653             {
00654            writeIndent();
00655                writeValue( childValue );
00656             }
00657             if ( ++index == size )
00658             {
00659                writeCommentAfterValueOnSameLine( childValue );
00660                break;
00661             }
00662             *document_ << ",";
00663             writeCommentAfterValueOnSameLine( childValue );
00664          }
00665          unindent();
00666          writeWithIndent( "]" );
00667       }
00668       else // output on a single line
00669       {
00670          JSON_ASSERT( childValues_.size() == size );
00671          *document_ << "[ ";
00672          for ( unsigned index =0; index < size; ++index )
00673          {
00674             if ( index > 0 )
00675                *document_ << ", ";
00676             *document_ << childValues_[index];
00677          }
00678          *document_ << " ]";
00679       }
00680    }
00681 }
00682 
00683 
00684 bool
00685 StyledStreamWriter::isMultineArray( const Value &value )
00686 {
00687    int size = value.size();
00688    bool isMultiLine = size*3 >= rightMargin_ ;
00689    childValues_.clear();
00690    for ( int index =0; index < size  &&  !isMultiLine; ++index )
00691    {
00692       const Value &childValue = value[index];
00693       isMultiLine = isMultiLine  ||
00694                      ( (childValue.isArray()  ||  childValue.isObject())  &&
00695                         childValue.size() > 0 );
00696    }
00697    if ( !isMultiLine ) // check if line length > max line length
00698    {
00699       childValues_.reserve( size );
00700       addChildValues_ = true;
00701       int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
00702       for ( int index =0; index < size  &&  !isMultiLine; ++index )
00703       {
00704          writeValue( value[index] );
00705          lineLength += int( childValues_[index].length() );
00706          isMultiLine = isMultiLine  &&  hasCommentForValue( value[index] );
00707       }
00708       addChildValues_ = false;
00709       isMultiLine = isMultiLine  ||  lineLength >= rightMargin_;
00710    }
00711    return isMultiLine;
00712 }
00713 
00714 
00715 void
00716 StyledStreamWriter::pushValue( const std::string &value )
00717 {
00718    if ( addChildValues_ )
00719       childValues_.push_back( value );
00720    else
00721       *document_ << value;
00722 }
00723 
00724 
00725 void
00726 StyledStreamWriter::writeIndent()
00727 {
00728   /*
00729     Some comments in this method would have been nice. ;-)
00730 
00731    if ( !document_.empty() )
00732    {
00733       char last = document_[document_.length()-1];
00734       if ( last == ' ' )     // already indented
00735          return;
00736       if ( last != '\n' )    // Comments may add new-line
00737          *document_ << '\n';
00738    }
00739   */
00740    *document_ << '\n' << indentString_;
00741 }
00742 
00743 
00744 void
00745 StyledStreamWriter::writeWithIndent( const std::string &value )
00746 {
00747    writeIndent();
00748    *document_ << value;
00749 }
00750 
00751 
00752 void
00753 StyledStreamWriter::indent()
00754 {
00755    indentString_ += indentation_;
00756 }
00757 
00758 
00759 void
00760 StyledStreamWriter::unindent()
00761 {
00762    JSON_ASSERT( indentString_.size() >= indentation_.size() );
00763    indentString_.resize( indentString_.size() - indentation_.size() );
00764 }
00765 
00766 
00767 void
00768 StyledStreamWriter::writeCommentBeforeValue( const Value &root )
00769 {
00770    if ( !root.hasComment( commentBefore ) )
00771       return;
00772    *document_ << normalizeEOL( root.getComment( commentBefore ) );
00773    *document_ << "\n";
00774 }
00775 
00776 
00777 void
00778 StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
00779 {
00780    if ( root.hasComment( commentAfterOnSameLine ) )
00781       *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
00782 
00783    if ( root.hasComment( commentAfter ) )
00784    {
00785       *document_ << "\n";
00786       *document_ << normalizeEOL( root.getComment( commentAfter ) );
00787       *document_ << "\n";
00788    }
00789 }
00790 
00791 
00792 bool
00793 StyledStreamWriter::hasCommentForValue( const Value &value )
00794 {
00795    return value.hasComment( commentBefore )
00796           ||  value.hasComment( commentAfterOnSameLine )
00797           ||  value.hasComment( commentAfter );
00798 }
00799 
00800 
00801 std::string
00802 StyledStreamWriter::normalizeEOL( const std::string &text )
00803 {
00804    std::string normalized;
00805    normalized.reserve( text.length() );
00806    const char *begin = text.c_str();
00807    const char *end = begin + text.length();
00808    const char *current = begin;
00809    while ( current != end )
00810    {
00811       char c = *current++;
00812       if ( c == '\r' ) // mac or dos EOL
00813       {
00814          if ( *current == '\n' ) // convert dos EOL
00815             ++current;
00816          normalized += '\n';
00817       }
00818       else // handle unix EOL & other char
00819          normalized += c;
00820    }
00821    return normalized;
00822 }
00823 
00824 
00825 std::ostream& operator<<( std::ostream &sout, const Value &root )
00826 {
00827    Json::StyledStreamWriter writer;
00828    writer.write(sout, root);
00829    return sout;
00830 }
00831 
00832 
00833 } // namespace Json
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator