1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 ///
25 /// \file js/json.cpp
26 
27 #include "pxr/pxr.h"
28 #include "pxr/base/js/json.h"
29 
30 #include "pxr/base/tf/diagnostic.h"
31 #include "pxr/base/tf/stringUtils.h"
32 
33 #include <iostream>
34 #include <vector>
35 
36 #if PXR_USE_NAMESPACES
37 #define PXRJS PXR_NS
38 #else
39 #define PXRJS PXRJS
40 #endif
41 
42 // Place rapidjson into a namespace to prevent conflicts with d2.
43 #define RAPIDJSON_NAMESPACE PXRJS::rapidjson
44 #define RAPIDJSON_NAMESPACE_BEGIN namespace PXRJS { namespace rapidjson {
45 #define RAPIDJSON_NAMESPACE_END } }
46 
47 #include "rapidjson/allocators.h"
48 #include "rapidjson/document.h"
49 #include "rapidjson/reader.h"
50 #include "rapidjson/ostreamwrapper.h"
51 #include "rapidjson/prettywriter.h"
52 #include "rapidjson/error/error.h"
53 #include "rapidjson/error/en.h"
54 
55 namespace rj = RAPIDJSON_NAMESPACE;
56 
57 namespace {
58 PXR_NAMESPACE_USING_DIRECTIVE
59 
60 struct _InputHandler : public rj::BaseReaderHandler<rj::UTF8<>, _InputHandler>
61 {
Null__anon2a1bb4850111::_InputHandler62     bool Null() {
63         values.emplace_back();
64         return true;
65     }
Bool__anon2a1bb4850111::_InputHandler66     bool Bool(bool b) {
67         values.emplace_back(b);
68         return true;
69     }
Int__anon2a1bb4850111::_InputHandler70     bool Int(int i) {
71         values.emplace_back(i);
72         return true;
73     }
Uint__anon2a1bb4850111::_InputHandler74     bool Uint(unsigned u) {
75         values.emplace_back(static_cast<uint64_t>(u));
76         return true;
77     }
Int64__anon2a1bb4850111::_InputHandler78     bool Int64(int64_t i) {
79         values.emplace_back(i);
80         return true;
81     }
Uint64__anon2a1bb4850111::_InputHandler82     bool Uint64(uint64_t u) {
83         values.emplace_back(u);
84         return true;
85     }
Double__anon2a1bb4850111::_InputHandler86     bool Double(double d) {
87         values.emplace_back(d);
88         return true;
89     }
String__anon2a1bb4850111::_InputHandler90     bool String(const char* str, rj::SizeType length, bool /* copy */) {
91         values.emplace_back(std::string(str, length));
92         return true;
93     }
Key__anon2a1bb4850111::_InputHandler94     bool Key(const char* str, rj::SizeType length, bool /* copy */) {
95         keys.emplace_back(str, length);
96         return true;
97     }
StartObject__anon2a1bb4850111::_InputHandler98     bool StartObject() {
99         return true;
100     }
EndObject__anon2a1bb4850111::_InputHandler101     bool EndObject(rj::SizeType memberCount) {
102         const size_t kstart = keys.size() - memberCount;
103         const size_t vstart = values.size() - memberCount;
104 
105         JsObject object;
106         for (size_t i = 0; i < memberCount; ++i) {
107             object.insert(
108                 std::make_pair(
109                     std::move(keys[kstart + i]),
110                     std::move(values[vstart + i])));
111         }
112 
113         keys.resize(kstart);
114         values.resize(vstart);
115 
116         values.emplace_back(std::move(object));
117         return true;
118     }
BeginArray__anon2a1bb4850111::_InputHandler119     bool BeginArray() {
120         return true;
121     }
EndArray__anon2a1bb4850111::_InputHandler122     bool EndArray(rj::SizeType elementCount) {
123         std::vector<JsValue> valueArray(
124             values.end() - elementCount, values.end());
125         values.resize(values.size() - elementCount);
126         values.emplace_back(std::move(valueArray));
127         return true;
128     }
129 
130 public:
131     std::vector<JsObject::key_type> keys;
132     std::vector<JsObject::mapped_type> values;
133 };
134 
135 // This class is needed to override writing out doubles. There is a bug in
136 // rapidJSON when writing out some double values. These classes uses the Tf
137 // library to do the conversion instead.
138 // See: https://github.com/Tencent/rapidjson/issues/954
139 
140 template <class TBase>
141 class _WriterFix : public TBase
142 {
143 public:
144     using Base = TBase;
145     using Base::Base;
146 
Double(double d)147     bool Double(double d) {
148         constexpr int bufferSize = 32;
149         char buffer[bufferSize];
150         TfDoubleToString(d, buffer, bufferSize, true);
151 
152         return Base::RawValue(buffer, strlen(buffer), rj::kNumberType);
153      }
154 };
155 
156 }
157 
158 PXR_NAMESPACE_OPEN_SCOPE
159 
160 template <typename Allocator>
161 static rj::Value
_ToImplObjectValue(const JsObject & object,Allocator & allocator)162 _ToImplObjectValue(
163     const JsObject& object,
164     Allocator& allocator)
165 {
166     rj::Value value(rj::kObjectType);
167 
168     for (const auto& p : object) {
169         value.AddMember(
170             rj::Value(p.first.c_str(), allocator).Move(),
171             _JsValueToImplValue(p.second, allocator),
172             allocator);
173     }
174 
175     return value;
176 }
177 
178 template <typename Allocator>
179 static rj::Value
_ToImplArrayValue(const JsArray & array,Allocator & allocator)180 _ToImplArrayValue(
181     const JsArray& array,
182     Allocator& allocator)
183 {
184     rj::Value value(rj::kArrayType);
185 
186     for (const auto& e : array) {
187         value.PushBack(
188             rj::Value(_JsValueToImplValue(e, allocator)).Move(),
189             allocator);
190     }
191 
192     return value;
193 }
194 
195 template <typename Allocator>
196 static rj::Value
_JsValueToImplValue(const JsValue & value,Allocator & allocator)197 _JsValueToImplValue(
198     const JsValue& value,
199     Allocator& allocator)
200 {
201     switch (value.GetType()) {
202     case JsValue::ObjectType:
203         return _ToImplObjectValue(value.GetJsObject(), allocator);
204     case JsValue::ArrayType:
205         return _ToImplArrayValue(value.GetJsArray(), allocator);
206     case JsValue::BoolType:
207         return rj::Value(value.GetBool());
208     case JsValue::StringType:
209         return rj::Value(value.GetString().c_str(), allocator);
210     case JsValue::RealType:
211         return rj::Value(value.GetReal());
212     case JsValue::IntType:
213         return value.IsUInt64() ?
214             rj::Value(value.GetUInt64()) : rj::Value(value.GetInt64());
215     case JsValue::NullType:
216         return rj::Value();
217     default: {
218         TF_CODING_ERROR("Unknown JsValue type");
219         return rj::Value();
220         }
221     }
222 }
223 
224 
225 JsValue
JsParseStream(std::istream & istr,JsParseError * error)226 JsParseStream(
227     std::istream& istr,
228     JsParseError* error)
229 {
230     if (!istr) {
231         TF_CODING_ERROR("Stream error");
232         return JsValue();
233     }
234 
235     // Parse streams by reading into a string first. This makes it easier to
236     // yield good error messages that include line and column numbers, rather
237     // than the character offset that rapidjson currently provides.
238     return JsParseString(std::string(
239         (std::istreambuf_iterator<char>(istr)),
240          std::istreambuf_iterator<char>()),
241         error);
242 }
243 
244 JsValue
JsParseString(const std::string & data,JsParseError * error)245 JsParseString(
246     const std::string& data,
247     JsParseError* error)
248 {
249     if (data.empty()) {
250         TF_CODING_ERROR("JSON string is empty");
251         return JsValue();
252     }
253 
254     _InputHandler handler;
255     rj::Reader reader;
256     rj::StringStream ss(data.c_str());
257     // Need Full precision flag to round trip double values correctly.
258     rj::ParseResult result =
259         reader.Parse<rj::kParseFullPrecisionFlag|rj::kParseStopWhenDoneFlag>(
260             ss, handler);
261 
262     if (!result) {
263         if (error) {
264             // Rapidjson only provides a character offset for errors, not
265             // line/column information like other parsers (like json_spirit,
266             // upon which this library was previously implemented). Analyze
267             // the input data to convert the offset to line/column.
268             error->line = 1;
269             const size_t eoff = result.Offset();
270             size_t nlpos = 0;
271             for (size_t i = 0; i < eoff; ++i) {
272                 if (data[i] == '\n') {
273                     error->line++;
274                     nlpos = i;
275                 }
276             }
277             error->column = eoff - nlpos;
278             error->reason = rj::GetParseError_En(result.Code());
279         }
280         return JsValue();
281     }
282 
283     TF_VERIFY(handler.values.size() == 1,
284         "Unexpected value count: %zu", handler.values.size());
285 
286     return handler.values.empty() ? JsValue() : handler.values.front();
287 }
288 
289 void
JsWriteToStream(const JsValue & value,std::ostream & ostr)290 JsWriteToStream(
291     const JsValue& value,
292     std::ostream& ostr)
293 {
294     if (!ostr) {
295         TF_CODING_ERROR("Stream error");
296         return;
297     }
298 
299     rj::Document d;
300     const rj::Value ivalue = _JsValueToImplValue(value, d.GetAllocator());
301 
302     rj::OStreamWrapper os(ostr);
303     _WriterFix<rj::PrettyWriter<rj::OStreamWrapper>> writer(os);
304     writer.SetFormatOptions(rj::kFormatSingleLineArray);
305     ivalue.Accept(writer);
306 }
307 
308 std::string
JsWriteToString(const JsValue & value)309 JsWriteToString(
310     const JsValue& value)
311 {
312     rj::Document d;
313     const rj::Value ivalue = _JsValueToImplValue(value, d.GetAllocator());
314 
315     rj::StringBuffer buffer;
316      _WriterFix<rj::PrettyWriter<rj::StringBuffer>> writer(buffer);
317     writer.SetFormatOptions(rj::kFormatSingleLineArray);
318     ivalue.Accept(writer);
319 
320     return buffer.GetString();
321 }
322 
323 
324 void
JsWriteValue(JsWriter * writer,const JsValue & js)325 JsWriteValue(JsWriter* writer, const JsValue& js)
326 {
327     if (!writer) {
328         return;
329     }
330 
331     if (js.IsObject()) {
332         const JsObject& obj = js.GetJsObject();
333         writer->BeginObject();
334         for (const JsObject::value_type& field : obj) {
335             writer->WriteKey(field.first);
336             JsWriteValue(writer, field.second);
337         }
338         writer->EndObject();
339     } else if (js.IsArray()) {
340         const JsArray& array = js.GetJsArray();
341         writer->BeginArray();
342         for (const JsValue& elem : array) {
343             JsWriteValue(writer, elem);
344         }
345         writer->EndArray();
346     } else if (js.IsUInt64()) {
347         writer->WriteValue(js.GetUInt64());
348     } else if (js.IsString()) {
349         writer->WriteValue(js.GetString());
350     } else if (js.IsBool()) {
351         writer->WriteValue(js.GetBool());
352     } else if (js.IsReal()) {
353         writer->WriteValue(js.GetReal());
354     } else if (js.IsInt()) {
355         writer->WriteValue(js.GetInt64());
356     } else if (js.IsNull()) {
357         writer->WriteValue(nullptr);
358     }
359 }
360 
361 //
362 // JsWriter
363 //
364 
365 namespace {
366 
367 // This helper interface is to wrap rapidJSON Writer and PrettyWriter so we can
368 // choose which writer to use at runtime.
369 class Js_PolymorphicWriterInterface
370 {
371 public:
372     virtual ~Js_PolymorphicWriterInterface();
373     virtual bool Null () = 0;
374     virtual bool Bool (bool b) = 0;
375     virtual bool Int (int i) = 0;
376     virtual bool Uint (unsigned u) = 0;
377     virtual bool Int64 (int64_t i64) = 0;
378     virtual bool Uint64 (uint64_t u64) = 0;
379     virtual bool Double (double d) = 0;
380     virtual bool String (const char *str, size_t length) = 0;
381     virtual bool StartObject () = 0;
382     virtual bool Key (const char *str, size_t length) = 0;
383     virtual bool EndObject () = 0;
384     virtual bool StartArray () = 0;
385     virtual bool EndArray() = 0;
386 };
387 
388 Js_PolymorphicWriterInterface::~Js_PolymorphicWriterInterface() = default;
389 
390 // Wraps the rapidJSON class and exposes its interface via virtual functions.
391 template <class TWriter>
392 class Js_PolymorphicWriter : public Js_PolymorphicWriterInterface, public TWriter
393 {
394 public:
395     using Writer = TWriter;
396     using Writer::Writer;
397 
Null()398     bool Null () override {
399         return Writer::Null();
400     }
Bool(bool b)401     bool Bool (bool b) override {
402         return Writer::Bool(b);
403     }
Int(int i)404     bool Int (int i) override {
405         return Writer::Int(i);
406     }
Uint(unsigned u)407     bool Uint (unsigned u) override {
408         return Writer::Uint(u);
409     }
Int64(int64_t i64)410     bool Int64 (int64_t i64) override {
411         return Writer::Int64(i64);
412     }
Uint64(uint64_t u64)413     bool Uint64 (uint64_t u64) override {
414         return Writer::Uint64(u64);
415     }
Double(double d)416     bool Double (double d) override {
417         return Writer::Double(d);
418     }
String(const char * str,size_t length)419     bool String (const char *str, size_t length) override {
420         return Writer::String(str, length);
421     }
StartObject()422     bool StartObject () override {
423         return Writer::StartObject();
424     }
Key(const char * str,size_t length)425     bool Key (const char *str, size_t length) override {
426         return Writer::Key(str, length);
427     }
EndObject()428     bool EndObject () override {
429         return Writer::EndObject();
430     }
StartArray()431     bool StartArray () override {
432         return Writer::StartArray();
433     }
EndArray()434     bool EndArray() override {
435         return Writer::EndArray();
436     }
437 };
438 
439 }
440 
441 // JsWriter is a wrapper around a Js_PolymorphicWriterInterface instance.
442 class JsWriter::_Impl
443 {
444     using PrettyWriter =
445         Js_PolymorphicWriter<_WriterFix<rj::PrettyWriter<rj::OStreamWrapper>>>;
446     using Writer =
447         Js_PolymorphicWriter<_WriterFix<rj::Writer<rj::OStreamWrapper>>>;
448 
449 public:
_Impl(std::ostream & s,Style style)450     _Impl(std::ostream& s, Style style)
451     : _strWrapper(s) {
452         switch (style) {
453             case Style::Compact:
454                 _writer = std::unique_ptr<Writer>(new Writer(_strWrapper));
455                 break;
456             case Style::Pretty:
457                 _writer = std::unique_ptr<PrettyWriter>(
458                     new PrettyWriter(_strWrapper));
459                 break;
460         }
461     }
462 
GetWriter()463     Js_PolymorphicWriterInterface* GetWriter() {
464         return _writer.get();
465     }
466 
467 private:
468     std::unique_ptr<Js_PolymorphicWriterInterface> _writer;
469     rj::OStreamWrapper _strWrapper;
470 };
471 
JsWriter(std::ostream & ostr,Style style)472 JsWriter::JsWriter(std::ostream& ostr, Style style)
473     : _impl(new _Impl(ostr, style))
474 {
475 
476 }
477 
478 JsWriter::~JsWriter() = default;
479 
WriteValue(std::nullptr_t)480 bool JsWriter::WriteValue(std::nullptr_t)
481 {
482     return _impl->GetWriter()->Null();
483 }
484 
WriteValue(bool b)485 bool JsWriter::WriteValue(bool b )
486 {
487     return _impl->GetWriter()->Bool(b);
488 }
489 
WriteValue(int i)490 bool JsWriter::WriteValue(int i )
491 {
492     return _impl->GetWriter()->Int(i);
493 }
494 
WriteValue(unsigned u)495 bool JsWriter::WriteValue(unsigned u )
496 {
497     return _impl->GetWriter()->Uint(u);
498 }
499 
WriteValue(int64_t i64)500 bool JsWriter::WriteValue(int64_t i64 )
501 {
502     return _impl->GetWriter()->Int64(i64);
503 }
504 
WriteValue(uint64_t u64)505 bool JsWriter::WriteValue(uint64_t u64 )
506 {
507     return _impl->GetWriter()->Uint64(u64);
508 }
509 
WriteValue(double d)510 bool JsWriter::WriteValue(double d )
511 {
512     return _impl->GetWriter()->Double(d);
513 }
514 
WriteValue(const std::string & s)515 bool JsWriter::WriteValue(const std::string& s)
516 {
517     return _impl->GetWriter()->String(s.c_str(), s.length());
518 }
519 
WriteValue(const char * s)520 bool JsWriter::WriteValue(const char* s)
521 {
522     return _impl->GetWriter()->String(s, strlen(s));
523 }
524 
BeginObject()525 bool JsWriter::BeginObject( )
526 {
527     return _impl->GetWriter()->StartObject();
528 }
529 
WriteKey(const std::string & k)530 bool JsWriter::WriteKey(const std::string& k)
531 {
532     return _impl->GetWriter()->Key(k.c_str(), k.length());
533 }
534 
WriteKey(const char * k)535 bool JsWriter::WriteKey(const char* k)
536 {
537     return _impl->GetWriter()->Key(k, strlen(k));
538 }
539 
_Key(const char * s,size_t len)540 bool JsWriter::_Key(const char* s, size_t len)
541 {
542     return _impl->GetWriter()->Key(s, len);
543 }
544 
_String(const char * s,size_t len)545 bool JsWriter::_String(const char* s, size_t len)
546 {
547     return _impl->GetWriter()->String(s, len);
548 }
549 
EndObject()550 bool JsWriter::EndObject( )
551 {
552     return _impl->GetWriter()->EndObject();
553 }
554 
BeginArray()555 bool JsWriter::BeginArray( )
556 {
557     return _impl->GetWriter()->StartArray();
558 }
559 
EndArray()560 bool JsWriter::EndArray( )
561 {
562     return _impl->GetWriter()->EndArray();
563 }
564 
565 PXR_NAMESPACE_CLOSE_SCOPE
566