buffet: add C++ variant type (Any).

Any is the class that adds support for native variant types.

Any can store any C++ type which is copyable. Moveable-only
types (such as std::unique_ptr) cannot be stored directly,
however you can store pointers (or references) to such types.

Any stores small data embedded into the class itself without
having to allocate a separate storage for it. Larger data
(roughly, larger than 8 bytes) or non trivially-copyable types
are stored in a separate memory block and the pointer to that
block is stored in the Any class itself.

Any supports move semantics. You can move the stored value
from one Any into another without copying it.

At this point, there is no support for comparing the Any class
(equal or less than), so you cannot use it as a key in a map,
however you still can store it as a value.

The code that extracts the data from Any must know the exact
type stored. Calling Any::Get<T>() for incompatible type is
not supported. However you can use Any::TryGet<T>() instead
and provide a default value, which will be returned if Any
does not contain the expected data.

You can also inspect the type of the contained data by
using Any::GetType() and Any::IsTypeCompatible<T>() methods.

BUG=chromium:366709
TEST=All existing and new unit tests pass.

Change-Id: Ic47529935ddd39c792dff1db8667c76db91b16d4
Reviewed-on: https://chromium-review.googlesource.com/198590
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/any_internal_impl.h b/buffet/any_internal_impl.h
new file mode 100644
index 0000000..cbf0bb8
--- /dev/null
+++ b/buffet/any_internal_impl.h
@@ -0,0 +1,215 @@
+// 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_
+