|  | // Copyright 2014 The Chromium OS Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | // Internal implementation of buffet::Any class. | 
|  |  | 
|  | #ifndef BUFFET_ANY_INTERNAL_IMPL_H_ | 
|  | #define BUFFET_ANY_INTERNAL_IMPL_H_ | 
|  |  | 
|  | #include <type_traits> | 
|  | #include <typeinfo> | 
|  | #include <utility> | 
|  |  | 
|  | #include <base/logging.h> | 
|  |  | 
|  | namespace buffet { | 
|  |  | 
|  | namespace internal_details { | 
|  |  | 
|  | // An extension to std::is_convertible to allow conversion from an enum to | 
|  | // an integral type which std::is_convertible does not indicate as supported. | 
|  | template <typename From, typename To> | 
|  | struct IsConvertible : public std::integral_constant<bool, | 
|  | std::is_convertible<From, To>::value || | 
|  | (std::is_enum<From>::value && std::is_integral<To>::value)> { | 
|  | }; | 
|  | // TryConvert is a helper function that does a safe compile-time conditional | 
|  | // type cast between data types that may not be always convertible. | 
|  | // From and To are the source and destination types. | 
|  | // The function returns true if conversion was possible/successful. | 
|  | template <typename From, typename To> | 
|  | inline typename std::enable_if<IsConvertible<From, To>::value, bool>::type | 
|  | TryConvert(const From& in, To* out) { | 
|  | *out = static_cast<To>(in); | 
|  | return true; | 
|  | } | 
|  | template <typename From, typename To> | 
|  | inline typename std::enable_if<!IsConvertible<From, To>::value, bool>::type | 
|  | TryConvert(const From& in, To* out) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | struct Buffer;  // Forward declaration of data buffer container. | 
|  |  | 
|  | // Abstract base class for contained variant data. | 
|  | struct Data { | 
|  | virtual ~Data() {} | 
|  | // Returns the type information for the contained data. | 
|  | virtual const std::type_info& GetType() const = 0; | 
|  | // Copies the contained data to the output |buffer|. | 
|  | virtual void CopyTo(Buffer* buffer) const = 0; | 
|  | // Checks if the contained data is an integer type (not necessarily an 'int'). | 
|  | virtual bool IsConvertibleToInteger() const = 0; | 
|  | // Gets the contained integral value as an integer. | 
|  | virtual intmax_t GetAsInteger() const = 0; | 
|  | }; | 
|  |  | 
|  | // Concrete implementation of variant data of type T. | 
|  | template<typename T> | 
|  | struct TypedData : public Data { | 
|  | explicit TypedData(const T& value) : value_(value) {} | 
|  |  | 
|  | virtual const std::type_info& GetType() const override { return typeid(T); } | 
|  | virtual void CopyTo(Buffer* buffer) const override; | 
|  | virtual bool IsConvertibleToInteger() const override { | 
|  | return std::is_integral<T>::value || std::is_enum<T>::value; | 
|  | } | 
|  | virtual intmax_t GetAsInteger() const override { | 
|  | intmax_t int_val = 0; | 
|  | bool converted = TryConvert(value_, &int_val); | 
|  | CHECK(converted) << "Unable to convert value of type " << typeid(T).name() | 
|  | << " to integer"; | 
|  | return int_val; | 
|  | } | 
|  | // Special method to copy data of the same type | 
|  | // without reallocating the buffer. | 
|  | void FastAssign(const T& source) { value_ = source; } | 
|  |  | 
|  | T value_; | 
|  | }; | 
|  |  | 
|  | // Buffer class that stores the contained variant data. | 
|  | // To improve performance and reduce memory fragmentation, small variants | 
|  | // are stored in pre-allocated memory buffers that are part of the Any class. | 
|  | // If the memory requirements are larger than the set limit or the type is | 
|  | // non-trivially copyable, then the contained class is allocated in a separate | 
|  | // memory block and the pointer to that memory is contained within this memory | 
|  | // buffer class. | 
|  | class Buffer { | 
|  | public: | 
|  | enum StorageType { kExternal, kContained }; | 
|  | Buffer() : external_ptr_(nullptr), storage_(kExternal) {} | 
|  | ~Buffer() { | 
|  | Clear(); | 
|  | } | 
|  |  | 
|  | Buffer(const Buffer& rhs) : Buffer() { | 
|  | rhs.CopyTo(this); | 
|  | } | 
|  | Buffer& operator=(const Buffer& rhs) { | 
|  | rhs.CopyTo(this); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | // Returns the underlying pointer to contained data. Uses either the pointer | 
|  | // or the raw data depending on |storage_| type. | 
|  | inline Data* GetDataPtr() { | 
|  | return (storage_ == kExternal) ? | 
|  | external_ptr_ : reinterpret_cast<Data*>(contained_buffer_); | 
|  | } | 
|  | inline const Data* GetDataPtr() const { | 
|  | return (storage_ == kExternal) ? | 
|  | external_ptr_ : reinterpret_cast<const Data*>(contained_buffer_); | 
|  | } | 
|  |  | 
|  | // Destroys the contained object (and frees memory if needed). | 
|  | void Clear() { | 
|  | Data* data = GetDataPtr(); | 
|  | if (storage_ == kExternal) { | 
|  | delete data; | 
|  | } else { | 
|  | // Call the destructor manually, since the object was constructed inline | 
|  | // in the pre-allocated buffer. We still need to call the destructor | 
|  | // to free any associated resources, but we can't call delete |data| here. | 
|  | data->~Data(); | 
|  | } | 
|  | external_ptr_ = nullptr; | 
|  | storage_ = kExternal; | 
|  | } | 
|  |  | 
|  | // Stores a value of type T. | 
|  | template<typename T> | 
|  | void Assign(T value) { | 
|  | using Type = typename std::decay<T>::type; | 
|  | using DataType = TypedData<Type>; | 
|  | Data* ptr = GetDataPtr(); | 
|  | if (ptr && ptr->GetType() == typeid(Type)) { | 
|  | // We assign the data to the variant container, which already | 
|  | // has the data of the same type. Do fast copy with no memory | 
|  | // reallocation. | 
|  | DataType* typed_ptr = static_cast<DataType*>(ptr); | 
|  | typed_ptr->FastAssign(value); | 
|  | } else { | 
|  | Clear(); | 
|  | // TODO(avakulenko): [see crbug.com/379833] | 
|  | // Unfortunately, GCC doesn't support std::is_trivially_copyable<T> yet, | 
|  | // so using std::is_trivial instead, which is a bit more restrictive. | 
|  | // Once GCC has support for is_trivially_copyable, update the following. | 
|  | if (!std::is_trivial<Type>::value || | 
|  | sizeof(DataType) > sizeof(contained_buffer_)) { | 
|  | // If it is too big or not trivially copyable, allocate it separately. | 
|  | external_ptr_ = new DataType(value); | 
|  | storage_ = kExternal; | 
|  | } else { | 
|  | // Otherwise just use the pre-allocated buffer. | 
|  | DataType* address = reinterpret_cast<DataType*>(contained_buffer_); | 
|  | // Make sure we still call the copy constructor. | 
|  | // Call the constructor manually by using placement 'new'. | 
|  | new (address) DataType(value); | 
|  | storage_ = kContained; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Helper methods to retrieve a reference to contained data. | 
|  | // These assume that type checking has already been performed by Any | 
|  | // so the type cast is valid and will succeed. | 
|  | template<typename T> | 
|  | const T& GetData() const { | 
|  | using DataType = internal_details::TypedData<typename std::decay<T>::type>; | 
|  | return static_cast<const DataType*>(GetDataPtr())->value_; | 
|  | } | 
|  | template<typename T> | 
|  | T& GetData() { | 
|  | using DataType = internal_details::TypedData<typename std::decay<T>::type>; | 
|  | return static_cast<DataType*>(GetDataPtr())->value_; | 
|  | } | 
|  |  | 
|  | // Returns true if the buffer has no contained data. | 
|  | bool IsEmpty() const { | 
|  | return (storage_ == kExternal && external_ptr_ == nullptr); | 
|  | } | 
|  |  | 
|  | // Copies the data from the current buffer into the |destination|. | 
|  | void CopyTo(Buffer* destination) const { | 
|  | if (IsEmpty()) { | 
|  | destination->Clear(); | 
|  | } else { | 
|  | GetDataPtr()->CopyTo(destination); | 
|  | } | 
|  | } | 
|  |  | 
|  | union { | 
|  | // |external_ptr_| is a pointer to a larger object allocated in | 
|  | // a separate memory block. | 
|  | Data* external_ptr_; | 
|  | // |contained_buffer_| is a pre-allocated buffer for smaller/simple objects. | 
|  | // Pre-allocate enough memory to store objects as big as "double". | 
|  | unsigned char contained_buffer_[sizeof(TypedData<double>)]; | 
|  | }; | 
|  | // Depending on a value of |storage_|, either |external_ptr_| or | 
|  | // |contained_buffer_| above is used to get a pointer to memory containing | 
|  | // the variant data. | 
|  | StorageType storage_;  // Declare after the union to eliminate member padding. | 
|  | }; | 
|  |  | 
|  | template<typename T> | 
|  | void TypedData<T>::CopyTo(Buffer* buffer) const { buffer->Assign(value_); } | 
|  |  | 
|  | }  // namespace internal_details | 
|  |  | 
|  | }  // namespace buffet | 
|  |  | 
|  | #endif  // BUFFET_ANY_INTERNAL_IMPL_H_ | 
|  |  |