Add output formatters for more mojom types.

Add output formatters for printing the contents of Mojo arrays,
maps, and structs.  These are very useful for writing debug logs.

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

R=viettrungluu@chromium.org, abarth, jamesr, viettrungluu
BUG=

Review URL: https://codereview.chromium.org/1532993002 .
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 01726c9..0b6464f 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -10,6 +10,7 @@
 mojo_sdk_source_set("serialization") {
   sources = [
     "array.h",
+    "formatting.h",
     "lib/array_internal.cc",
     "lib/array_internal.h",
     "lib/array_serialization.h",
diff --git a/mojo/public/cpp/bindings/formatting.h b/mojo/public/cpp/bindings/formatting.h
new file mode 100644
index 0000000..af6377a
--- /dev/null
+++ b/mojo/public/cpp/bindings/formatting.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_FORMATTING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_FORMATTING_H_
+
+#include <iosfwd>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+
+namespace mojo {
+
+// Prints the contents of an array to an output stream in a human-readable
+// format.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const mojo::Array<T>& array) {
+  if (array) {
+    os << "[";
+    bool first = true;
+    for (auto it = array.storage().cbegin(); it != array.storage().cend();
+         ++it) {
+      if (first)
+        first = false;
+      else
+        os << ", ";
+      os << *it;
+    }
+    os << "]";
+  } else {
+    os << "null";
+  }
+  return os;
+}
+
+// Prints the contents of a map to an output stream in a human-readable
+// format.
+template <typename Key, typename Value>
+std::ostream& operator<<(std::ostream& os, const mojo::Map<Key, Value>& map) {
+  if (map) {
+    os << "{";
+    bool first = true;
+    for (auto it = map.cbegin(); it != map.cend(); ++it) {
+      if (first)
+        first = false;
+      else
+        os << ", ";
+      os << it.GetKey() << ": " << it.GetValue();
+    }
+    os << "}";
+  } else {
+    os << "null";
+  }
+  return os;
+}
+
+// Prints the pointee of a Mojo structure pointer to an output stream
+// assuming there exists an operator<< overload that accepts a const
+// reference to the object.
+template <typename T, typename = typename T::Data_>
+auto operator<<(std::ostream& os, const T* value) -> decltype(os << *value) {
+  return value ? os << *value : os << "null";
+}
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const StructPtr<T>& value) {
+  return os << value.get();
+}
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const InlinedStructPtr<T>& value) {
+  return os << value.get();
+}
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_FORMATTING_H_
diff --git a/mojo/public/cpp/bindings/string.h b/mojo/public/cpp/bindings/string.h
index 4cf8631..7f2ca31 100644
--- a/mojo/public/cpp/bindings/string.h
+++ b/mojo/public/cpp/bindings/string.h
@@ -5,6 +5,7 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
 
+#include <iosfwd>
 #include <string>
 
 #include "mojo/public/cpp/bindings/lib/array_internal.h"
@@ -122,6 +123,8 @@
   return !(a == b);
 }
 
+// TODO(jeffbrown): Decide whether this should print a sentinel value
+// such as "<null>" when formatting null strings.
 inline std::ostream& operator<<(std::ostream& out, const String& s) {
   return out << s.get();
 }
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index d082323..0ccb023 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -19,6 +19,7 @@
     "constant_unittest.cc",
     "container_test_util.cc",
     "equals_unittest.cc",
+    "formatting_unittest.cc",
     "handle_passing_unittest.cc",
     "interface_ptr_unittest.cc",
     "interface_unittest.cc",
