summaryrefslogtreecommitdiffstats
path: root/sync/internal_api
diff options
context:
space:
mode:
authormaxbogue <maxbogue@chromium.org>2015-10-29 11:39:19 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-29 18:40:28 +0000
commit95e33335f64bff190d67d8958a3ffac701f6da6f (patch)
tree3e50077e0b59ee1bbe4b79e380308c29708023a4 /sync/internal_api
parent68cfb908f4a01b95b64eac573825325dd7bdf55d (diff)
downloadchromium_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.h115
-rw-r--r--sync/internal_api/public/util/proto_value_ptr_unittest.cc208
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