Bug 1596562: Add ParamTraits specializations for mozilla::Vector and std::vector; r=froydnj,bwc
authorAaron Klotz <aklotz@mozilla.com>
Mon, 18 Nov 2019 19:45:41 +0000
changeset 502461 855640e1c096c294c882261ee141b6ce4689d0b8
parent 502460 320e0eac589ddda1bb228d6ae3ebd359b9b29639
child 502462 1e67fb39ab0cb2a0835b6274d776d93d899f90a0
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, bwc
bugs1596562
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1596562: Add ParamTraits specializations for mozilla::Vector and std::vector; r=froydnj,bwc While the need for adding `mozilla::Vector` is self-evident, we also need `std::vector` so that we can send some pre-existing telemetry data structures that use it. The new implementations are basically modified from the `nsTArray` specializations. Note that the `mozilla::Vector` specialization does support any type of allocator, so we still check for OOM failures in that case. This patch also removes the specialization for `std::vector` that lives in WebRTC in favour of the centralized implementation. Differential Revision: https://phabricator.services.mozilla.com/D53085
dom/media/webrtc/WebrtcIPCTraits.h
ipc/glue/IPCMessageUtils.h
--- a/dom/media/webrtc/WebrtcIPCTraits.h
+++ b/dom/media/webrtc/WebrtcIPCTraits.h
@@ -15,46 +15,16 @@
 #include <vector>
 
 namespace mozilla {
 typedef std::vector<std::string> StringVector;
 }
 
 namespace IPC {
 
-template <typename T>
-struct ParamTraits<std::vector<T>> {
-  typedef std::vector<T> paramType;
-
-  static void Write(Message* aMsg, const paramType& aParam) {
-    aMsg->WriteUInt32(aParam.size());
-    for (const T& elem : aParam) {
-      WriteParam(aMsg, elem);
-    }
-  }
-
-  static bool Read(const Message* aMsg, PickleIterator* aIter,
-                   paramType* aResult) {
-    uint32_t size;
-    if (!aMsg->ReadUInt32(aIter, &size)) {
-      return false;
-    }
-    while (size--) {
-      // Only works when T is movable. Meh.
-      T elem;
-      if (!ReadParam(aMsg, aIter, &elem)) {
-        return false;
-      }
-      aResult->emplace_back(std::move(elem));
-    }
-
-    return true;
-  }
-};
-
 template <>
 struct ParamTraits<mozilla::dom::OwningStringOrStringSequence> {
   typedef mozilla::dom::OwningStringOrStringSequence paramType;
 
   // Ugh. OwningStringOrStringSequence already has this enum, but it is
   // private generated code. So we have to re-create it.
   enum Type { kUninitialized, kString, kStringSequence };
 
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -19,21 +19,24 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/net/WebSocketFrame.h"
 #include "mozilla/TimeStamp.h"
 #ifdef XP_WIN
 #  include "mozilla/TimeStamp_windows.h"
 #endif
 #include "mozilla/TypeTraits.h"
 #include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/Vector.h"
 
 #include <limits>
 #include <stdint.h>
 #include <type_traits>
+#include <vector>
 
+#include "nsDebug.h"
 #include "nsExceptionHandler.h"
 #include "nsHashKeys.h"
 #include "nsID.h"
 #include "nsILoadInfo.h"
 #include "nsIWidget.h"
 #include "nsMemory.h"
 #include "nsString.h"
 #include "nsTArray.h"
@@ -633,16 +636,172 @@ struct ParamTraits<FallibleTArray<E>> {
   }
 };
 
 template <typename E, size_t N>
 struct ParamTraits<AutoTArray<E, N>> : ParamTraits<nsTArray<E>> {
   typedef AutoTArray<E, N> paramType;
 };
 
+template <typename E, size_t N, typename AP>
+struct ParamTraits<mozilla::Vector<E, N, AP>> {
+  typedef mozilla::Vector<E, N, AP> paramType;
+
+  // We write arrays of integer or floating-point data using a single pickling
+  // call, rather than writing each element individually.  We deliberately do
+  // not use mozilla::IsPod here because it is perfectly reasonable to have
+  // a data structure T for which IsPod<T>::value is true, yet also have a
+  // ParamTraits<T> specialization.
+  static const bool sUseWriteBytes =
+      (mozilla::IsIntegral<E>::value || mozilla::IsFloatingPoint<E>::value);
+
+  static void Write(Message* aMsg, const paramType& aParam) {
+    uint32_t length = aParam.length();
+    WriteParam(aMsg, length);
+
+    if (sUseWriteBytes) {
+      int pickledLength = 0;
+      MOZ_RELEASE_ASSERT(ByteLengthIsValid(length, sizeof(E), &pickledLength));
+      aMsg->WriteBytes(aParam.begin(), pickledLength);
+      return;
+    }
+
+    for (const E& elem : aParam) {
+      WriteParam(aMsg, elem);
+    }
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter,
+                   paramType* aResult) {
+    uint32_t length;
+    if (!ReadParam(aMsg, aIter, &length)) {
+      return false;
+    }
+
+    if (sUseWriteBytes) {
+      int pickledLength = 0;
+      if (!ByteLengthIsValid(length, sizeof(E), &pickledLength)) {
+        return false;
+      }
+
+      if (!aResult->resizeUninitialized(length)) {
+        // So that OOM failure shows up as OOM crash instead of IPC FatalError.
+        NS_ABORT_OOM(length * sizeof(E));
+      }
+
+      E* elements = aResult->begin();
+      return aMsg->ReadBytesInto(aIter, elements, pickledLength);
+    }
+
+    // Each ReadParam<E> may read more than 1 byte each; this is an attempt
+    // to minimally validate that the length isn't much larger than what's
+    // actually available in aMsg.
+    if (!aMsg->HasBytesAvailable(aIter, length)) {
+      return false;
+    }
+
+    if (!aResult->resize(length)) {
+      // So that OOM failure shows up as OOM crash instead of IPC FatalError.
+      NS_ABORT_OOM(length * sizeof(E));
+    }
+
+    for (uint32_t index = 0; index < length; ++index) {
+      if (!ReadParam(aMsg, aIter, &((*aResult)[index]))) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog) {
+    for (uint32_t index = 0, len = aParam.length(); index < len; ++index) {
+      if (index) {
+        aLog->append(L" ");
+      }
+      LogParam(aParam[index], aLog);
+    }
+  }
+};
+
+template <typename E>
+struct ParamTraits<std::vector<E>> {
+  typedef std::vector<E> paramType;
+
+  // We write arrays of integer or floating-point data using a single pickling
+  // call, rather than writing each element individually.  We deliberately do
+  // not use mozilla::IsPod here because it is perfectly reasonable to have
+  // a data structure T for which IsPod<T>::value is true, yet also have a
+  // ParamTraits<T> specialization.
+  static const bool sUseWriteBytes =
+      (mozilla::IsIntegral<E>::value || mozilla::IsFloatingPoint<E>::value);
+
+  static void Write(Message* aMsg, const paramType& aParam) {
+    uint32_t length = aParam.size();
+    WriteParam(aMsg, length);
+
+    if (sUseWriteBytes) {
+      int pickledLength = 0;
+      MOZ_RELEASE_ASSERT(ByteLengthIsValid(length, sizeof(E), &pickledLength));
+      aMsg->WriteBytes(aParam.data(), pickledLength);
+      return;
+    }
+
+    for (const E& elem : aParam) {
+      WriteParam(aMsg, elem);
+    }
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter,
+                   paramType* aResult) {
+    uint32_t length;
+    if (!ReadParam(aMsg, aIter, &length)) {
+      return false;
+    }
+
+    if (sUseWriteBytes) {
+      int pickledLength = 0;
+      if (!ByteLengthIsValid(length, sizeof(E), &pickledLength)) {
+        return false;
+      }
+
+      aResult->resize(length);
+
+      E* elements = aResult->data();
+      return aMsg->ReadBytesInto(aIter, elements, pickledLength);
+    }
+
+    // Each ReadParam<E> may read more than 1 byte each; this is an attempt
+    // to minimally validate that the length isn't much larger than what's
+    // actually available in aMsg.
+    if (!aMsg->HasBytesAvailable(aIter, length)) {
+      return false;
+    }
+
+    aResult->resize(length);
+
+    for (uint32_t index = 0; index < length; ++index) {
+      if (!ReadParam(aMsg, aIter, &((*aResult)[index]))) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog) {
+    for (uint32_t index = 0, len = aParam.size(); index < len; ++index) {
+      if (index) {
+        aLog->append(L" ");
+      }
+      LogParam(aParam[index], aLog);
+    }
+  }
+};
+
 template <>
 struct ParamTraits<float> {
   typedef float paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     aMsg->WriteBytes(&aParam, sizeof(paramType));
   }