diff options
author | maxbogue <maxbogue@chromium.org> | 2015-10-29 11:39:19 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-29 18:40:28 +0000 |
commit | 95e33335f64bff190d67d8958a3ffac701f6da6f (patch) | |
tree | 3e50077e0b59ee1bbe4b79e380308c29708023a4 /sync/internal_api | |
parent | 68cfb908f4a01b95b64eac573825325dd7bdf55d (diff) | |
download | chromium_src-95e33335f64bff190d67d8958a3ffac701f6da6f.zip chromium_src-95e33335f64bff190d67d8958a3ffac701f6da6f.tar.gz chromium_src-95e33335f64bff190d67d8958a3ffac701f6da6f.tar.bz2 |
[Sync] Introduce EntityData and EntityMetadata.
BUG=536895
Review URL: https://codereview.chromium.org/1421663004
Cr-Commit-Position: refs/heads/master@{#356899}
Diffstat (limited to 'sync/internal_api')
-rw-r--r-- | sync/internal_api/public/util/proto_value_ptr.h | 115 | ||||
-rw-r--r-- | sync/internal_api/public/util/proto_value_ptr_unittest.cc | 208 |
2 files changed, 323 insertions, 0 deletions
diff --git a/sync/internal_api/public/util/proto_value_ptr.h b/sync/internal_api/public/util/proto_value_ptr.h new file mode 100644 index 0000000..540abd7 --- /dev/null +++ b/sync/internal_api/public/util/proto_value_ptr.h @@ -0,0 +1,115 @@ +// Copyright 2015 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 SYNC_SYNCABLE_ENTRY_PROTO_FIELD_PTR_H_ +#define SYNC_SYNCABLE_ENTRY_PROTO_FIELD_PTR_H_ + +#include "base/gtest_prod_util.h" +#include "base/memory/ref_counted.h" +#include "sync/protocol/attachments.pb.h" +#include "sync/protocol/sync.pb.h" + +namespace syncer_v2 { +class EntityData; +} + +namespace syncer { +namespace syncable { + +// Default traits struct for ProtoValuePtr - adapts a +// ::google::protobuf::MessageLite derived type to be used with ProtoValuePtr. +template <typename T> +struct DefaultProtoValuePtrTraits { + // Deep copy the value from |src| to |dest|. + static void CopyValue(T* dest, const T& src) { dest->CopyFrom(src); } + // Parse the value from BLOB. + static void ParseFromBlob(T* dest, const void* blob, int length) { + dest->ParseFromArray(blob, length); + } + // True if the |value| is a non-default value. + static bool HasValue(const T& value) { return value.ByteSize() > 0; } + // Default value for the type. + static const T& DefaultValue() { return T::default_instance(); } +}; + +// This is a smart pointer to a ::google::protobuf::MessageLite derived type +// that implements immutable, shareable, copy-on-write semantics. +// +// Additionally this class helps to avoid storing multiple copies of default +// instances of the wrapped type. +// +// Copying ProtoValuePtr results in ref-counted sharing of the +// underlying wrapper and the value contained in the wrapper. +// +// The public interface includes only immutable access to the wrapped value. +// The only way to assign a value to ProtoValuePtr is through a +// private SetValue function which is called from EntryKernel. That results +// in stopping sharing the previous value and creating a wrapper to the new +// value. +template <typename T, typename Traits = DefaultProtoValuePtrTraits<T>> +class ProtoValuePtr { + private: + // Immutable shareable ref-counted wrapper that embeds the value. + class Wrapper : public base::RefCountedThreadSafe<Wrapper> { + public: + Wrapper(const T& value) { Traits::CopyValue(&value_, value); } + const T& value() const { return value_; } + // Create wrapper by deserializing a BLOB. + static Wrapper* ParseFromBlob(const void* blob, int length) { + Wrapper* wrapper = new Wrapper; + Traits::ParseFromBlob(&wrapper->value_, blob, length); + return wrapper; + } + + private: + friend class base::RefCountedThreadSafe<Wrapper>; + Wrapper() {} + ~Wrapper() {} + + T value_; + }; + + ProtoValuePtr() {} + ~ProtoValuePtr() {} + + public: + const T& value() const { + return wrapper_ ? wrapper_->value() : Traits::DefaultValue(); + } + + const T* operator->() const { + const T& wrapped_instance = value(); + return &wrapped_instance; + } + + private: + friend class syncer_v2::EntityData; + friend struct EntryKernel; + FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, BasicTest); + FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, SharingTest); + FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, ParsingTest); + + void set_value(const T& new_value) { + if (Traits::HasValue(new_value)) { + wrapper_ = new Wrapper(new_value); + } else { + // Don't store default value. + wrapper_ = nullptr; + } + } + + void load(const void* blob, int length) { + wrapper_ = Wrapper::ParseFromBlob(blob, length); + } + + scoped_refptr<Wrapper> wrapper_; +}; + +typedef ProtoValuePtr<sync_pb::EntitySpecifics> EntitySpecificsPtr; +typedef ProtoValuePtr<sync_pb::AttachmentMetadata> AttachmentMetadataPtr; + +} // namespace syncable +} // namespace syncer + +#endif // SYNC_SYNCABLE_ENTRY_PROTO_FIELD_PTR_H_ diff --git a/sync/internal_api/public/util/proto_value_ptr_unittest.cc b/sync/internal_api/public/util/proto_value_ptr_unittest.cc new file mode 100644 index 0000000..4479b5d --- /dev/null +++ b/sync/internal_api/public/util/proto_value_ptr_unittest.cc @@ -0,0 +1,208 @@ +// Copyright 2015 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 "sync/internal_api/public/util/proto_value_ptr.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer { +namespace syncable { + +namespace { + +// TestValue class is used as a template argument with ProtoValuePtr<T> +class TestValue { + public: + TestValue() : value_(0), is_initialized_(false), is_default_(false) {} + explicit TestValue(int value) + : value_(value), is_initialized_(true), is_default_(false) {} + + ~TestValue() { g_delete_count++; } + + static void ResetCounters() { + g_copy_count = 0; + g_parse_count = 0; + g_delete_count = 0; + } + + static int copy_count() { return g_copy_count; } + static int parse_count() { return g_parse_count; } + static int delete_count() { return g_delete_count; } + + int value() const { return value_; } + bool is_initialized() const { return is_initialized_; } + bool is_default() const { return is_default_; } + + // TestValue uses the default traits struct with ProtoValuePtr<TestValue>. + // The following 4 functions are expected by the traits struct to exist + // in this class. + void CopyFrom(const TestValue& from) { + // Expected to always copy from an initialized instance + // to an uninitialized one. + // Not expected either value to be default. + ASSERT_FALSE(is_initialized()); + ASSERT_FALSE(is_default()); + ASSERT_TRUE(from.is_initialized()); + ASSERT_FALSE(from.is_default()); + value_ = from.value(); + is_initialized_ = true; + g_copy_count++; + } + + void ParseFromArray(const void* blob, int length) { + // Similarly to CopyFrom this is expected to be called on + // an uninitialized instance. + ASSERT_FALSE(is_initialized()); + ASSERT_FALSE(is_default()); + // The blob is an address of an integer + ASSERT_EQ(static_cast<int>(sizeof(int)), length); + value_ = *static_cast<const int*>(blob); + is_initialized_ = true; + g_parse_count++; + } + + int ByteSize() const { return is_initialized() ? sizeof(int) : 0; } + + static const TestValue& default_instance() { + static TestValue default_instance; + default_instance.is_default_ = true; + return default_instance; + } + + private: + static int g_copy_count; + static int g_parse_count; + static int g_delete_count; + + int value_; + bool is_initialized_; + bool is_default_; + + DISALLOW_COPY_AND_ASSIGN(TestValue); +}; + +// Static initializers. +int TestValue::g_copy_count = 0; +int TestValue::g_parse_count = 0; +int TestValue::g_delete_count = 0; + +} // namespace + +typedef ProtoValuePtr<TestValue> TestPtr; + +class ProtoValuePtrTest : public testing::Test { + public: + void SetUp() override { TestValue::ResetCounters(); } + + static bool WrappedValuesAreShared(const TestPtr& ptr1, const TestPtr& ptr2) { + const TestValue& wrapped_value_1 = ptr1.value(); + const TestValue& wrapped_value_2 = ptr2.value(); + // Compare addresses. + return &wrapped_value_1 == &wrapped_value_2; + } +}; + +TEST_F(ProtoValuePtrTest, BasicTest) { + // Basic assignment and default value. + TestValue t1(1); + { + TestPtr ptr1; + EXPECT_TRUE(ptr1->is_default()); + + ptr1.set_value(t1); + EXPECT_FALSE(ptr1->is_default()); + EXPECT_EQ(1, ptr1->value()); + } + + EXPECT_EQ(1, TestValue::copy_count()); + EXPECT_EQ(1, TestValue::delete_count()); +} + +TEST_F(ProtoValuePtrTest, SharingTest) { + // Sharing between two pointers. + TestValue empty; + TestValue t2(2); + TestValue t3(3); + { + TestPtr ptr2; + TestPtr ptr3; + + EXPECT_TRUE(ptr2->is_default()); + EXPECT_TRUE(ptr3->is_default()); + EXPECT_EQ(0, TestValue::copy_count()); + EXPECT_EQ(0, TestValue::delete_count()); + + ptr2.set_value(t2); + EXPECT_EQ(1, TestValue::copy_count()); + EXPECT_EQ(0, TestValue::delete_count()); + + ptr3 = ptr2; + // Both |ptr2| and |ptr3| now share the same value "2". + // No additional copies expected. + EXPECT_EQ(1, TestValue::copy_count()); + EXPECT_EQ(0, TestValue::delete_count()); + EXPECT_FALSE(ptr3->is_default()); + EXPECT_EQ(2, ptr3->value()); + EXPECT_TRUE(WrappedValuesAreShared(ptr2, ptr3)); + + // Stop sharing - |ptr2| is "3" and |ptr3| is still "2". + ptr2.set_value(t3); + EXPECT_FALSE(WrappedValuesAreShared(ptr2, ptr3)); + EXPECT_EQ(3, ptr2->value()); + EXPECT_EQ(2, ptr3->value()); + // No extra copies or deletions expected. + EXPECT_EQ(2, TestValue::copy_count()); + EXPECT_EQ(0, TestValue::delete_count()); + + // |ptr3| still has the old value. + EXPECT_EQ(2, ptr3->value()); + + // Share again. Both values are "3". + ptr3 = ptr2; + EXPECT_EQ(3, ptr3->value()); + // This should have resulted in deleting the wrapper for the value "2". + EXPECT_EQ(1, TestValue::delete_count()); + // No extra copies expected. + EXPECT_EQ(2, TestValue::copy_count()); + + // Set default value to one of the pointers. + ptr2.set_value(empty); + EXPECT_TRUE(ptr2->is_default()); + // The other one is still intact. + EXPECT_FALSE(ptr3->is_default()); + EXPECT_EQ(3, ptr3->value()); + // No extra copies or deletions expected. + EXPECT_EQ(1, TestValue::delete_count()); + EXPECT_EQ(2, TestValue::copy_count()); + + // Copy the default value between the pointers. + ptr3 = ptr2; + EXPECT_TRUE(ptr3->is_default()); + // The wrapper for "3" is now deleted. + EXPECT_EQ(2, TestValue::delete_count()); + } + + // No extra deletions expected upon leaving the scope. + EXPECT_EQ(2, TestValue::delete_count()); +} + +TEST_F(ProtoValuePtrTest, ParsingTest) { + int v1 = 21; + + { + TestPtr ptr1; + + ptr1.load(&v1, sizeof(int)); + + EXPECT_EQ(1, TestValue::parse_count()); + EXPECT_EQ(0, TestValue::copy_count()); + + EXPECT_EQ(v1, ptr1->value()); + } + + EXPECT_EQ(1, TestValue::delete_count()); +} + +} // namespace syncable +} // namespace syncer |