![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
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 *¤t ) 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