Commit 7e7a9798 authored by Jan Wilken Dörrie's avatar Jan Wilken Dörrie Committed by Commit Bot

[base] Add list utility methods to Value

This change adds a few list utility methods to base::Value. This change
is in preparation for switching the return type of the mutable GetList()
from ListStorage& to base::span<base::Value>, at which point it won't be
possible anymore to remove Values using std::vector's API.

Bug: 646113
Change-Id: Ia10e7f43723722e0a66794466c6ffca0fe221c4a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1857118Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#705953}
parent ec77e5b2
......@@ -352,6 +352,11 @@ span<const Value> Value::GetList() const {
return list_;
}
Value::ListStorage Value::TakeList() {
CHECK(is_list());
return std::exchange(list_, ListStorage());
}
void Value::Append(bool value) {
CHECK(is_list());
list_.emplace_back(value);
......@@ -397,6 +402,24 @@ void Value::Append(Value&& value) {
list_.emplace_back(std::move(value));
}
bool Value::EraseListIter(ListStorage::const_iterator iter) {
CHECK(is_list());
if (iter == list_.end())
return false;
list_.erase(iter);
return true;
}
bool Value::EraseListIter(CheckedContiguousConstIterator<Value> iter) {
const auto offset = iter - static_cast<const Value*>(this)->GetList().begin();
return EraseListIter(list_.begin() + offset);
}
size_t Value::EraseListValue(const Value& val) {
return EraseListValueIf([&val](const Value& other) { return val == other; });
}
Value* Value::FindKey(StringPiece key) {
return const_cast<Value*>(static_cast<const Value*>(this)->FindKey(key));
}
......
......@@ -32,6 +32,7 @@
#include <vector>
#include "base/base_export.h"
#include "base/containers/checked_iterators.h"
#include "base/containers/flat_map.h"
#include "base/containers/span.h"
#include "base/macros.h"
......@@ -180,6 +181,11 @@ class BASE_EXPORT Value {
ListStorage& GetList();
span<const Value> GetList() const;
// Transfers ownership of the the underlying list to the caller. Subsequent
// calls to GetList() will return an empty list.
// Note: This CHECKs that type() is Type::LIST.
ListStorage TakeList();
// Appends |value| to the end of the list.
// Note: These CHECK that type() is Type::LIST.
void Append(bool value);
......@@ -192,6 +198,28 @@ class BASE_EXPORT Value {
void Append(StringPiece16 value);
void Append(Value&& value);
// Erases the Value pointed to by |iter|. Returns false if |iter| is out of
// bounds.
// Note: This CHECKs that type() is Type::LIST.
bool EraseListIter(ListStorage::const_iterator iter);
bool EraseListIter(CheckedContiguousConstIterator<Value> iter);
// Erases all Values that compare equal to |val|. Returns the number of
// deleted Values.
// Note: This CHECKs that type() is Type::LIST.
size_t EraseListValue(const Value& val);
// Erases all Values for which |pred| returns true. Returns the number of
// deleted Values.
// Note: This CHECKs that type() is Type::LIST.
template <typename Predicate>
size_t EraseListValueIf(Predicate pred) {
CHECK(is_list());
const size_t old_size = list_.size();
base::EraseIf(list_, pred);
return old_size - list_.size();
}
// |FindKey| looks up |key| in the underlying dictionary. If found, it returns
// a pointer to the element. Otherwise it returns nullptr.
// returned. Callers are expected to perform a check against null before using
......
......@@ -458,6 +458,34 @@ TEST(ValuesTest, MoveList) {
EXPECT_EQ(123, blank.GetList().back().GetInt());
}
TEST(ValuesTest, TakeList) {
// Prepare a list with a value of each type.
ListValue value;
value.Append(Value(Value::Type::NONE));
value.Append(Value(Value::Type::BOOLEAN));
value.Append(Value(Value::Type::INTEGER));
value.Append(Value(Value::Type::DOUBLE));
value.Append(Value(Value::Type::STRING));
value.Append(Value(Value::Type::BINARY));
value.Append(Value(Value::Type::LIST));
value.Append(Value(Value::Type::DICTIONARY));
// Take ownership of the list and make sure its contents are what we expect.
auto list = value.TakeList();
EXPECT_EQ(8u, list.size());
EXPECT_TRUE(list[0].is_none());
EXPECT_TRUE(list[1].is_bool());
EXPECT_TRUE(list[2].is_int());
EXPECT_TRUE(list[3].is_double());
EXPECT_TRUE(list[4].is_string());
EXPECT_TRUE(list[5].is_blob());
EXPECT_TRUE(list[6].is_list());
EXPECT_TRUE(list[7].is_dict());
// Validate that |value| no longer contains values.
EXPECT_TRUE(value.GetList().empty());
}
TEST(ValuesTest, Append) {
ListValue value;
value.Append(true);
......@@ -496,6 +524,67 @@ TEST(ValuesTest, Append) {
EXPECT_TRUE(value.GetList().back().is_list());
}
TEST(ValuesTest, EraseListIter) {
ListValue value;
value.Append(1);
value.Append(2);
value.Append(3);
EXPECT_TRUE(value.EraseListIter(value.GetList().begin() + 1));
EXPECT_EQ(2u, value.GetList().size());
EXPECT_EQ(1, value.GetList()[0].GetInt());
EXPECT_EQ(3, value.GetList()[1].GetInt());
EXPECT_TRUE(value.EraseListIter(value.GetList().begin()));
EXPECT_EQ(1u, value.GetList().size());
EXPECT_EQ(3, value.GetList()[0].GetInt());
EXPECT_TRUE(value.EraseListIter(value.GetList().begin()));
EXPECT_TRUE(value.GetList().empty());
EXPECT_FALSE(value.EraseListIter(value.GetList().begin()));
}
TEST(ValuesTest, EraseListValue) {
ListValue value;
value.Append(1);
value.Append(2);
value.Append(2);
value.Append(3);
EXPECT_EQ(2u, value.EraseListValue(Value(2)));
EXPECT_EQ(2u, value.GetList().size());
EXPECT_EQ(1, value.GetList()[0].GetInt());
EXPECT_EQ(3, value.GetList()[1].GetInt());
EXPECT_EQ(1u, value.EraseListValue(Value(1)));
EXPECT_EQ(1u, value.GetList().size());
EXPECT_EQ(3, value.GetList()[0].GetInt());
EXPECT_EQ(1u, value.EraseListValue(Value(3)));
EXPECT_TRUE(value.GetList().empty());
EXPECT_EQ(0u, value.EraseListValue(Value(3)));
}
TEST(ValuesTest, EraseListValueIf) {
ListValue value;
value.Append(1);
value.Append(2);
value.Append(2);
value.Append(3);
EXPECT_EQ(3u, value.EraseListValueIf(
[](const auto& val) { return val >= Value(2); }));
EXPECT_EQ(1u, value.GetList().size());
EXPECT_EQ(1, value.GetList()[0].GetInt());
EXPECT_EQ(1u, value.EraseListValueIf([](const auto& val) { return true; }));
EXPECT_TRUE(value.GetList().empty());
EXPECT_EQ(0u, value.EraseListValueIf([](const auto& val) { return true; }));
}
TEST(ValuesTest, FindKey) {
Value::DictStorage storage;
storage.emplace("foo", std::make_unique<Value>("bar"));
......
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