summaryrefslogtreecommitdiffstats
path: root/sync/syncable/proto_value_ptr.h
blob: dea3a68aeb3a57dc8126000c97df05489ec1cf87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// 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 {
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 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_