[mojo] Use base::Pickle for Message storage.

This CL modifies the mojo::Message implementation so that
its storage is always backed by a base::Pickle.

Part of a series of changes to support custom mojom serialization:

  1. https://codereview.chromium.org/1515423002
  2. https://codereview.chromium.org/1517043004
  3. https://codereview.chromium.org/1524693002
  4. https://codereview.chromium.org/1520153002
  5. This CL
  6. https://codereview.chromium.org/1526533002
  7. https://codereview.chromium.org/1524703002

BUG=569669

Review URL: https://codereview.chromium.org/1524613002

Cr-Commit-Position: refs/heads/master@{#365766}
diff --git a/base/pickle.cc b/base/pickle.cc
index 6fcdece3..678c425 100644
--- a/base/pickle.cc
+++ b/base/pickle.cc
@@ -329,6 +329,13 @@
   header_ = reinterpret_cast<Header*>(p);
 }
 
+void* Pickle::ClaimBytes(size_t num_bytes) {
+  void* p = ClaimUninitializedBytesInternal(num_bytes);
+  CHECK(p);
+  memset(p, 0, num_bytes);
+  return p;
+}
+
 size_t Pickle::GetTotalAllocatedSize() const {
   if (capacity_after_header_ == kCapacityReadOnly)
     return 0;
@@ -384,10 +391,9 @@
 template void Pickle::WriteBytesStatic<4>(const void* data);
 template void Pickle::WriteBytesStatic<8>(const void* data);
 
-inline void Pickle::WriteBytesCommon(const void* data, size_t length) {
+inline void* Pickle::ClaimUninitializedBytesInternal(size_t length) {
   DCHECK_NE(kCapacityReadOnly, capacity_after_header_)
       << "oops: pickle is readonly";
-  MSAN_CHECK_MEM_IS_INITIALIZED(data, length);
   size_t data_len = bits::Align(length, sizeof(uint32_t));
   DCHECK_GE(data_len, length);
 #ifdef ARCH_CPU_64_BITS
@@ -404,10 +410,18 @@
   }
 
   char* write = mutable_payload() + write_offset_;
-  memcpy(write, data, length);
-  memset(write + length, 0, data_len - length);
+  memset(write + length, 0, data_len - length);  // Always initialize padding
   header_->payload_size = static_cast<uint32_t>(new_size);
   write_offset_ = new_size;
+  return write;
+}
+
+inline void Pickle::WriteBytesCommon(const void* data, size_t length) {
+  DCHECK_NE(kCapacityReadOnly, capacity_after_header_)
+      << "oops: pickle is readonly";
+  MSAN_CHECK_MEM_IS_INITIALIZED(data, length);
+  void* write = ClaimUninitializedBytesInternal(length);
+  memcpy(write, data, length);
 }
 
 }  // namespace base
diff --git a/base/pickle.h b/base/pickle.h
index 72b33ddc..8e82c62 100644
--- a/base/pickle.h
+++ b/base/pickle.h
@@ -258,6 +258,13 @@
   // of the header.
   void Resize(size_t new_capacity);
 
+  // Claims |num_bytes| bytes of payload. This is similar to Reserve() in that
+  // it may grow the capacity, but it also advances the write offset of the
+  // pickle by |num_bytes|. Claimed memory, including padding, is zeroed.
+  //
+  // Returns the address of the first byte claimed.
+  void* ClaimBytes(size_t num_bytes);
+
   // Find the end of the pickled data that starts at range_start.  Returns NULL
   // if the entire Pickle is not found in the given data range.
   static const char* FindNext(size_t header_size,
@@ -299,6 +306,8 @@
     WriteBytesStatic<sizeof(data)>(&data);
     return true;
   }
+
+  inline void* ClaimUninitializedBytesInternal(size_t num_bytes);
   inline void WriteBytesCommon(const void* data, size_t length);
 
   FRIEND_TEST_ALL_PREFIXES(PickleTest, DeepCopyResize);
diff --git a/base/pickle_unittest.cc b/base/pickle_unittest.cc
index f58e7ece..6804614 100644
--- a/base/pickle_unittest.cc
+++ b/base/pickle_unittest.cc
@@ -524,4 +524,50 @@
   EXPECT_EQ(pickle.capacity_after_header(), pickle2.capacity_after_header());
 }
 
