Mojo: Add a C++-style SharedBuffer API.

Review-Url: https://codereview.chromium.org/2036053002
Cr-Commit-Position: refs/heads/master@{#397957}


CrOS-Libchrome-Original-Commit: a7ee0a2ccde8c99665de2b5859e903087c3d3c31
diff --git a/mojo/mojo_public.gyp b/mojo/mojo_public.gyp
index 2c770cb..7096de5 100644
--- a/mojo/mojo_public.gyp
+++ b/mojo/mojo_public.gyp
@@ -96,6 +96,7 @@
       'target_name': 'mojo_cpp_system',
       'type': 'static_library',
       'sources': [
+        'public/cpp/system/buffer.cc',
         'public/cpp/system/buffer.h',
         'public/cpp/system/core.h',
         'public/cpp/system/data_pipe.h',
diff --git a/mojo/public/cpp/system/buffer.cc b/mojo/public/cpp/system/buffer.cc
new file mode 100644
index 0000000..c186465
--- /dev/null
+++ b/mojo/public/cpp/system/buffer.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 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/system/buffer.h"
+
+namespace mojo {
+
+ScopedSharedBufferHandle SharedBufferHandle::Clone(
+    SharedBufferHandle::AccessMode access_mode) const {
+  ScopedSharedBufferHandle result;
+  if (!is_valid())
+    return result;
+
+  MojoDuplicateBufferHandleOptions options = {
+      sizeof(options), MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE};
+  if (access_mode == AccessMode::READ_ONLY)
+    options.flags |= MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY;
+  SharedBufferHandle result_handle;
+  MojoDuplicateBufferHandle(value(), &options, result_handle.mutable_value());
+  result.reset(result_handle);
+  return result;
+}
+
+ScopedSharedBufferMapping SharedBufferHandle::Map(uint64_t size) const {
+  return MapAtOffset(size, 0);
+}
+
+ScopedSharedBufferMapping SharedBufferHandle::MapAtOffset(
+    uint64_t size,
+    uint64_t offset) const {
+  void* buffer = nullptr;
+  MojoMapBuffer(value(), offset, size, &buffer, MOJO_MAP_BUFFER_FLAG_NONE);
+  return ScopedSharedBufferMapping(buffer);
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/buffer.h b/mojo/public/cpp/system/buffer.h
index a2d524e..27ba814 100644
--- a/mojo/public/cpp/system/buffer.h
+++ b/mojo/public/cpp/system/buffer.h
@@ -14,27 +14,61 @@
 
 #include <stdint.h>
 
+#include <memory>
+
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "mojo/public/c/system/buffer.h"
 #include "mojo/public/cpp/system/handle.h"
 
 namespace mojo {
+namespace internal {
+
+struct Unmapper {
+  void operator()(void* buffer) {
+    MojoResult result = MojoUnmapBuffer(buffer);
+    DCHECK_EQ(MOJO_RESULT_OK, result);
+  }
+};
+
+}  // namespace internal
+
+using ScopedSharedBufferMapping = std::unique_ptr<void, internal::Unmapper>;
+
+class SharedBufferHandle;
+
+typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle;
 
 // A strongly-typed representation of a |MojoHandle| referring to a shared
 // buffer.
 class SharedBufferHandle : public Handle {
  public:
+  enum class AccessMode {
+    READ_WRITE,
+    READ_ONLY,
+  };
+
   SharedBufferHandle() {}
   explicit SharedBufferHandle(MojoHandle value) : Handle(value) {}
 
   // Copying and assignment allowed.
+
+  // Clones this shared buffer handle. If |access_mode| is READ_ONLY or this is
+  // a read-only handle, the new handle will be read-only. On failure, this will
+  // return an empty result.
+  ScopedSharedBufferHandle Clone(AccessMode = AccessMode::READ_WRITE) const;
+
+  // Maps |size| bytes of this shared buffer. On failure, this will return a
+  // null mapping.
+  ScopedSharedBufferMapping Map(uint64_t size) const;
+
+  // Maps |size| bytes of this shared buffer, starting |offset| bytes into the
+  // buffer. On failure, this will return a null mapping.
+  ScopedSharedBufferMapping MapAtOffset(uint64_t size, uint64_t offset) const;
 };
 
 static_assert(sizeof(SharedBufferHandle) == sizeof(Handle),
               "Bad size for C++ SharedBufferHandle");
-
-typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle;
 static_assert(sizeof(ScopedSharedBufferHandle) == sizeof(SharedBufferHandle),
               "Bad size for C++ ScopedSharedBufferHandle");
 
diff --git a/mojo/public/cpp/system/handle.h b/mojo/public/cpp/system/handle.h
index d0466b0..f142e8b 100644
--- a/mojo/public/cpp/system/handle.h
+++ b/mojo/public/cpp/system/handle.h
@@ -96,6 +96,7 @@
   }
 
   const HandleType& get() const { return handle_; }
+  const HandleType* operator->() const { return &handle_; }
 
   template <typename PassedHandleType>
   static ScopedHandleBase<HandleType> From(
diff --git a/mojo/public/cpp/system/tests/core_unittest.cc b/mojo/public/cpp/system/tests/core_unittest.cc
index b500be3..9a95635 100644
--- a/mojo/public/cpp/system/tests/core_unittest.cc
+++ b/mojo/public/cpp/system/tests/core_unittest.cc
@@ -481,6 +481,47 @@
   EXPECT_FALSE(buffer2.is_valid());
 }
 
+TEST(CoreCppTest, BasicSharedBuffer) {
+  ScopedSharedBufferHandle h0;
+
+  // Create a shared buffer (|h0|).
+  {
+    SharedBuffer buffer(100);
+    h0 = std::move(buffer.handle);
+  }
+
+  // Map everything.
+  ScopedSharedBufferMapping mapping = h0->Map(100);
+  ASSERT_TRUE(mapping);
+  static_cast<char*>(mapping.get())[50] = 'x';
+
+  // Duplicate |h0| to |h1|.
+  ScopedSharedBufferHandle h1 =
+      h0->Clone(SharedBufferHandle::AccessMode::READ_ONLY);
+  ASSERT_TRUE(h1.is_valid());
+
+  // Close |h0|.
+  h0.reset();
+
+  // The mapping should still be good.
+  static_cast<char*>(mapping.get())[51] = 'y';
+
+  // Unmap it.
+  mapping.reset();
+
+  // Map half of |h1|.
+  mapping = h1->MapAtOffset(50, 50);
+  ASSERT_TRUE(mapping);
+
+  // It should have what we wrote.
+  EXPECT_EQ('x', static_cast<char*>(mapping.get())[0]);
+  EXPECT_EQ('y', static_cast<char*>(mapping.get())[1]);
+
+  // Unmap it.
+  mapping.reset();
+  h1.reset();
+}
+
 // TODO(vtl): Write data pipe tests.
 
 }  // namespace