Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-03-17 11:00:36

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