1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/values.h"
6
7 #include <string.h>
8
9 #include <algorithm>
10 #include <cmath>
11 #include <iterator>
12 #include <new>
13 #include <ostream>
14 #include <utility>
15
16 #include "base/json/json_writer.h"
17 #include "base/logging.h"
18 #include "base/memory/ptr_util.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22
23 namespace base {
24
25 namespace {
26
27 const char* const kTypeNames[] = {"null", "boolean", "integer", "string",
28 "binary", "dictionary", "list"};
29 static_assert(std::size(kTypeNames) ==
30 static_cast<size_t>(Value::Type::LIST) + 1,
31 "kTypeNames Has Wrong Size");
32
33 std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node);
34
35 // Make a deep copy of |node|, but don't include empty lists or dictionaries
36 // in the copy. It's possible for this function to return NULL and it
37 // expects |node| to always be non-NULL.
CopyListWithoutEmptyChildren(const Value & list)38 std::unique_ptr<Value> CopyListWithoutEmptyChildren(const Value& list) {
39 Value copy(Value::Type::LIST);
40 for (const auto& entry : list.GetList()) {
41 std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(entry);
42 if (child_copy)
43 copy.GetList().push_back(std::move(*child_copy));
44 }
45 return copy.GetList().empty() ? nullptr
46 : std::make_unique<Value>(std::move(copy));
47 }
48
CopyDictionaryWithoutEmptyChildren(const DictionaryValue & dict)49 std::unique_ptr<DictionaryValue> CopyDictionaryWithoutEmptyChildren(
50 const DictionaryValue& dict) {
51 std::unique_ptr<DictionaryValue> copy;
52 for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
53 std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(it.value());
54 if (child_copy) {
55 if (!copy)
56 copy = std::make_unique<DictionaryValue>();
57 copy->SetWithoutPathExpansion(it.key(), std::move(child_copy));
58 }
59 }
60 return copy;
61 }
62
CopyWithoutEmptyChildren(const Value & node)63 std::unique_ptr<Value> CopyWithoutEmptyChildren(const Value& node) {
64 switch (node.type()) {
65 case Value::Type::LIST:
66 return CopyListWithoutEmptyChildren(static_cast<const ListValue&>(node));
67
68 case Value::Type::DICTIONARY:
69 return CopyDictionaryWithoutEmptyChildren(
70 static_cast<const DictionaryValue&>(node));
71
72 default:
73 return std::make_unique<Value>(node.Clone());
74 }
75 }
76
77 } // namespace
78
79 // static
CreateWithCopiedBuffer(const char * buffer,size_t size)80 std::unique_ptr<Value> Value::CreateWithCopiedBuffer(const char* buffer,
81 size_t size) {
82 return std::make_unique<Value>(BlobStorage(buffer, buffer + size));
83 }
84
85 // static
FromUniquePtrValue(std::unique_ptr<Value> val)86 Value Value::FromUniquePtrValue(std::unique_ptr<Value> val) {
87 return std::move(*val);
88 }
89
90 // static
ToUniquePtrValue(Value val)91 std::unique_ptr<Value> Value::ToUniquePtrValue(Value val) {
92 return std::make_unique<Value>(std::move(val));
93 }
94
Value(Value && that)95 Value::Value(Value&& that) noexcept {
96 InternalMoveConstructFrom(std::move(that));
97 }
98
Value()99 Value::Value() noexcept : type_(Type::NONE) {}
100
Value(Type type)101 Value::Value(Type type) : type_(type) {
102 // Initialize with the default value.
103 switch (type_) {
104 case Type::NONE:
105 return;
106
107 case Type::BOOLEAN:
108 bool_value_ = false;
109 return;
110 case Type::INTEGER:
111 int_value_ = 0;
112 return;
113 case Type::STRING:
114 new (&string_value_) std::string();
115 return;
116 case Type::BINARY:
117 new (&binary_value_) BlobStorage();
118 return;
119 case Type::DICTIONARY:
120 new (&dict_) DictStorage();
121 return;
122 case Type::LIST:
123 new (&list_) ListStorage();
124 return;
125 }
126 }
127
Value(bool in_bool)128 Value::Value(bool in_bool) : type_(Type::BOOLEAN), bool_value_(in_bool) {}
129
Value(int in_int)130 Value::Value(int in_int) : type_(Type::INTEGER), int_value_(in_int) {}
131
Value(const char * in_string)132 Value::Value(const char* in_string) : Value(std::string(in_string)) {}
133
Value(std::string_view in_string)134 Value::Value(std::string_view in_string) : Value(std::string(in_string)) {}
135
Value(std::string && in_string)136 Value::Value(std::string&& in_string) noexcept
137 : type_(Type::STRING), string_value_(std::move(in_string)) {
138 DCHECK(IsStringUTF8(string_value_));
139 }
140
Value(const char16_t * in_string16)141 Value::Value(const char16_t* in_string16)
142 : Value(std::u16string_view(in_string16)) {}
143
Value(std::u16string_view in_string16)144 Value::Value(std::u16string_view in_string16)
145 : Value(UTF16ToUTF8(in_string16)) {}
146
Value(const BlobStorage & in_blob)147 Value::Value(const BlobStorage& in_blob)
148 : type_(Type::BINARY), binary_value_(in_blob) {}
149
Value(BlobStorage && in_blob)150 Value::Value(BlobStorage&& in_blob) noexcept
151 : type_(Type::BINARY), binary_value_(std::move(in_blob)) {}
152
Value(const DictStorage & in_dict)153 Value::Value(const DictStorage& in_dict) : type_(Type::DICTIONARY), dict_() {
154 dict_.reserve(in_dict.size());
155 for (const auto& it : in_dict) {
156 dict_.try_emplace(dict_.end(), it.first,
157 std::make_unique<Value>(it.second->Clone()));
158 }
159 }
160
Value(DictStorage && in_dict)161 Value::Value(DictStorage&& in_dict) noexcept
162 : type_(Type::DICTIONARY), dict_(std::move(in_dict)) {}
163
Value(const ListStorage & in_list)164 Value::Value(const ListStorage& in_list) : type_(Type::LIST), list_() {
165 list_.reserve(in_list.size());
166 for (const auto& val : in_list)
167 list_.emplace_back(val.Clone());
168 }
169
Value(ListStorage && in_list)170 Value::Value(ListStorage&& in_list) noexcept
171 : type_(Type::LIST), list_(std::move(in_list)) {}
172
operator =(Value && that)173 Value& Value::operator=(Value&& that) noexcept {
174 InternalCleanup();
175 InternalMoveConstructFrom(std::move(that));
176
177 return *this;
178 }
179
Clone() const180 Value Value::Clone() const {
181 switch (type_) {
182 case Type::NONE:
183 return Value();
184 case Type::BOOLEAN:
185 return Value(bool_value_);
186 case Type::INTEGER:
187 return Value(int_value_);
188 case Type::STRING:
189 return Value(string_value_);
190 case Type::BINARY:
191 return Value(binary_value_);
192 case Type::DICTIONARY:
193 return Value(dict_);
194 case Type::LIST:
195 return Value(list_);
196 }
197
198 NOTREACHED();
199 return Value();
200 }
201
~Value()202 Value::~Value() {
203 InternalCleanup();
204 }
205
206 // static
GetTypeName(Value::Type type)207 const char* Value::GetTypeName(Value::Type type) {
208 DCHECK_GE(static_cast<int>(type), 0);
209 DCHECK_LT(static_cast<size_t>(type), std::size(kTypeNames));
210 return kTypeNames[static_cast<size_t>(type)];
211 }
212
GetBool() const213 bool Value::GetBool() const {
214 CHECK(is_bool());
215 return bool_value_;
216 }
217
GetInt() const218 int Value::GetInt() const {
219 CHECK(is_int());
220 return int_value_;
221 }
222
GetString() const223 const std::string& Value::GetString() const {
224 CHECK(is_string());
225 return string_value_;
226 }
227
GetBlob() const228 const Value::BlobStorage& Value::GetBlob() const {
229 CHECK(is_blob());
230 return binary_value_;
231 }
232
GetList()233 Value::ListStorage& Value::GetList() {
234 CHECK(is_list());
235 return list_;
236 }
237
GetList() const238 const Value::ListStorage& Value::GetList() const {
239 CHECK(is_list());
240 return list_;
241 }
242
FindKey(std::string_view key)243 Value* Value::FindKey(std::string_view key) {
244 return const_cast<Value*>(static_cast<const Value*>(this)->FindKey(key));
245 }
246
FindKey(std::string_view key) const247 const Value* Value::FindKey(std::string_view key) const {
248 CHECK(is_dict());
249 auto found = dict_.find(key);
250 if (found == dict_.end())
251 return nullptr;
252 return found->second.get();
253 }
254
FindKeyOfType(std::string_view key,Type type)255 Value* Value::FindKeyOfType(std::string_view key, Type type) {
256 return const_cast<Value*>(
257 static_cast<const Value*>(this)->FindKeyOfType(key, type));
258 }
259
FindKeyOfType(std::string_view key,Type type) const260 const Value* Value::FindKeyOfType(std::string_view key, Type type) const {
261 const Value* result = FindKey(key);
262 if (!result || result->type() != type)
263 return nullptr;
264 return result;
265 }
266
RemoveKey(std::string_view key)267 bool Value::RemoveKey(std::string_view key) {
268 CHECK(is_dict());
269 // NOTE: Can't directly return dict_->erase(key) due to MSVC warning C4800.
270 return dict_.erase(key) != 0;
271 }
272
SetKey(std::string_view key,Value value)273 Value* Value::SetKey(std::string_view key, Value value) {
274 CHECK(is_dict());
275 // NOTE: We can't use |insert_or_assign| here, as only |try_emplace| does
276 // an explicit conversion from std::string_view to std::string if necessary.
277 auto val_ptr = std::make_unique<Value>(std::move(value));
278 auto result = dict_.try_emplace(key, std::move(val_ptr));
279 if (!result.second) {
280 // val_ptr is guaranteed to be still intact at this point.
281 result.first->second = std::move(val_ptr);
282 }
283 return result.first->second.get();
284 }
285
SetKey(std::string && key,Value value)286 Value* Value::SetKey(std::string&& key, Value value) {
287 CHECK(is_dict());
288 return dict_
289 .insert_or_assign(std::move(key),
290 std::make_unique<Value>(std::move(value)))
291 .first->second.get();
292 }
293
SetKey(const char * key,Value value)294 Value* Value::SetKey(const char* key, Value value) {
295 return SetKey(std::string_view(key), std::move(value));
296 }
297
FindPath(std::initializer_list<std::string_view> path)298 Value* Value::FindPath(std::initializer_list<std::string_view> path) {
299 return const_cast<Value*>(const_cast<const Value*>(this)->FindPath(path));
300 }
301
FindPath(span<const std::string_view> path)302 Value* Value::FindPath(span<const std::string_view> path) {
303 return const_cast<Value*>(const_cast<const Value*>(this)->FindPath(path));
304 }
305
FindPath(std::initializer_list<std::string_view> path) const306 const Value* Value::FindPath(
307 std::initializer_list<std::string_view> path) const {
308 DCHECK_GE(path.size(), 2u) << "Use FindKey() for a path of length 1.";
309 return FindPath(make_span(path.begin(), path.size()));
310 }
311
FindPath(span<const std::string_view> path) const312 const Value* Value::FindPath(span<const std::string_view> path) const {
313 const Value* cur = this;
314 for (const std::string_view& component : path) {
315 if (!cur->is_dict() || (cur = cur->FindKey(component)) == nullptr)
316 return nullptr;
317 }
318 return cur;
319 }
320
FindPathOfType(std::initializer_list<std::string_view> path,Type type)321 Value* Value::FindPathOfType(std::initializer_list<std::string_view> path,
322 Type type) {
323 return const_cast<Value*>(
324 const_cast<const Value*>(this)->FindPathOfType(path, type));
325 }
326
FindPathOfType(span<const std::string_view> path,Type type)327 Value* Value::FindPathOfType(span<const std::string_view> path, Type type) {
328 return const_cast<Value*>(
329 const_cast<const Value*>(this)->FindPathOfType(path, type));
330 }
331
FindPathOfType(std::initializer_list<std::string_view> path,Type type) const332 const Value* Value::FindPathOfType(std::initializer_list<std::string_view> path,
333 Type type) const {
334 DCHECK_GE(path.size(), 2u) << "Use FindKeyOfType() for a path of length 1.";
335 return FindPathOfType(make_span(path.begin(), path.size()), type);
336 }
337
FindPathOfType(span<const std::string_view> path,Type type) const338 const Value* Value::FindPathOfType(span<const std::string_view> path,
339 Type type) const {
340 const Value* result = FindPath(path);
341 if (!result || result->type() != type)
342 return nullptr;
343 return result;
344 }
345
SetPath(std::initializer_list<std::string_view> path,Value value)346 Value* Value::SetPath(std::initializer_list<std::string_view> path,
347 Value value) {
348 DCHECK_GE(path.size(), 2u) << "Use SetKey() for a path of length 1.";
349 return SetPath(make_span(path.begin(), path.size()), std::move(value));
350 }
351
SetPath(span<const std::string_view> path,Value value)352 Value* Value::SetPath(span<const std::string_view> path, Value value) {
353 DCHECK_NE(path.begin(), path.end()); // Can't be empty path.
354
355 // Walk/construct intermediate dictionaries. The last element requires
356 // special handling so skip it in this loop.
357 Value* cur = this;
358 const std::string_view* cur_path = path.begin();
359 for (; (cur_path + 1) < path.end(); ++cur_path) {
360 if (!cur->is_dict())
361 return nullptr;
362
363 // Use lower_bound to avoid doing the search twice for missing keys.
364 const std::string_view path_component = *cur_path;
365 auto found = cur->dict_.lower_bound(path_component);
366 if (found == cur->dict_.end() || found->first != path_component) {
367 // No key found, insert one.
368 auto inserted = cur->dict_.try_emplace(
369 found, path_component, std::make_unique<Value>(Type::DICTIONARY));
370 cur = inserted->second.get();
371 } else {
372 cur = found->second.get();
373 }
374 }
375
376 // "cur" will now contain the last dictionary to insert or replace into.
377 if (!cur->is_dict())
378 return nullptr;
379 return cur->SetKey(*cur_path, std::move(value));
380 }
381
RemovePath(std::initializer_list<std::string_view> path)382 bool Value::RemovePath(std::initializer_list<std::string_view> path) {
383 DCHECK_GE(path.size(), 2u) << "Use RemoveKey() for a path of length 1.";
384 return RemovePath(make_span(path.begin(), path.size()));
385 }
386
RemovePath(span<const std::string_view> path)387 bool Value::RemovePath(span<const std::string_view> path) {
388 if (!is_dict() || path.empty())
389 return false;
390
391 if (path.size() == 1)
392 return RemoveKey(path[0]);
393
394 auto found = dict_.find(path[0]);
395 if (found == dict_.end() || !found->second->is_dict())
396 return false;
397
398 bool removed = found->second->RemovePath(path.subspan(1));
399 if (removed && found->second->dict_.empty())
400 dict_.erase(found);
401
402 return removed;
403 }
404
DictItems()405 Value::dict_iterator_proxy Value::DictItems() {
406 CHECK(is_dict());
407 return dict_iterator_proxy(&dict_);
408 }
409
DictItems() const410 Value::const_dict_iterator_proxy Value::DictItems() const {
411 CHECK(is_dict());
412 return const_dict_iterator_proxy(&dict_);
413 }
414
DictSize() const415 size_t Value::DictSize() const {
416 CHECK(is_dict());
417 return dict_.size();
418 }
419
DictEmpty() const420 bool Value::DictEmpty() const {
421 CHECK(is_dict());
422 return dict_.empty();
423 }
424
GetAsBoolean(bool * out_value) const425 bool Value::GetAsBoolean(bool* out_value) const {
426 if (out_value && is_bool()) {
427 *out_value = bool_value_;
428 return true;
429 }
430 return is_bool();
431 }
432
GetAsInteger(int * out_value) const433 bool Value::GetAsInteger(int* out_value) const {
434 if (out_value && is_int()) {
435 *out_value = int_value_;
436 return true;
437 }
438 return is_int();
439 }
440
GetAsString(std::string * out_value) const441 bool Value::GetAsString(std::string* out_value) const {
442 if (out_value && is_string()) {
443 *out_value = string_value_;
444 return true;
445 }
446 return is_string();
447 }
448
GetAsString(std::u16string * out_value) const449 bool Value::GetAsString(std::u16string* out_value) const {
450 if (out_value && is_string()) {
451 *out_value = UTF8ToUTF16(string_value_);
452 return true;
453 }
454 return is_string();
455 }
456
GetAsString(const Value ** out_value) const457 bool Value::GetAsString(const Value** out_value) const {
458 if (out_value && is_string()) {
459 *out_value = static_cast<const Value*>(this);
460 return true;
461 }
462 return is_string();
463 }
464
GetAsString(std::string_view * out_value) const465 bool Value::GetAsString(std::string_view* out_value) const {
466 if (out_value && is_string()) {
467 *out_value = string_value_;
468 return true;
469 }
470 return is_string();
471 }
472
GetAsList(ListValue ** out_value)473 bool Value::GetAsList(ListValue** out_value) {
474 if (out_value && is_list()) {
475 *out_value = static_cast<ListValue*>(this);
476 return true;
477 }
478 return is_list();
479 }
480
GetAsList(const ListValue ** out_value) const481 bool Value::GetAsList(const ListValue** out_value) const {
482 if (out_value && is_list()) {
483 *out_value = static_cast<const ListValue*>(this);
484 return true;
485 }
486 return is_list();
487 }
488
GetAsDictionary(DictionaryValue ** out_value)489 bool Value::GetAsDictionary(DictionaryValue** out_value) {
490 if (out_value && is_dict()) {
491 *out_value = static_cast<DictionaryValue*>(this);
492 return true;
493 }
494 return is_dict();
495 }
496
GetAsDictionary(const DictionaryValue ** out_value) const497 bool Value::GetAsDictionary(const DictionaryValue** out_value) const {
498 if (out_value && is_dict()) {
499 *out_value = static_cast<const DictionaryValue*>(this);
500 return true;
501 }
502 return is_dict();
503 }
504
DeepCopy() const505 Value* Value::DeepCopy() const {
506 return new Value(Clone());
507 }
508
CreateDeepCopy() const509 std::unique_ptr<Value> Value::CreateDeepCopy() const {
510 return std::make_unique<Value>(Clone());
511 }
512
operator ==(const Value & lhs,const Value & rhs)513 bool operator==(const Value& lhs, const Value& rhs) {
514 if (lhs.type_ != rhs.type_)
515 return false;
516
517 switch (lhs.type_) {
518 case Value::Type::NONE:
519 return true;
520 case Value::Type::BOOLEAN:
521 return lhs.bool_value_ == rhs.bool_value_;
522 case Value::Type::INTEGER:
523 return lhs.int_value_ == rhs.int_value_;
524 case Value::Type::STRING:
525 return lhs.string_value_ == rhs.string_value_;
526 case Value::Type::BINARY:
527 return lhs.binary_value_ == rhs.binary_value_;
528 // TODO(crbug.com/646113): Clean this up when DictionaryValue and ListValue
529 // are completely inlined.
530 case Value::Type::DICTIONARY:
531 if (lhs.dict_.size() != rhs.dict_.size())
532 return false;
533 return std::equal(
534 std::begin(lhs.dict_), std::end(lhs.dict_), std::begin(rhs.dict_),
535 [](const auto& u, const auto& v) {
536 return std::tie(u.first, *u.second) == std::tie(v.first, *v.second);
537 });
538 case Value::Type::LIST:
539 return lhs.list_ == rhs.list_;
540 }
541
542 NOTREACHED();
543 return false;
544 }
545
operator !=(const Value & lhs,const Value & rhs)546 bool operator!=(const Value& lhs, const Value& rhs) {
547 return !(lhs == rhs);
548 }
549
operator <(const Value & lhs,const Value & rhs)550 bool operator<(const Value& lhs, const Value& rhs) {
551 if (lhs.type_ != rhs.type_)
552 return lhs.type_ < rhs.type_;
553
554 switch (lhs.type_) {
555 case Value::Type::NONE:
556 return false;
557 case Value::Type::BOOLEAN:
558 return lhs.bool_value_ < rhs.bool_value_;
559 case Value::Type::INTEGER:
560 return lhs.int_value_ < rhs.int_value_;
561 case Value::Type::STRING:
562 return lhs.string_value_ < rhs.string_value_;
563 case Value::Type::BINARY:
564 return lhs.binary_value_ < rhs.binary_value_;
565 // TODO(crbug.com/646113): Clean this up when DictionaryValue and ListValue
566 // are completely inlined.
567 case Value::Type::DICTIONARY:
568 return std::lexicographical_compare(
569 std::begin(lhs.dict_), std::end(lhs.dict_), std::begin(rhs.dict_),
570 std::end(rhs.dict_),
571 [](const Value::DictStorage::value_type& u,
572 const Value::DictStorage::value_type& v) {
573 return std::tie(u.first, *u.second) < std::tie(v.first, *v.second);
574 });
575 case Value::Type::LIST:
576 return lhs.list_ < rhs.list_;
577 }
578
579 NOTREACHED();
580 return false;
581 }
582
operator >(const Value & lhs,const Value & rhs)583 bool operator>(const Value& lhs, const Value& rhs) {
584 return rhs < lhs;
585 }
586
operator <=(const Value & lhs,const Value & rhs)587 bool operator<=(const Value& lhs, const Value& rhs) {
588 return !(rhs < lhs);
589 }
590
operator >=(const Value & lhs,const Value & rhs)591 bool operator>=(const Value& lhs, const Value& rhs) {
592 return !(lhs < rhs);
593 }
594
Equals(const Value * other) const595 bool Value::Equals(const Value* other) const {
596 DCHECK(other);
597 return *this == *other;
598 }
599
InternalMoveConstructFrom(Value && that)600 void Value::InternalMoveConstructFrom(Value&& that) {
601 type_ = that.type_;
602
603 switch (type_) {
604 case Type::NONE:
605 return;
606 case Type::BOOLEAN:
607 bool_value_ = that.bool_value_;
608 return;
609 case Type::INTEGER:
610 int_value_ = that.int_value_;
611 return;
612 case Type::STRING:
613 new (&string_value_) std::string(std::move(that.string_value_));
614 return;
615 case Type::BINARY:
616 new (&binary_value_) BlobStorage(std::move(that.binary_value_));
617 return;
618 case Type::DICTIONARY:
619 new (&dict_) DictStorage(std::move(that.dict_));
620 return;
621 case Type::LIST:
622 new (&list_) ListStorage(std::move(that.list_));
623 return;
624 }
625 }
626
InternalCleanup()627 void Value::InternalCleanup() {
628 switch (type_) {
629 case Type::NONE:
630 case Type::BOOLEAN:
631 case Type::INTEGER:
632 // Nothing to do
633 return;
634
635 case Type::STRING:
636 string_value_.~basic_string();
637 return;
638 case Type::BINARY:
639 binary_value_.~BlobStorage();
640 return;
641 case Type::DICTIONARY:
642 dict_.~DictStorage();
643 return;
644 case Type::LIST:
645 list_.~ListStorage();
646 return;
647 }
648 }
649
650 ///////////////////// DictionaryValue ////////////////////
651
652 // static
From(std::unique_ptr<Value> value)653 std::unique_ptr<DictionaryValue> DictionaryValue::From(
654 std::unique_ptr<Value> value) {
655 DictionaryValue* out;
656 if (value && value->GetAsDictionary(&out)) {
657 [[maybe_unused]] auto released = value.release();
658 return WrapUnique(out);
659 }
660 return nullptr;
661 }
662
DictionaryValue()663 DictionaryValue::DictionaryValue() : Value(Type::DICTIONARY) {}
DictionaryValue(const DictStorage & in_dict)664 DictionaryValue::DictionaryValue(const DictStorage& in_dict) : Value(in_dict) {}
DictionaryValue(DictStorage && in_dict)665 DictionaryValue::DictionaryValue(DictStorage&& in_dict) noexcept
666 : Value(std::move(in_dict)) {}
667
HasKey(std::string_view key) const668 bool DictionaryValue::HasKey(std::string_view key) const {
669 DCHECK(IsStringUTF8(key));
670 auto current_entry = dict_.find(key);
671 DCHECK((current_entry == dict_.end()) || current_entry->second);
672 return current_entry != dict_.end();
673 }
674
Clear()675 void DictionaryValue::Clear() {
676 dict_.clear();
677 }
678
Set(std::string_view path,std::unique_ptr<Value> in_value)679 Value* DictionaryValue::Set(std::string_view path,
680 std::unique_ptr<Value> in_value) {
681 DCHECK(IsStringUTF8(path));
682 DCHECK(in_value);
683
684 std::string_view current_path(path);
685 Value* current_dictionary = this;
686 for (size_t delimiter_position = current_path.find('.');
687 delimiter_position != std::string_view::npos;
688 delimiter_position = current_path.find('.')) {
689 // Assume that we're indexing into a dictionary.
690 std::string_view key = current_path.substr(0, delimiter_position);
691 Value* child_dictionary =
692 current_dictionary->FindKeyOfType(key, Type::DICTIONARY);
693 if (!child_dictionary) {
694 child_dictionary =
695 current_dictionary->SetKey(key, Value(Type::DICTIONARY));
696 }
697
698 current_dictionary = child_dictionary;
699 current_path = current_path.substr(delimiter_position + 1);
700 }
701
702 return static_cast<DictionaryValue*>(current_dictionary)
703 ->SetWithoutPathExpansion(current_path, std::move(in_value));
704 }
705
SetBoolean(std::string_view path,bool in_value)706 Value* DictionaryValue::SetBoolean(std::string_view path, bool in_value) {
707 return Set(path, std::make_unique<Value>(in_value));
708 }
709
SetInteger(std::string_view path,int in_value)710 Value* DictionaryValue::SetInteger(std::string_view path, int in_value) {
711 return Set(path, std::make_unique<Value>(in_value));
712 }
713
SetString(std::string_view path,std::string_view in_value)714 Value* DictionaryValue::SetString(std::string_view path,
715 std::string_view in_value) {
716 return Set(path, std::make_unique<Value>(in_value));
717 }
718
SetString(std::string_view path,const std::u16string & in_value)719 Value* DictionaryValue::SetString(std::string_view path,
720 const std::u16string& in_value) {
721 return Set(path, std::make_unique<Value>(in_value));
722 }
723
SetDictionary(std::string_view path,std::unique_ptr<DictionaryValue> in_value)724 DictionaryValue* DictionaryValue::SetDictionary(
725 std::string_view path,
726 std::unique_ptr<DictionaryValue> in_value) {
727 return static_cast<DictionaryValue*>(Set(path, std::move(in_value)));
728 }
729
SetList(std::string_view path,std::unique_ptr<ListValue> in_value)730 ListValue* DictionaryValue::SetList(std::string_view path,
731 std::unique_ptr<ListValue> in_value) {
732 return static_cast<ListValue*>(Set(path, std::move(in_value)));
733 }
734
SetWithoutPathExpansion(std::string_view key,std::unique_ptr<Value> in_value)735 Value* DictionaryValue::SetWithoutPathExpansion(
736 std::string_view key,
737 std::unique_ptr<Value> in_value) {
738 // NOTE: We can't use |insert_or_assign| here, as only |try_emplace| does
739 // an explicit conversion from std::string_view to std::string if necessary.
740 auto result = dict_.try_emplace(key, std::move(in_value));
741 if (!result.second) {
742 // in_value is guaranteed to be still intact at this point.
743 result.first->second = std::move(in_value);
744 }
745 return result.first->second.get();
746 }
747
Get(std::string_view path,const Value ** out_value) const748 bool DictionaryValue::Get(std::string_view path,
749 const Value** out_value) const {
750 DCHECK(IsStringUTF8(path));
751 std::string_view current_path(path);
752 const DictionaryValue* current_dictionary = this;
753 for (size_t delimiter_position = current_path.find('.');
754 delimiter_position != std::string::npos;
755 delimiter_position = current_path.find('.')) {
756 const DictionaryValue* child_dictionary = nullptr;
757 if (!current_dictionary->GetDictionaryWithoutPathExpansion(
758 current_path.substr(0, delimiter_position), &child_dictionary)) {
759 return false;
760 }
761
762 current_dictionary = child_dictionary;
763 current_path = current_path.substr(delimiter_position + 1);
764 }
765
766 return current_dictionary->GetWithoutPathExpansion(current_path, out_value);
767 }
768
Get(std::string_view path,Value ** out_value)769 bool DictionaryValue::Get(std::string_view path, Value** out_value) {
770 return static_cast<const DictionaryValue&>(*this).Get(
771 path, const_cast<const Value**>(out_value));
772 }
773
GetBoolean(std::string_view path,bool * bool_value) const774 bool DictionaryValue::GetBoolean(std::string_view path,
775 bool* bool_value) const {
776 const Value* value;
777 if (!Get(path, &value))
778 return false;
779
780 return value->GetAsBoolean(bool_value);
781 }
782
GetInteger(std::string_view path,int * out_value) const783 bool DictionaryValue::GetInteger(std::string_view path, int* out_value) const {
784 const Value* value;
785 if (!Get(path, &value))
786 return false;
787
788 return value->GetAsInteger(out_value);
789 }
790
GetString(std::string_view path,std::string * out_value) const791 bool DictionaryValue::GetString(std::string_view path,
792 std::string* out_value) const {
793 const Value* value;
794 if (!Get(path, &value))
795 return false;
796
797 return value->GetAsString(out_value);
798 }
799
GetString(std::string_view path,std::u16string * out_value) const800 bool DictionaryValue::GetString(std::string_view path,
801 std::u16string* out_value) const {
802 const Value* value;
803 if (!Get(path, &value))
804 return false;
805
806 return value->GetAsString(out_value);
807 }
808
GetStringASCII(std::string_view path,std::string * out_value) const809 bool DictionaryValue::GetStringASCII(std::string_view path,
810 std::string* out_value) const {
811 std::string out;
812 if (!GetString(path, &out))
813 return false;
814
815 if (!IsStringASCII(out)) {
816 NOTREACHED();
817 return false;
818 }
819
820 out_value->assign(out);
821 return true;
822 }
823
GetBinary(std::string_view path,const Value ** out_value) const824 bool DictionaryValue::GetBinary(std::string_view path,
825 const Value** out_value) const {
826 const Value* value;
827 bool result = Get(path, &value);
828 if (!result || !value->is_blob())
829 return false;
830
831 if (out_value)
832 *out_value = value;
833
834 return true;
835 }
836
GetBinary(std::string_view path,Value ** out_value)837 bool DictionaryValue::GetBinary(std::string_view path, Value** out_value) {
838 return static_cast<const DictionaryValue&>(*this).GetBinary(
839 path, const_cast<const Value**>(out_value));
840 }
841
GetDictionary(std::string_view path,const DictionaryValue ** out_value) const842 bool DictionaryValue::GetDictionary(std::string_view path,
843 const DictionaryValue** out_value) const {
844 const Value* value;
845 bool result = Get(path, &value);
846 if (!result || !value->is_dict())
847 return false;
848
849 if (out_value)
850 *out_value = static_cast<const DictionaryValue*>(value);
851
852 return true;
853 }
854
GetDictionary(std::string_view path,DictionaryValue ** out_value)855 bool DictionaryValue::GetDictionary(std::string_view path,
856 DictionaryValue** out_value) {
857 return static_cast<const DictionaryValue&>(*this).GetDictionary(
858 path, const_cast<const DictionaryValue**>(out_value));
859 }
860
GetList(std::string_view path,const ListValue ** out_value) const861 bool DictionaryValue::GetList(std::string_view path,
862 const ListValue** out_value) const {
863 const Value* value;
864 bool result = Get(path, &value);
865 if (!result || !value->is_list())
866 return false;
867
868 if (out_value)
869 *out_value = static_cast<const ListValue*>(value);
870
871 return true;
872 }
873
GetList(std::string_view path,ListValue ** out_value)874 bool DictionaryValue::GetList(std::string_view path, ListValue** out_value) {
875 return static_cast<const DictionaryValue&>(*this).GetList(
876 path, const_cast<const ListValue**>(out_value));
877 }
878
GetWithoutPathExpansion(std::string_view key,const Value ** out_value) const879 bool DictionaryValue::GetWithoutPathExpansion(std::string_view key,
880 const Value** out_value) const {
881 DCHECK(IsStringUTF8(key));
882 auto entry_iterator = dict_.find(key);
883 if (entry_iterator == dict_.end())
884 return false;
885
886 if (out_value)
887 *out_value = entry_iterator->second.get();
888 return true;
889 }
890
GetWithoutPathExpansion(std::string_view key,Value ** out_value)891 bool DictionaryValue::GetWithoutPathExpansion(std::string_view key,
892 Value** out_value) {
893 return static_cast<const DictionaryValue&>(*this).GetWithoutPathExpansion(
894 key, const_cast<const Value**>(out_value));
895 }
896
GetBooleanWithoutPathExpansion(std::string_view key,bool * out_value) const897 bool DictionaryValue::GetBooleanWithoutPathExpansion(std::string_view key,
898 bool* out_value) const {
899 const Value* value;
900 if (!GetWithoutPathExpansion(key, &value))
901 return false;
902
903 return value->GetAsBoolean(out_value);
904 }
905
GetIntegerWithoutPathExpansion(std::string_view key,int * out_value) const906 bool DictionaryValue::GetIntegerWithoutPathExpansion(std::string_view key,
907 int* out_value) const {
908 const Value* value;
909 if (!GetWithoutPathExpansion(key, &value))
910 return false;
911
912 return value->GetAsInteger(out_value);
913 }
914
GetStringWithoutPathExpansion(std::string_view key,std::string * out_value) const915 bool DictionaryValue::GetStringWithoutPathExpansion(
916 std::string_view key,
917 std::string* out_value) const {
918 const Value* value;
919 if (!GetWithoutPathExpansion(key, &value))
920 return false;
921
922 return value->GetAsString(out_value);
923 }
924
GetStringWithoutPathExpansion(std::string_view key,std::u16string * out_value) const925 bool DictionaryValue::GetStringWithoutPathExpansion(
926 std::string_view key,
927 std::u16string* out_value) const {
928 const Value* value;
929 if (!GetWithoutPathExpansion(key, &value))
930 return false;
931
932 return value->GetAsString(out_value);
933 }
934
GetDictionaryWithoutPathExpansion(std::string_view key,const DictionaryValue ** out_value) const935 bool DictionaryValue::GetDictionaryWithoutPathExpansion(
936 std::string_view key,
937 const DictionaryValue** out_value) const {
938 const Value* value;
939 bool result = GetWithoutPathExpansion(key, &value);
940 if (!result || !value->is_dict())
941 return false;
942
943 if (out_value)
944 *out_value = static_cast<const DictionaryValue*>(value);
945
946 return true;
947 }
948
GetDictionaryWithoutPathExpansion(std::string_view key,DictionaryValue ** out_value)949 bool DictionaryValue::GetDictionaryWithoutPathExpansion(
950 std::string_view key,
951 DictionaryValue** out_value) {
952 const DictionaryValue& const_this =
953 static_cast<const DictionaryValue&>(*this);
954 return const_this.GetDictionaryWithoutPathExpansion(
955 key, const_cast<const DictionaryValue**>(out_value));
956 }
957
GetListWithoutPathExpansion(std::string_view key,const ListValue ** out_value) const958 bool DictionaryValue::GetListWithoutPathExpansion(
959 std::string_view key,
960 const ListValue** out_value) const {
961 const Value* value;
962 bool result = GetWithoutPathExpansion(key, &value);
963 if (!result || !value->is_list())
964 return false;
965
966 if (out_value)
967 *out_value = static_cast<const ListValue*>(value);
968
969 return true;
970 }
971
GetListWithoutPathExpansion(std::string_view key,ListValue ** out_value)972 bool DictionaryValue::GetListWithoutPathExpansion(std::string_view key,
973 ListValue** out_value) {
974 return static_cast<const DictionaryValue&>(*this).GetListWithoutPathExpansion(
975 key, const_cast<const ListValue**>(out_value));
976 }
977
Remove(std::string_view path,std::unique_ptr<Value> * out_value)978 bool DictionaryValue::Remove(std::string_view path,
979 std::unique_ptr<Value>* out_value) {
980 DCHECK(IsStringUTF8(path));
981 std::string_view current_path(path);
982 DictionaryValue* current_dictionary = this;
983 size_t delimiter_position = current_path.rfind('.');
984 if (delimiter_position != std::string_view::npos) {
985 if (!GetDictionary(current_path.substr(0, delimiter_position),
986 ¤t_dictionary))
987 return false;
988 current_path = current_path.substr(delimiter_position + 1);
989 }
990
991 return current_dictionary->RemoveWithoutPathExpansion(current_path,
992 out_value);
993 }
994
RemoveWithoutPathExpansion(std::string_view key,std::unique_ptr<Value> * out_value)995 bool DictionaryValue::RemoveWithoutPathExpansion(
996 std::string_view key,
997 std::unique_ptr<Value>* out_value) {
998 DCHECK(IsStringUTF8(key));
999 auto entry_iterator = dict_.find(key);
1000 if (entry_iterator == dict_.end())
1001 return false;
1002
1003 if (out_value)
1004 *out_value = std::move(entry_iterator->second);
1005 dict_.erase(entry_iterator);
1006 return true;
1007 }
1008
RemovePath(std::string_view path,std::unique_ptr<Value> * out_value)1009 bool DictionaryValue::RemovePath(std::string_view path,
1010 std::unique_ptr<Value>* out_value) {
1011 bool result = false;
1012 size_t delimiter_position = path.find('.');
1013
1014 if (delimiter_position == std::string::npos)
1015 return RemoveWithoutPathExpansion(path, out_value);
1016
1017 std::string_view subdict_path = path.substr(0, delimiter_position);
1018 DictionaryValue* subdict = nullptr;
1019 if (!GetDictionary(subdict_path, &subdict))
1020 return false;
1021 result = subdict->RemovePath(path.substr(delimiter_position + 1), out_value);
1022 if (result && subdict->empty())
1023 RemoveWithoutPathExpansion(subdict_path, nullptr);
1024
1025 return result;
1026 }
1027
DeepCopyWithoutEmptyChildren() const1028 std::unique_ptr<DictionaryValue> DictionaryValue::DeepCopyWithoutEmptyChildren()
1029 const {
1030 std::unique_ptr<DictionaryValue> copy =
1031 CopyDictionaryWithoutEmptyChildren(*this);
1032 if (!copy)
1033 copy = std::make_unique<DictionaryValue>();
1034 return copy;
1035 }
1036
MergeDictionary(const DictionaryValue * dictionary)1037 void DictionaryValue::MergeDictionary(const DictionaryValue* dictionary) {
1038 CHECK(dictionary->is_dict());
1039 for (DictionaryValue::Iterator it(*dictionary); !it.IsAtEnd(); it.Advance()) {
1040 const Value* merge_value = &it.value();
1041 // Check whether we have to merge dictionaries.
1042 if (merge_value->is_dict()) {
1043 DictionaryValue* sub_dict;
1044 if (GetDictionaryWithoutPathExpansion(it.key(), &sub_dict)) {
1045 sub_dict->MergeDictionary(
1046 static_cast<const DictionaryValue*>(merge_value));
1047 continue;
1048 }
1049 }
1050 // All other cases: Make a copy and hook it up.
1051 SetKey(it.key(), merge_value->Clone());
1052 }
1053 }
1054
Swap(DictionaryValue * other)1055 void DictionaryValue::Swap(DictionaryValue* other) {
1056 CHECK(other->is_dict());
1057 dict_.swap(other->dict_);
1058 }
1059
Iterator(const DictionaryValue & target)1060 DictionaryValue::Iterator::Iterator(const DictionaryValue& target)
1061 : target_(target), it_(target.dict_.begin()) {}
1062
1063 DictionaryValue::Iterator::Iterator(const Iterator& other) = default;
1064
1065 DictionaryValue::Iterator::~Iterator() = default;
1066
DeepCopy() const1067 DictionaryValue* DictionaryValue::DeepCopy() const {
1068 return new DictionaryValue(dict_);
1069 }
1070
CreateDeepCopy() const1071 std::unique_ptr<DictionaryValue> DictionaryValue::CreateDeepCopy() const {
1072 return std::make_unique<DictionaryValue>(dict_);
1073 }
1074
1075 ///////////////////// ListValue ////////////////////
1076
1077 // static
From(std::unique_ptr<Value> value)1078 std::unique_ptr<ListValue> ListValue::From(std::unique_ptr<Value> value) {
1079 ListValue* out;
1080 if (value && value->GetAsList(&out)) {
1081 [[maybe_unused]] auto result = value.release();
1082 return WrapUnique(out);
1083 }
1084 return nullptr;
1085 }
1086
ListValue()1087 ListValue::ListValue() : Value(Type::LIST) {}
ListValue(const ListStorage & in_list)1088 ListValue::ListValue(const ListStorage& in_list) : Value(in_list) {}
ListValue(ListStorage && in_list)1089 ListValue::ListValue(ListStorage&& in_list) noexcept
1090 : Value(std::move(in_list)) {}
1091
Clear()1092 void ListValue::Clear() {
1093 list_.clear();
1094 }
1095
Reserve(size_t n)1096 void ListValue::Reserve(size_t n) {
1097 list_.reserve(n);
1098 }
1099
Set(size_t index,std::unique_ptr<Value> in_value)1100 bool ListValue::Set(size_t index, std::unique_ptr<Value> in_value) {
1101 if (!in_value)
1102 return false;
1103
1104 if (index >= list_.size())
1105 list_.resize(index + 1);
1106
1107 list_[index] = std::move(*in_value);
1108 return true;
1109 }
1110
Get(size_t index,const Value ** out_value) const1111 bool ListValue::Get(size_t index, const Value** out_value) const {
1112 if (index >= list_.size())
1113 return false;
1114
1115 if (out_value)
1116 *out_value = &list_[index];
1117
1118 return true;
1119 }
1120
Get(size_t index,Value ** out_value)1121 bool ListValue::Get(size_t index, Value** out_value) {
1122 return static_cast<const ListValue&>(*this).Get(
1123 index, const_cast<const Value**>(out_value));
1124 }
1125
GetBoolean(size_t index,bool * bool_value) const1126 bool ListValue::GetBoolean(size_t index, bool* bool_value) const {
1127 const Value* value;
1128 if (!Get(index, &value))
1129 return false;
1130
1131 return value->GetAsBoolean(bool_value);
1132 }
1133
GetInteger(size_t index,int * out_value) const1134 bool ListValue::GetInteger(size_t index, int* out_value) const {
1135 const Value* value;
1136 if (!Get(index, &value))
1137 return false;
1138
1139 return value->GetAsInteger(out_value);
1140 }
1141
GetString(size_t index,std::string * out_value) const1142 bool ListValue::GetString(size_t index, std::string* out_value) const {
1143 const Value* value;
1144 if (!Get(index, &value))
1145 return false;
1146
1147 return value->GetAsString(out_value);
1148 }
1149
GetString(size_t index,std::u16string * out_value) const1150 bool ListValue::GetString(size_t index, std::u16string* out_value) const {
1151 const Value* value;
1152 if (!Get(index, &value))
1153 return false;
1154
1155 return value->GetAsString(out_value);
1156 }
1157
GetDictionary(size_t index,const DictionaryValue ** out_value) const1158 bool ListValue::GetDictionary(size_t index,
1159 const DictionaryValue** out_value) const {
1160 const Value* value;
1161 bool result = Get(index, &value);
1162 if (!result || !value->is_dict())
1163 return false;
1164
1165 if (out_value)
1166 *out_value = static_cast<const DictionaryValue*>(value);
1167
1168 return true;
1169 }
1170
GetDictionary(size_t index,DictionaryValue ** out_value)1171 bool ListValue::GetDictionary(size_t index, DictionaryValue** out_value) {
1172 return static_cast<const ListValue&>(*this).GetDictionary(
1173 index, const_cast<const DictionaryValue**>(out_value));
1174 }
1175
GetList(size_t index,const ListValue ** out_value) const1176 bool ListValue::GetList(size_t index, const ListValue** out_value) const {
1177 const Value* value;
1178 bool result = Get(index, &value);
1179 if (!result || !value->is_list())
1180 return false;
1181
1182 if (out_value)
1183 *out_value = static_cast<const ListValue*>(value);
1184
1185 return true;
1186 }
1187
GetList(size_t index,ListValue ** out_value)1188 bool ListValue::GetList(size_t index, ListValue** out_value) {
1189 return static_cast<const ListValue&>(*this).GetList(
1190 index, const_cast<const ListValue**>(out_value));
1191 }
1192
Remove(size_t index,std::unique_ptr<Value> * out_value)1193 bool ListValue::Remove(size_t index, std::unique_ptr<Value>* out_value) {
1194 if (index >= list_.size())
1195 return false;
1196
1197 if (out_value)
1198 *out_value = std::make_unique<Value>(std::move(list_[index]));
1199
1200 list_.erase(list_.begin() + index);
1201 return true;
1202 }
1203
Remove(const Value & value,size_t * index)1204 bool ListValue::Remove(const Value& value, size_t* index) {
1205 auto it = std::find(list_.begin(), list_.end(), value);
1206
1207 if (it == list_.end())
1208 return false;
1209
1210 if (index)
1211 *index = std::distance(list_.begin(), it);
1212
1213 list_.erase(it);
1214 return true;
1215 }
1216
Erase(iterator iter,std::unique_ptr<Value> * out_value)1217 ListValue::iterator ListValue::Erase(iterator iter,
1218 std::unique_ptr<Value>* out_value) {
1219 if (out_value)
1220 *out_value = std::make_unique<Value>(std::move(*iter));
1221
1222 return list_.erase(iter);
1223 }
1224
Append(std::unique_ptr<Value> in_value)1225 void ListValue::Append(std::unique_ptr<Value> in_value) {
1226 list_.push_back(std::move(*in_value));
1227 }
1228
AppendBoolean(bool in_value)1229 void ListValue::AppendBoolean(bool in_value) {
1230 list_.emplace_back(in_value);
1231 }
1232
AppendInteger(int in_value)1233 void ListValue::AppendInteger(int in_value) {
1234 list_.emplace_back(in_value);
1235 }
1236
AppendString(std::string_view in_value)1237 void ListValue::AppendString(std::string_view in_value) {
1238 list_.emplace_back(in_value);
1239 }
1240
AppendString(const std::u16string & in_value)1241 void ListValue::AppendString(const std::u16string& in_value) {
1242 list_.emplace_back(in_value);
1243 }
1244
AppendStrings(const std::vector<std::string> & in_values)1245 void ListValue::AppendStrings(const std::vector<std::string>& in_values) {
1246 list_.reserve(list_.size() + in_values.size());
1247 for (const auto& in_value : in_values)
1248 list_.emplace_back(in_value);
1249 }
1250
AppendStrings(const std::vector<std::u16string> & in_values)1251 void ListValue::AppendStrings(const std::vector<std::u16string>& in_values) {
1252 list_.reserve(list_.size() + in_values.size());
1253 for (const auto& in_value : in_values)
1254 list_.emplace_back(in_value);
1255 }
1256
AppendIfNotPresent(std::unique_ptr<Value> in_value)1257 bool ListValue::AppendIfNotPresent(std::unique_ptr<Value> in_value) {
1258 DCHECK(in_value);
1259 if (ContainsValue(list_, *in_value))
1260 return false;
1261
1262 list_.push_back(std::move(*in_value));
1263 return true;
1264 }
1265
Insert(size_t index,std::unique_ptr<Value> in_value)1266 bool ListValue::Insert(size_t index, std::unique_ptr<Value> in_value) {
1267 DCHECK(in_value);
1268 if (index > list_.size())
1269 return false;
1270
1271 list_.insert(list_.begin() + index, std::move(*in_value));
1272 return true;
1273 }
1274
Find(const Value & value) const1275 ListValue::const_iterator ListValue::Find(const Value& value) const {
1276 return std::find(list_.begin(), list_.end(), value);
1277 }
1278
Swap(ListValue * other)1279 void ListValue::Swap(ListValue* other) {
1280 CHECK(other->is_list());
1281 list_.swap(other->list_);
1282 }
1283
DeepCopy() const1284 ListValue* ListValue::DeepCopy() const {
1285 return new ListValue(list_);
1286 }
1287
CreateDeepCopy() const1288 std::unique_ptr<ListValue> ListValue::CreateDeepCopy() const {
1289 return std::make_unique<ListValue>(list_);
1290 }
1291
1292 ValueSerializer::~ValueSerializer() = default;
1293
1294 ValueDeserializer::~ValueDeserializer() = default;
1295
operator <<(std::ostream & out,const Value & value)1296 std::ostream& operator<<(std::ostream& out, const Value& value) {
1297 std::string json;
1298 JSONWriter::WriteWithOptions(value, JSONWriter::OPTIONS_PRETTY_PRINT, &json);
1299 return out << json;
1300 }
1301
operator <<(std::ostream & out,const Value::Type & type)1302 std::ostream& operator<<(std::ostream& out, const Value::Type& type) {
1303 if (static_cast<int>(type) < 0 ||
1304 static_cast<size_t>(type) >= std::size(kTypeNames))
1305 return out << "Invalid Type (index = " << static_cast<int>(type) << ")";
1306 return out << Value::GetTypeName(type);
1307 }
1308
1309 } // namespace base
1310