Bug 1287246 (Part 2) - Store the state for unbuffered reads in a separate structure in StreamingLexer. r=njn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Fri, 15 Jul 2016 22:29:48 -0700
changeset 305379 d858390c1386134041658ba79e4bdae6c5b29ba7
parent 305378 3ce690bdd3a87b4af237e2bb0cc2391a62832d0a
child 305380 1888681263a28f2056ada6dbec134282b9d9ef2f
push id30460
push usercbook@mozilla.com
push dateMon, 18 Jul 2016 15:08:19 +0000
treeherdermozilla-central@cde56ead650f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1287246
milestone50.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 1287246 (Part 2) - Store the state for unbuffered reads in a separate structure in StreamingLexer. r=njn
image/StreamingLexer.h
--- a/image/StreamingLexer.h
+++ b/image/StreamingLexer.h
@@ -264,17 +264,16 @@ private:
  * state directly.
  */
 template <typename State, size_t InlineBufferSize = 16>
 class StreamingLexer
 {
 public:
   explicit StreamingLexer(LexerTransition<State> aStartState)
     : mTransition(TerminalState::FAILURE)
-    , mToReadUnbuffered(0)
   {
     SetTransition(aStartState);
   }
 
   template <typename Func>
   LexerResult Lex(SourceBufferIterator& aIterator,
                   IResumable* aOnResume,
                   Func aFunc)
@@ -282,19 +281,22 @@ public:
     if (mTransition.NextStateIsTerminal()) {
       // We've already reached a terminal state. We never deliver any more data
       // in this case; just return the terminal state again immediately.
       return LexerResult(mTransition.NextStateAsTerminal());
     }
 
     Maybe<LexerResult> result;
     do {
+      MOZ_ASSERT_IF(mTransition.Buffering() == BufferingStrategy::UNBUFFERED,
+                    mUnbufferedState);
+
       // Figure out how much we need to read.
       const size_t toRead = mTransition.Buffering() == BufferingStrategy::UNBUFFERED
-                          ? mToReadUnbuffered
+                          ? mUnbufferedState->mBytesRemaining
                           : mTransition.Size() - mBuffer.length();
 
       // Attempt to advance the iterator by |toRead| bytes.
       switch (aIterator.AdvanceOrScheduleResume(toRead, aOnResume)) {
         case SourceBufferIterator::WAITING:
           // We can't continue because the rest of the data hasn't arrived from
           // the network yet. We don't have to do anything special; the
           // SourceBufferIterator will ensure that |aOnResume| gets called when
@@ -331,48 +333,56 @@ public:
     return *result;
   }
 
 private:
   template <typename Func>
   Maybe<LexerResult> UnbufferedRead(SourceBufferIterator& aIterator, Func aFunc)
   {
     MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::UNBUFFERED);
+    MOZ_ASSERT(mUnbufferedState);
     MOZ_ASSERT(mBuffer.empty(),
                "Buffered read at the same time as unbuffered read?");
+    MOZ_ASSERT(aIterator.Length() <= mUnbufferedState->mBytesRemaining,
+               "Read too much data during unbuffered read?");
 
-    if (mToReadUnbuffered > 0) {
+    if (mUnbufferedState->mBytesRemaining > 0) {
       // Call aFunc with the unbuffered state to indicate that we're in the
       // middle of an unbuffered read. We enforce that any state transition
       // passed back to us is either a terminal state or takes us back to the
       // unbuffered state.
       LexerTransition<State> unbufferedTransition =
         aFunc(mTransition.UnbufferedState(), aIterator.Data(), aIterator.Length());
+
+      // If we reached a terminal state, we're done.
       if (unbufferedTransition.NextStateIsTerminal()) {
         return SetTransition(unbufferedTransition);
       }
 
+      // We're continuing the read. Update our position.
       MOZ_ASSERT(mTransition.UnbufferedState() ==
                    unbufferedTransition.NextState());
+      mUnbufferedState->mBytesRemaining -=
+        std::min(mUnbufferedState->mBytesRemaining, aIterator.Length());
 
-      mToReadUnbuffered -= aIterator.Length();
-      if (mToReadUnbuffered != 0) {
+      // If we haven't read everything yet, try to read more data.
+      if (mUnbufferedState->mBytesRemaining != 0) {
         return Nothing();  // Keep processing.
       }
     }
 
     // We're done with the unbuffered read, so transition to the next state.
     return SetTransition(aFunc(mTransition.NextState(), nullptr, 0));
   }
 
   template <typename Func>
   Maybe<LexerResult> BufferedRead(SourceBufferIterator& aIterator, Func aFunc)
   {
     MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
-    MOZ_ASSERT(mToReadUnbuffered == 0,
+    MOZ_ASSERT(!mUnbufferedState,
                "Buffered read at the same time as unbuffered read?");
     MOZ_ASSERT(mBuffer.length() < mTransition.Size() ||
                (mBuffer.length() == 0 && mTransition.Size() == 0),
                "Buffered more than we needed?");
 
     // If we have all the data, we don't actually need to buffer anything.
     if (mBuffer.empty() && aIterator.Length() == mTransition.Size()) {
       return SetTransition(aFunc(mTransition.NextState(),
@@ -403,32 +413,42 @@ private:
   }
 
   Maybe<LexerResult> SetTransition(const LexerTransition<State>& aTransition)
   {
     mTransition = aTransition;
 
     // Get rid of anything left over from the previous state.
     mBuffer.clear();
-    mToReadUnbuffered = 0;
+    mUnbufferedState = Nothing();
 
     // If we reached a terminal state, let the caller know.
     if (mTransition.NextStateIsTerminal()) {
       return Some(LexerResult(mTransition.NextStateAsTerminal()));
     }
 
     // If we're entering an unbuffered state, record how long we'll stay in it.
     if (mTransition.Buffering() == BufferingStrategy::UNBUFFERED) {
-      mToReadUnbuffered = mTransition.Size();
+      mUnbufferedState.emplace(mTransition.Size());
     }
 
     return Nothing();  // Keep processing.
   }
 
+  // State that tracks our position within an unbuffered read.
+  struct UnbufferedState
+  {
+    explicit UnbufferedState(size_t aBytesRemaining)
+      : mBytesRemaining(aBytesRemaining)
+    { }
+
+    size_t mBytesRemaining;
+  };
+
   Vector<char, InlineBufferSize> mBuffer;
   LexerTransition<State> mTransition;
-  size_t mToReadUnbuffered;
+  Maybe<UnbufferedState> mUnbufferedState;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_StreamingLexer_h