1 /*!
2  * Copyright (c) by Contributors 2019-2021
3  */
4 #include <cctype>
5 #include <cstddef>
6 #include <iterator>
7 #include <locale>
8 #include <sstream>
9 #include <limits>
10 #include <cmath>
11 
12 #include "charconv.h"
13 #include "xgboost/base.h"
14 #include "xgboost/logging.h"
15 #include "xgboost/json.h"
16 #include "xgboost/json_io.h"
17 
18 namespace xgboost {
19 
Save(Json json)20 void JsonWriter::Save(Json json) {
21   json.ptr_->Save(this);
22 }
23 
Visit(JsonArray const * arr)24 void JsonWriter::Visit(JsonArray const* arr) {
25   stream_->emplace_back('[');
26   auto const& vec = arr->GetArray();
27   size_t size = vec.size();
28   for (size_t i = 0; i < size; ++i) {
29     auto const& value = vec[i];
30     this->Save(value);
31     if (i != size - 1) {
32       stream_->emplace_back(',');
33     }
34   }
35   stream_->emplace_back(']');
36 }
37 
Visit(JsonObject const * obj)38 void JsonWriter::Visit(JsonObject const* obj) {
39   stream_->emplace_back('{');
40   size_t i = 0;
41   size_t size = obj->GetObject().size();
42 
43   for (auto& value : obj->GetObject()) {
44     auto s = String{value.first};
45     this->Visit(&s);
46     stream_->emplace_back(':');
47     this->Save(value.second);
48 
49     if (i != size-1) {
50       stream_->emplace_back(',');
51     }
52     i++;
53   }
54 
55   stream_->emplace_back('}');
56 }
57 
Visit(JsonNumber const * num)58 void JsonWriter::Visit(JsonNumber const* num) {
59   char number[NumericLimits<float>::kToCharsSize];
60   auto res = to_chars(number, number + sizeof(number), num->GetNumber());
61   auto end = res.ptr;
62   auto ori_size = stream_->size();
63   stream_->resize(stream_->size() + end - number);
64   std::memcpy(stream_->data() + ori_size, number, end - number);
65 }
66 
Visit(JsonInteger const * num)67 void JsonWriter::Visit(JsonInteger const* num) {
68   char i2s_buffer_[NumericLimits<int64_t>::kToCharsSize];
69   auto i = num->GetInteger();
70   auto ret = to_chars(i2s_buffer_, i2s_buffer_ + NumericLimits<int64_t>::kToCharsSize, i);
71   auto end = ret.ptr;
72   CHECK(ret.ec == std::errc());
73   auto digits = std::distance(i2s_buffer_, end);
74   auto ori_size = stream_->size();
75   stream_->resize(ori_size + digits);
76   std::memcpy(stream_->data() + ori_size, i2s_buffer_, digits);
77 }
78 
Visit(JsonNull const *)79 void JsonWriter::Visit(JsonNull const* ) {
80     auto s = stream_->size();
81     stream_->resize(s + 4);
82     auto& buf = (*stream_);
83     buf[s + 0] = 'n';
84     buf[s + 1] = 'u';
85     buf[s + 2] = 'l';
86     buf[s + 3] = 'l';
87 }
88 
Visit(JsonString const * str)89 void JsonWriter::Visit(JsonString const* str) {
90   std::string buffer;
91   buffer += '"';
92   auto const& string = str->GetString();
93   for (size_t i = 0; i < string.length(); i++) {
94     const char ch = string[i];
95     if (ch == '\\') {
96       if (i < string.size() && string[i+1] == 'u') {
97         buffer += "\\";
98       } else {
99         buffer += "\\\\";
100       }
101     } else if (ch == '"') {
102       buffer += "\\\"";
103     } else if (ch == '\b') {
104       buffer += "\\b";
105     } else if (ch == '\f') {
106       buffer += "\\f";
107     } else if (ch == '\n') {
108       buffer += "\\n";
109     } else if (ch == '\r') {
110       buffer += "\\r";
111     } else if (ch == '\t') {
112       buffer += "\\t";
113     } else if (static_cast<uint8_t>(ch) <= 0x1f) {
114       // Unit separator
115       char buf[8];
116       snprintf(buf, sizeof buf, "\\u%04x", ch);
117       buffer += buf;
118     } else {
119       buffer += ch;
120     }
121   }
122   buffer += '"';
123 
124   auto s = stream_->size();
125   stream_->resize(s + buffer.size());
126   std::memcpy(stream_->data() + s, buffer.data(), buffer.size());
127 }
128 
Visit(JsonBoolean const * boolean)129 void JsonWriter::Visit(JsonBoolean const* boolean) {
130   bool val = boolean->GetBoolean();
131   auto s = stream_->size();
132   if (val) {
133     stream_->resize(s + 4);
134     auto& buf = (*stream_);
135     buf[s + 0] = 't';
136     buf[s + 1] = 'r';
137     buf[s + 2] = 'u';
138     buf[s + 3] = 'e';
139   } else {
140     stream_->resize(s + 5);
141     auto& buf = (*stream_);
142     buf[s + 0] = 'f';
143     buf[s + 1] = 'a';
144     buf[s + 2] = 'l';
145     buf[s + 3] = 's';
146     buf[s + 4] = 'e';
147   }
148 }
149 
150 // Value
TypeStr() const151 std::string Value::TypeStr() const {
152   switch (kind_) {
153     case ValueKind::kString:  return "String";  break;
154     case ValueKind::kNumber:  return "Number";  break;
155     case ValueKind::kObject:  return "Object";  break;
156     case ValueKind::kArray:   return "Array";   break;
157     case ValueKind::kBoolean: return "Boolean"; break;
158     case ValueKind::kNull:    return "Null";    break;
159     case ValueKind::kInteger: return "Integer"; break;
160   }
161   return "";
162 }
163 
164 // Only used for keeping old compilers happy about non-reaching return
165 // statement.
DummyJsonObject()166 Json& DummyJsonObject() {
167   static Json obj;
168   return obj;
169 }
170 
171 // Json Object
JsonObject(JsonObject && that)172 JsonObject::JsonObject(JsonObject && that) noexcept :
173     Value(ValueKind::kObject), object_{std::move(that.object_)} {}
174 
JsonObject(std::map<std::string,Json> && object)175 JsonObject::JsonObject(std::map<std::string, Json> &&object) noexcept
176     : Value(ValueKind::kObject), object_{std::move(object)} {}
177 
operator [](std::string const & key)178 Json& JsonObject::operator[](std::string const & key) {
179   return object_[key];
180 }
181 
operator [](int)182 Json& JsonObject::operator[](int ) {
183   LOG(FATAL) << "Object of type "
184              << Value::TypeStr() << " can not be indexed by Integer.";
185   return DummyJsonObject();
186 }
187 
operator ==(Value const & rhs) const188 bool JsonObject::operator==(Value const& rhs) const {
189   if (!IsA<JsonObject>(&rhs)) {
190     return false;
191   }
192   return object_ == Cast<JsonObject const>(&rhs)->GetObject();
193 }
194 
operator =(Value const & rhs)195 Value& JsonObject::operator=(Value const &rhs) {
196   JsonObject const* casted = Cast<JsonObject const>(&rhs);
197   object_ = casted->GetObject();
198   return *this;
199 }
200 
Save(JsonWriter * writer)201 void JsonObject::Save(JsonWriter* writer) {
202   writer->Visit(this);
203 }
204 
205 // Json String
operator [](std::string const &)206 Json& JsonString::operator[](std::string const& ) {
207   LOG(FATAL) << "Object of type "
208              << Value::TypeStr() << " can not be indexed by string.";
209   return DummyJsonObject();
210 }
211 
operator [](int)212 Json& JsonString::operator[](int ) {
213   LOG(FATAL) << "Object of type "
214              << Value::TypeStr() << " can not be indexed by Integer."
215              << "  Please try obtaining std::string first.";
216   return DummyJsonObject();
217 }
218 
operator ==(Value const & rhs) const219 bool JsonString::operator==(Value const& rhs) const {
220   if (!IsA<JsonString>(&rhs)) { return false; }
221   return Cast<JsonString const>(&rhs)->GetString() == str_;
222 }
223 
operator =(Value const & rhs)224 Value & JsonString::operator=(Value const &rhs) {
225   JsonString const* casted = Cast<JsonString const>(&rhs);
226   str_ = casted->GetString();
227   return *this;
228 }
229 
230 // FIXME: UTF-8 parsing support.
Save(JsonWriter * writer)231 void JsonString::Save(JsonWriter* writer) {
232   writer->Visit(this);
233 }
234 
235 // Json Array
JsonArray(JsonArray && that)236 JsonArray::JsonArray(JsonArray && that) noexcept :
237     Value(ValueKind::kArray), vec_{std::move(that.vec_)} {}
238 
operator [](std::string const &)239 Json& JsonArray::operator[](std::string const& ) {
240   LOG(FATAL) << "Object of type "
241              << Value::TypeStr() << " can not be indexed by string.";
242   return DummyJsonObject();
243 }
244 
operator [](int ind)245 Json& JsonArray::operator[](int ind) {
246   return vec_.at(ind);
247 }
248 
operator ==(Value const & rhs) const249 bool JsonArray::operator==(Value const& rhs) const {
250   if (!IsA<JsonArray>(&rhs)) { return false; }
251   auto& arr = Cast<JsonArray const>(&rhs)->GetArray();
252   return std::equal(arr.cbegin(), arr.cend(), vec_.cbegin());
253 }
254 
operator =(Value const & rhs)255 Value & JsonArray::operator=(Value const &rhs) {
256   JsonArray const* casted = Cast<JsonArray const>(&rhs);
257   vec_ = casted->GetArray();
258   return *this;
259 }
260 
Save(JsonWriter * writer)261 void JsonArray::Save(JsonWriter* writer) {
262   writer->Visit(this);
263 }
264 
265 // Json Number
operator [](std::string const &)266 Json& JsonNumber::operator[](std::string const& ) {
267   LOG(FATAL) << "Object of type "
268              << Value::TypeStr() << " can not be indexed by string.";
269   return DummyJsonObject();
270 }
271 
operator [](int)272 Json& JsonNumber::operator[](int ) {
273   LOG(FATAL) << "Object of type "
274              << Value::TypeStr() << " can not be indexed by Integer.";
275   return DummyJsonObject();
276 }
277 
operator ==(Value const & rhs) const278 bool JsonNumber::operator==(Value const& rhs) const {
279   if (!IsA<JsonNumber>(&rhs)) { return false; }
280   auto r_num = Cast<JsonNumber const>(&rhs)->GetNumber();
281   if (std::isinf(number_)) {
282     return std::isinf(r_num);
283   }
284   if (std::isnan(number_)) {
285     return std::isnan(r_num);
286   }
287   return number_ - r_num == 0;
288 }
289 
operator =(Value const & rhs)290 Value & JsonNumber::operator=(Value const &rhs) {
291   JsonNumber const* casted = Cast<JsonNumber const>(&rhs);
292   number_ = casted->GetNumber();
293   return *this;
294 }
295 
Save(JsonWriter * writer)296 void JsonNumber::Save(JsonWriter* writer) {
297   writer->Visit(this);
298 }
299 
300 // Json Integer
operator [](std::string const &)301 Json& JsonInteger::operator[](std::string const& ) {
302   LOG(FATAL) << "Object of type "
303              << Value::TypeStr() << " can not be indexed by string.";
304   return DummyJsonObject();
305 }
306 
operator [](int)307 Json& JsonInteger::operator[](int ) {
308   LOG(FATAL) << "Object of type "
309              << Value::TypeStr() << " can not be indexed by Integer.";
310   return DummyJsonObject();
311 }
312 
operator ==(Value const & rhs) const313 bool JsonInteger::operator==(Value const& rhs) const {
314   if (!IsA<JsonInteger>(&rhs)) { return false; }
315   return integer_ == Cast<JsonInteger const>(&rhs)->GetInteger();
316 }
317 
operator =(Value const & rhs)318 Value & JsonInteger::operator=(Value const &rhs) {
319   JsonInteger const* casted = Cast<JsonInteger const>(&rhs);
320   integer_ = casted->GetInteger();
321   return *this;
322 }
323 
Save(JsonWriter * writer)324 void JsonInteger::Save(JsonWriter* writer) {
325   writer->Visit(this);
326 }
327 
328 // Json Null
operator [](std::string const &)329 Json& JsonNull::operator[](std::string const& ) {
330   LOG(FATAL) << "Object of type "
331              << Value::TypeStr() << " can not be indexed by string.";
332   return DummyJsonObject();
333 }
334 
operator [](int)335 Json& JsonNull::operator[](int ) {
336   LOG(FATAL) << "Object of type "
337              << Value::TypeStr() << " can not be indexed by Integer.";
338   return DummyJsonObject();
339 }
340 
operator ==(Value const & rhs) const341 bool JsonNull::operator==(Value const& rhs) const {
342   if (!IsA<JsonNull>(&rhs)) { return false; }
343   return true;
344 }
345 
operator =(Value const & rhs)346 Value & JsonNull::operator=(Value const &rhs) {
347   Cast<JsonNull const>(&rhs);  // Checking only.
348   return *this;
349 }
350 
Save(JsonWriter * writer)351 void JsonNull::Save(JsonWriter* writer) {
352   writer->Visit(this);
353 }
354 
355 // Json Boolean
operator [](std::string const &)356 Json& JsonBoolean::operator[](std::string const& ) {
357   LOG(FATAL) << "Object of type "
358              << Value::TypeStr() << " can not be indexed by string.";
359   return DummyJsonObject();
360 }
361 
operator [](int)362 Json& JsonBoolean::operator[](int ) {
363   LOG(FATAL) << "Object of type "
364              << Value::TypeStr() << " can not be indexed by Integer.";
365   return DummyJsonObject();
366 }
367 
operator ==(Value const & rhs) const368 bool JsonBoolean::operator==(Value const& rhs) const {
369   if (!IsA<JsonBoolean>(&rhs)) { return false; }
370   return boolean_ == Cast<JsonBoolean const>(&rhs)->GetBoolean();
371 }
372 
operator =(Value const & rhs)373 Value & JsonBoolean::operator=(Value const &rhs) {
374   JsonBoolean const* casted = Cast<JsonBoolean const>(&rhs);
375   boolean_ = casted->GetBoolean();
376   return *this;
377 }
378 
Save(JsonWriter * writer)379 void JsonBoolean::Save(JsonWriter *writer) {
380   writer->Visit(this);
381 }
382 
383 size_t constexpr JsonReader::kMaxNumLength;
384 
Parse()385 Json JsonReader::Parse() {
386   while (true) {
387     SkipSpaces();
388     char c = PeekNextChar();
389     if (c == -1) { break; }
390 
391     if (c == '{') {
392       return ParseObject();
393     } else if ( c == '[' ) {
394       return ParseArray();
395     } else if ( c == '-' || std::isdigit(c) ||
396                 c == 'N' || c == 'I') {
397       // For now we only accept `NaN`, not `nan` as the later violates LR(1) with `null`.
398       return ParseNumber();
399     } else if ( c == '\"' ) {
400       return ParseString();
401     } else if ( c == 't' || c == 'f' ) {
402       return ParseBoolean();
403     } else if (c == 'n') {
404       return ParseNull();
405     } else {
406       Error("Unknown construct");
407     }
408   }
409   return {};
410 }
411 
Load()412 Json JsonReader::Load() {
413   Json result = Parse();
414   return result;
415 }
416 
Error(std::string msg) const417 void JsonReader::Error(std::string msg) const {
418   // just copy it.
419   std::istringstream str_s(raw_str_.substr(0, raw_str_.size()));
420 
421   msg += ", around character position: " + std::to_string(cursor_.Pos());
422   msg += '\n';
423 
424   if (cursor_.Pos() == 0) {
425     LOG(FATAL) << msg << ", \"" << str_s.str() << " \"";
426   }
427 
428   constexpr size_t kExtend = 8;
429   auto beg = static_cast<int64_t>(cursor_.Pos()) -
430              static_cast<int64_t>(kExtend) < 0 ? 0 : cursor_.Pos() - kExtend;
431   auto end = cursor_.Pos() + kExtend >= raw_str_.size() ?
432              raw_str_.size() : cursor_.Pos() + kExtend;
433 
434   std::string const& raw_portion = raw_str_.substr(beg, end - beg);
435   std::string portion;
436   for (auto c : raw_portion) {
437     if (c == '\n') {
438       portion += "\\n";
439     } else if (c == '\0') {
440       portion += "\\0";
441     } else {
442       portion += c;
443     }
444   }
445 
446   msg += "    ";
447   msg += portion;
448   msg += '\n';
449 
450   msg += "    ";
451   for (size_t i = beg; i < cursor_.Pos() - 1; ++i) {
452     msg += '~';
453   }
454   msg += '^';
455   for (size_t i = cursor_.Pos(); i < end; ++i) {
456     msg += '~';
457   }
458   LOG(FATAL) << msg;
459 }
460 
461 namespace {
IsSpace(char c)462 bool IsSpace(char c) { return c == ' ' || c == '\n' || c == '\r' || c == '\t'; }
463 }  // anonymous namespace
464 
465 // Json class
SkipSpaces()466 void JsonReader::SkipSpaces() {
467   while (cursor_.Pos() < raw_str_.size()) {
468     char c = raw_str_[cursor_.Pos()];
469     if (IsSpace(c)) {
470       cursor_.Forward();
471     } else {
472       break;
473     }
474   }
475 }
476 
ParseStr(std::string const & str)477 void ParseStr(std::string const& str) {
478   size_t end = 0;
479   for (size_t i = 0; i < str.size(); ++i) {
480     if (str[i] == '"' && i > 0 && str[i-1] != '\\') {
481       end = i;
482       break;
483     }
484   }
485   std::string result;
486   result.resize(end);
487 }
488 
ParseString()489 Json JsonReader::ParseString() {
490   char ch { GetConsecutiveChar('\"') };  // NOLINT
491   std::ostringstream output;
492   std::string str;
493   while (true) {
494     ch = GetNextChar();
495     if (ch == '\\') {
496       char next = static_cast<char>(GetNextChar());
497       switch (next) {
498         case 'r':  str += u8"\r"; break;
499         case 'n':  str += u8"\n"; break;
500         case '\\': str += u8"\\"; break;
501         case 't':  str += u8"\t"; break;
502         case '\"': str += u8"\""; break;
503         case 'u':
504           str += ch;
505           str += 'u';
506           break;
507         default: Error("Unknown escape");
508       }
509     } else {
510       if (ch == '\"') break;
511       str += ch;
512     }
513     if (ch == EOF || ch == '\r' || ch == '\n') {
514       Expect('\"', ch);
515     }
516   }
517   return Json(std::move(str));
518 }
519 
ParseNull()520 Json JsonReader::ParseNull() {
521   char ch = GetNextNonSpaceChar();
522   std::string buffer{ch};
523   for (size_t i = 0; i < 3; ++i) {
524     buffer.push_back(GetNextChar());
525   }
526   if (buffer != "null") {
527     Error("Expecting null value \"null\"");
528   }
529   return Json{JsonNull()};
530 }
531 
ParseArray()532 Json JsonReader::ParseArray() {
533   std::vector<Json> data;
534 
535   char ch { GetConsecutiveChar('[') };  // NOLINT
536   while (true) {
537     if (PeekNextChar() == ']') {
538       GetConsecutiveChar(']');
539       return Json(std::move(data));
540     }
541     auto obj = Parse();
542     data.emplace_back(obj);
543     ch = GetNextNonSpaceChar();
544     if (ch == ']') break;
545     if (ch != ',') {
546       Expect(',', ch);
547     }
548   }
549 
550   return Json(std::move(data));
551 }
552 
ParseObject()553 Json JsonReader::ParseObject() {
554   GetConsecutiveChar('{');
555 
556   std::map<std::string, Json> data;
557   SkipSpaces();
558   char ch = PeekNextChar();
559 
560   if (ch == '}') {
561     GetConsecutiveChar('}');
562     return Json(std::move(data));
563   }
564 
565   while (true) {
566     SkipSpaces();
567     ch = PeekNextChar();
568     CHECK_NE(ch, -1) << "cursor_.Pos(): " << cursor_.Pos() << ", "
569                      << "raw_str_.size():" << raw_str_.size();
570     if (ch != '"') {
571       Expect('"', ch);
572     }
573     Json key = ParseString();
574 
575     ch = GetNextNonSpaceChar();
576 
577     if (ch != ':') {
578       Expect(':', ch);
579     }
580 
581     Json value { Parse() };
582 
583     data[get<String>(key)] = std::move(value);
584 
585     ch = GetNextNonSpaceChar();
586 
587     if (ch == '}') break;
588     if (ch != ',') {
589       Expect(',', ch);
590     }
591   }
592 
593   return Json(std::move(data));
594 }
595 
ParseNumber()596 Json JsonReader::ParseNumber() {
597   // Adopted from sajson with some simplifications and small optimizations.
598   char const* p = raw_str_.c_str() + cursor_.Pos();
599   char const* const beg = p;  // keep track of current pointer
600 
601   // TODO(trivialfis): Add back all the checks for number
602   if (XGBOOST_EXPECT(*p == 'N', false)) {
603     GetConsecutiveChar('N');
604     GetConsecutiveChar('a');
605     GetConsecutiveChar('N');
606     return Json(static_cast<Number::Float>(std::numeric_limits<float>::quiet_NaN()));
607   }
608 
609   bool negative = false;
610   switch (*p) {
611   case '-': {
612     negative = true;
613     ++p;
614     break;
615   }
616   case '+': {
617     negative = false;
618     ++p;
619     break;
620   }
621   default: {
622     break;
623   }
624   }
625 
626   if (XGBOOST_EXPECT(*p == 'I', false)) {
627     cursor_.Forward(std::distance(beg, p));  // +/-
628     for (auto i : {'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'}) {
629       GetConsecutiveChar(i);
630     }
631     auto f = std::numeric_limits<float>::infinity();
632     if (negative) {
633       f = -f;
634     }
635     return Json(static_cast<Number::Float>(f));
636   }
637 
638   bool is_float = false;
639 
640   int64_t i = 0;
641 
642   if (*p == '0') {
643     i = 0;
644     p++;
645   }
646 
647   while (XGBOOST_EXPECT(*p >= '0' && *p <= '9', true)) {
648     i = i * 10 + (*p - '0');
649     p++;
650   }
651 
652   if (*p == '.') {
653     p++;
654     is_float = true;
655 
656     while (*p >= '0' && *p <= '9') {
657       i = i * 10 + (*p - '0');
658       p++;
659     }
660   }
661 
662   if (*p == 'E' || *p == 'e') {
663     is_float = true;
664     p++;
665 
666     switch (*p) {
667     case '-':
668     case '+': {
669       p++;
670       break;
671     }
672     default:
673       break;
674     }
675 
676     if (XGBOOST_EXPECT(*p >= '0' && *p <= '9', true)) {
677       p++;
678       while (*p >= '0' && *p <= '9') {
679         p++;
680       }
681     } else {
682       Error("Expecting digit");
683     }
684   }
685 
686   auto moved = std::distance(beg, p);
687   this->cursor_.Forward(moved);
688 
689   if (is_float) {
690     float f;
691     auto ret = from_chars(beg, p, f);
692     if (XGBOOST_EXPECT(ret.ec != std::errc(), false)) {
693       // Compatible with old format that generates very long mantissa from std stream.
694       f = std::strtof(beg, nullptr);
695     }
696     return Json(static_cast<Number::Float>(f));
697   } else {
698     if (negative) {
699       i = -i;
700     }
701     return Json(JsonInteger(i));
702   }
703 }
704 
ParseBoolean()705 Json JsonReader::ParseBoolean() {
706   bool result = false;
707   char ch = GetNextNonSpaceChar();
708   std::string const t_value = u8"true";
709   std::string const f_value = u8"false";
710   std::string buffer;
711 
712   if (ch == 't') {
713     GetConsecutiveChar('r');
714     GetConsecutiveChar('u');
715     GetConsecutiveChar('e');
716     result = true;
717   } else {
718     GetConsecutiveChar('a');
719     GetConsecutiveChar('l');
720     GetConsecutiveChar('s');
721     GetConsecutiveChar('e');
722     result = false;
723   }
724   return Json{JsonBoolean{result}};
725 }
726 
Load(StringView str)727 Json Json::Load(StringView str) {
728   JsonReader reader(str);
729   Json json{reader.Load()};
730   return json;
731 }
732 
Load(JsonReader * reader)733 Json Json::Load(JsonReader* reader) {
734   Json json{reader->Load()};
735   return json;
736 }
737 
Dump(Json json,std::string * str)738 void Json::Dump(Json json, std::string* str) {
739   std::vector<char> buffer;
740   JsonWriter writer(&buffer);
741   writer.Save(json);
742   str->resize(buffer.size());
743   std::copy(buffer.cbegin(), buffer.cend(), str->begin());
744 }
745 
746 Json& Json::operator=(Json const &other) = default;
747 
operator <<(std::ostream & os,StringView const v)748 std::ostream &operator<<(std::ostream &os, StringView const v) {
749   for (auto c : v) {
750     os.put(c);
751   }
752   return os;
753 }
754 
755 static_assert(std::is_nothrow_move_constructible<Json>::value, "");
756 static_assert(std::is_nothrow_move_constructible<Object>::value, "");
757 static_assert(std::is_nothrow_move_constructible<Array>::value, "");
758 static_assert(std::is_nothrow_move_constructible<String>::value, "");
759 }  // namespace xgboost
760