diff --git a/mojo/public/cpp/bindings/tests/formatting_unittest.cc b/mojo/public/cpp/bindings/tests/formatting_unittest.cc
new file mode 100644
index 0000000..9082243
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/formatting_unittest.cc
@@ -0,0 +1,104 @@
+// 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 <sstream>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/formatting.h"
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+RectPtr MakeRect(int32_t factor = 1) {
+  RectPtr rect(Rect::New());
+  rect->x = 1 * factor;
+  rect->y = 2 * factor;
+  rect->width = 10 * factor;
+  rect->height = 20 * factor;
+  return rect;
+}
+}  // namespace
+
+std::ostream& operator<<(std::ostream& os, const Rect& value) {
+  return os << "{x=" << value.x << ", y=" << value.y
+            << ", width=" << value.width << ", height=" << value.height << "}";
+}
+
+std::ostream& operator<<(std::ostream& os, const RectPair& value) {
+  return os << "{first=" << value.first << ", second=" << value.second << "}";
+}
+
+TEST(FormattingTest, Arrays) {
+  Array<int32_t> null_array;
+  Array<int32_t> empty_array;
+  empty_array.resize(0);
+  Array<int32_t> one_element_array;
+  one_element_array.push_back(123);
+  Array<int32_t> three_element_array;
+  three_element_array.push_back(4);
+  three_element_array.push_back(5);
+  three_element_array.push_back(6);
+
+  std::ostringstream so;
+  so << "null_array=" << null_array << ", empty_array=" << empty_array
+     << ", one_element_array=" << one_element_array
+     << ", three_element_array=" << three_element_array;
+
+  EXPECT_EQ(
+      "null_array=null, "
+      "empty_array=[], "
+      "one_element_array=[123], "
+      "three_element_array=[4, 5, 6]",
+      so.str());
+}
+
+TEST(FormattingTest, Maps) {
+  Map<int32_t, std::string> null_map;
+  Map<int32_t, std::string> empty_map;
+  empty_map.mark_non_null();
+  Map<int32_t, std::string> one_element_map;
+  one_element_map.insert(123, "abc");
+  Map<int32_t, std::string> three_element_map;
+  three_element_map.insert(4, "d");
+  three_element_map.insert(5, "e");
+  three_element_map.insert(6, "f");
+
+  std::ostringstream so;
+  so << "null_map=" << null_map << ", empty_map=" << empty_map
+     << ", one_element_map=" << one_element_map
+     << ", three_element_map=" << three_element_map;
+
+  EXPECT_EQ(
+      "null_map=null, "
+      "empty_map={}, "
+      "one_element_map={123: abc}, "
+      "three_element_map={4: d, 5: e, 6: f}",
+      so.str());
+}
+
+TEST(FormattingTest, Structs) {
+  InlinedStructPtr<Rect> inlined_struct_ptr = MakeRect(1);
+  InlinedStructPtr<Rect> null_inlined_struct_ptr;
+  StructPtr<RectPair> struct_ptr = RectPair::New();
+  struct_ptr->first = MakeRect(2);
+  StructPtr<RectPair> null_struct_ptr;
+
+  std::ostringstream so;
+  so << "inlined_struct_ptr=" << inlined_struct_ptr
+     << ", null_inlined_struct_ptr=" << null_inlined_struct_ptr
+     << ", struct_ptr=" << struct_ptr
+     << ", null_struct_ptr=" << null_struct_ptr;
+  EXPECT_EQ(
+      "inlined_struct_ptr={x=1, y=2, width=10, height=20}, "
+      "null_inlined_struct_ptr=null, "
+      "struct_ptr={first={x=2, y=4, width=20, height=40}, second=null}, "
+      "null_struct_ptr=null",
+      so.str());
+}
+
+}  // namespace test
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/string_unittest.cc b/mojo/public/cpp/bindings/tests/string_unittest.cc
index 0769071..4089039 100644
--- a/mojo/public/cpp/bindings/tests/string_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/string_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <sstream>
+
 #include "mojo/public/cpp/bindings/string.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -116,5 +118,14 @@
   EXPECT_FALSE(real < null);
 }
 
+TEST(StringTest, OutputFormatting) {
+  String s("abc");
+  String null;
+
+  std::ostringstream so;
+  so << "s=" << s << ", null=" << null;
+  EXPECT_EQ("s=abc, null=", so.str());
+}
+
 }  // namespace test
 }  // namespace mojo