+namespace {
+
+// Publicly exposes the ClaimBytes interface for testing.
+class TestingPickle : public Pickle {
+ public:
+  TestingPickle() {}
+
+  void* ClaimBytes(size_t num_bytes) { return Pickle::ClaimBytes(num_bytes); }
+};
+
+}  // namespace
+
+// Checks that claimed bytes are zero-initialized.
+TEST(PickleTest, ClaimBytesInitialization) {
+  static const int kChunkSize = 64;
+  TestingPickle pickle;
+  const char* bytes = static_cast<const char*>(pickle.ClaimBytes(kChunkSize));
+  for (size_t i = 0; i < kChunkSize; ++i) {
+    EXPECT_EQ(0, bytes[i]);
+  }
+}
+
+// Checks that ClaimBytes properly advances the write offset.
+TEST(PickleTest, ClaimBytes) {
+  std::string data("Hello, world!");
+
+  TestingPickle pickle;
+  pickle.WriteSizeT(data.size());
+  void* bytes = pickle.ClaimBytes(data.size());
+  pickle.WriteInt(42);
+  memcpy(bytes, data.data(), data.size());
+
+  PickleIterator iter(pickle);
+  size_t out_data_length;
+  EXPECT_TRUE(iter.ReadSizeT(&out_data_length));
+  EXPECT_EQ(data.size(), out_data_length);
+
+  const char* out_data = nullptr;
+  EXPECT_TRUE(iter.ReadBytes(&out_data, out_data_length));
+  EXPECT_EQ(data, std::string(out_data, out_data_length));
+
+  int out_value;
+  EXPECT_TRUE(iter.ReadInt(&out_value));
+  EXPECT_EQ(42, out_value);
+}
+
 }  // namespace base
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 3cc4dbf..f18ad01 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -53,6 +53,8 @@
     "lib/multiplex_router.cc",
     "lib/multiplex_router.h",
     "lib/no_interface.cc",
+    "lib/pickle_buffer.cc",
+    "lib/pickle_buffer.h",
     "lib/pipe_control_message_handler.cc",
     "lib/pipe_control_message_handler.h",
     "lib/pipe_control_message_handler_delegate.h",
@@ -83,6 +85,7 @@
 
   public_deps = [
     ":callback",
+    "//base",
     "//mojo/public/cpp/system",
   ]
 
diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h
index c3b570e..98bbce0e 100644
--- a/mojo/public/cpp/bindings/lib/buffer.h
+++ b/mojo/public/cpp/bindings/lib/buffer.h
@@ -10,12 +10,18 @@
 namespace mojo {
 namespace internal {
 
+class PickleBuffer;
+
 // Buffer provides a way to allocate memory. Allocations are 8-byte aligned and
 // zero-initialized. Allocations remain valid for the lifetime of the Buffer.
 class Buffer {
  public:
   virtual ~Buffer() {}
   virtual void* Allocate(size_t num_bytes) = 0;
+
+  // TODO(rockot): Remove this. It's a hack to get a PickleBuffer in
+  // Serialize_ calls without having to update every call site.
+  virtual PickleBuffer* AsPickleBuffer() = 0;
 };
 
 }  // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
index c81fc6e..bfaea59 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.cc
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
@@ -38,6 +38,8 @@
   return result;
 }
 
