ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/cf.schmorp.de/dclient/src/backend/json/json_writer.cpp
Revision: 1.1
Committed: Sun Oct 17 08:15:06 2010 UTC (16 years ago) by sf-pippijn
Branch: MAIN
CVS Tags: HEAD
Log Message:
initial import

File Contents

# User Rev Content
1 sf-pippijn 1.1 #include <json/writer.h>
2     #include <utility>
3     #include <assert.h>
4     #include <stdio.h>
5     #include <string.h>
6     #include <iostream>
7     #include <sstream>
8     #include <iomanip>
9    
10     #if _MSC_VER >= 1400 // VC++ 8.0
11     #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
12     #endif
13    
14     namespace Json
15     {
16    
17     static bool
18     isControlCharacter (char ch)
19     {
20     return ch > 0 && ch <= 0x1F;
21     }
22    
23     static bool
24     containsControlCharacter (const char *str)
25     {
26     while (*str)
27     if (isControlCharacter (*(str++)))
28     return true;
29     return false;
30     }
31    
32     static void
33     uintToString (unsigned int value, char * &current)
34     {
35     *--current = 0;
36     do
37     {
38     *--current = (value % 10) + '0';
39     value /= 10;
40     }
41     while (value != 0);
42     }
43    
44     std::string
45     valueToString (Int value)
46     {
47     char buffer[32];
48     char *current = buffer + sizeof (buffer);
49     bool isNegative = value < 0;
50    
51     if (isNegative)
52     value = -value;
53     uintToString (UInt (value), current);
54     if (isNegative)
55     *--current = '-';
56     assert (current >= buffer);
57     return current;
58     }
59    
60     std::string
61     valueToString (UInt value)
62     {
63     char buffer[32];
64     char *current = buffer + sizeof (buffer);
65    
66     uintToString (value, current);
67     assert (current >= buffer);
68     return current;
69     }
70    
71     std::string
72     valueToString (double value)
73     {
74     char buffer[32];
75    
76     #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) /* Use secure version with visual studio 2005 to avoid warning. */
77     sprintf_s (buffer, sizeof (buffer), "%#.16g", value);
78     #else
79     sprintf (buffer, "%#.16g", value);
80     #endif
81     char *ch = buffer + strlen (buffer) - 1;
82     if (*ch != '0')
83     return buffer; /* nothing to truncate, so save time */
84     while (ch > buffer && *ch == '0')
85     --ch;
86     char *last_nonzero = ch;
87     while (ch >= buffer)
88     {
89     switch (*ch)
90     {
91     case '0':
92     case '1':
93     case '2':
94     case '3':
95     case '4':
96     case '5':
97     case '6':
98     case '7':
99     case '8':
100     case '9':
101     --ch;
102     continue;
103     case '.':
104     /* Truncate zeroes to save bytes in output, but keep one. */
105     *(last_nonzero + 2) = '\0';
106     return buffer;
107     default:
108     return buffer;
109     }
110     }
111     return buffer;
112     }
113    
114     std::string
115     valueToString (bool value)
116     {
117     return value ? "true" : "false";
118     }
119    
120     std::string
121     valueToQuotedString (const char *value)
122     {
123     /* Not sure how to handle unicode... */
124     if (strpbrk (value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter (value))
125     return std::string ("\"") + value + "\"";
126     /*
127     * We have to walk value and escape any special characters.
128     * Appending to std::string is not efficient, but this should be rare.
129     * (Note: forward slashes are *not* rare, but I am not escaping them.)
130     */
131     unsigned maxsize = strlen (value) * 2 + 3; /* allescaped+quotes+NULL */
132     std::string result;
133     result.reserve (maxsize); /* to avoid lots of mallocs */
134     result += "\"";
135     for (const char *c = value; *c != 0; ++c)
136     {
137     switch (*c)
138     {
139     case '\"':
140     result += "\\\"";
141     break;
142     case '\\':
143     result += "\\\\";
144     break;
145     case '\b':
146     result += "\\b";
147     break;
148     case '\f':
149     result += "\\f";
150     break;
151     case '\n':
152     result += "\\n";
153     break;
154     case '\r':
155     result += "\\r";
156     break;
157     case '\t':
158     result += "\\t";
159     break;
160     /*
161     * case '/':
162     * Even though \/ is considered a legal escape in JSON, a bare
163     * slash is also legal, so I see no reason to escape it.
164     * (I hope I am not misunderstanding something.
165     * blep notes: actually escaping \/ may be useful in javascript to avoid </
166     * sequence.
167     * Should add a flag to allow this compatibility mode and prevent this
168     * sequence from occurring.
169     */
170     default:
171     if (isControlCharacter (*c))
172     {
173     std::ostringstream oss;
174     oss << "\\u" << std::hex << std::uppercase << std::setfill ('0') << std::setw (4) << static_cast<int> (*c);
175     result += oss.str ();
176     }
177     else
178     result += *c;
179     break;
180     }
181     }
182     result += "\"";
183     return result;
184     }
185    
186     /*
187     * Class Writer
188     * //////////////////////////////////////////////////////////////////
189     */
190     Writer::~Writer ()
191     {
192     }
193    
194     /*
195     * Class FastWriter
196     * //////////////////////////////////////////////////////////////////
197     */
198    
199     FastWriter::FastWriter ()
200     : yamlCompatiblityEnabled_ (false)
201     {
202     }
203    
204     void
205     FastWriter::enableYAMLCompatibility ()
206     {
207     yamlCompatiblityEnabled_ = true;
208     }
209    
210     std::string
211     FastWriter::write (const Value &root)
212     {
213     document_ = "";
214     writeValue (root);
215     return document_;
216     }
217    
218     void
219     FastWriter::writeValue (const Value &value)
220     {
221     switch (value.type ())
222     {
223     case nullValue:
224     document_ += "null";
225     break;
226     case intValue:
227     document_ += valueToString (value.asInt ());
228     break;
229     case uintValue:
230     document_ += valueToString (value.asUInt ());
231     break;
232     case realValue:
233     document_ += valueToString (value.asDouble ());
234     break;
235     case stringValue:
236     document_ += valueToQuotedString (value.asCString ());
237     break;
238     case booleanValue:
239     document_ += valueToString (value.asBool ());
240     break;
241     case arrayValue:
242     {
243     document_ += "[";
244     int size = value.size ();
245     for (int index = 0; index < size; ++index)
246     {
247     if (index > 0)
248     document_ += ",";
249     writeValue (value[index]);
250     }
251     document_ += "]";
252     break;
253     }
254     case objectValue:
255     {
256     Value::Members members (value.getMemberNames ());
257     document_ += "{";
258     for (Value::Members::iterator it = members.begin ();
259     it != members.end ();
260     ++it)
261     {
262     const std::string &name = *it;
263     if (it != members.begin ())
264     document_ += ",";
265     document_ += valueToQuotedString (name.c_str ());
266     document_ += yamlCompatiblityEnabled_ ? ": "
267     : ":";
268     writeValue (value[name]);
269     }
270     document_ += "}";
271     break;
272     }
273     }
274     }
275    
276     /*
277     * Class StyledWriter
278     * //////////////////////////////////////////////////////////////////
279     */
280    
281     StyledWriter::StyledWriter ()
282     : rightMargin_ (74)
283     , indentSize_ (3)
284     {
285     }
286    
287     std::string
288     StyledWriter::write (const Value &root)
289     {
290     document_ = "";
291     addChildValues_ = false;
292     indentString_ = "";
293     writeCommentBeforeValue (root);
294     writeValue (root);
295     writeCommentAfterValueOnSameLine (root);
296     document_ += "\n";
297     return document_;
298     }
299    
300     void
301     StyledWriter::writeValue (const Value &value)
302     {
303     switch (value.type ())
304     {
305     case nullValue:
306     pushValue ("null");
307     break;
308     case intValue:
309     pushValue (valueToString (value.asInt ()));
310     break;
311     case uintValue:
312     pushValue (valueToString (value.asUInt ()));
313     break;
314     case realValue:
315     pushValue (valueToString (value.asDouble ()));
316     break;
317     case stringValue:
318     pushValue (valueToQuotedString (value.asCString ()));
319     break;
320     case booleanValue:
321     pushValue (valueToString (value.asBool ()));
322     break;
323     case arrayValue:
324     writeArrayValue (value);
325     break;
326     case objectValue:
327     {
328     Value::Members members (value.getMemberNames ());
329     if (members.empty ())
330     pushValue ("{}");
331     else
332     {
333     writeWithIndent ("{");
334     indent ();
335     Value::Members::iterator it = members.begin ();
336     while (true)
337     {
338     const std::string &name = *it;
339     const Value &childValue = value[name];
340     writeCommentBeforeValue (childValue);
341     writeWithIndent (valueToQuotedString (name.c_str ()));
342     document_ += " : ";
343     writeValue (childValue);
344     if (++it == members.end ())
345     {
346     writeCommentAfterValueOnSameLine (childValue);
347     break;
348     }
349     document_ += ",";
350     writeCommentAfterValueOnSameLine (childValue);
351     }
352     unindent ();
353     writeWithIndent ("}");
354     }
355     break;
356     }
357     }
358     }
359    
360     void
361     StyledWriter::writeArrayValue (const Value &value)
362     {
363     unsigned size = value.size ();
364    
365     if (size == 0)
366     pushValue ("[]");
367     else
368     {
369     bool isArrayMultiLine = isMultineArray (value);
370     if (isArrayMultiLine)
371     {
372     writeWithIndent ("[");
373     indent ();
374     bool hasChildValue = !childValues_.empty ();
375     unsigned index = 0;
376     while (true)
377     {
378     const Value &childValue = value[index];
379     writeCommentBeforeValue (childValue);
380     if (hasChildValue)
381     writeWithIndent (childValues_[index]);
382     else
383     {
384     writeIndent ();
385     writeValue (childValue);
386     }
387     if (++index == size)
388     {
389     writeCommentAfterValueOnSameLine (childValue);
390     break;
391     }
392     document_ += ",";
393     writeCommentAfterValueOnSameLine (childValue);
394     }
395     unindent ();
396     writeWithIndent ("]");
397     }
398     else /* output on a single line */
399     {
400     assert (childValues_.size () == size);
401     document_ += "[ ";
402     for (unsigned index = 0; index < size; ++index)
403     {
404     if (index > 0)
405     document_ += ", ";
406     document_ += childValues_[index];
407     }
408     document_ += " ]";
409     }
410     }
411     }
412    
413     bool
414     StyledWriter::isMultineArray (const Value &value)
415     {
416     int size = value.size ();
417     bool isMultiLine = size * 3 >= rightMargin_;
418    
419     childValues_.clear ();
420     for (int index = 0; index < size && !isMultiLine; ++index)
421     {
422     const Value &childValue = value[index];
423     isMultiLine = isMultiLine ||
424     ((childValue.isArray () || childValue.isObject ()) &&
425     childValue.size () > 0);
426     }
427     if (!isMultiLine) /* check if line length > max line length */
428     {
429     childValues_.reserve (size);
430     addChildValues_ = true;
431     int lineLength = 4 + (size - 1) * 2; /* '[ ' + ', '*n + ' ]' */
432     for (int index = 0; index < size && !isMultiLine; ++index)
433     {
434     writeValue (value[index]);
435     lineLength += int(childValues_[index].length ());
436     isMultiLine = isMultiLine && hasCommentForValue (value[index]);
437     }
438     addChildValues_ = false;
439     isMultiLine = isMultiLine || lineLength >= rightMargin_;
440     }
441     return isMultiLine;
442     }
443    
444     void
445     StyledWriter::pushValue (const std::string &value)
446     {
447     if (addChildValues_)
448     childValues_.push_back (value);
449     else
450     document_ += value;
451     }
452    
453     void
454     StyledWriter::writeIndent ()
455     {
456     if (!document_.empty ())
457     {
458     char last = document_[document_.length () - 1];
459     if (last == ' ') /* already indented */
460     return;
461     if (last != '\n') /* Comments may add new-line */
462     document_ += '\n';
463     }
464     document_ += indentString_;
465     }
466    
467     void
468     StyledWriter::writeWithIndent (const std::string &value)
469     {
470     writeIndent ();
471     document_ += value;
472     }
473    
474     void
475     StyledWriter::indent ()
476     {
477     indentString_ += std::string (indentSize_, ' ');
478     }
479    
480     void
481     StyledWriter::unindent ()
482     {
483     assert (int(indentString_.size ()) >= indentSize_);
484     indentString_.resize (indentString_.size () - indentSize_);
485     }
486    
487     void
488     StyledWriter::writeCommentBeforeValue (const Value &root)
489     {
490     if (!root.hasComment (commentBefore))
491     return;
492     document_ += normalizeEOL (root.getComment (commentBefore));
493     document_ += "\n";
494     }
495    
496     void
497     StyledWriter::writeCommentAfterValueOnSameLine (const Value &root)
498     {
499     if (root.hasComment (commentAfterOnSameLine))
500     document_ += " " + normalizeEOL (root.getComment (commentAfterOnSameLine));
501    
502     if (root.hasComment (commentAfter))
503     {
504     document_ += "\n";
505     document_ += normalizeEOL (root.getComment (commentAfter));
506     document_ += "\n";
507     }
508     }
509    
510     bool
511     StyledWriter::hasCommentForValue (const Value &value)
512     {
513     return value.hasComment (commentBefore)
514     || value.hasComment (commentAfterOnSameLine)
515     || value.hasComment (commentAfter);
516     }
517    
518     std::string
519     StyledWriter::normalizeEOL (const std::string &text)
520     {
521     std::string normalized;
522    
523     normalized.reserve (text.length ());
524     const char *begin = text.c_str ();
525     const char *end = begin + text.length ();
526     const char *current = begin;
527     while (current != end)
528     {
529     char c = *current++;
530     if (c == '\r') /* mac or dos EOL */
531     {
532     if (*current == '\n') /* convert dos EOL */
533     ++current;
534     normalized += '\n';
535     }
536     else /* handle unix EOL & other char */
537     normalized += c;
538     }
539     return normalized;
540     }
541    
542     /*
543     * Class StyledStreamWriter
544     * //////////////////////////////////////////////////////////////////
545     */
546    
547     StyledStreamWriter::StyledStreamWriter (std::string indentation)
548     : document_ (NULL)
549     , indentation_ (indentation)
550     , rightMargin_ (74)
551     {
552     }
553    
554     void
555     StyledStreamWriter::write (std::ostream &out, const Value &root)
556     {
557     document_ = &out;
558     addChildValues_ = false;
559     indentString_ = "";
560     writeCommentBeforeValue (root);
561     writeValue (root);
562     writeCommentAfterValueOnSameLine (root);
563     *document_ << "\n";
564     document_ = NULL; /* Forget the stream, for safety. */
565     }
566    
567     void
568     StyledStreamWriter::writeValue (const Value &value)
569     {
570     switch (value.type ())
571     {
572     case nullValue:
573     pushValue ("null");
574     break;
575     case intValue:
576     pushValue (valueToString (value.asInt ()));
577     break;
578     case uintValue:
579     pushValue (valueToString (value.asUInt ()));
580     break;
581     case realValue:
582     pushValue (valueToString (value.asDouble ()));
583     break;
584     case stringValue:
585     pushValue (valueToQuotedString (value.asCString ()));
586     break;
587     case booleanValue:
588     pushValue (valueToString (value.asBool ()));
589     break;
590     case arrayValue:
591     writeArrayValue (value);
592     break;
593     case objectValue:
594     {
595     Value::Members members (value.getMemberNames ());
596     if (members.empty ())
597     pushValue ("{}");
598     else
599     {
600     writeWithIndent ("{");
601     indent ();
602     Value::Members::iterator it = members.begin ();
603     while (true)
604     {
605     const std::string &name = *it;
606     const Value &childValue = value[name];
607     writeCommentBeforeValue (childValue);
608     writeWithIndent (valueToQuotedString (name.c_str ()));
609     *document_ << " : ";
610     writeValue (childValue);
611     if (++it == members.end ())
612     {
613     writeCommentAfterValueOnSameLine (childValue);
614     break;
615     }
616     *document_ << ",";
617     writeCommentAfterValueOnSameLine (childValue);
618     }
619     unindent ();
620     writeWithIndent ("}");
621     }
622     break;
623     }
624     }
625     }
626    
627     void
628     StyledStreamWriter::writeArrayValue (const Value &value)
629     {
630     unsigned size = value.size ();
631    
632     if (size == 0)
633     pushValue ("[]");
634     else
635     {
636     bool isArrayMultiLine = isMultineArray (value);
637     if (isArrayMultiLine)
638     {
639     writeWithIndent ("[");
640     indent ();
641     bool hasChildValue = !childValues_.empty ();
642     unsigned index = 0;
643     while (true)
644     {
645     const Value &childValue = value[index];
646     writeCommentBeforeValue (childValue);
647     if (hasChildValue)
648     writeWithIndent (childValues_[index]);
649     else
650     {
651     writeIndent ();
652     writeValue (childValue);
653     }
654     if (++index == size)
655     {
656     writeCommentAfterValueOnSameLine (childValue);
657     break;
658     }
659     *document_ << ",";
660     writeCommentAfterValueOnSameLine (childValue);
661     }
662     unindent ();
663     writeWithIndent ("]");
664     }
665     else /* output on a single line */
666     {
667     assert (childValues_.size () == size);
668     *document_ << "[ ";
669     for (unsigned index = 0; index < size; ++index)
670     {
671     if (index > 0)
672     *document_ << ", ";
673     *document_ << childValues_[index];
674     }
675     *document_ << " ]";
676     }
677     }
678     }
679    
680     bool
681     StyledStreamWriter::isMultineArray (const Value &value)
682     {
683     int size = value.size ();
684     bool isMultiLine = size * 3 >= rightMargin_;
685    
686     childValues_.clear ();
687     for (int index = 0; index < size && !isMultiLine; ++index)
688     {
689     const Value &childValue = value[index];
690     isMultiLine = isMultiLine ||
691     ((childValue.isArray () || childValue.isObject ()) &&
692     childValue.size () > 0);
693     }
694     if (!isMultiLine) /* check if line length > max line length */
695     {
696     childValues_.reserve (size);
697     addChildValues_ = true;
698     int lineLength = 4 + (size - 1) * 2; /* '[ ' + ', '*n + ' ]' */
699     for (int index = 0; index < size && !isMultiLine; ++index)
700     {
701     writeValue (value[index]);
702     lineLength += int(childValues_[index].length ());
703     isMultiLine = isMultiLine && hasCommentForValue (value[index]);
704     }
705     addChildValues_ = false;
706     isMultiLine = isMultiLine || lineLength >= rightMargin_;
707     }
708     return isMultiLine;
709     }
710    
711     void
712     StyledStreamWriter::pushValue (const std::string &value)
713     {
714     if (addChildValues_)
715     childValues_.push_back (value);
716     else
717     *document_ << value;
718     }
719    
720     void
721     StyledStreamWriter::writeIndent ()
722     {
723     /*
724     * Some comments in this method would have been nice. ;-)
725     *
726     * if ( !document_.empty() )
727     * {
728     * char last = document_[document_.length()-1];
729     * if ( last == ' ' ) // already indented
730     * return;
731     * if ( last != '\n' ) // Comments may add new-line
732     * *document_ << '\n';
733     * }
734     */
735     *document_ << '\n' << indentString_;
736     }
737    
738     void
739     StyledStreamWriter::writeWithIndent (const std::string &value)
740     {
741     writeIndent ();
742     *document_ << value;
743     }
744    
745     void
746     StyledStreamWriter::indent ()
747     {
748     indentString_ += indentation_;
749     }
750    
751     void
752     StyledStreamWriter::unindent ()
753     {
754     assert (indentString_.size () >= indentation_.size ());
755     indentString_.resize (indentString_.size () - indentation_.size ());
756     }
757    
758     void
759     StyledStreamWriter::writeCommentBeforeValue (const Value &root)
760     {
761     if (!root.hasComment (commentBefore))
762     return;
763     *document_ << normalizeEOL (root.getComment (commentBefore));
764     *document_ << "\n";
765     }
766    
767     void
768     StyledStreamWriter::writeCommentAfterValueOnSameLine (const Value &root)
769     {
770     if (root.hasComment (commentAfterOnSameLine))
771     *document_ << " " + normalizeEOL (root.getComment (commentAfterOnSameLine));
772    
773     if (root.hasComment (commentAfter))
774     {
775     *document_ << "\n";
776     *document_ << normalizeEOL (root.getComment (commentAfter));
777     *document_ << "\n";
778     }
779     }
780    
781     bool
782     StyledStreamWriter::hasCommentForValue (const Value &value)
783     {
784     return value.hasComment (commentBefore)
785     || value.hasComment (commentAfterOnSameLine)
786     || value.hasComment (commentAfter);
787     }
788    
789     std::string
790     StyledStreamWriter::normalizeEOL (const std::string &text)
791     {
792     std::string normalized;
793    
794     normalized.reserve (text.length ());
795     const char *begin = text.c_str ();
796     const char *end = begin + text.length ();
797     const char *current = begin;
798     while (current != end)
799     {
800     char c = *current++;
801     if (c == '\r') /* mac or dos EOL */
802     {
803     if (*current == '\n') /* convert dos EOL */
804     ++current;
805     normalized += '\n';
806     }
807     else /* handle unix EOL & other char */
808     normalized += c;
809     }
810     return normalized;
811     }
812    
813     std::ostream &
814     operator << (std::ostream &sout, const Value &root)
815     {
816     Json::FastWriter writer;
817    
818     return sout << writer.write (root);
819     }
820    
821     } // namespace Json