Commit 44efa9da authored by jdoerrie's avatar jdoerrie Committed by Commit Bot

Reland "Add Dictionary API to base::Value"

This is a reland of 37c3e0b6
Original change's description:
> Add Dictionary API to base::Value
> 
> This change introduces |FindKey|, |FindKeyOfType|, |SetKey|, |DictEnd| and
> |DictItems| to base::Value. These methods are part of the new API proposal and
> abstract away the pointer based underlying storage of Dictionary Values.
> 
> Bug: 646113
> Change-Id: I09150f3b2fd47e24074caa50a30a058fc4f558a7
> Reviewed-on: https://chromium-review.googlesource.com/561682
> Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
> Reviewed-by: Brett Wilson <brettw@chromium.org>
> Reviewed-by: Vladimir Levin <vmpstr@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#486709}

TBR=brettw@chromium.org

Bug: 646113
Change-Id: I76ee2149f18da83474a16586ee77b8e6be185814
Reviewed-on: https://chromium-review.googlesource.com/571805Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#486756}
parent f3aaa1c4
...@@ -1025,6 +1025,8 @@ component("base") { ...@@ -1025,6 +1025,8 @@ component("base") {
"unguessable_token.h", "unguessable_token.h",
"value_conversions.cc", "value_conversions.cc",
"value_conversions.h", "value_conversions.h",
"value_iterators.cc",
"value_iterators.h",
"values.cc", "values.cc",
"values.h", "values.h",
"version.cc", "version.cc",
...@@ -2249,6 +2251,7 @@ test("base_unittests") { ...@@ -2249,6 +2251,7 @@ test("base_unittests") {
"tracked_objects_unittest.cc", "tracked_objects_unittest.cc",
"tuple_unittest.cc", "tuple_unittest.cc",
"unguessable_token_unittest.cc", "unguessable_token_unittest.cc",
"value_iterators_unittest.cc",
"values_unittest.cc", "values_unittest.cc",
"version_unittest.cc", "version_unittest.cc",
"vlog_unittest.cc", "vlog_unittest.cc",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/value_iterators.h"
namespace base {
namespace detail {
// ----------------------------------------------------------------------------
// dict_iterator.
dict_iterator::pointer::pointer(const reference& ref) : ref_(ref) {}
dict_iterator::pointer::pointer(const pointer& ptr) = default;
dict_iterator::dict_iterator(DictStorage::iterator dict_iter)
: dict_iter_(dict_iter) {}
dict_iterator::dict_iterator(const dict_iterator& dict_iter) = default;
dict_iterator& dict_iterator::operator=(const dict_iterator& dict_iter) =
default;
dict_iterator::~dict_iterator() = default;
dict_iterator::reference dict_iterator::operator*() {
return {dict_iter_->first, *dict_iter_->second};
}
dict_iterator::pointer dict_iterator::operator->() {
return pointer(operator*());
}
dict_iterator& dict_iterator::operator++() {
++dict_iter_;
return *this;
}
dict_iterator dict_iterator::operator++(int) {
dict_iterator tmp(*this);
++dict_iter_;
return tmp;
}
dict_iterator& dict_iterator::operator--() {
--dict_iter_;
return *this;
}
dict_iterator dict_iterator::operator--(int) {
dict_iterator tmp(*this);
--dict_iter_;
return tmp;
}
bool operator==(const dict_iterator& lhs, const dict_iterator& rhs) {
return lhs.dict_iter_ == rhs.dict_iter_;
}
bool operator!=(const dict_iterator& lhs, const dict_iterator& rhs) {
return !(lhs == rhs);
}
// ----------------------------------------------------------------------------
// const_dict_iterator.
const_dict_iterator::pointer::pointer(const reference& ref) : ref_(ref) {}
const_dict_iterator::pointer::pointer(const pointer& ptr) = default;
const_dict_iterator::const_dict_iterator(DictStorage::const_iterator dict_iter)
: dict_iter_(dict_iter) {}
const_dict_iterator::const_dict_iterator(const const_dict_iterator& dict_iter) =
default;
const_dict_iterator& const_dict_iterator::operator=(
const const_dict_iterator& dict_iter) = default;
const_dict_iterator::~const_dict_iterator() = default;
const_dict_iterator::reference const_dict_iterator::operator*() const {
return {dict_iter_->first, *dict_iter_->second};
}
const_dict_iterator::pointer const_dict_iterator::operator->() const {
return pointer(operator*());
}
const_dict_iterator& const_dict_iterator::operator++() {
++dict_iter_;
return *this;
}
const_dict_iterator const_dict_iterator::operator++(int) {
const_dict_iterator tmp(*this);
++dict_iter_;
return tmp;
}
const_dict_iterator& const_dict_iterator::operator--() {
--dict_iter_;
return *this;
}
const_dict_iterator const_dict_iterator::operator--(int) {
const_dict_iterator tmp(*this);
--dict_iter_;
return tmp;
}
bool operator==(const const_dict_iterator& lhs,
const const_dict_iterator& rhs) {
return lhs.dict_iter_ == rhs.dict_iter_;
}
bool operator!=(const const_dict_iterator& lhs,
const const_dict_iterator& rhs) {
return !(lhs == rhs);
}
// ----------------------------------------------------------------------------
// dict_iterator_proxy.
dict_iterator_proxy::dict_iterator_proxy(DictStorage* storage)
: storage_(storage) {}
dict_iterator_proxy::iterator dict_iterator_proxy::begin() {
return iterator(storage_->begin());
}
dict_iterator_proxy::const_iterator dict_iterator_proxy::begin() const {
return const_iterator(storage_->begin());
}
dict_iterator_proxy::iterator dict_iterator_proxy::end() {
return iterator(storage_->end());
}
dict_iterator_proxy::const_iterator dict_iterator_proxy::end() const {
return const_iterator(storage_->end());
}
dict_iterator_proxy::reverse_iterator dict_iterator_proxy::rbegin() {
return reverse_iterator(end());
}
dict_iterator_proxy::const_reverse_iterator dict_iterator_proxy::rbegin()
const {
return const_reverse_iterator(end());
}
dict_iterator_proxy::reverse_iterator dict_iterator_proxy::rend() {
return reverse_iterator(begin());
}
dict_iterator_proxy::const_reverse_iterator dict_iterator_proxy::rend() const {
return const_reverse_iterator(begin());
}
dict_iterator_proxy::const_iterator dict_iterator_proxy::cbegin() const {
return const_iterator(begin());
}
dict_iterator_proxy::const_iterator dict_iterator_proxy::cend() const {
return const_iterator(end());
}
dict_iterator_proxy::const_reverse_iterator dict_iterator_proxy::crbegin()
const {
return const_reverse_iterator(rbegin());
}
dict_iterator_proxy::const_reverse_iterator dict_iterator_proxy::crend() const {
return const_reverse_iterator(rend());
}
// ----------------------------------------------------------------------------
// const_dict_iterator_proxy.
const_dict_iterator_proxy::const_dict_iterator_proxy(const DictStorage* storage)
: storage_(storage) {}
const_dict_iterator_proxy::const_iterator const_dict_iterator_proxy::begin()
const {
return const_iterator(storage_->begin());
}
const_dict_iterator_proxy::const_iterator const_dict_iterator_proxy::end()
const {
return const_iterator(storage_->end());
}
const_dict_iterator_proxy::const_reverse_iterator
const_dict_iterator_proxy::rbegin() const {
return const_reverse_iterator(end());
}
const_dict_iterator_proxy::const_reverse_iterator
const_dict_iterator_proxy::rend() const {
return const_reverse_iterator(begin());
}
const_dict_iterator_proxy::const_iterator const_dict_iterator_proxy::cbegin()
const {
return const_iterator(begin());
}
const_dict_iterator_proxy::const_iterator const_dict_iterator_proxy::cend()
const {
return const_iterator(end());
}
const_dict_iterator_proxy::const_reverse_iterator
const_dict_iterator_proxy::crbegin() const {
return const_reverse_iterator(rbegin());
}
const_dict_iterator_proxy::const_reverse_iterator
const_dict_iterator_proxy::crend() const {
return const_reverse_iterator(rend());
}
} // namespace detail
} // namespace base
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_VALUE_ITERATORS_H_
#define BASE_VALUE_ITERATORS_H_
#include <memory>
#include <string>
#include <utility>
#include "base/base_export.h"
#include "base/containers/flat_map.h"
#include "base/macros.h"
namespace base {
class Value;
namespace detail {
using DictStorage = base::flat_map<std::string, std::unique_ptr<Value>>;
// This iterator closely resembles DictStorage::iterator, with one
// important exception. It abstracts the underlying unique_ptr away, meaning its
// value_type is std::pair<const std::string, Value>. It's reference type is a
// std::pair<const std::string&, Value&>, so that callers have read-write
// access without incurring a copy.
class BASE_EXPORT dict_iterator {
public:
using difference_type = DictStorage::iterator::difference_type;
using value_type = std::pair<const std::string, Value>;
using reference = std::pair<const std::string&, Value&>;
using iterator_category = std::bidirectional_iterator_tag;
class pointer {
public:
explicit pointer(const reference& ref);
pointer(const pointer& ptr);
pointer& operator=(const pointer& ptr) = delete;
reference* operator->() { return &ref_; }
private:
reference ref_;
};
explicit dict_iterator(DictStorage::iterator dict_iter);
dict_iterator(const dict_iterator& dict_iter);
dict_iterator& operator=(const dict_iterator& dict_iter);
~dict_iterator();
reference operator*();
pointer operator->();
dict_iterator& operator++();
dict_iterator operator++(int);
dict_iterator& operator--();
dict_iterator operator--(int);
BASE_EXPORT friend bool operator==(const dict_iterator& lhs,
const dict_iterator& rhs);
BASE_EXPORT friend bool operator!=(const dict_iterator& lhs,
const dict_iterator& rhs);
private:
DictStorage::iterator dict_iter_;
};
// This iterator closely resembles DictStorage::const_iterator, with one
// important exception. It abstracts the underlying unique_ptr away, meaning its
// value_type is std::pair<const std::string, Value>. It's reference type is a
// std::pair<const std::string&, const Value&>, so that callers have read-only
// access without incurring a copy.
class BASE_EXPORT const_dict_iterator {
public:
using difference_type = DictStorage::const_iterator::difference_type;
using value_type = std::pair<const std::string, Value>;
using reference = std::pair<const std::string&, const Value&>;
using iterator_category = std::bidirectional_iterator_tag;
class pointer {
public:
explicit pointer(const reference& ref);
pointer(const pointer& ptr);
pointer& operator=(const pointer& ptr) = delete;
const reference* operator->() const { return &ref_; }
private:
const reference ref_;
};
explicit const_dict_iterator(DictStorage::const_iterator dict_iter);
const_dict_iterator(const const_dict_iterator& dict_iter);
const_dict_iterator& operator=(const const_dict_iterator& dict_iter);
~const_dict_iterator();
reference operator*() const;
pointer operator->() const;
const_dict_iterator& operator++();
const_dict_iterator operator++(int);
const_dict_iterator& operator--();
const_dict_iterator operator--(int);
BASE_EXPORT friend bool operator==(const const_dict_iterator& lhs,
const const_dict_iterator& rhs);
BASE_EXPORT friend bool operator!=(const const_dict_iterator& lhs,
const const_dict_iterator& rhs);
private:
DictStorage::const_iterator dict_iter_;
};
// This class wraps the various |begin| and |end| methods of the underlying
// DictStorage in dict_iterators and const_dict_iterators. This allows callers
// to use this class for easy iteration over the underlying values, granting
// them either read-only or read-write access, depending on the
// const-qualification.
class BASE_EXPORT dict_iterator_proxy {
public:
using key_type = DictStorage::key_type;
using mapped_type = DictStorage::mapped_type::element_type;
using value_type = std::pair<key_type, mapped_type>;
using key_compare = DictStorage::key_compare;
using size_type = DictStorage::size_type;
using difference_type = DictStorage::difference_type;
using iterator = dict_iterator;
using const_iterator = const_dict_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
explicit dict_iterator_proxy(DictStorage* storage);
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
const_dict_iterator cbegin() const;
const_dict_iterator cend() const;
const_reverse_iterator crbegin() const;
const_reverse_iterator crend() const;
private:
DictStorage* storage_;
};
// This class wraps the various const |begin| and |end| methods of the
// underlying DictStorage in const_dict_iterators. This allows callers to use
// this class for easy iteration over the underlying values, granting them
// either read-only access.
class BASE_EXPORT const_dict_iterator_proxy {
public:
using key_type = const DictStorage::key_type;
using mapped_type = const DictStorage::mapped_type::element_type;
using value_type = std::pair<key_type, mapped_type>;
using key_compare = DictStorage::key_compare;
using size_type = DictStorage::size_type;
using difference_type = DictStorage::difference_type;
using iterator = const_dict_iterator;
using const_iterator = const_dict_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
explicit const_dict_iterator_proxy(const DictStorage* storage);
const_iterator begin() const;
const_iterator end() const;
const_reverse_iterator rbegin() const;
const_reverse_iterator rend() const;
const_iterator cbegin() const;
const_iterator cend() const;
const_reverse_iterator crbegin() const;
const_reverse_iterator crend() const;
private:
const DictStorage* storage_;
};
} // namespace detail
} // namespace base
#endif // BASE_VALUE_ITERATORS_H_
This diff is collapsed.
...@@ -244,6 +244,99 @@ const Value::ListStorage& Value::GetList() const { ...@@ -244,6 +244,99 @@ const Value::ListStorage& Value::GetList() const {
return *list_; return *list_;
} }
Value::dict_iterator Value::FindKey(const char* key) {
return FindKey(std::string(key));
}
Value::dict_iterator Value::FindKey(const std::string& key) {
CHECK(is_dict());
return dict_iterator(dict_->find(key));
}
Value::const_dict_iterator Value::FindKey(const char* key) const {
return FindKey(std::string(key));
}
Value::const_dict_iterator Value::FindKey(const std::string& key) const {
CHECK(is_dict());
return const_dict_iterator(dict_->find(key));
}
Value::dict_iterator Value::FindKeyOfType(const char* key, Type type) {
return FindKeyOfType(std::string(key), type);
}
Value::dict_iterator Value::FindKeyOfType(const std::string& key, Type type) {
CHECK(is_dict());
auto iter = dict_->find(key);
return dict_iterator((iter != dict_->end() && iter->second->IsType(type))
? iter
: dict_->end());
}
Value::const_dict_iterator Value::FindKeyOfType(const char* key,
Type type) const {
return FindKeyOfType(std::string(key), type);
}
Value::const_dict_iterator Value::FindKeyOfType(const std::string& key,
Type type) const {
CHECK(is_dict());
auto iter = dict_->find(key);
return const_dict_iterator(
(iter != dict_->end() && iter->second->IsType(type)) ? iter
: dict_->end());
}
Value::dict_iterator Value::SetKey(const char* key, Value value) {
return SetKey(std::string(key), std::move(value));
}
Value::dict_iterator Value::SetKey(const std::string& key, Value value) {
CHECK(is_dict());
auto iter = dict_->find(key);
if (iter != dict_->end()) {
*iter->second = std::move(value);
return dict_iterator(iter);
}
return dict_iterator(
dict_->emplace(key, MakeUnique<Value>(std::move(value))).first);
}
Value::dict_iterator Value::SetKey(std::string&& key, Value value) {
CHECK(is_dict());
auto iter = dict_->find(key);
if (iter != dict_->end()) {
*iter->second = std::move(value);
return dict_iterator(iter);
}
return dict_iterator(
dict_->emplace(std::move(key), MakeUnique<Value>(std::move(value)))
.first);
}
Value::dict_iterator Value::DictEnd() {
CHECK(is_dict());
return dict_iterator(dict_->end());
}
Value::const_dict_iterator Value::DictEnd() const {
CHECK(is_dict());
return const_dict_iterator(dict_->end());
}
Value::dict_iterator_proxy Value::DictItems() {
CHECK(is_dict());
return dict_iterator_proxy(&*dict_);
}
Value::const_dict_iterator_proxy Value::DictItems() const {
CHECK(is_dict());
return const_dict_iterator_proxy(&*dict_);
}
bool Value::GetAsBoolean(bool* out_value) const { bool Value::GetAsBoolean(bool* out_value) const {
if (out_value && is_bool()) { if (out_value && is_bool()) {
*out_value = bool_value_; *out_value = bool_value_;
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "base/memory/manual_constructor.h" #include "base/memory/manual_constructor.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/value_iterators.h"
namespace base { namespace base {
...@@ -137,6 +138,59 @@ class BASE_EXPORT Value { ...@@ -137,6 +138,59 @@ class BASE_EXPORT Value {
ListStorage& GetList(); ListStorage& GetList();
const ListStorage& GetList() const; const ListStorage& GetList() const;
using dict_iterator = detail::dict_iterator;
using const_dict_iterator = detail::const_dict_iterator;
using dict_iterator_proxy = detail::dict_iterator_proxy;
using const_dict_iterator_proxy = detail::const_dict_iterator_proxy;
// TODO(crbug.com/646113): Replace the following const char* and const
// std::string& overloads with a single base::StringPiece overload when
// base::less becomes available.
// |FindKey| looks up |key| in the underlying dictionary. If found, it returns
// an iterator to the element. Otherwise the end iterator of the dictionary is
// returned. Callers are expected to compare the returned iterator against
// |DictEnd()| in order to determine whether |key| was present.
// Note: This fatally asserts if type() is not Type::DICTIONARY.
dict_iterator FindKey(const char* key);
dict_iterator FindKey(const std::string& key);
const_dict_iterator FindKey(const char* key) const;
const_dict_iterator FindKey(const std::string& key) const;
// |FindKeyOfType| is similar to |FindKey|, but it also requires the found
// value to have type |type|. If no type is found, or the found value is of a
// different type the end iterator of the dictionary is returned.
// Callers are expected to compare the returned iterator against |DictEnd()|
// in order to determine whether |key| was present and of the correct |type|.
// Note: This fatally asserts if type() is not Type::DICTIONARY.
dict_iterator FindKeyOfType(const char* key, Type type);
dict_iterator FindKeyOfType(const std::string& key, Type type);
const_dict_iterator FindKeyOfType(const char* key, Type type) const;
const_dict_iterator FindKeyOfType(const std::string& key, Type type) const;
// |SetKey| looks up |key| in the underlying dictionary and sets the mapped
// value to |value|. If |key| could not be found, a new element is inserted.
// An iterator to the modifed item is returned.
// Note: This fatally asserts if type() is not Type::DICTIONARY.
dict_iterator SetKey(const char* key, Value value);
dict_iterator SetKey(const std::string& key, Value value);
dict_iterator SetKey(std::string&& key, Value value);
// |DictEnd| returns the end iterator of the underlying dictionary. It is
// intended to be used with |FindKey| in order to determine whether a given
// key is present in the dictionary.
// Note: This fatally asserts if type() is not Type::DICTIONARY.
dict_iterator DictEnd();
const_dict_iterator DictEnd() const;
// |DictItems| returns a proxy object that exposes iterators to the underlying
// dictionary. These are intended for iteration over all items in the
// dictionary and are compatible with for-each loops and standard library
// algorithms.
// Note: This fatally asserts if type() is not Type::DICTIONARY.
dict_iterator_proxy DictItems();
const_dict_iterator_proxy DictItems() const;
// These methods allow the convenient retrieval of the contents of the Value. // These methods allow the convenient retrieval of the contents of the Value.
// If the current object can be converted into the given type, the value is // If the current object can be converted into the given type, the value is
// returned through the |out_value| parameter and true is returned; // returned through the |out_value| parameter and true is returned;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment