Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-05-20 22:39:47

0001 #include "EventFilter/Utilities/interface/writer.h"
0002 #include <utility>
0003 #include <cassert>
0004 #include <cstdio>
0005 #include <cstring>
0006 #include <iostream>
0007 #include <sstream>
0008 #include <iomanip>
0009 
0010 #if _MSC_VER >= 1400             // VC++ 8.0
0011 #pragma warning(disable : 4996)  // disable warning about strdup being deprecated.
0012 #endif
0013 
0014 namespace jsoncollector {
0015   namespace Json {
0016 
0017     static bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }
0018 
0019     static bool containsControlCharacter(const char *str) {
0020       while (*str) {
0021         if (isControlCharacter(*(str++)))
0022           return true;
0023       }
0024       return false;
0025     }
0026     static void uintToString(unsigned int value, char *&current) {
0027       *--current = 0;
0028       do {
0029         *--current = (value % 10) + '0';
0030         value /= 10;
0031       } while (value != 0);
0032     }
0033 
0034     std::string valueToString(Int value) {
0035       char buffer[32];
0036       char *current = buffer + sizeof(buffer);
0037       bool isNegative = value < 0;
0038       if (isNegative)
0039         value = -value;
0040       uintToString(UInt(value), current);
0041       if (isNegative)
0042         *--current = '-';
0043       assert(current >= buffer);
0044       return current;
0045     }
0046 
0047     std::string valueToString(UInt value) {
0048       char buffer[32];
0049       char *current = buffer + sizeof(buffer);
0050       uintToString(value, current);
0051       assert(current >= buffer);
0052       return current;
0053     }
0054 
0055     std::string valueToString(double value) {
0056       char buffer[32];
0057 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)  // Use secure version with visual studio 2005 to avoid warning.
0058       sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
0059 #else
0060       sprintf(buffer, "%#.16g", value);
0061 #endif
0062       char *ch = buffer + strlen(buffer) - 1;
0063       if (*ch != '0')
0064         return buffer;  // nothing to truncate, so save time
0065       while (ch > buffer && *ch == '0') {
0066         --ch;
0067       }
0068       char *last_nonzero = ch;
0069       while (ch >= buffer) {
0070         switch (*ch) {
0071           case '0':
0072           case '1':
0073           case '2':
0074           case '3':
0075           case '4':
0076           case '5':
0077           case '6':
0078           case '7':
0079           case '8':
0080           case '9':
0081             --ch;
0082             continue;
0083           case '.':
0084             // Truncate zeroes to save bytes in output, but keep one.
0085             *(last_nonzero + 2) = '\0';
0086             return buffer;
0087           default:
0088             return buffer;
0089         }
0090       }
0091       return buffer;
0092     }
0093 
0094     std::string valueToString(bool value) { return value ? "true" : "false"; }
0095 
0096     std::string valueToQuotedString(const char *value) {
0097       // Not sure how to handle unicode...
0098       if (strpbrk(value, "\"\\\b\f\n\r\t") == nullptr && !containsControlCharacter(value))
0099         return std::string("\"") + value + "\"";
0100       // We have to walk value and escape any special characters.
0101       // Appending to std::string is not efficient, but this should be rare.
0102       // (Note: forward slashes are *not* rare, but I am not escaping them.)
0103       unsigned maxsize = strlen(value) * 2 + 3;  // allescaped+quotes+NULL
0104       std::string result;
0105       result.reserve(maxsize);  // to avoid lots of mallocs
0106       result += "\"";
0107       for (const char *c = value; *c != 0; ++c) {
0108         switch (*c) {
0109           case '\"':
0110             result += "\\\"";
0111             break;
0112           case '\\':
0113             result += "\\\\";
0114             break;
0115           case '\b':
0116             result += "\\b";
0117             break;
0118           case '\f':
0119             result += "\\f";
0120             break;
0121           case '\n':
0122             result += "\\n";
0123             break;
0124           case '\r':
0125             result += "\\r";
0126             break;
0127           case '\t':
0128             result += "\\t";
0129             break;
0130           //case '/':
0131           // Even though \/ is considered a legal escape in JSON, a bare
0132           // slash is also legal, so I see no reason to escape it.
0133           // (I hope I am not misunderstanding something.
0134           // blep notes: actually escaping \/ may be useful in javascript to avoid </
0135           // sequence.
0136           // Should add a flag to allow this compatibility mode and prevent this
0137           // sequence from occurring.
0138           default:
0139             if (isControlCharacter(*c)) {
0140               std::ostringstream oss;
0141               oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
0142               result += oss.str();
0143             } else {
0144               result += *c;
0145             }
0146             break;
0147         }
0148       }
0149       result += "\"";
0150       return result;
0151     }
0152 
0153     // Class Writer
0154     // //////////////////////////////////////////////////////////////////
0155     Writer::~Writer() {}
0156 
0157     // Class FastWriter
0158     // //////////////////////////////////////////////////////////////////
0159 
0160     FastWriter::FastWriter() : yamlCompatiblityEnabled_(false) {}
0161 
0162     void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
0163 
0164     std::string FastWriter::write(const Value &root) {
0165       document_ = "";
0166       writeValue(root);
0167       document_ += "\n";
0168       return document_;
0169     }
0170 
0171     void FastWriter::writeValue(const Value &value) {
0172       switch (value.type()) {
0173         case nullValue:
0174           document_ += "null";
0175           break;
0176         case intValue:
0177           document_ += valueToString(value.asInt());
0178           break;
0179         case uintValue:
0180           document_ += valueToString(value.asUInt());
0181           break;
0182         case realValue:
0183           document_ += valueToString(value.asDouble());
0184           break;
0185         case stringValue:
0186           document_ += valueToQuotedString(value.asCString());
0187           break;
0188         case booleanValue:
0189           document_ += valueToString(value.asBool());
0190           break;
0191         case arrayValue: {
0192           document_ += "[";
0193           int size = value.size();
0194           for (int index = 0; index < size; ++index) {
0195             if (index > 0)
0196               document_ += ",";
0197             writeValue(value[index]);
0198           }
0199           document_ += "]";
0200         } break;
0201         case objectValue: {
0202           Value::Members members(value.getMemberNames());
0203           document_ += "{";
0204           for (Value::Members::iterator it = members.begin(); it != members.end(); ++it) {
0205             const std::string &name = *it;
0206             if (it != members.begin())
0207               document_ += ",";
0208             document_ += valueToQuotedString(name.c_str());
0209             document_ += yamlCompatiblityEnabled_ ? ": " : ":";
0210             writeValue(value[name]);
0211           }
0212           document_ += "}";
0213         } break;
0214       }
0215     }
0216 
0217     // Class StyledWriter
0218     // //////////////////////////////////////////////////////////////////
0219 
0220     StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3) {}
0221 
0222     std::string StyledWriter::write(const Value &root) {
0223       document_ = "";
0224       addChildValues_ = false;
0225       indentString_ = "";
0226       writeCommentBeforeValue(root);
0227       writeValue(root);
0228       writeCommentAfterValueOnSameLine(root);
0229       document_ += "\n";
0230       return document_;
0231     }
0232 
0233     void StyledWriter::writeValue(const Value &value) {
0234       switch (value.type()) {
0235         case nullValue:
0236           pushValue("null");
0237           break;
0238         case intValue:
0239           pushValue(valueToString(value.asInt()));
0240           break;
0241         case uintValue:
0242           pushValue(valueToString(value.asUInt()));
0243           break;
0244         case realValue:
0245           pushValue(valueToString(value.asDouble()));
0246           break;
0247         case stringValue:
0248           pushValue(valueToQuotedString(value.asCString()));
0249           break;
0250         case booleanValue:
0251           pushValue(valueToString(value.asBool()));
0252           break;
0253         case arrayValue:
0254           writeArrayValue(value);
0255           break;
0256         case objectValue: {
0257           Value::Members members(value.getMemberNames());
0258           if (members.empty())
0259             pushValue("{}");
0260           else {
0261             writeWithIndent("{");
0262             indent();
0263             Value::Members::iterator it = members.begin();
0264             while (true) {
0265               const std::string &name = *it;
0266               const Value &childValue = value[name];
0267               writeCommentBeforeValue(childValue);
0268               writeWithIndent(valueToQuotedString(name.c_str()));
0269               document_ += " : ";
0270               writeValue(childValue);
0271               if (++it == members.end()) {
0272                 writeCommentAfterValueOnSameLine(childValue);
0273                 break;
0274               }
0275               document_ += ",";
0276               writeCommentAfterValueOnSameLine(childValue);
0277             }
0278             unindent();
0279             writeWithIndent("}");
0280           }
0281         } break;
0282       }
0283     }
0284 
0285     void StyledWriter::writeArrayValue(const Value &value) {
0286       unsigned size = value.size();
0287       if (size == 0)
0288         pushValue("[]");
0289       else {
0290         bool isArrayMultiLine = isMultineArray(value);
0291         if (isArrayMultiLine) {
0292           writeWithIndent("[");
0293           indent();
0294           bool hasChildValue = !childValues_.empty();
0295           unsigned index = 0;
0296           while (true) {
0297             const Value &childValue = value[index];
0298             writeCommentBeforeValue(childValue);
0299             if (hasChildValue)
0300               writeWithIndent(childValues_[index]);
0301             else {
0302               writeIndent();
0303               writeValue(childValue);
0304             }
0305             if (++index == size) {
0306               writeCommentAfterValueOnSameLine(childValue);
0307               break;
0308             }
0309             document_ += ",";
0310             writeCommentAfterValueOnSameLine(childValue);
0311           }
0312           unindent();
0313           writeWithIndent("]");
0314         } else  // output on a single line
0315         {
0316           assert(childValues_.size() == size);
0317           document_ += "[ ";
0318           for (unsigned index = 0; index < size; ++index) {
0319             if (index > 0)
0320               document_ += ", ";
0321             document_ += childValues_[index];
0322           }
0323           document_ += " ]";
0324         }
0325       }
0326     }
0327 
0328     bool StyledWriter::isMultineArray(const Value &value) {
0329       int size = value.size();
0330       bool isMultiLine = size * 3 >= rightMargin_;
0331       childValues_.clear();
0332       for (int index = 0; index < size && !isMultiLine; ++index) {
0333         const Value &childValue = value[index];
0334         isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && !childValue.empty());
0335       }
0336       if (!isMultiLine)  // check if line length > max line length
0337       {
0338         childValues_.reserve(size);
0339         addChildValues_ = true;
0340         int lineLength = 4 + (size - 1) * 2;  // '[ ' + ', '*n + ' ]'
0341         for (int index = 0; index < size && !isMultiLine; ++index) {
0342           writeValue(value[index]);
0343           lineLength += int(childValues_[index].length());
0344           isMultiLine = isMultiLine && hasCommentForValue(value[index]);
0345         }
0346         addChildValues_ = false;
0347         isMultiLine = isMultiLine || lineLength >= rightMargin_;
0348       }
0349       return isMultiLine;
0350     }
0351 
0352     void StyledWriter::pushValue(const std::string &value) {
0353       if (addChildValues_)
0354         childValues_.push_back(value);
0355       else
0356         document_ += value;
0357     }
0358 
0359     void StyledWriter::writeIndent() {
0360       if (!document_.empty()) {
0361         char last = document_[document_.length() - 1];
0362         if (last == ' ')  // already indented
0363           return;
0364         if (last != '\n')  // Comments may add new-line
0365           document_ += '\n';
0366       }
0367       document_ += indentString_;
0368     }
0369 
0370     void StyledWriter::writeWithIndent(const std::string &value) {
0371       writeIndent();
0372       document_ += value;
0373     }
0374 
0375     void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
0376 
0377     void StyledWriter::unindent() {
0378       assert(int(indentString_.size()) >= indentSize_);
0379       indentString_.resize(indentString_.size() - indentSize_);
0380     }
0381 
0382     void StyledWriter::writeCommentBeforeValue(const Value &root) {
0383       if (!root.hasComment(commentBefore))
0384         return;
0385       document_ += normalizeEOL(root.getComment(commentBefore));
0386       document_ += "\n";
0387     }
0388 
0389     void StyledWriter::writeCommentAfterValueOnSameLine(const Value &root) {
0390       if (root.hasComment(commentAfterOnSameLine))
0391         document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
0392 
0393       if (root.hasComment(commentAfter)) {
0394         document_ += "\n";
0395         document_ += normalizeEOL(root.getComment(commentAfter));
0396         document_ += "\n";
0397       }
0398     }
0399 
0400     bool StyledWriter::hasCommentForValue(const Value &value) {
0401       return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) ||
0402              value.hasComment(commentAfter);
0403     }
0404 
0405     std::string StyledWriter::normalizeEOL(const std::string &text) {
0406       std::string normalized;
0407       normalized.reserve(text.length());
0408       const char *begin = text.c_str();
0409       const char *end = begin + text.length();
0410       const char *current = begin;
0411       while (current != end) {
0412         char c = *current++;
0413         if (c == '\r')  // mac or dos EOL
0414         {
0415           if (*current == '\n')  // convert dos EOL
0416             ++current;
0417           normalized += '\n';
0418         } else  // handle unix EOL & other char
0419           normalized += c;
0420       }
0421       return normalized;
0422     }
0423 
0424     // Class StyledStreamWriter
0425     // //////////////////////////////////////////////////////////////////
0426 
0427     StyledStreamWriter::StyledStreamWriter(std::string indentation)
0428         : document_(nullptr), rightMargin_(74), indentation_(indentation) {}
0429 
0430     void StyledStreamWriter::write(std::ostream &out, const Value &root) {
0431       document_ = &out;
0432       addChildValues_ = false;
0433       indentString_ = "";
0434       writeCommentBeforeValue(root);
0435       writeValue(root);
0436       writeCommentAfterValueOnSameLine(root);
0437       *document_ << "\n";
0438       document_ = nullptr;  // Forget the stream, for safety.
0439     }
0440 
0441     void StyledStreamWriter::writeValue(const Value &value) {
0442       switch (value.type()) {
0443         case nullValue:
0444           pushValue("null");
0445           break;
0446         case intValue:
0447           pushValue(valueToString(value.asInt()));
0448           break;
0449         case uintValue:
0450           pushValue(valueToString(value.asUInt()));
0451           break;
0452         case realValue:
0453           pushValue(valueToString(value.asDouble()));
0454           break;
0455         case stringValue:
0456           pushValue(valueToQuotedString(value.asCString()));
0457           break;
0458         case booleanValue:
0459           pushValue(valueToString(value.asBool()));
0460           break;
0461         case arrayValue:
0462           writeArrayValue(value);
0463           break;
0464         case objectValue: {
0465           Value::Members members(value.getMemberNames());
0466           if (members.empty())
0467             pushValue("{}");
0468           else {
0469             writeWithIndent("{");
0470             indent();
0471             Value::Members::iterator it = members.begin();
0472             while (true) {
0473               const std::string &name = *it;
0474               const Value &childValue = value[name];
0475               writeCommentBeforeValue(childValue);
0476               writeWithIndent(valueToQuotedString(name.c_str()));
0477               *document_ << " : ";
0478               writeValue(childValue);
0479               if (++it == members.end()) {
0480                 writeCommentAfterValueOnSameLine(childValue);
0481                 break;
0482               }
0483               *document_ << ",";
0484               writeCommentAfterValueOnSameLine(childValue);
0485             }
0486             unindent();
0487             writeWithIndent("}");
0488           }
0489         } break;
0490       }
0491     }
0492 
0493     void StyledStreamWriter::writeArrayValue(const Value &value) {
0494       unsigned size = value.size();
0495       if (size == 0)
0496         pushValue("[]");
0497       else {
0498         bool isArrayMultiLine = isMultineArray(value);
0499         if (isArrayMultiLine) {
0500           writeWithIndent("[");
0501           indent();
0502           bool hasChildValue = !childValues_.empty();
0503           unsigned index = 0;
0504           while (true) {
0505             const Value &childValue = value[index];
0506             writeCommentBeforeValue(childValue);
0507             if (hasChildValue)
0508               writeWithIndent(childValues_[index]);
0509             else {
0510               writeIndent();
0511               writeValue(childValue);
0512             }
0513             if (++index == size) {
0514               writeCommentAfterValueOnSameLine(childValue);
0515               break;
0516             }
0517             *document_ << ",";
0518             writeCommentAfterValueOnSameLine(childValue);
0519           }
0520           unindent();
0521           writeWithIndent("]");
0522         } else  // output on a single line
0523         {
0524           assert(childValues_.size() == size);
0525           *document_ << "[ ";
0526           for (unsigned index = 0; index < size; ++index) {
0527             if (index > 0)
0528               *document_ << ", ";
0529             *document_ << childValues_[index];
0530           }
0531           *document_ << " ]";
0532         }
0533       }
0534     }
0535 
0536     bool StyledStreamWriter::isMultineArray(const Value &value) {
0537       int size = value.size();
0538       bool isMultiLine = size * 3 >= rightMargin_;
0539       childValues_.clear();
0540       for (int index = 0; index < size && !isMultiLine; ++index) {
0541         const Value &childValue = value[index];
0542         isMultiLine = isMultiLine || ((childValue.isArray() || childValue.isObject()) && !childValue.empty());
0543       }
0544       if (!isMultiLine)  // check if line length > max line length
0545       {
0546         childValues_.reserve(size);
0547         addChildValues_ = true;
0548         int lineLength = 4 + (size - 1) * 2;  // '[ ' + ', '*n + ' ]'
0549         for (int index = 0; index < size && !isMultiLine; ++index) {
0550           writeValue(value[index]);
0551           lineLength += int(childValues_[index].length());
0552           isMultiLine = isMultiLine && hasCommentForValue(value[index]);
0553         }
0554         addChildValues_ = false;
0555         isMultiLine = isMultiLine || lineLength >= rightMargin_;
0556       }
0557       return isMultiLine;
0558     }
0559 
0560     void StyledStreamWriter::pushValue(const std::string &value) {
0561       if (addChildValues_)
0562         childValues_.push_back(value);
0563       else
0564         *document_ << value;
0565     }
0566 
0567     void StyledStreamWriter::writeIndent() {
0568       /*
0569     Some comments in this method would have been nice. ;-)
0570 
0571    if ( !document_.empty() )
0572    {
0573       char last = document_[document_.length()-1];
0574       if ( last == ' ' )     // already indented
0575          return;
0576       if ( last != '\n' )    // Comments may add new-line
0577          *document_ << '\n';
0578    }
0579   */
0580       *document_ << '\n' << indentString_;
0581     }
0582 
0583     void StyledStreamWriter::writeWithIndent(const std::string &value) {
0584       writeIndent();
0585       *document_ << value;
0586     }
0587 
0588     void StyledStreamWriter::indent() { indentString_ += indentation_; }
0589 
0590     void StyledStreamWriter::unindent() {
0591       assert(indentString_.size() >= indentation_.size());
0592       indentString_.resize(indentString_.size() - indentation_.size());
0593     }
0594 
0595     void StyledStreamWriter::writeCommentBeforeValue(const Value &root) {
0596       if (!root.hasComment(commentBefore))
0597         return;
0598       *document_ << normalizeEOL(root.getComment(commentBefore));
0599       *document_ << "\n";
0600     }
0601 
0602     void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value &root) {
0603       if (root.hasComment(commentAfterOnSameLine))
0604         *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
0605 
0606       if (root.hasComment(commentAfter)) {
0607         *document_ << "\n";
0608         *document_ << normalizeEOL(root.getComment(commentAfter));
0609         *document_ << "\n";
0610       }
0611     }
0612 
0613     bool StyledStreamWriter::hasCommentForValue(const Value &value) {
0614       return value.hasComment(commentBefore) || value.hasComment(commentAfterOnSameLine) ||
0615              value.hasComment(commentAfter);
0616     }
0617 
0618     std::string StyledStreamWriter::normalizeEOL(const std::string &text) {
0619       std::string normalized;
0620       normalized.reserve(text.length());
0621       const char *begin = text.c_str();
0622       const char *end = begin + text.length();
0623       const char *current = begin;
0624       while (current != end) {
0625         char c = *current++;
0626         if (c == '\r')  // mac or dos EOL
0627         {
0628           if (*current == '\n')  // convert dos EOL
0629             ++current;
0630           normalized += '\n';
0631         } else  // handle unix EOL & other char
0632           normalized += c;
0633       }
0634       return normalized;
0635     }
0636 
0637     std::ostream &operator<<(std::ostream &sout, const Value &root) {
0638       Json::StyledStreamWriter writer;
0639       writer.write(sout, root);
0640       return sout;
0641     }
0642 
0643   }  // namespace Json
0644 }  // namespace jsoncollector