LCOV - code coverage report
Current view: top level - image - StreamingLexer.h (source / functions) Hit Total Coverage
Test: output.info Lines: 128 173 74.0 %
Date: 2017-07-14 16:53:18 Functions: 57 187 30.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /**
       8             :  * StreamingLexer is a lexing framework designed to make it simple to write
       9             :  * image decoders without worrying about the details of how the data is arriving
      10             :  * from the network.
      11             :  */
      12             : 
      13             : #ifndef mozilla_image_StreamingLexer_h
      14             : #define mozilla_image_StreamingLexer_h
      15             : 
      16             : #include <algorithm>
      17             : #include <cstdint>
      18             : #include "mozilla/Assertions.h"
      19             : #include "mozilla/Attributes.h"
      20             : #include "mozilla/Maybe.h"
      21             : #include "mozilla/Move.h"
      22             : #include "mozilla/Variant.h"
      23             : #include "mozilla/Vector.h"
      24             : 
      25             : namespace mozilla {
      26             : namespace image {
      27             : 
      28             : /// Buffering behaviors for StreamingLexer transitions.
      29             : enum class BufferingStrategy
      30             : {
      31             :   BUFFERED,   // Data will be buffered and processed in one chunk.
      32             :   UNBUFFERED  // Data will be processed as it arrives, in multiple chunks.
      33             : };
      34             : 
      35             : /// Control flow behaviors for StreamingLexer transitions.
      36             : enum class ControlFlowStrategy
      37             : {
      38             :   CONTINUE,  // If there's enough data, proceed to the next state immediately.
      39             :   YIELD      // Yield to the caller before proceeding to the next state.
      40             : };
      41             : 
      42             : /// Possible terminal states for the lexer.
      43             : enum class TerminalState
      44             : {
      45             :   SUCCESS,
      46             :   FAILURE
      47             : };
      48             : 
      49             : /// Possible yield reasons for the lexer.
      50             : enum class Yield
      51             : {
      52             :   NEED_MORE_DATA,   // The lexer cannot continue without more data.
      53             :   OUTPUT_AVAILABLE  // There is output available for the caller to consume.
      54             : };
      55             : 
      56             : /// The result of a call to StreamingLexer::Lex().
      57             : typedef Variant<TerminalState, Yield> LexerResult;
      58             : 
      59             : /**
      60             :  * LexerTransition is a type used to give commands to the lexing framework.
      61             :  * Code that uses StreamingLexer can create LexerTransition values using the
      62             :  * static methods on Transition, and then return them to the lexing framework
      63             :  * for execution.
      64             :  */
      65             : template <typename State>
      66         699 : class LexerTransition
      67             : {
      68             : public:
      69             :   // This is implicit so that Terminate{Success,Failure}() can return a
      70             :   // TerminalState and have it implicitly converted to a
      71             :   // LexerTransition<State>, which avoids the need for a "<State>"
      72             :   // qualification to the Terminate{Success,Failure}() callsite.
      73          99 :   MOZ_IMPLICIT LexerTransition(TerminalState aFinalState)
      74          99 :     : mNextState(aFinalState)
      75          99 :   {}
      76             : 
      77         495 :   bool NextStateIsTerminal() const
      78             :   {
      79         495 :     return mNextState.template is<TerminalState>();
      80             :   }
      81             : 
      82          33 :   TerminalState NextStateAsTerminal() const
      83             :   {
      84          33 :     return mNextState.template as<TerminalState>();
      85             :   }
      86             : 
      87         128 :   State NextState() const
      88             :   {
      89         128 :     return mNextState.template as<NonTerminalState>().mState;
      90             :   }
      91             : 
      92         173 :   State UnbufferedState() const
      93             :   {
      94         173 :     return *mNextState.template as<NonTerminalState>().mUnbufferedState;
      95             :   }
      96             : 
      97         131 :   size_t Size() const
      98             :   {
      99         131 :     return mNextState.template as<NonTerminalState>().mSize;
     100             :   }
     101             : 
     102         350 :   BufferingStrategy Buffering() const
     103             :   {
     104         350 :     return mNextState.template as<NonTerminalState>().mBufferingStrategy;
     105             :   }
     106             : 
     107         195 :   ControlFlowStrategy ControlFlow() const
     108             :   {
     109         195 :     return mNextState.template as<NonTerminalState>().mControlFlowStrategy;
     110             :   }
     111             : 
     112             : private:
     113             :   friend struct Transition;
     114             : 
     115         187 :   LexerTransition(State aNextState,
     116             :                   const Maybe<State>& aUnbufferedState,
     117             :                   size_t aSize,
     118             :                   BufferingStrategy aBufferingStrategy,
     119             :                   ControlFlowStrategy aControlFlowStrategy)
     120             :     : mNextState(NonTerminalState(aNextState, aUnbufferedState, aSize,
     121         187 :                                   aBufferingStrategy, aControlFlowStrategy))
     122         187 :   {}
     123             : 
     124         935 :   struct NonTerminalState
     125             :   {
     126             :     State mState;
     127             :     Maybe<State> mUnbufferedState;
     128             :     size_t mSize;
     129             :     BufferingStrategy mBufferingStrategy;
     130             :     ControlFlowStrategy mControlFlowStrategy;
     131             : 
     132         187 :     NonTerminalState(State aState,
     133             :                      const Maybe<State>& aUnbufferedState,
     134             :                      size_t aSize,
     135             :                      BufferingStrategy aBufferingStrategy,
     136             :                      ControlFlowStrategy aControlFlowStrategy)
     137             :       : mState(aState)
     138             :       , mUnbufferedState(aUnbufferedState)
     139             :       , mSize(aSize)
     140             :       , mBufferingStrategy(aBufferingStrategy)
     141         187 :       , mControlFlowStrategy(aControlFlowStrategy)
     142             :     {
     143         187 :       MOZ_ASSERT_IF(mBufferingStrategy == BufferingStrategy::UNBUFFERED,
     144             :                     mUnbufferedState);
     145         187 :       MOZ_ASSERT_IF(mUnbufferedState,
     146             :                     mBufferingStrategy == BufferingStrategy::UNBUFFERED);
     147         187 :     }
     148             :   };
     149             : 
     150             :   Variant<NonTerminalState, TerminalState> mNextState;
     151             : };
     152             : 
     153             : struct Transition
     154             : {
     155             :   /// Transition to @aNextState, buffering @aSize bytes of data.
     156             :   template <typename State>
     157             :   static LexerTransition<State>
     158          20 :   To(const State& aNextState, size_t aSize)
     159             :   {
     160             :     return LexerTransition<State>(aNextState, Nothing(), aSize,
     161             :                                   BufferingStrategy::BUFFERED,
     162          20 :                                   ControlFlowStrategy::CONTINUE);
     163             :   }
     164             : 
     165             :   /// Yield to the caller, transitioning to @aNextState when Lex() is next
     166             :   /// invoked. The same data that was delivered for the current state will be
     167             :   /// delivered again.
     168             :   template <typename State>
     169             :   static LexerTransition<State>
     170           0 :   ToAfterYield(const State& aNextState)
     171             :   {
     172             :     return LexerTransition<State>(aNextState, Nothing(), 0,
     173             :                                   BufferingStrategy::BUFFERED,
     174           0 :                                   ControlFlowStrategy::YIELD);
     175             :   }
     176             : 
     177             :   /**
     178             :    * Transition to @aNextState via @aUnbufferedState, reading @aSize bytes of
     179             :    * data unbuffered.
     180             :    *
     181             :    * The unbuffered data will be delivered in state @aUnbufferedState, which may
     182             :    * be invoked repeatedly until all @aSize bytes have been delivered. Then,
     183             :    * @aNextState will be invoked with no data. No state transitions are allowed
     184             :    * from @aUnbufferedState except for transitions to a terminal state, so
     185             :    * @aNextState will always be reached unless lexing terminates early.
     186             :    */
     187             :   template <typename State>
     188             :   static LexerTransition<State>
     189          34 :   ToUnbuffered(const State& aNextState,
     190             :                const State& aUnbufferedState,
     191             :                size_t aSize)
     192             :   {
     193             :     return LexerTransition<State>(aNextState, Some(aUnbufferedState), aSize,
     194             :                                   BufferingStrategy::UNBUFFERED,
     195          34 :                                   ControlFlowStrategy::CONTINUE);
     196             :   }
     197             : 
     198             :   /**
     199             :    * Continue receiving unbuffered data. @aUnbufferedState should be the same
     200             :    * state as the @aUnbufferedState specified in the preceding call to
     201             :    * ToUnbuffered().
     202             :    *
     203             :    * This should be used during an unbuffered read initiated by ToUnbuffered().
     204             :    */
     205             :   template <typename State>
     206             :   static LexerTransition<State>
     207          99 :   ContinueUnbuffered(const State& aUnbufferedState)
     208             :   {
     209             :     return LexerTransition<State>(aUnbufferedState, Nothing(), 0,
     210             :                                   BufferingStrategy::BUFFERED,
     211          99 :                                   ControlFlowStrategy::CONTINUE);
     212             :   }
     213             : 
     214             :   /**
     215             :    * Continue receiving unbuffered data. @aUnbufferedState should be the same
     216             :    * state as the @aUnbufferedState specified in the preceding call to
     217             :    * ToUnbuffered(). @aSize indicates the amount of data that has already been
     218             :    * consumed; the next state will receive the same data that was delivered to
     219             :    * the current state, without the first @aSize bytes.
     220             :    *
     221             :    * This should be used during an unbuffered read initiated by ToUnbuffered().
     222             :    */
     223             :   template <typename State>
     224             :   static LexerTransition<State>
     225          34 :   ContinueUnbufferedAfterYield(const State& aUnbufferedState, size_t aSize)
     226             :   {
     227             :     return LexerTransition<State>(aUnbufferedState, Nothing(), aSize,
     228             :                                   BufferingStrategy::BUFFERED,
     229          34 :                                   ControlFlowStrategy::YIELD);
     230             :   }
     231             : 
     232             :   /**
     233             :    * Terminate lexing, ending up in terminal state SUCCESS. (The implicit
     234             :    * LexerTransition constructor will convert the result to a LexerTransition
     235             :    * as needed.)
     236             :    *
     237             :    * No more data will be delivered after this function is used.
     238             :    */
     239             :   static TerminalState
     240          66 :   TerminateSuccess()
     241             :   {
     242          66 :     return TerminalState::SUCCESS;
     243             :   }
     244             : 
     245             :   /**
     246             :    * Terminate lexing, ending up in terminal state FAILURE. (The implicit
     247             :    * LexerTransition constructor will convert the result to a LexerTransition
     248             :    * as needed.)
     249             :    *
     250             :    * No more data will be delivered after this function is used.
     251             :    */
     252             :   static TerminalState
     253           0 :   TerminateFailure()
     254             :   {
     255           0 :     return TerminalState::FAILURE;
     256             :   }
     257             : 
     258             : private:
     259             :   Transition();
     260             : };
     261             : 
     262             : /**
     263             :  * StreamingLexer is a lexing framework designed to make it simple to write
     264             :  * image decoders without worrying about the details of how the data is arriving
     265             :  * from the network.
     266             :  *
     267             :  * To use StreamingLexer:
     268             :  *
     269             :  *  - Create a State type. This should be an |enum class| listing all of the
     270             :  *    states that you can be in while lexing the image format you're trying to
     271             :  *    read.
     272             :  *
     273             :  *  - Add an instance of StreamingLexer<State> to your decoder class. Initialize
     274             :  *    it with a Transition::To() the state that you want to start lexing in, and
     275             :  *    a Transition::To() the state you'd like to use to handle truncated data.
     276             :  *
     277             :  *  - In your decoder's DoDecode() method, call Lex(), passing in the input
     278             :  *    data and length that are passed to DoDecode(). You also need to pass
     279             :  *    a lambda which dispatches to lexing code for each state based on the State
     280             :  *    value that's passed in. The lambda generally should just continue a
     281             :  *    |switch| statement that calls different methods for each State value. Each
     282             :  *    method should return a LexerTransition<State>, which the lambda should
     283             :  *    return in turn.
     284             :  *
     285             :  *  - Write the methods that actually implement lexing for your image format.
     286             :  *    These methods should return either Transition::To(), to move on to another
     287             :  *    state, or Transition::Terminate{Success,Failure}(), if lexing has
     288             :  *    terminated in either success or failure. (There are also additional
     289             :  *    transitions for unbuffered reads; see below.)
     290             :  *
     291             :  * That's the basics. The StreamingLexer will track your position in the input
     292             :  * and buffer enough data so that your lexing methods can process everything in
     293             :  * one pass. Lex() returns Yield::NEED_MORE_DATA if more data is needed, in
     294             :  * which case you should just return from DoDecode(). If lexing reaches a
     295             :  * terminal state, Lex() returns TerminalState::SUCCESS or
     296             :  * TerminalState::FAILURE, and you can check which one to determine if lexing
     297             :  * succeeded or failed and do any necessary cleanup.
     298             :  *
     299             :  * Sometimes, the input data is truncated. StreamingLexer will notify you when
     300             :  * this happens by invoking the truncated data state you passed to the
     301             :  * constructor. At this point you can attempt to recover and return
     302             :  * TerminalState::SUCCESS or TerminalState::FAILURE, depending on whether you
     303             :  * were successful. Note that you can't return anything other than a terminal
     304             :  * state in this situation, since there's no more data to read. For the same
     305             :  * reason, your truncated data state shouldn't require any data. (That is, the
     306             :  * @aSize argument you pass to Transition::To() must be zero.) Violating these
     307             :  * requirements will trigger assertions and an immediate transition to
     308             :  * TerminalState::FAILURE.
     309             :  *
     310             :  * Some lexers may want to *avoid* buffering in some cases, and just process the
     311             :  * data as it comes in. This is useful if, for example, you just want to skip
     312             :  * over a large section of data; there's no point in buffering data you're just
     313             :  * going to ignore.
     314             :  *
     315             :  * You can begin an unbuffered read with Transition::ToUnbuffered(). This works
     316             :  * a little differently than Transition::To() in that you specify *two* states.
     317             :  * The @aUnbufferedState argument specifies a state that will be called
     318             :  * repeatedly with unbuffered data, as soon as it arrives. The implementation
     319             :  * for that state should return either a transition to a terminal state, or a
     320             :  * Transition::ContinueUnbuffered() to the same @aUnbufferedState. (From a
     321             :  * technical perspective, it's not necessary to specify the state again, but
     322             :  * it's helpful to human readers.) Once the amount of data requested in the
     323             :  * original call to Transition::ToUnbuffered() has been delivered, Lex() will
     324             :  * transition to the @aNextState state specified via Transition::ToUnbuffered().
     325             :  * That state will be invoked with *no* data; it's just called to signal that
     326             :  * the unbuffered read is over.
     327             :  *
     328             :  * It's sometimes useful for a lexer to provide incremental results, rather
     329             :  * than simply running to completion and presenting all its output at once. For
     330             :  * example, when decoding animated images, it may be useful to produce each
     331             :  * frame incrementally. StreamingLexer supports this by allowing a lexer to
     332             :  * yield.
     333             :  *
     334             :  * To yield back to the caller, a state implementation can simply return
     335             :  * Transition::ToAfterYield(). ToAfterYield()'s @aNextState argument specifies
     336             :  * the next state that the lexer should transition to, just like when using
     337             :  * Transition::To(), but there are two differences. One is that Lex() will
     338             :  * return to the caller before processing any more data when it encounters a
     339             :  * yield transition. This provides an opportunity for the caller to interact with the
     340             :  * lexer's intermediate results. The second difference is that @aNextState
     341             :  * will be called with *the same data as the state that you returned
     342             :  * Transition::ToAfterYield() from*. This allows a lexer to partially consume
     343             :  * the data, return intermediate results, and then finish consuming the data
     344             :  * when @aNextState is called.
     345             :  *
     346             :  * It's also possible to yield during an unbuffered read. Just return a
     347             :  * Transition::ContinueUnbufferedAfterYield(). Just like with
     348             :  * Transition::ContinueUnbuffered(), the @aUnbufferedState must be the same as
     349             :  * the one originally passed to Transition::ToUnbuffered(). The second argument,
     350             :  * @aSize, specifies the amount of data that the lexer has already consumed.
     351             :  * When @aUnbufferedState is next invoked, it will get the same data that it
     352             :  * received previously, except that the first @aSize bytes will be excluded.
     353             :  * This makes it easy to consume unbuffered data incrementally.
     354             :  *
     355             :  * XXX(seth): We should be able to get of the |State| stuff totally once bug
     356             :  * 1198451 lands, since we can then just return a function representing the next
     357             :  * state directly.
     358             :  */
     359             : template <typename State, size_t InlineBufferSize = 16>
     360          33 : class StreamingLexer
     361             : {
     362             : public:
     363          33 :   StreamingLexer(const LexerTransition<State>& aStartState,
     364             :                  const LexerTransition<State>& aTruncatedState)
     365             :     : mTransition(TerminalState::FAILURE)
     366          33 :     , mTruncatedTransition(aTruncatedState)
     367             :   {
     368          66 :     if (!aStartState.NextStateIsTerminal() &&
     369          33 :         aStartState.ControlFlow() == ControlFlowStrategy::YIELD) {
     370             :       // Allowing a StreamingLexer to start in a yield state doesn't make sense
     371             :       // semantically (since yield states are supposed to deliver the same data
     372             :       // as previous states, and there's no previous state here), but more
     373             :       // importantly, it's necessary to advance a SourceBufferIterator at least
     374             :       // once before you can read from it, and adding the necessary checks to
     375             :       // Lex() to avoid that issue has the potential to mask real bugs. So
     376             :       // instead, it's better to forbid starting in a yield state.
     377           0 :       MOZ_ASSERT_UNREACHABLE("Starting in a yield state");
     378             :       return;
     379             :     }
     380             : 
     381          33 :     if (!aTruncatedState.NextStateIsTerminal() &&
     382           0 :           (aTruncatedState.ControlFlow() == ControlFlowStrategy::YIELD ||
     383           0 :            aTruncatedState.Buffering() == BufferingStrategy::UNBUFFERED ||
     384           0 :            aTruncatedState.Size() != 0)) {
     385             :       // The truncated state can't receive any data because, by definition,
     386             :       // there is no more data to receive. That means that yielding or an
     387             :       // unbuffered read would not make sense, and that the state must require
     388             :       // zero bytes.
     389           0 :       MOZ_ASSERT_UNREACHABLE("Truncated state makes no sense");
     390             :       return;
     391             :     }
     392             : 
     393          33 :     SetTransition(aStartState);
     394          33 :   }
     395             : 
     396             :   template <typename Func>
     397          67 :   LexerResult Lex(SourceBufferIterator& aIterator,
     398             :                   IResumable* aOnResume,
     399             :                   Func aFunc)
     400             :   {
     401          67 :     if (mTransition.NextStateIsTerminal()) {
     402             :       // We've already reached a terminal state. We never deliver any more data
     403             :       // in this case; just return the terminal state again immediately.
     404           0 :       return LexerResult(mTransition.NextStateAsTerminal());
     405             :     }
     406             : 
     407         134 :     Maybe<LexerResult> result;
     408             : 
     409             :     // If the lexer requested a yield last time, we deliver the same data again
     410             :     // before we read anything else from |aIterator|. Note that although to the
     411             :     // callers of Lex(), Yield::NEED_MORE_DATA is just another type of yield,
     412             :     // internally they're different in that we don't redeliver the same data in
     413             :     // the Yield::NEED_MORE_DATA case, and |mYieldingToState| is not set. This
     414             :     // means that for Yield::NEED_MORE_DATA, we go directly to the loop below.
     415          67 :     if (mYieldingToState) {
     416          68 :       result = mTransition.Buffering() == BufferingStrategy::UNBUFFERED
     417          68 :              ? UnbufferedReadAfterYield(aIterator, aFunc)
     418             :              : BufferedReadAfterYield(aIterator, aFunc);
     419             :     }
     420             : 
     421         181 :     while (!result) {
     422          57 :       MOZ_ASSERT_IF(mTransition.Buffering() == BufferingStrategy::UNBUFFERED,
     423             :                     mUnbufferedState);
     424             : 
     425             :       // Figure out how much we need to read.
     426          57 :       const size_t toRead = mTransition.Buffering() == BufferingStrategy::UNBUFFERED
     427          77 :                           ? mUnbufferedState->mBytesRemaining
     428          77 :                           : mTransition.Size() - mBuffer.length();
     429             : 
     430             :       // Attempt to advance the iterator by |toRead| bytes.
     431          57 :       switch (aIterator.AdvanceOrScheduleResume(toRead, aOnResume)) {
     432             :         case SourceBufferIterator::WAITING:
     433             :           // We can't continue because the rest of the data hasn't arrived from
     434             :           // the network yet. We don't have to do anything special; the
     435             :           // SourceBufferIterator will ensure that |aOnResume| gets called when
     436             :           // more data is available.
     437           0 :           result = Some(LexerResult(Yield::NEED_MORE_DATA));
     438           0 :           break;
     439             : 
     440             :         case SourceBufferIterator::COMPLETE:
     441             :           // The data is truncated; if not, the lexer would've reached a
     442             :           // terminal state by now. We only get to
     443             :           // SourceBufferIterator::COMPLETE after every byte of data has been
     444             :           // delivered to the lexer.
     445           0 :           result = Truncated(aIterator, aFunc);
     446           0 :           break;
     447             : 
     448             :         case SourceBufferIterator::READY:
     449             :           // Process the new data that became available.
     450          57 :           MOZ_ASSERT(aIterator.Data());
     451             : 
     452         114 :           result = mTransition.Buffering() == BufferingStrategy::UNBUFFERED
     453         114 :                  ? UnbufferedRead(aIterator, aFunc)
     454             :                  : BufferedRead(aIterator, aFunc);
     455          57 :           break;
     456             : 
     457             :         default:
     458           0 :           MOZ_ASSERT_UNREACHABLE("Unknown SourceBufferIterator state");
     459             :           result = SetTransition(Transition::TerminateFailure());
     460             :       }
     461             :     };
     462             : 
     463          67 :     return *result;
     464             :   }
     465             : 
     466             : private:
     467             :   template <typename Func>
     468          37 :   Maybe<LexerResult> UnbufferedRead(SourceBufferIterator& aIterator, Func aFunc)
     469             :   {
     470          37 :     MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::UNBUFFERED);
     471          37 :     MOZ_ASSERT(mUnbufferedState);
     472          37 :     MOZ_ASSERT(!mYieldingToState);
     473          37 :     MOZ_ASSERT(mBuffer.empty(),
     474             :                "Buffered read at the same time as unbuffered read?");
     475          37 :     MOZ_ASSERT(aIterator.Length() <= mUnbufferedState->mBytesRemaining,
     476             :                "Read too much data during unbuffered read?");
     477          37 :     MOZ_ASSERT(mUnbufferedState->mBytesConsumedInCurrentChunk == 0,
     478             :                "Already consumed data in the current chunk, but not yielding?");
     479             : 
     480          37 :     if (mUnbufferedState->mBytesRemaining == 0) {
     481             :       // We're done with the unbuffered read, so transition to the next state.
     482           3 :       return SetTransition(aFunc(mTransition.NextState(), nullptr, 0));
     483             :     }
     484             : 
     485             :     return ContinueUnbufferedRead(aIterator.Data(), aIterator.Length(),
     486          34 :                                   aIterator.Length(), aFunc);
     487             :   }
     488             : 
     489             :   template <typename Func>
     490          34 :   Maybe<LexerResult> UnbufferedReadAfterYield(SourceBufferIterator& aIterator, Func aFunc)
     491             :   {
     492          34 :     MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::UNBUFFERED);
     493          34 :     MOZ_ASSERT(mUnbufferedState);
     494          34 :     MOZ_ASSERT(mYieldingToState);
     495          34 :     MOZ_ASSERT(mBuffer.empty(),
     496             :                "Buffered read at the same time as unbuffered read?");
     497          34 :     MOZ_ASSERT(aIterator.Length() <= mUnbufferedState->mBytesRemaining,
     498             :                "Read too much data during unbuffered read?");
     499          34 :     MOZ_ASSERT(mUnbufferedState->mBytesConsumedInCurrentChunk <= aIterator.Length(),
     500             :                "Consumed more data than the current chunk holds?");
     501          34 :     MOZ_ASSERT(mTransition.UnbufferedState() == *mYieldingToState);
     502             : 
     503          34 :     mYieldingToState = Nothing();
     504             : 
     505          34 :     if (mUnbufferedState->mBytesRemaining == 0) {
     506             :       // We're done with the unbuffered read, so transition to the next state.
     507           0 :       return SetTransition(aFunc(mTransition.NextState(), nullptr, 0));
     508             :     }
     509             : 
     510             :     // Since we've yielded, we may have already consumed some data in this
     511             :     // chunk. Make the necessary adjustments. (Note that the std::min call is
     512             :     // just belt-and-suspenders to keep this code memory safe even if there's
     513             :     // a bug somewhere.)
     514             :     const size_t toSkip =
     515          34 :       std::min(mUnbufferedState->mBytesConsumedInCurrentChunk, aIterator.Length());
     516          34 :     const char* data = aIterator.Data() + toSkip;
     517          34 :     const size_t length = aIterator.Length() - toSkip;
     518             : 
     519             :     // If |length| is zero, we've hit the end of the current chunk. This only
     520             :     // happens if we yield right at the end of a chunk. Rather than call |aFunc|
     521             :     // with a |length| of zero bytes (which seems potentially surprising to
     522             :     // decoder authors), we go ahead and read more data.
     523          34 :     if (length == 0) {
     524           0 :       return FinishCurrentChunkOfUnbufferedRead(aIterator.Length());
     525             :     }
     526             : 
     527          34 :     return ContinueUnbufferedRead(data, length, aIterator.Length(), aFunc);
     528             :   }
     529             : 
     530             :   template <typename Func>
     531          68 :   Maybe<LexerResult> ContinueUnbufferedRead(const char* aData,
     532             :                                             size_t aLength,
     533             :                                             size_t aChunkLength,
     534             :                                             Func aFunc)
     535             :   {
     536             :     // Call aFunc with the unbuffered state to indicate that we're in the
     537             :     // middle of an unbuffered read. We enforce that any state transition
     538             :     // passed back to us is either a terminal state or takes us back to the
     539             :     // unbuffered state.
     540             :     LexerTransition<State> unbufferedTransition =
     541         136 :       aFunc(mTransition.UnbufferedState(), aData, aLength);
     542             : 
     543             :     // If we reached a terminal state, we're done.
     544          68 :     if (unbufferedTransition.NextStateIsTerminal()) {
     545          31 :       return SetTransition(unbufferedTransition);
     546             :     }
     547             : 
     548          37 :     MOZ_ASSERT(mTransition.UnbufferedState() ==
     549             :                  unbufferedTransition.NextState());
     550             : 
     551             :     // Perform bookkeeping.
     552          37 :     if (unbufferedTransition.ControlFlow() == ControlFlowStrategy::YIELD) {
     553          34 :       mUnbufferedState->mBytesConsumedInCurrentChunk += unbufferedTransition.Size();
     554          34 :       return SetTransition(unbufferedTransition);
     555             :     }
     556             : 
     557           3 :     MOZ_ASSERT(unbufferedTransition.Size() == 0);
     558           3 :     return FinishCurrentChunkOfUnbufferedRead(aChunkLength);
     559             :   }
     560             : 
     561           3 :   Maybe<LexerResult> FinishCurrentChunkOfUnbufferedRead(size_t aChunkLength)
     562             :   {
     563             :     // We've finished an unbuffered read of a chunk of length |aChunkLength|, so
     564             :     // update |myBytesRemaining| to reflect that we're |aChunkLength| closer to
     565             :     // the end of the unbuffered read. (The std::min call is just
     566             :     // belt-and-suspenders to keep this code memory safe even if there's a bug
     567             :     // somewhere.)
     568           3 :     mUnbufferedState->mBytesRemaining -=
     569           3 :       std::min(mUnbufferedState->mBytesRemaining, aChunkLength);
     570             : 
     571             :     // Since we're moving on to a new chunk, we can forget about the count of
     572             :     // bytes consumed by yielding in the current chunk.
     573           3 :     mUnbufferedState->mBytesConsumedInCurrentChunk = 0;
     574             : 
     575           3 :     return Nothing();  // Keep processing.
     576             :   }
     577             : 
     578             :   template <typename Func>
     579          20 :   Maybe<LexerResult> BufferedRead(SourceBufferIterator& aIterator, Func aFunc)
     580             :   {
     581          20 :     MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
     582          20 :     MOZ_ASSERT(!mYieldingToState);
     583          20 :     MOZ_ASSERT(!mUnbufferedState,
     584             :                "Buffered read at the same time as unbuffered read?");
     585          20 :     MOZ_ASSERT(mBuffer.length() < mTransition.Size() ||
     586             :                (mBuffer.length() == 0 && mTransition.Size() == 0),
     587             :                "Buffered more than we needed?");
     588             : 
     589             :     // If we have all the data, we don't actually need to buffer anything.
     590          20 :     if (mBuffer.empty() && aIterator.Length() == mTransition.Size()) {
     591             :       return SetTransition(aFunc(mTransition.NextState(),
     592             :                                  aIterator.Data(),
     593          20 :                                  aIterator.Length()));
     594             :     }
     595             : 
     596             :     // We do need to buffer, so make sure the buffer has enough capacity. We
     597             :     // deliberately wait until we know for sure we need to buffer to call
     598             :     // reserve() since it could require memory allocation.
     599           0 :     if (!mBuffer.reserve(mTransition.Size())) {
     600           0 :       return SetTransition(Transition::TerminateFailure());
     601             :     }
     602             : 
     603             :     // Append the new data we just got to the buffer.
     604           0 :     if (!mBuffer.append(aIterator.Data(), aIterator.Length())) {
     605           0 :       return SetTransition(Transition::TerminateFailure());
     606             :     }
     607             : 
     608           0 :     if (mBuffer.length() != mTransition.Size()) {
     609           0 :       return Nothing();  // Keep processing.
     610             :     }
     611             : 
     612             :     // We've buffered everything, so transition to the next state.
     613           0 :     return SetTransition(aFunc(mTransition.NextState(),
     614             :                                mBuffer.begin(),
     615           0 :                                mBuffer.length()));
     616             :   }
     617             : 
     618             :   template <typename Func>
     619           0 :   Maybe<LexerResult> BufferedReadAfterYield(SourceBufferIterator& aIterator,
     620             :                                             Func aFunc)
     621             :   {
     622           0 :     MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
     623           0 :     MOZ_ASSERT(mYieldingToState);
     624           0 :     MOZ_ASSERT(!mUnbufferedState,
     625             :                "Buffered read at the same time as unbuffered read?");
     626           0 :     MOZ_ASSERT(mBuffer.length() <= mTransition.Size(),
     627             :                "Buffered more than we needed?");
     628             : 
     629           0 :     State nextState = Move(*mYieldingToState);
     630             : 
     631             :     // After a yield, we need to take the same data that we delivered to the
     632             :     // last state, and deliver it again to the new state. We know that this is
     633             :     // happening right at a state transition, and that the last state was a
     634             :     // buffered read, so there are two cases:
     635             : 
     636             :     // 1. We got the data from the SourceBufferIterator directly.
     637           0 :     if (mBuffer.empty() && aIterator.Length() == mTransition.Size()) {
     638             :       return SetTransition(aFunc(nextState,
     639             :                                  aIterator.Data(),
     640           0 :                                  aIterator.Length()));
     641             :     }
     642             : 
     643             :     // 2. We got the data from the buffer.
     644           0 :     if (mBuffer.length() == mTransition.Size()) {
     645           0 :       return SetTransition(aFunc(nextState,
     646             :                                  mBuffer.begin(),
     647           0 :                                  mBuffer.length()));
     648             :     }
     649             : 
     650             :     // Anything else indicates a bug.
     651           0 :     MOZ_ASSERT_UNREACHABLE("Unexpected state encountered during yield");
     652             :     return SetTransition(Transition::TerminateFailure());
     653             :   }
     654             : 
     655             :   template <typename Func>
     656           0 :   Maybe<LexerResult> Truncated(SourceBufferIterator& aIterator,
     657             :                                Func aFunc)
     658             :   {
     659             :     // The data is truncated. Let the lexer clean up and decide which terminal
     660             :     // state we should end up in.
     661             :     LexerTransition<State> transition
     662             :       = mTruncatedTransition.NextStateIsTerminal()
     663           0 :       ? mTruncatedTransition
     664           0 :       : aFunc(mTruncatedTransition.NextState(), nullptr, 0);
     665             : 
     666           0 :     if (!transition.NextStateIsTerminal()) {
     667           0 :       MOZ_ASSERT_UNREACHABLE("Truncated state didn't lead to terminal state?");
     668             :       return SetTransition(Transition::TerminateFailure());
     669             :     }
     670             : 
     671             :     // If the SourceBuffer was completed with a failing state, we end in
     672             :     // TerminalState::FAILURE no matter what. This only happens in exceptional
     673             :     // situations like SourceBuffer itself encountering a failure due to OOM.
     674           0 :     if (NS_FAILED(aIterator.CompletionStatus())) {
     675           0 :       return SetTransition(Transition::TerminateFailure());
     676             :     }
     677             : 
     678           0 :     return SetTransition(transition);
     679             :   }
     680             : 
     681         121 :   Maybe<LexerResult> SetTransition(const LexerTransition<State>& aTransition)
     682             :   {
     683             :     // There should be no transitions while we're buffering for a buffered read
     684             :     // unless they're to terminal states. (The terminal state transitions would
     685             :     // generally be triggered by error handling code.)
     686         121 :     MOZ_ASSERT_IF(!mBuffer.empty(),
     687             :                   aTransition.NextStateIsTerminal() ||
     688             :                   mBuffer.length() == mTransition.Size());
     689             : 
     690             :     // Similarly, the only transitions allowed in the middle of an unbuffered
     691             :     // read are to a terminal state, or a yield to the same state. Otherwise, we
     692             :     // should remain in the same state until the unbuffered read completes.
     693         121 :     MOZ_ASSERT_IF(mUnbufferedState,
     694             :                   aTransition.NextStateIsTerminal() ||
     695             :                   (aTransition.ControlFlow() == ControlFlowStrategy::YIELD &&
     696             :                    aTransition.NextState() == mTransition.UnbufferedState()) ||
     697             :                   mUnbufferedState->mBytesRemaining == 0);
     698             : 
     699             :     // If this transition is a yield, save the next state and return. We'll
     700             :     // handle the rest when Lex() gets called again.
     701         209 :     if (!aTransition.NextStateIsTerminal() &&
     702          88 :         aTransition.ControlFlow() == ControlFlowStrategy::YIELD) {
     703          34 :       mYieldingToState = Some(aTransition.NextState());
     704          34 :       return Some(LexerResult(Yield::OUTPUT_AVAILABLE));
     705             :     }
     706             : 
     707             :     // Update our transition.
     708          87 :     mTransition = aTransition;
     709             : 
     710             :     // Get rid of anything left over from the previous state.
     711          87 :     mBuffer.clear();
     712          87 :     mYieldingToState = Nothing();
     713          87 :     mUnbufferedState = Nothing();
     714             : 
     715             :     // If we reached a terminal state, let the caller know.
     716          87 :     if (mTransition.NextStateIsTerminal()) {
     717          33 :       return Some(LexerResult(mTransition.NextStateAsTerminal()));
     718             :     }
     719             : 
     720             :     // If we're entering an unbuffered state, record how long we'll stay in it.
     721          54 :     if (mTransition.Buffering() == BufferingStrategy::UNBUFFERED) {
     722          34 :       mUnbufferedState.emplace(mTransition.Size());
     723             :     }
     724             : 
     725          54 :     return Nothing();  // Keep processing.
     726             :   }
     727             : 
     728             :   // State that tracks our position within an unbuffered read.
     729             :   struct UnbufferedState
     730             :   {
     731          34 :     explicit UnbufferedState(size_t aBytesRemaining)
     732             :       : mBytesRemaining(aBytesRemaining)
     733          34 :       , mBytesConsumedInCurrentChunk(0)
     734          34 :     { }
     735             : 
     736             :     size_t mBytesRemaining;
     737             :     size_t mBytesConsumedInCurrentChunk;
     738             :   };
     739             : 
     740             :   Vector<char, InlineBufferSize> mBuffer;
     741             :   LexerTransition<State> mTransition;
     742             :   const LexerTransition<State> mTruncatedTransition;
     743             :   Maybe<State> mYieldingToState;
     744             :   Maybe<UnbufferedState> mUnbufferedState;
     745             : };
     746             : 
     747             : } // namespace image
     748             : } // namespace mozilla
     749             : 
     750             : #endif // mozilla_image_StreamingLexer_h

Generated by: LCOV version 1.13