blob: 32a5f520f55f3525eba3bf4228108a1871e62755 [file] [log] [blame]
// 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 COMPONENTS_SYNC_BASE_PROTO_VALUE_PTR_H_
#define COMPONENTS_SYNC_BASE_PROTO_VALUE_PTR_H_
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/trace_event/memory_usage_estimator.h"
namespace syncer {
class ProcessorEntityTracker;
struct EntityData;
namespace syncable {
struct EntryKernel;
} // 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); }
// Swap the value with the source (to avoid deep copying).
static void SwapValue(T* dest, T* src) { dest->Swap(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:
explicit Wrapper(const T& value) { Traits::CopyValue(&value_, value); }
explicit Wrapper(T* value) { Traits::SwapValue(&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_;
};
public:
ProtoValuePtr() {}
~ProtoValuePtr() {}
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 syncable::EntryKernel;
friend struct EntityData;
friend class ProcessorEntityTracker;
FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, ValueAssignment);
FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, ValueSwap);
FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, SharingTest);
FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, ParsingTest);
// set the value to copy of |new_value|.
void set_value(const T& new_value) {
if (Traits::HasValue(new_value)) {
wrapper_ = new Wrapper(new_value);
} else {
// Don't store default value.
reset();
}
}
void reset() { wrapper_ = nullptr; }
// Take over |src| value (swap).
void swap_value(T* src) {
if (Traits::HasValue(*src)) {
wrapper_ = new Wrapper(src);
} else {
// Don't store default value.
reset();
}
}
void load(const void* blob, int length) {
wrapper_ = Wrapper::ParseFromBlob(blob, length);
}
scoped_refptr<Wrapper> wrapper_;
};
template <typename T, typename Traits>
size_t EstimateMemoryUsage(const ProtoValuePtr<T, Traits>& ptr) {
// Including estimators from base::trace_event (memory_usage_estimator.h)
// allows to resolve EstimateMemoryUsage name in cases when T is not a sync
// protobuf and thus sync_pb::EstimateMemoryUsage doesn't get matched.
using base::trace_event::EstimateMemoryUsage;
return &ptr.value() != &Traits::DefaultValue()
? EstimateMemoryUsage(ptr.value())
: 0;
}
} // namespace syncer
#endif // COMPONENTS_SYNC_BASE_PROTO_VALUE_PTR_H_