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/. */
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
13 #ifndef mozilla_image_StreamingLexer_h
14 #define mozilla_image_StreamingLexer_h
20 #include "SourceBuffer.h"
21 #include "mozilla/Assertions.h"
22 #include "mozilla/Attributes.h"
23 #include "mozilla/Maybe.h"
24 #include "mozilla/Variant.h"
25 #include "mozilla/Vector.h"
30 /// Buffering behaviors for StreamingLexer transitions.
31 enum class BufferingStrategy
{
32 BUFFERED
, // Data will be buffered and processed in one chunk.
33 UNBUFFERED
// Data will be processed as it arrives, in multiple chunks.
36 /// Control flow behaviors for StreamingLexer transitions.
37 enum class ControlFlowStrategy
{
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.
42 /// Possible terminal states for the lexer.
43 enum class TerminalState
{ SUCCESS
, FAILURE
};
45 /// Possible yield reasons for the lexer.
47 NEED_MORE_DATA
, // The lexer cannot continue without more data.
48 OUTPUT_AVAILABLE
// There is output available for the caller to consume.
51 /// The result of a call to StreamingLexer::Lex().
52 typedef Variant
<TerminalState
, Yield
> LexerResult
;
55 * LexerTransition is a type used to give commands to the lexing framework.
56 * Code that uses StreamingLexer can create LexerTransition values using the
57 * static methods on Transition, and then return them to the lexing framework
60 template <typename State
>
61 class LexerTransition
{
63 // This is implicit so that Terminate{Success,Failure}() can return a
64 // TerminalState and have it implicitly converted to a
65 // LexerTransition<State>, which avoids the need for a "<State>"
66 // qualification to the Terminate{Success,Failure}() callsite.
67 MOZ_IMPLICIT
LexerTransition(TerminalState aFinalState
)
68 : mNextState(aFinalState
) {}
70 bool NextStateIsTerminal() const {
71 return mNextState
.template is
<TerminalState
>();
74 TerminalState
NextStateAsTerminal() const {
75 return mNextState
.template as
<TerminalState
>();
78 State
NextState() const {
79 return mNextState
.template as
<NonTerminalState
>().mState
;
82 State
UnbufferedState() const {
83 return *mNextState
.template as
<NonTerminalState
>().mUnbufferedState
;
87 return mNextState
.template as
<NonTerminalState
>().mSize
;
90 BufferingStrategy
Buffering() const {
91 return mNextState
.template as
<NonTerminalState
>().mBufferingStrategy
;
94 ControlFlowStrategy
ControlFlow() const {
95 return mNextState
.template as
<NonTerminalState
>().mControlFlowStrategy
;
99 friend struct Transition
;
101 LexerTransition(State aNextState
, const Maybe
<State
>& aUnbufferedState
,
102 size_t aSize
, BufferingStrategy aBufferingStrategy
,
103 ControlFlowStrategy aControlFlowStrategy
)
104 : mNextState(NonTerminalState(aNextState
, aUnbufferedState
, aSize
,
105 aBufferingStrategy
, aControlFlowStrategy
)) {
108 struct NonTerminalState
{
110 Maybe
<State
> mUnbufferedState
;
112 BufferingStrategy mBufferingStrategy
;
113 ControlFlowStrategy mControlFlowStrategy
;
115 NonTerminalState(State aState
, const Maybe
<State
>& aUnbufferedState
,
116 size_t aSize
, BufferingStrategy aBufferingStrategy
,
117 ControlFlowStrategy aControlFlowStrategy
)
119 mUnbufferedState(aUnbufferedState
),
121 mBufferingStrategy(aBufferingStrategy
),
122 mControlFlowStrategy(aControlFlowStrategy
) {
123 MOZ_ASSERT_IF(mBufferingStrategy
== BufferingStrategy::UNBUFFERED
,
125 MOZ_ASSERT_IF(mUnbufferedState
,
126 mBufferingStrategy
== BufferingStrategy::UNBUFFERED
);
130 Variant
<NonTerminalState
, TerminalState
> mNextState
;
134 /// Transition to @aNextState, buffering @aSize bytes of data.
135 template <typename State
>
136 static LexerTransition
<State
> To(const State
& aNextState
, size_t aSize
) {
137 return LexerTransition
<State
>(aNextState
, Nothing(), aSize
,
138 BufferingStrategy::BUFFERED
,
139 ControlFlowStrategy::CONTINUE
);
142 /// Yield to the caller, transitioning to @aNextState when Lex() is next
143 /// invoked. The same data that was delivered for the current state will be
145 template <typename State
>
146 static LexerTransition
<State
> ToAfterYield(const State
& aNextState
) {
147 return LexerTransition
<State
>(aNextState
, Nothing(), 0,
148 BufferingStrategy::BUFFERED
,
149 ControlFlowStrategy::YIELD
);
153 * Transition to @aNextState via @aUnbufferedState, reading @aSize bytes of
156 * The unbuffered data will be delivered in state @aUnbufferedState, which may
157 * be invoked repeatedly until all @aSize bytes have been delivered. Then,
158 * @aNextState will be invoked with no data. No state transitions are allowed
159 * from @aUnbufferedState except for transitions to a terminal state, so
160 * @aNextState will always be reached unless lexing terminates early.
162 template <typename State
>
163 static LexerTransition
<State
> ToUnbuffered(const State
& aNextState
,
164 const State
& aUnbufferedState
,
166 return LexerTransition
<State
>(aNextState
, Some(aUnbufferedState
), aSize
,
167 BufferingStrategy::UNBUFFERED
,
168 ControlFlowStrategy::CONTINUE
);
172 * Continue receiving unbuffered data. @aUnbufferedState should be the same
173 * state as the @aUnbufferedState specified in the preceding call to
176 * This should be used during an unbuffered read initiated by ToUnbuffered().
178 template <typename State
>
179 static LexerTransition
<State
> ContinueUnbuffered(
180 const State
& aUnbufferedState
) {
181 return LexerTransition
<State
>(aUnbufferedState
, Nothing(), 0,
182 BufferingStrategy::BUFFERED
,
183 ControlFlowStrategy::CONTINUE
);
187 * Continue receiving unbuffered data. @aUnbufferedState should be the same
188 * state as the @aUnbufferedState specified in the preceding call to
189 * ToUnbuffered(). @aSize indicates the amount of data that has already been
190 * consumed; the next state will receive the same data that was delivered to
191 * the current state, without the first @aSize bytes.
193 * This should be used during an unbuffered read initiated by ToUnbuffered().
195 template <typename State
>
196 static LexerTransition
<State
> ContinueUnbufferedAfterYield(
197 const State
& aUnbufferedState
, size_t aSize
) {
198 return LexerTransition
<State
>(aUnbufferedState
, Nothing(), aSize
,
199 BufferingStrategy::BUFFERED
,
200 ControlFlowStrategy::YIELD
);
204 * Terminate lexing, ending up in terminal state SUCCESS. (The implicit
205 * LexerTransition constructor will convert the result to a LexerTransition
208 * No more data will be delivered after this function is used.
210 static TerminalState
TerminateSuccess() { return TerminalState::SUCCESS
; }
213 * Terminate lexing, ending up in terminal state FAILURE. (The implicit
214 * LexerTransition constructor will convert the result to a LexerTransition
217 * No more data will be delivered after this function is used.
219 static TerminalState
TerminateFailure() { return TerminalState::FAILURE
; }
226 * StreamingLexer is a lexing framework designed to make it simple to write
227 * image decoders without worrying about the details of how the data is arriving
230 * To use StreamingLexer:
232 * - Create a State type. This should be an |enum class| listing all of the
233 * states that you can be in while lexing the image format you're trying to
236 * - Add an instance of StreamingLexer<State> to your decoder class. Initialize
237 * it with a Transition::To() the state that you want to start lexing in, and
238 * a Transition::To() the state you'd like to use to handle truncated data.
240 * - In your decoder's DoDecode() method, call Lex(), passing in the input
241 * data and length that are passed to DoDecode(). You also need to pass
242 * a lambda which dispatches to lexing code for each state based on the State
243 * value that's passed in. The lambda generally should just continue a
244 * |switch| statement that calls different methods for each State value. Each
245 * method should return a LexerTransition<State>, which the lambda should
248 * - Write the methods that actually implement lexing for your image format.
249 * These methods should return either Transition::To(), to move on to another
250 * state, or Transition::Terminate{Success,Failure}(), if lexing has
251 * terminated in either success or failure. (There are also additional
252 * transitions for unbuffered reads; see below.)
254 * That's the basics. The StreamingLexer will track your position in the input
255 * and buffer enough data so that your lexing methods can process everything in
256 * one pass. Lex() returns Yield::NEED_MORE_DATA if more data is needed, in
257 * which case you should just return from DoDecode(). If lexing reaches a
258 * terminal state, Lex() returns TerminalState::SUCCESS or
259 * TerminalState::FAILURE, and you can check which one to determine if lexing
260 * succeeded or failed and do any necessary cleanup.
262 * Sometimes, the input data is truncated. StreamingLexer will notify you when
263 * this happens by invoking the truncated data state you passed to the
264 * constructor. At this point you can attempt to recover and return
265 * TerminalState::SUCCESS or TerminalState::FAILURE, depending on whether you
266 * were successful. Note that you can't return anything other than a terminal
267 * state in this situation, since there's no more data to read. For the same
268 * reason, your truncated data state shouldn't require any data. (That is, the
269 * @aSize argument you pass to Transition::To() must be zero.) Violating these
270 * requirements will trigger assertions and an immediate transition to
271 * TerminalState::FAILURE.
273 * Some lexers may want to *avoid* buffering in some cases, and just process the
274 * data as it comes in. This is useful if, for example, you just want to skip
275 * over a large section of data; there's no point in buffering data you're just
278 * You can begin an unbuffered read with Transition::ToUnbuffered(). This works
279 * a little differently than Transition::To() in that you specify *two* states.
280 * The @aUnbufferedState argument specifies a state that will be called
281 * repeatedly with unbuffered data, as soon as it arrives. The implementation
282 * for that state should return either a transition to a terminal state, or a
283 * Transition::ContinueUnbuffered() to the same @aUnbufferedState. (From a
284 * technical perspective, it's not necessary to specify the state again, but
285 * it's helpful to human readers.) Once the amount of data requested in the
286 * original call to Transition::ToUnbuffered() has been delivered, Lex() will
287 * transition to the @aNextState state specified via Transition::ToUnbuffered().
288 * That state will be invoked with *no* data; it's just called to signal that
289 * the unbuffered read is over.
291 * It's sometimes useful for a lexer to provide incremental results, rather
292 * than simply running to completion and presenting all its output at once. For
293 * example, when decoding animated images, it may be useful to produce each
294 * frame incrementally. StreamingLexer supports this by allowing a lexer to
297 * To yield back to the caller, a state implementation can simply return
298 * Transition::ToAfterYield(). ToAfterYield()'s @aNextState argument specifies
299 * the next state that the lexer should transition to, just like when using
300 * Transition::To(), but there are two differences. One is that Lex() will
301 * return to the caller before processing any more data when it encounters a
302 * yield transition. This provides an opportunity for the caller to interact
303 * with the lexer's intermediate results. The second difference is that
304 * @aNextState will be called with *the same data as the state that you returned
305 * Transition::ToAfterYield() from*. This allows a lexer to partially consume
306 * the data, return intermediate results, and then finish consuming the data
307 * when @aNextState is called.
309 * It's also possible to yield during an unbuffered read. Just return a
310 * Transition::ContinueUnbufferedAfterYield(). Just like with
311 * Transition::ContinueUnbuffered(), the @aUnbufferedState must be the same as
312 * the one originally passed to Transition::ToUnbuffered(). The second argument,
313 * @aSize, specifies the amount of data that the lexer has already consumed.
314 * When @aUnbufferedState is next invoked, it will get the same data that it
315 * received previously, except that the first @aSize bytes will be excluded.
316 * This makes it easy to consume unbuffered data incrementally.
318 * XXX(seth): We should be able to get of the |State| stuff totally once bug
319 * 1198451 lands, since we can then just return a function representing the next
322 template <typename State
, size_t InlineBufferSize
= 16>
323 class StreamingLexer
{
325 StreamingLexer(const LexerTransition
<State
>& aStartState
,
326 const LexerTransition
<State
>& aTruncatedState
)
327 : mTransition(TerminalState::FAILURE
),
328 mTruncatedTransition(aTruncatedState
) {
329 if (!aStartState
.NextStateIsTerminal() &&
330 aStartState
.ControlFlow() == ControlFlowStrategy::YIELD
) {
331 // Allowing a StreamingLexer to start in a yield state doesn't make sense
332 // semantically (since yield states are supposed to deliver the same data
333 // as previous states, and there's no previous state here), but more
334 // importantly, it's necessary to advance a SourceBufferIterator at least
335 // once before you can read from it, and adding the necessary checks to
336 // Lex() to avoid that issue has the potential to mask real bugs. So
337 // instead, it's better to forbid starting in a yield state.
338 MOZ_ASSERT_UNREACHABLE("Starting in a yield state");
342 if (!aTruncatedState
.NextStateIsTerminal() &&
343 (aTruncatedState
.ControlFlow() == ControlFlowStrategy::YIELD
||
344 aTruncatedState
.Buffering() == BufferingStrategy::UNBUFFERED
||
345 aTruncatedState
.Size() != 0)) {
346 // The truncated state can't receive any data because, by definition,
347 // there is no more data to receive. That means that yielding or an
348 // unbuffered read would not make sense, and that the state must require
350 MOZ_ASSERT_UNREACHABLE("Truncated state makes no sense");
354 SetTransition(aStartState
);
358 * From the given SourceBufferIterator, aIterator, create a new iterator at
359 * the same position, with the given read limit, aReadLimit. The read limit
360 * applies after adjusting for the position. If the given iterator has been
361 * advanced, but required buffering inside StreamingLexer, the position
362 * of the cloned iterator will be at the beginning of buffered data; this
363 * should match the perspective of the caller.
365 Maybe
<SourceBufferIterator
> Clone(SourceBufferIterator
& aIterator
,
366 size_t aReadLimit
) const {
367 // In order to advance to the current position of the iterator from the
368 // perspective of the caller, we need to take into account if we are
370 size_t pos
= aIterator
.Position();
371 if (!mBuffer
.empty()) {
372 pos
+= aIterator
.Length();
373 MOZ_ASSERT(pos
> mBuffer
.length());
374 pos
-= mBuffer
.length();
377 size_t readLimit
= aReadLimit
;
378 if (aReadLimit
!= SIZE_MAX
) {
382 SourceBufferIterator other
= aIterator
.Owner()->Iterator(readLimit
);
384 // Since the current iterator has already advanced to this point, we
385 // know that the state can only be READY or COMPLETE. That does not mean
386 // everything is stored in a single chunk, and may require multiple Advance
387 // calls to get where we want to be.
388 SourceBufferIterator::State state
;
390 state
= other
.Advance(pos
);
391 if (state
!= SourceBufferIterator::READY
) {
392 // The only way we should fail to advance over data we already seen is
393 // if we hit an error while inserting data into the buffer. This will
394 // cause an early exit.
395 MOZ_ASSERT(NS_FAILED(other
.CompletionStatus()));
398 MOZ_ASSERT(pos
>= other
.Length());
399 pos
-= other
.Length();
402 // Force the data pointer to be where we expect it to be.
403 state
= other
.Advance(0);
404 if (state
!= SourceBufferIterator::READY
) {
405 // The current position could be the end of the buffer, in which case
406 // there is no point cloning with no more data to read.
407 MOZ_ASSERT(state
== SourceBufferIterator::COMPLETE
);
410 return Some(std::move(other
));
413 template <typename Func
>
414 LexerResult
Lex(SourceBufferIterator
& aIterator
, IResumable
* aOnResume
,
416 if (mTransition
.NextStateIsTerminal()) {
417 // We've already reached a terminal state. We never deliver any more data
418 // in this case; just return the terminal state again immediately.
419 return LexerResult(mTransition
.NextStateAsTerminal());
422 Maybe
<LexerResult
> result
;
424 // If the lexer requested a yield last time, we deliver the same data again
425 // before we read anything else from |aIterator|. Note that although to the
426 // callers of Lex(), Yield::NEED_MORE_DATA is just another type of yield,
427 // internally they're different in that we don't redeliver the same data in
428 // the Yield::NEED_MORE_DATA case, and |mYieldingToState| is not set. This
429 // means that for Yield::NEED_MORE_DATA, we go directly to the loop below.
430 if (mYieldingToState
) {
431 result
= mTransition
.Buffering() == BufferingStrategy::UNBUFFERED
432 ? UnbufferedReadAfterYield(aIterator
, aFunc
)
433 : BufferedReadAfterYield(aIterator
, aFunc
);
437 MOZ_ASSERT_IF(mTransition
.Buffering() == BufferingStrategy::UNBUFFERED
,
440 // Figure out how much we need to read.
441 const size_t toRead
=
442 mTransition
.Buffering() == BufferingStrategy::UNBUFFERED
443 ? mUnbufferedState
->mBytesRemaining
444 : mTransition
.Size() - mBuffer
.length();
446 // Attempt to advance the iterator by |toRead| bytes.
447 switch (aIterator
.AdvanceOrScheduleResume(toRead
, aOnResume
)) {
448 case SourceBufferIterator::WAITING
:
449 // We can't continue because the rest of the data hasn't arrived from
450 // the network yet. We don't have to do anything special; the
451 // SourceBufferIterator will ensure that |aOnResume| gets called when
452 // more data is available.
453 result
= Some(LexerResult(Yield::NEED_MORE_DATA
));
456 case SourceBufferIterator::COMPLETE
:
457 // The data is truncated; if not, the lexer would've reached a
458 // terminal state by now. We only get to
459 // SourceBufferIterator::COMPLETE after every byte of data has been
460 // delivered to the lexer.
461 result
= Truncated(aIterator
, aFunc
);
464 case SourceBufferIterator::READY
:
465 // Process the new data that became available.
466 MOZ_ASSERT(aIterator
.Data());
468 result
= mTransition
.Buffering() == BufferingStrategy::UNBUFFERED
469 ? UnbufferedRead(aIterator
, aFunc
)
470 : BufferedRead(aIterator
, aFunc
);
474 MOZ_ASSERT_UNREACHABLE("Unknown SourceBufferIterator state");
475 result
= SetTransition(Transition::TerminateFailure());
483 template <typename Func
>
484 Maybe
<LexerResult
> UnbufferedRead(SourceBufferIterator
& aIterator
,
486 MOZ_ASSERT(mTransition
.Buffering() == BufferingStrategy::UNBUFFERED
);
487 MOZ_ASSERT(mUnbufferedState
);
488 MOZ_ASSERT(!mYieldingToState
);
489 MOZ_ASSERT(mBuffer
.empty(),
490 "Buffered read at the same time as unbuffered read?");
491 MOZ_ASSERT(aIterator
.Length() <= mUnbufferedState
->mBytesRemaining
,
492 "Read too much data during unbuffered read?");
493 MOZ_ASSERT(mUnbufferedState
->mBytesConsumedInCurrentChunk
== 0,
494 "Already consumed data in the current chunk, but not yielding?");
496 if (mUnbufferedState
->mBytesRemaining
== 0) {
497 // We're done with the unbuffered read, so transition to the next state.
498 return SetTransition(aFunc(mTransition
.NextState(), nullptr, 0));
501 return ContinueUnbufferedRead(aIterator
.Data(), aIterator
.Length(),
502 aIterator
.Length(), aFunc
);
505 template <typename Func
>
506 Maybe
<LexerResult
> UnbufferedReadAfterYield(SourceBufferIterator
& aIterator
,
508 MOZ_ASSERT(mTransition
.Buffering() == BufferingStrategy::UNBUFFERED
);
509 MOZ_ASSERT(mUnbufferedState
);
510 MOZ_ASSERT(mYieldingToState
);
511 MOZ_ASSERT(mBuffer
.empty(),
512 "Buffered read at the same time as unbuffered read?");
513 MOZ_ASSERT(aIterator
.Length() <= mUnbufferedState
->mBytesRemaining
,
514 "Read too much data during unbuffered read?");
516 mUnbufferedState
->mBytesConsumedInCurrentChunk
<= aIterator
.Length(),
517 "Consumed more data than the current chunk holds?");
518 MOZ_ASSERT(mTransition
.UnbufferedState() == *mYieldingToState
);
520 mYieldingToState
= Nothing();
522 if (mUnbufferedState
->mBytesRemaining
== 0) {
523 // We're done with the unbuffered read, so transition to the next state.
524 return SetTransition(aFunc(mTransition
.NextState(), nullptr, 0));
527 // Since we've yielded, we may have already consumed some data in this
528 // chunk. Make the necessary adjustments. (Note that the std::min call is
529 // just belt-and-suspenders to keep this code memory safe even if there's
531 const size_t toSkip
= std::min(
532 mUnbufferedState
->mBytesConsumedInCurrentChunk
, aIterator
.Length());
533 const char* data
= aIterator
.Data() + toSkip
;
534 const size_t length
= aIterator
.Length() - toSkip
;
536 // If |length| is zero, we've hit the end of the current chunk. This only
537 // happens if we yield right at the end of a chunk. Rather than call |aFunc|
538 // with a |length| of zero bytes (which seems potentially surprising to
539 // decoder authors), we go ahead and read more data.
541 return FinishCurrentChunkOfUnbufferedRead(aIterator
.Length());
544 return ContinueUnbufferedRead(data
, length
, aIterator
.Length(), aFunc
);
547 template <typename Func
>
548 Maybe
<LexerResult
> ContinueUnbufferedRead(const char* aData
, size_t aLength
,
549 size_t aChunkLength
, Func aFunc
) {
550 // Call aFunc with the unbuffered state to indicate that we're in the
551 // middle of an unbuffered read. We enforce that any state transition
552 // passed back to us is either a terminal state or takes us back to the
554 LexerTransition
<State
> unbufferedTransition
=
555 aFunc(mTransition
.UnbufferedState(), aData
, aLength
);
557 // If we reached a terminal state, we're done.
558 if (unbufferedTransition
.NextStateIsTerminal()) {
559 return SetTransition(unbufferedTransition
);
562 MOZ_ASSERT(mTransition
.UnbufferedState() ==
563 unbufferedTransition
.NextState());
565 // Perform bookkeeping.
566 if (unbufferedTransition
.ControlFlow() == ControlFlowStrategy::YIELD
) {
567 mUnbufferedState
->mBytesConsumedInCurrentChunk
+=
568 unbufferedTransition
.Size();
569 return SetTransition(unbufferedTransition
);
572 MOZ_ASSERT(unbufferedTransition
.Size() == 0);
573 return FinishCurrentChunkOfUnbufferedRead(aChunkLength
);
576 Maybe
<LexerResult
> FinishCurrentChunkOfUnbufferedRead(size_t aChunkLength
) {
577 // We've finished an unbuffered read of a chunk of length |aChunkLength|, so
578 // update |myBytesRemaining| to reflect that we're |aChunkLength| closer to
579 // the end of the unbuffered read. (The std::min call is just
580 // belt-and-suspenders to keep this code memory safe even if there's a bug
582 mUnbufferedState
->mBytesRemaining
-=
583 std::min(mUnbufferedState
->mBytesRemaining
, aChunkLength
);
585 // Since we're moving on to a new chunk, we can forget about the count of
586 // bytes consumed by yielding in the current chunk.
587 mUnbufferedState
->mBytesConsumedInCurrentChunk
= 0;
589 return Nothing(); // Keep processing.
592 template <typename Func
>
593 Maybe
<LexerResult
> BufferedRead(SourceBufferIterator
& aIterator
, Func aFunc
) {
594 MOZ_ASSERT(mTransition
.Buffering() == BufferingStrategy::BUFFERED
);
595 MOZ_ASSERT(!mYieldingToState
);
596 MOZ_ASSERT(!mUnbufferedState
,
597 "Buffered read at the same time as unbuffered read?");
598 MOZ_ASSERT(mBuffer
.length() < mTransition
.Size() ||
599 (mBuffer
.length() == 0 && mTransition
.Size() == 0),
600 "Buffered more than we needed?");
602 // If we have all the data, we don't actually need to buffer anything.
603 if (mBuffer
.empty() && aIterator
.Length() == mTransition
.Size()) {
604 return SetTransition(
605 aFunc(mTransition
.NextState(), aIterator
.Data(), aIterator
.Length()));
608 // We do need to buffer, so make sure the buffer has enough capacity. We
609 // deliberately wait until we know for sure we need to buffer to call
610 // reserve() since it could require memory allocation.
611 if (!mBuffer
.reserve(mTransition
.Size())) {
612 return SetTransition(Transition::TerminateFailure());
615 // Append the new data we just got to the buffer.
616 if (!mBuffer
.append(aIterator
.Data(), aIterator
.Length())) {
617 return SetTransition(Transition::TerminateFailure());
620 if (mBuffer
.length() != mTransition
.Size()) {
621 return Nothing(); // Keep processing.
624 // We've buffered everything, so transition to the next state.
625 return SetTransition(
626 aFunc(mTransition
.NextState(), mBuffer
.begin(), mBuffer
.length()));
629 template <typename Func
>
630 Maybe
<LexerResult
> BufferedReadAfterYield(SourceBufferIterator
& aIterator
,
632 MOZ_ASSERT(mTransition
.Buffering() == BufferingStrategy::BUFFERED
);
633 MOZ_ASSERT(mYieldingToState
);
634 MOZ_ASSERT(!mUnbufferedState
,
635 "Buffered read at the same time as unbuffered read?");
636 MOZ_ASSERT(mBuffer
.length() <= mTransition
.Size(),
637 "Buffered more than we needed?");
639 State nextState
= std::move(*mYieldingToState
);
641 // After a yield, we need to take the same data that we delivered to the
642 // last state, and deliver it again to the new state. We know that this is
643 // happening right at a state transition, and that the last state was a
644 // buffered read, so there are two cases:
646 // 1. We got the data from the SourceBufferIterator directly.
647 if (mBuffer
.empty() && aIterator
.Length() == mTransition
.Size()) {
648 return SetTransition(
649 aFunc(nextState
, aIterator
.Data(), aIterator
.Length()));
652 // 2. We got the data from the buffer.
653 if (mBuffer
.length() == mTransition
.Size()) {
654 return SetTransition(aFunc(nextState
, mBuffer
.begin(), mBuffer
.length()));
657 // Anything else indicates a bug.
658 MOZ_ASSERT_UNREACHABLE("Unexpected state encountered during yield");
659 return SetTransition(Transition::TerminateFailure());
662 template <typename Func
>
663 Maybe
<LexerResult
> Truncated(SourceBufferIterator
& aIterator
, Func aFunc
) {
664 // The data is truncated. Let the lexer clean up and decide which terminal
665 // state we should end up in.
666 LexerTransition
<State
> transition
=
667 mTruncatedTransition
.NextStateIsTerminal()
668 ? mTruncatedTransition
669 : aFunc(mTruncatedTransition
.NextState(), nullptr, 0);
671 if (!transition
.NextStateIsTerminal()) {
672 MOZ_ASSERT_UNREACHABLE("Truncated state didn't lead to terminal state?");
673 return SetTransition(Transition::TerminateFailure());
676 // If the SourceBuffer was completed with a failing state, we end in
677 // TerminalState::FAILURE no matter what. This only happens in exceptional
678 // situations like SourceBuffer itself encountering a failure due to OOM.
679 if (NS_FAILED(aIterator
.CompletionStatus())) {
680 return SetTransition(Transition::TerminateFailure());
683 return SetTransition(transition
);
686 Maybe
<LexerResult
> SetTransition(const LexerTransition
<State
>& aTransition
) {
687 // There should be no transitions while we're buffering for a buffered read
688 // unless they're to terminal states. (The terminal state transitions would
689 // generally be triggered by error handling code.)
690 MOZ_ASSERT_IF(!mBuffer
.empty(), aTransition
.NextStateIsTerminal() ||
691 mBuffer
.length() == mTransition
.Size());
693 // Similarly, the only transitions allowed in the middle of an unbuffered
694 // read are to a terminal state, or a yield to the same state. Otherwise, we
695 // should remain in the same state until the unbuffered read completes.
698 aTransition
.NextStateIsTerminal() ||
699 (aTransition
.ControlFlow() == ControlFlowStrategy::YIELD
&&
700 aTransition
.NextState() == mTransition
.UnbufferedState()) ||
701 mUnbufferedState
->mBytesRemaining
== 0);
703 // If this transition is a yield, save the next state and return. We'll
704 // handle the rest when Lex() gets called again.
705 if (!aTransition
.NextStateIsTerminal() &&
706 aTransition
.ControlFlow() == ControlFlowStrategy::YIELD
) {
707 mYieldingToState
= Some(aTransition
.NextState());
708 return Some(LexerResult(Yield::OUTPUT_AVAILABLE
));
711 // Update our transition.
712 mTransition
= aTransition
;
714 // Get rid of anything left over from the previous state.
716 mYieldingToState
= Nothing();
717 mUnbufferedState
= Nothing();
719 // If we reached a terminal state, let the caller know.
720 if (mTransition
.NextStateIsTerminal()) {
721 return Some(LexerResult(mTransition
.NextStateAsTerminal()));
724 // If we're entering an unbuffered state, record how long we'll stay in it.
725 if (mTransition
.Buffering() == BufferingStrategy::UNBUFFERED
) {
726 mUnbufferedState
.emplace(mTransition
.Size());
729 return Nothing(); // Keep processing.
732 // State that tracks our position within an unbuffered read.
733 struct UnbufferedState
{
734 explicit UnbufferedState(size_t aBytesRemaining
)
735 : mBytesRemaining(aBytesRemaining
), mBytesConsumedInCurrentChunk(0) {}
737 size_t mBytesRemaining
;
738 size_t mBytesConsumedInCurrentChunk
;
741 Vector
<char, InlineBufferSize
> mBuffer
;
742 LexerTransition
<State
> mTransition
;
743 const LexerTransition
<State
> mTruncatedTransition
;
744 Maybe
<State
> mYieldingToState
;
745 Maybe
<UnbufferedState
> mUnbufferedState
;
749 } // namespace mozilla
751 #endif // mozilla_image_StreamingLexer_h