+PickleBuffer* FixedBuffer::AsPickleBuffer() { return nullptr; }
+
 FixedBufferForTesting::FixedBufferForTesting(size_t size) {
   size_ = internal::Align(size);
   // Use calloc here to ensure all message memory is zero'd out.
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h
index 83eaf97..5e389f1f 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.h
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -49,6 +49,8 @@
   // memory is zero-filled.
   void* Allocate(size_t num_bytes) override;
 
+  PickleBuffer* AsPickleBuffer() override;
+
  protected:
   char* ptr_;
   size_t cursor_;
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index 6b563e7..23ef162 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -13,54 +13,30 @@
 namespace mojo {
 
 Message::Message() {
-  Initialize();
 }
 
 Message::~Message() {
-  FreeDataAndCloseHandles();
+  CloseHandles();
 }
 
-void Message::Reset() {
-  FreeDataAndCloseHandles();
-
-  handles_.clear();
-  Initialize();
-}
-
-void Message::AllocData(uint32_t num_bytes) {
-  MOJO_DCHECK(!data_);
-  data_num_bytes_ = num_bytes;
-  data_ = static_cast<internal::MessageData*>(calloc(num_bytes, 1));
-}
-
-void Message::AllocUninitializedData(uint32_t num_bytes) {
-  MOJO_DCHECK(!data_);
-  data_num_bytes_ = num_bytes;
-  data_ = static_cast<internal::MessageData*>(malloc(num_bytes));
+void Message::Initialize(size_t capacity, bool zero_initialized) {
+  DCHECK(!buffer_);
+  buffer_.reset(new internal::PickleBuffer(capacity, zero_initialized));
 }
 
 void Message::MoveTo(Message* destination) {
   MOJO_DCHECK(this != destination);
 
-  destination->FreeDataAndCloseHandles();
-
   // No copy needed.
-  destination->data_num_bytes_ = data_num_bytes_;
-  destination->data_ = data_;
+  std::swap(destination->buffer_, buffer_);
   std::swap(destination->handles_, handles_);
 
+  CloseHandles();
   handles_.clear();
-  Initialize();
+  buffer_.reset();
 }
 
-void Message::Initialize() {
-  data_num_bytes_ = 0;
-  data_ = nullptr;
-}
-
-void Message::FreeDataAndCloseHandles() {
-  free(data_);
-
+void Message::CloseHandles() {
   for (std::vector<Handle>::iterator it = handles_.begin();
        it != handles_.end(); ++it) {
     if (it->is_valid())
@@ -84,16 +60,18 @@
     return rv;
 
   Message message;
-  message.AllocUninitializedData(num_bytes);
+  message.Initialize(num_bytes, false /* zero_initialized */);
+
+  void* mutable_data = message.buffer()->Allocate(num_bytes);
   message.mutable_handles()->resize(num_handles);
 
   rv = ReadMessageRaw(
       handle,
-      message.mutable_data(),
+      mutable_data,
       &num_bytes,
       message.mutable_handles()->empty()
           ? nullptr
-          : reinterpret_cast<MojoHandle*>(&message.mutable_handles()->front()),
+          : reinterpret_cast<MojoHandle*>(message.mutable_handles()->data()),
       &num_handles,
       MOJO_READ_MESSAGE_FLAG_NONE);
   if (receiver && rv == MOJO_RESULT_OK)
diff --git a/mojo/public/cpp/bindings/lib/message_builder.cc b/mojo/public/cpp/bindings/lib/message_builder.cc
index 25f1862..5319f4b 100644
--- a/mojo/public/cpp/bindings/lib/message_builder.cc
+++ b/mojo/public/cpp/bindings/lib/message_builder.cc
@@ -17,10 +17,10 @@
 }
 
 MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size) {
-  Initialize(sizeof(MessageHeader) + payload_size);
+  InitializeMessage(sizeof(MessageHeader) + payload_size);
 
   MessageHeader* header;
-  Allocate(&buf_, &header);
+  Allocate(message_.buffer(), &header);
   header->version = 0;
   header->name = name;
 }
@@ -30,18 +30,19 @@
 
 MessageBuilder::MessageBuilder() {}
 
-void MessageBuilder::Initialize(size_t size) {
-  message_.AllocData(static_cast<uint32_t>(Align(size)));
-  buf_.Initialize(message_.mutable_data(), message_.data_num_bytes());
+void MessageBuilder::InitializeMessage(size_t size) {
+  message_.Initialize(static_cast<uint32_t>(Align(size)),
+                      true /* zero_initialized */);
 }
 
 MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name,
                                                          size_t payload_size,
                                                          uint32_t flags,
                                                          uint64_t request_id) {
-  Initialize(sizeof(MessageHeaderWithRequestID) + payload_size);
+  InitializeMessage(sizeof(MessageHeaderWithRequestID) + payload_size);
+
   MessageHeaderWithRequestID* header;
-  Allocate(&buf_, &header);
+  Allocate(message_.buffer(), &header);
   header->version = 1;
   header->name = name;
   header->flags = flags;
diff --git a/mojo/public/cpp/bindings/lib/message_builder.h b/mojo/public/cpp/bindings/lib/message_builder.h
index 86ae71db..5fba7453 100644
--- a/mojo/public/cpp/bindings/lib/message_builder.h
+++ b/mojo/public/cpp/bindings/lib/message_builder.h
@@ -7,7 +7,6 @@
 
 #include <stdint.h>
 
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
 #include "mojo/public/cpp/bindings/lib/message_internal.h"
 #include "mojo/public/cpp/bindings/message.h"
 
@@ -21,15 +20,14 @@
   MessageBuilder(uint32_t name, size_t payload_size);
   ~MessageBuilder();
 
-  Buffer* buffer() { return &buf_; }
+  Buffer* buffer() { return message_.buffer(); }
   Message* message() { return &message_; }
 
  protected:
   MessageBuilder();
-  void Initialize(size_t size);
+  void InitializeMessage(size_t size);
 
   Message message_;
-  FixedBuffer buf_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(MessageBuilder);
 };
diff --git a/mojo/public/cpp/bindings/lib/message_internal.h b/mojo/public/cpp/bindings/lib/message_internal.h
index 97b9619..8bc2e0da 100644
--- a/mojo/public/cpp/bindings/lib/message_internal.h
+++ b/mojo/public/cpp/bindings/lib/message_internal.h
@@ -35,13 +35,6 @@
 static_assert(sizeof(MessageHeaderWithRequestID) == 32,
               "Bad sizeof(MessageHeaderWithRequestID)");
 
-struct MessageData {
-  MessageHeader header;
-};
-
-static_assert(sizeof(MessageData) == sizeof(MessageHeader),
-              "Bad sizeof(MessageData)");
-
 #pragma pack(pop)
 
 }  // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/pickle_buffer.cc b/mojo/public/cpp/bindings/lib/pickle_buffer.cc
new file mode 100644
index 0000000..cb481a9
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/pickle_buffer.cc
@@ -0,0 +1,96 @@
+// 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 "mojo/public/cpp/bindings/lib/pickle_buffer.h"
+
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/pickle.h"
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+
+namespace mojo {
+namespace internal {
+
+class PickleBuffer::Storage : public base::Pickle {
+ public:
+  explicit Storage(size_t num_bytes);
+  ~Storage() override {}
+
+  size_t available_capacity() const {
+    return capacity_after_header() - payload_size();
+  }
+
+  void* GetData() { return static_cast<void*>(mutable_payload()); }
+  void* Claim(size_t num_bytes) { return ClaimBytes(num_bytes); }
+
+ private:
+  // TODO(rockot): Stop wasting 8 bytes per buffer.
+  //
+  // We don't use Pickle's header at all, but its base header type consumes 4
+  // bytes. We waste another 4 bytes to keep our actual buffer aligned to an
+  // 8-byte boundary.
+  //
+  // The reason we don't use base::Pickle's header is that it stores payload
+  // length in the first 4 bytes. Mojo Messages are packed like mojom structs,
+  // where the first 4 bytes are header size rather than payload size.
+  struct PaddedHeader : public base::Pickle::Header {
+    uint32_t padding;
+  };
+
+  static_assert(sizeof(PaddedHeader) % 8 == 0,
+      "PickleBuffer requires a Pickle header size with 8-byte alignment.");
+
+  DISALLOW_COPY_AND_ASSIGN(Storage);
+};
+
+PickleBuffer::Storage::Storage(size_t num_bytes)
+    : base::Pickle(sizeof(PaddedHeader)) {
+  headerT<PaddedHeader>()->padding = 0;
+  Resize(num_bytes);
+}
+
+PickleBuffer::PickleBuffer(size_t num_bytes, bool zero_initialized)
+    : storage_(new Storage(num_bytes)) {
+  if (zero_initialized)
+    memset(storage_->GetData(), 0, num_bytes);
+}
+
+PickleBuffer::~PickleBuffer() {
+}
+
+const void* PickleBuffer::data() const { return storage_->GetData(); }
+
+size_t PickleBuffer::data_num_bytes() const { return storage_->payload_size(); }
+
+base::Pickle* PickleBuffer::pickle() const {
+  return storage_.get();
+}
+
+void* PickleBuffer::Allocate(size_t num_bytes) {
+  DCHECK(storage_);
+
+  // The last allocation may terminate in between 8-byte boundaries. Pad the
+  // front of this allocation if that's the case.
+  size_t padded_capacity = Align(storage_->payload_size());
+  DCHECK_GE(padded_capacity, storage_->payload_size());
+
+  size_t padding_bytes = padded_capacity - storage_->payload_size();
+  size_t allocation_size = padding_bytes + num_bytes;
+  const void* previous_data_location = storage_->GetData();
+  if (storage_->available_capacity() < allocation_size) {
+    NOTREACHED() <<
+        "Message buffers must be fully allocated before serialization.";
+    return nullptr;
+  }
+  char* p = static_cast<char*>(storage_->Claim(allocation_size));
+  DCHECK_EQ(storage_->GetData(), previous_data_location);
+  return p + padding_bytes;
+}
+
+PickleBuffer* PickleBuffer::AsPickleBuffer() { return this; }
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/pickle_buffer.h b/mojo/public/cpp/bindings/lib/pickle_buffer.h
new file mode 100644
index 0000000..efbfcc8
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/pickle_buffer.h
@@ -0,0 +1,53 @@
+// 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 MOJO_PUBLIC_CPP_BINDINGS_LIB_PICKLE_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_PICKLE_BUFFER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+
+namespace mojo {
+namespace internal {
+
+// An implementation of Buffer which uses base::Pickle for its backing. Note
+// that this does not use Pickle's header structure at all, instead storing
+// the complete Message (including header) in the Pickle's payload.
+class PickleBuffer : public Buffer {
+ public:
+  PickleBuffer(size_t num_bytes, bool zero_initialized);
+  ~PickleBuffer() override;
+
+  const void* data() const;
+
+  void* data() {
+    return const_cast<void*>(static_cast<const PickleBuffer*>(this)->data());
+  }
+
+  size_t data_num_bytes() const;
+
+  base::Pickle* pickle() const;
+
+ private:
+  class Storage;
+
+  // Buffer implementation. Note that this cannot grow the Pickle's capacity and
+  // it is an error to Allocate() more bytes in total than have been
+  // pre-allocated using ReserveCapacity() or ReserveUninitializedCapacity().
+  //
+  // This guarantees that the returned data is aligned on an 8-byte boundary.
+  void* Allocate(size_t num_bytes) override;
+  PickleBuffer* AsPickleBuffer() override;
+
+  scoped_ptr<Storage> storage_;
+
+  DISALLOW_COPY_AND_ASSIGN(PickleBuffer);
+};
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_PICKLE_BUFFER_H_
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
index b0a403b..ed77f310 100644
--- a/mojo/public/cpp/bindings/message.h
+++ b/mojo/public/cpp/bindings/message.h
@@ -5,9 +5,12 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
 
+#include <limits>
 #include <vector>
 
+#include "base/memory/scoped_ptr.h"
 #include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/bindings/lib/pickle_buffer.h"
 #include "mojo/public/cpp/environment/logging.h"
 
 namespace mojo {
@@ -21,66 +24,74 @@
   Message();
   ~Message();
 
-  void Reset();
-
-  void AllocData(uint32_t num_bytes);
-  void AllocUninitializedData(uint32_t num_bytes);
+  void Initialize(size_t capacity, bool zero_initialized);
 
   // Transfers data and handles to |destination|.
   void MoveTo(Message* destination);
 
-  uint32_t data_num_bytes() const { return data_num_bytes_; }
+  uint32_t data_num_bytes() const {
+    MOJO_DCHECK(buffer_->data_num_bytes() <=
+                std::numeric_limits<uint32_t>::max());
+    return static_cast<uint32_t>(buffer_->data_num_bytes());
+  }
 
   // Access the raw bytes of the message.
   const uint8_t* data() const {
-    return reinterpret_cast<const uint8_t*>(data_);
+    return static_cast<const uint8_t*>(buffer_->data());
   }
-  uint8_t* mutable_data() { return reinterpret_cast<uint8_t*>(data_); }
+
+  uint8_t* mutable_data() { return static_cast<uint8_t*>(buffer_->data()); }
 
   // Access the header.
-  const internal::MessageHeader* header() const { return &data_->header; }
+  const internal::MessageHeader* header() const {
+    return static_cast<const internal::MessageHeader*>(buffer_->data());
+  }
 
-  uint32_t interface_id() const { return data_->header.interface_id; }
-  void set_interface_id(uint32_t id) { data_->header.interface_id = id; }
+  internal::MessageHeader* header() {
+    return const_cast<internal::MessageHeader*>(
+        static_cast<const Message*>(this)->header());
+  }
 
-  uint32_t name() const { return data_->header.name; }
-  bool has_flag(uint32_t flag) const { return !!(data_->header.flags & flag); }
+  uint32_t interface_id() const { return header()->interface_id; }
+  void set_interface_id(uint32_t id) { header()->interface_id = id; }
+
+  uint32_t name() const { return header()->name; }
+  bool has_flag(uint32_t flag) const { return !!(header()->flags & flag); }
 
   // Access the request_id field (if present).
-  bool has_request_id() const { return data_->header.version >= 1; }
+  bool has_request_id() const { return header()->version >= 1; }
   uint64_t request_id() const {
     MOJO_DCHECK(has_request_id());
     return static_cast<const internal::MessageHeaderWithRequestID*>(
-               &data_->header)->request_id;
+               header())->request_id;
   }
   void set_request_id(uint64_t request_id) {
     MOJO_DCHECK(has_request_id());
-    static_cast<internal::MessageHeaderWithRequestID*>(&data_->header)
+    static_cast<internal::MessageHeaderWithRequestID*>(header())
         ->request_id = request_id;
   }
 
   // Access the payload.
-  const uint8_t* payload() const {
-    return reinterpret_cast<const uint8_t*>(data_) + data_->header.num_bytes;
-  }
-  uint8_t* mutable_payload() {
-    return reinterpret_cast<uint8_t*>(data_) + data_->header.num_bytes;
-  }
+  const uint8_t* payload() const { return data() + header()->num_bytes; }
+  uint8_t* mutable_payload() { return const_cast<uint8_t*>(payload()); }
   uint32_t payload_num_bytes() const {
-    MOJO_DCHECK(data_num_bytes_ >= data_->header.num_bytes);
-    return data_num_bytes_ - data_->header.num_bytes;
+    MOJO_DCHECK(buffer_->data_num_bytes() >= header()->num_bytes);
+    size_t num_bytes = buffer_->data_num_bytes() - header()->num_bytes;
+    MOJO_DCHECK(num_bytes <= std::numeric_limits<uint32_t>::max());
+    return static_cast<uint32_t>(num_bytes);
   }
 
   // Access the handles.
   const std::vector<Handle>* handles() const { return &handles_; }
   std::vector<Handle>* mutable_handles() { return &handles_; }
 
- private:
-  void Initialize();
-  void FreeDataAndCloseHandles();
+  // Access the underlying Buffer interface.
+  internal::Buffer* buffer() { return buffer_.get(); }
 
-  uint32_t data_num_bytes_;
-  internal::MessageData* data_;
+ private:
+  void CloseHandles();
+
+  scoped_ptr<internal::PickleBuffer> buffer_;
   std::vector<Handle> handles_;
 
   MOJO_DISALLOW_COPY_AND_ASSIGN(Message);
diff --git a/mojo/public/cpp/bindings/tests/validation_unittest.cc b/mojo/public/cpp/bindings/tests/validation_unittest.cc
index f3ff9ab..7153a59 100644
--- a/mojo/public/cpp/bindings/tests/validation_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -148,9 +148,10 @@
     return false;
   }
 
-  message->AllocUninitializedData(static_cast<uint32_t>(data.size()));
+  message->Initialize(static_cast<uint32_t>(data.size()),
+                      false /* zero_initialized */);
   if (!data.empty())
-    memcpy(message->mutable_data(), &data[0], data.size());
+    memcpy(message->buffer()->Allocate(data.size()), &data[0], data.size());
   message->mutable_handles()->resize(num_handles);
 
   return true;
diff --git a/third_party/mojo/mojo_public.gyp b/third_party/mojo/mojo_public.gyp
index d62e57e..d9d77eabf 100644
--- a/third_party/mojo/mojo_public.gyp
+++ b/third_party/mojo/mojo_public.gyp
@@ -147,6 +147,8 @@
         '../../mojo/public/cpp/bindings/lib/multiplex_router.cc',
         '../../mojo/public/cpp/bindings/lib/multiplex_router.h',
         '../../mojo/public/cpp/bindings/lib/no_interface.cc',
+        '../../mojo/public/cpp/bindings/lib/pickle_buffer.cc',
+        '../../mojo/public/cpp/bindings/lib/pickle_buffer.h',
         '../../mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc',
         '../../mojo/public/cpp/bindings/lib/pipe_control_message_handler.h',
         '../../mojo/public/cpp/bindings/lib/pipe_control_message_handler_delegate.h',
@@ -176,6 +178,7 @@
         '>@(mojom_generated_sources)',
       ],
       'dependencies': [
+        '../../base/base.gyp:base',
         'mojo_interface_bindings_cpp_sources',
       ],
     },