Bug 1587638: Add support for buffer switching to incremental encoder r=tcampbell
authorIain Ireland <iireland@mozilla.com>
Fri, 01 Nov 2019 20:28:23 +0000
changeset 500399 46a8774eed223bc230d8732882b51b9ac768b305
parent 500398 145c009565f90cc2fdda8b3ba4c2e9eef562b675
child 500400 cf26123bd822828d1606b4ba9e19588c859a1b35
push id114164
push useraiakab@mozilla.com
push dateTue, 05 Nov 2019 10:06:15 +0000
treeherdermozilla-inbound@4d585c7edc76 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1587638
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 1587638: Add support for buffer switching to incremental encoder r=tcampbell Differential Revision: https://phabricator.services.mozilla.com/D48784
js/src/vm/Xdr.cpp
js/src/vm/Xdr.h
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -60,25 +60,25 @@ XDRResult XDRState<mode>::codeChars(Lati
 
 template <XDRMode mode>
 XDRResult XDRState<mode>::codeChars(Utf8Unit* units, size_t count) {
   if (count == 0) {
     return Ok();
   }
 
   if (mode == XDR_ENCODE) {
-    uint8_t* ptr = buf.write(count);
+    uint8_t* ptr = buf->write(count);
     if (!ptr) {
       return fail(JS::TranscodeResult_Throw);
     }
 
     std::transform(units, units + count, ptr,
                    [](const Utf8Unit& unit) { return unit.toUint8(); });
   } else {
-    const uint8_t* ptr = buf.read(count);
+    const uint8_t* ptr = buf->read(count);
     if (!ptr) {
       return fail(JS::TranscodeResult_Failure_BadDecode);
     }
 
     std::transform(ptr, ptr + count, units,
                    [](const uint8_t& value) { return Utf8Unit(value); });
   }
 
@@ -88,25 +88,25 @@ XDRResult XDRState<mode>::codeChars(Utf8
 template <XDRMode mode>
 XDRResult XDRState<mode>::codeChars(char16_t* chars, size_t nchars) {
   if (nchars == 0) {
     return Ok();
   }
 
   size_t nbytes = nchars * sizeof(char16_t);
   if (mode == XDR_ENCODE) {
-    uint8_t* ptr = buf.write(nbytes);
+    uint8_t* ptr = buf->write(nbytes);
     if (!ptr) {
       return fail(JS::TranscodeResult_Throw);
     }
 
     // |mozilla::NativeEndian| correctly handles writing into unaligned |ptr|.
     mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
   } else {
-    const uint8_t* ptr = buf.read(nbytes);
+    const uint8_t* ptr = buf->read(nbytes);
     if (!ptr) {
       return fail(JS::TranscodeResult_Failure_BadDecode);
     }
 
     // |mozilla::NativeEndian| correctly handles reading from unaligned |ptr|.
     mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars, ptr, nchars);
   }
   return Ok();
@@ -261,16 +261,17 @@ XDRResult XDRState<mode>::codeScript(Mut
 
   if (mode == XDR_DECODE) {
     scriptp.set(nullptr);
   } else {
     MOZ_ASSERT(!scriptp->enclosingScope());
   }
 
   MOZ_TRY(VersionCheck(this));
+  MOZ_ASSERT(isMainBuf());
   MOZ_TRY(XDRScript(this, nullptr, nullptr, nullptr, scriptp));
 
   guard.release();
   return Ok();
 }
 
 template class js::XDRState<XDR_ENCODE>;
 template class js::XDRState<XDR_DECODE>;
@@ -368,17 +369,17 @@ AutoXDRTree::Key XDRIncrementalEncoder::
 void XDRIncrementalEncoder::createOrReplaceSubTree(AutoXDRTree* child) {
   AutoXDRTree* parent = scope_;
   child->parent_ = parent;
   scope_ = child;
   if (oom_) {
     return;
   }
 
-  size_t cursor = buf.cursor();
+  size_t cursor = buf->cursor();
 
   // End the parent slice here, set the key to the child.
   if (parent) {
     Slice& last = node_->back();
     last.sliceLength = cursor - last.sliceBegin;
     last.child = child->key_;
     MOZ_ASSERT_IF(uint32_t(parent->key_) != 0,
                   uint32_t(parent->key_ >> 32) <= uint32_t(child->key_ >> 32) &&
@@ -410,17 +411,17 @@ void XDRIncrementalEncoder::createOrRepl
 void XDRIncrementalEncoder::endSubTree() {
   AutoXDRTree* child = scope_;
   AutoXDRTree* parent = child->parent_;
   scope_ = parent;
   if (oom_) {
     return;
   }
 
-  size_t cursor = buf.cursor();
+  size_t cursor = buf->cursor();
 
   // End the child sub-tree.
   Slice& last = node_->back();
   last.sliceLength = cursor - last.sliceBegin;
   MOZ_ASSERT(last.child == AutoXDRTree::noSubTree);
 
   // Stop at the top-level.
   if (!parent) {
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -202,116 +202,131 @@ class XDRCoderBase {
 };
 
 /*
  * XDR serialization state.  All data is encoded in little endian.
  */
 template <XDRMode mode>
 class XDRState : public XDRCoderBase {
  protected:
-  XDRBuffer<mode> buf;
+  XDRBuffer<mode> mainBuf;
+  XDRBuffer<mode>* buf;
 
  public:
   XDRState(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
-      : buf(cx, buffer, cursor) {}
+      : mainBuf(cx, buffer, cursor), buf(&mainBuf) {}
 
   template <typename RangeType>
-  XDRState(JSContext* cx, const RangeType& range) : buf(cx, range) {}
+  XDRState(JSContext* cx, const RangeType& range)
+      : mainBuf(cx, range), buf(&mainBuf) {}
+
+  // No default copy constructor or copying assignment, because |buf|
+  // is an internal pointer.
+  XDRState(const XDRState&) = delete;
+  XDRState& operator=(const XDRState&) = delete;
 
   virtual ~XDRState(){};
 
-  JSContext* cx() const { return buf.cx(); }
+  JSContext* cx() const { return mainBuf.cx(); }
 
   virtual bool hasOptions() const { return false; }
   virtual const JS::ReadOnlyCompileOptions& options() {
     MOZ_CRASH("does not have options");
   }
   virtual bool hasScriptSourceObjectOut() const { return false; }
   virtual ScriptSourceObject** scriptSourceObjectOut() {
     MOZ_CRASH("does not have scriptSourceObjectOut.");
   }
 
+  virtual bool isMainBuf() { return true; }
+
+  virtual void switchToAtomBuf() { MOZ_CRASH("cannot switch to atom buffer."); }
+  virtual void switchToMainBuf() { MOZ_CRASH("cannot switch to main buffer."); }
+  virtual void switchToHeaderBuf() {
+    MOZ_CRASH("cannot switch to header buffer.");
+  }
+
   XDRResult fail(JS::TranscodeResult code) {
 #ifdef DEBUG
     MOZ_ASSERT(code != JS::TranscodeResult_Ok);
     MOZ_ASSERT(validateResultCode(cx(), code));
     setResultCode(code);
 #endif
     return mozilla::Err(code);
   }
 
   XDRResult peekData(const uint8_t** pptr, size_t length) {
-    const uint8_t* ptr = buf.read(length);
+    const uint8_t* ptr = buf->read(length);
     if (!ptr) {
       return fail(JS::TranscodeResult_Failure_BadDecode);
     }
     *pptr = ptr;
     return Ok();
   }
 
   XDRResult codeUint8(uint8_t* n) {
     if (mode == XDR_ENCODE) {
-      uint8_t* ptr = buf.write(sizeof(*n));
+      uint8_t* ptr = buf->write(sizeof(*n));
       if (!ptr) {
         return fail(JS::TranscodeResult_Throw);
       }
       *ptr = *n;
     } else {
-      const uint8_t* ptr = buf.read(sizeof(*n));
+      const uint8_t* ptr = buf->read(sizeof(*n));
       if (!ptr) {
         return fail(JS::TranscodeResult_Failure_BadDecode);
       }
       *n = *ptr;
     }
     return Ok();
   }
 
   XDRResult codeUint16(uint16_t* n) {
     if (mode == XDR_ENCODE) {
-      uint8_t* ptr = buf.write(sizeof(*n));
+      uint8_t* ptr = buf->write(sizeof(*n));
       if (!ptr) {
         return fail(JS::TranscodeResult_Throw);
       }
       mozilla::LittleEndian::writeUint16(ptr, *n);
     } else {
-      const uint8_t* ptr = buf.read(sizeof(*n));
+      const uint8_t* ptr = buf->read(sizeof(*n));
       if (!ptr) {
         return fail(JS::TranscodeResult_Failure_BadDecode);
       }
       *n = mozilla::LittleEndian::readUint16(ptr);
     }
     return Ok();
   }
 
   XDRResult codeUint32(uint32_t* n) {
     if (mode == XDR_ENCODE) {
-      uint8_t* ptr = buf.write(sizeof(*n));
+      uint8_t* ptr = buf->write(sizeof(*n));
       if (!ptr) {
         return fail(JS::TranscodeResult_Throw);
       }
       mozilla::LittleEndian::writeUint32(ptr, *n);
     } else {
-      const uint8_t* ptr = buf.read(sizeof(*n));
+      const uint8_t* ptr = buf->read(sizeof(*n));
       if (!ptr) {
         return fail(JS::TranscodeResult_Failure_BadDecode);
       }
       *n = mozilla::LittleEndian::readUint32(ptr);
     }
     return Ok();
   }
 
   XDRResult codeUint64(uint64_t* n) {
     if (mode == XDR_ENCODE) {
-      uint8_t* ptr = buf.write(sizeof(*n));
+      uint8_t* ptr = buf->write(sizeof(*n));
       if (!ptr) {
         return fail(JS::TranscodeResult_Throw);
       }
       mozilla::LittleEndian::writeUint64(ptr, *n);
     } else {
-      const uint8_t* ptr = buf.read(sizeof(*n));
+      const uint8_t* ptr = buf->read(sizeof(*n));
       if (!ptr) {
         return fail(JS::TranscodeResult_Failure_BadDecode);
       }
       *n = mozilla::LittleEndian::readUint64(ptr);
     }
     return Ok();
   }
 
@@ -365,23 +380,23 @@ class XDRState : public XDRCoderBase {
     return Ok();
   }
 
   XDRResult codeBytes(void* bytes, size_t len) {
     if (len == 0) {
       return Ok();
     }
     if (mode == XDR_ENCODE) {
-      uint8_t* ptr = buf.write(len);
+      uint8_t* ptr = buf->write(len);
       if (!ptr) {
         return fail(JS::TranscodeResult_Throw);
       }
       memcpy(ptr, bytes, len);
     } else {
-      const uint8_t* ptr = buf.read(len);
+      const uint8_t* ptr = buf->read(len);
       if (!ptr) {
         return fail(JS::TranscodeResult_Failure_BadDecode);
       }
       memcpy(bytes, ptr, len);
     }
     return Ok();
   }
 
@@ -512,31 +527,49 @@ class XDRIncrementalEncoder : public XDR
 
   // Last opened XDR-tree on the stack.
   AutoXDRTree* scope_;
   // Node corresponding to the opened scope.
   SlicesNode* node_;
   // Tree of slices.
   SlicesTree tree_;
   JS::TranscodeBuffer slices_;
+  // Map from atoms to their index in the atom buffer
   XDRAtomMap atomMap_;
+  // Atom buffer.
+  JS::TranscodeBuffer atoms_;
+  XDRBuffer<XDR_ENCODE> atomBuf_;
+  // Header buffer.
+  JS::TranscodeBuffer header_;
+  XDRBuffer<XDR_ENCODE> headerBuf_;
   bool oom_;
 
   class DepthFirstSliceIterator;
 
  public:
   explicit XDRIncrementalEncoder(JSContext* cx)
       : XDREncoder(cx, slices_, 0),
         scope_(nullptr),
         node_(nullptr),
         atomMap_(cx),
+        atomBuf_(cx, atoms_, 0),
+        headerBuf_(cx, header_, 0),
         oom_(false) {}
 
   virtual ~XDRIncrementalEncoder() {}
 
+  bool isMainBuf() override { return buf == &mainBuf; }
+
+  // Switch from streaming into the main buffer into the atom buffer.
+  void switchToAtomBuf() override { buf = &atomBuf_; }
+  // Switch to streaming into the main buffer.
+  void switchToMainBuf() override { buf = &mainBuf; }
+  // Switch to streaming into the header buffer.
+  void switchToHeaderBuf() override { buf = &headerBuf_; }
+
   AutoXDRTree::Key getTopLevelTreeKey() const override;
   AutoXDRTree::Key getTreeKey(JSFunction* fun) const override;
 
   void createOrReplaceSubTree(AutoXDRTree* child) override;
   void endSubTree() override;
 
   // Append the content collected during the incremental encoding into the
   // buffer given as argument.