Bug 1454184 [wpt PR 10474] - [Resource Timing] Align TAO parsing to spec, a=testonly
[gecko.git] / image / StreamingLexer.h
blob0dbdc87dc5143d23bc2f2e2868791c520b5131b7
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/. */
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.
13 #ifndef mozilla_image_StreamingLexer_h
14 #define mozilla_image_StreamingLexer_h
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 #include "SourceBuffer.h"
26 namespace mozilla {
27 namespace image {
29 /// Buffering behaviors for StreamingLexer transitions.
30 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
39 CONTINUE, // If there's enough data, proceed to the next state immediately.
40 YIELD // Yield to the caller before proceeding to the next state.
43 /// Possible terminal states for the lexer.
44 enum class TerminalState
46 SUCCESS,
47 FAILURE
50 /// Possible yield reasons for the lexer.
51 enum class Yield
53 NEED_MORE_DATA, // The lexer cannot continue without more data.
54 OUTPUT_AVAILABLE // There is output available for the caller to consume.
57 /// The result of a call to StreamingLexer::Lex().
58 typedef Variant<TerminalState, Yield> LexerResult;
60 /**
61 * LexerTransition is a type used to give commands to the lexing framework.
62 * Code that uses StreamingLexer can create LexerTransition values using the
63 * static methods on Transition, and then return them to the lexing framework
64 * for execution.
66 template <typename State>
67 class LexerTransition
69 public:
70 // This is implicit so that Terminate{Success,Failure}() can return a
71 // TerminalState and have it implicitly converted to a
72 // LexerTransition<State>, which avoids the need for a "<State>"
73 // qualification to the Terminate{Success,Failure}() callsite.
74 MOZ_IMPLICIT LexerTransition(TerminalState aFinalState)
75 : mNextState(aFinalState)
78 bool NextStateIsTerminal() const
80 return mNextState.template is<TerminalState>();
83 TerminalState NextStateAsTerminal() const
85 return mNextState.template as<TerminalState>();
88 State NextState() const
90 return mNextState.template as<NonTerminalState>().mState;
93 State UnbufferedState() const
95 return *mNextState.template as<NonTerminalState>().mUnbufferedState;
98 size_t Size() const
100 return mNextState.template as<NonTerminalState>().mSize;
103 BufferingStrategy Buffering() const
105 return mNextState.template as<NonTerminalState>().mBufferingStrategy;
108 ControlFlowStrategy ControlFlow() const
110 return mNextState.template as<NonTerminalState>().mControlFlowStrategy;
113 private:
114 friend struct Transition;
116 LexerTransition(State aNextState,
117 const Maybe<State>& aUnbufferedState,
118 size_t aSize,
119 BufferingStrategy aBufferingStrategy,
120 ControlFlowStrategy aControlFlowStrategy)
121 : mNextState(NonTerminalState(aNextState, aUnbufferedState, aSize,
122 aBufferingStrategy, aControlFlowStrategy))
125 struct NonTerminalState
127 State mState;
128 Maybe<State> mUnbufferedState;
129 size_t mSize;
130 BufferingStrategy mBufferingStrategy;
131 ControlFlowStrategy mControlFlowStrategy;
133 NonTerminalState(State aState,
134 const Maybe<State>& aUnbufferedState,
135 size_t aSize,
136 BufferingStrategy aBufferingStrategy,
137 ControlFlowStrategy aControlFlowStrategy)
138 : mState(aState)
139 , mUnbufferedState(aUnbufferedState)
140 , mSize(aSize)
141 , mBufferingStrategy(aBufferingStrategy)
142 , mControlFlowStrategy(aControlFlowStrategy)
144 MOZ_ASSERT_IF(mBufferingStrategy == BufferingStrategy::UNBUFFERED,
145 mUnbufferedState);
146 MOZ_ASSERT_IF(mUnbufferedState,
147 mBufferingStrategy == BufferingStrategy::UNBUFFERED);
151 Variant<NonTerminalState, TerminalState> mNextState;
154 struct Transition
156 /// Transition to @aNextState, buffering @aSize bytes of data.
157 template <typename State>
158 static LexerTransition<State>
159 To(const State& aNextState, size_t aSize)
161 return LexerTransition<State>(aNextState, Nothing(), aSize,
162 BufferingStrategy::BUFFERED,
163 ControlFlowStrategy::CONTINUE);
166 /// Yield to the caller, transitioning to @aNextState when Lex() is next
167 /// invoked. The same data that was delivered for the current state will be
168 /// delivered again.
169 template <typename State>
170 static LexerTransition<State>
171 ToAfterYield(const State& aNextState)
173 return LexerTransition<State>(aNextState, Nothing(), 0,
174 BufferingStrategy::BUFFERED,
175 ControlFlowStrategy::YIELD);
179 * Transition to @aNextState via @aUnbufferedState, reading @aSize bytes of
180 * data unbuffered.
182 * The unbuffered data will be delivered in state @aUnbufferedState, which may
183 * be invoked repeatedly until all @aSize bytes have been delivered. Then,
184 * @aNextState will be invoked with no data. No state transitions are allowed
185 * from @aUnbufferedState except for transitions to a terminal state, so
186 * @aNextState will always be reached unless lexing terminates early.
188 template <typename State>
189 static LexerTransition<State>
190 ToUnbuffered(const State& aNextState,
191 const State& aUnbufferedState,
192 size_t aSize)
194 return LexerTransition<State>(aNextState, Some(aUnbufferedState), aSize,
195 BufferingStrategy::UNBUFFERED,
196 ControlFlowStrategy::CONTINUE);
200 * Continue receiving unbuffered data. @aUnbufferedState should be the same
201 * state as the @aUnbufferedState specified in the preceding call to
202 * ToUnbuffered().
204 * This should be used during an unbuffered read initiated by ToUnbuffered().
206 template <typename State>
207 static LexerTransition<State>
208 ContinueUnbuffered(const State& aUnbufferedState)
210 return LexerTransition<State>(aUnbufferedState, Nothing(), 0,
211 BufferingStrategy::BUFFERED,
212 ControlFlowStrategy::CONTINUE);
216 * Continue receiving unbuffered data. @aUnbufferedState should be the same
217 * state as the @aUnbufferedState specified in the preceding call to
218 * ToUnbuffered(). @aSize indicates the amount of data that has already been
219 * consumed; the next state will receive the same data that was delivered to
220 * the current state, without the first @aSize bytes.
222 * This should be used during an unbuffered read initiated by ToUnbuffered().
224 template <typename State>
225 static LexerTransition<State>
226 ContinueUnbufferedAfterYield(const State& aUnbufferedState, size_t aSize)
228 return LexerTransition<State>(aUnbufferedState, Nothing(), aSize,
229 BufferingStrategy::BUFFERED,
230 ControlFlowStrategy::YIELD);
234 * Terminate lexing, ending up in terminal state SUCCESS. (The implicit
235 * LexerTransition constructor will convert the result to a LexerTransition
236 * as needed.)
238 * No more data will be delivered after this function is used.
240 static TerminalState
241 TerminateSuccess()
243 return TerminalState::SUCCESS;
247 * Terminate lexing, ending up in terminal state FAILURE. (The implicit
248 * LexerTransition constructor will convert the result to a LexerTransition
249 * as needed.)
251 * No more data will be delivered after this function is used.
253 static TerminalState
254 TerminateFailure()
256 return TerminalState::FAILURE;
259 private:
260 Transition();
264 * StreamingLexer is a lexing framework designed to make it simple to write
265 * image decoders without worrying about the details of how the data is arriving
266 * from the network.
268 * To use StreamingLexer:
270 * - Create a State type. This should be an |enum class| listing all of the
271 * states that you can be in while lexing the image format you're trying to
272 * read.
274 * - Add an instance of StreamingLexer<State> to your decoder class. Initialize
275 * it with a Transition::To() the state that you want to start lexing in, and
276 * a Transition::To() the state you'd like to use to handle truncated data.
278 * - In your decoder's DoDecode() method, call Lex(), passing in the input
279 * data and length that are passed to DoDecode(). You also need to pass
280 * a lambda which dispatches to lexing code for each state based on the State
281 * value that's passed in. The lambda generally should just continue a
282 * |switch| statement that calls different methods for each State value. Each
283 * method should return a LexerTransition<State>, which the lambda should
284 * return in turn.
286 * - Write the methods that actually implement lexing for your image format.
287 * These methods should return either Transition::To(), to move on to another
288 * state, or Transition::Terminate{Success,Failure}(), if lexing has
289 * terminated in either success or failure. (There are also additional
290 * transitions for unbuffered reads; see below.)
292 * That's the basics. The StreamingLexer will track your position in the input
293 * and buffer enough data so that your lexing methods can process everything in
294 * one pass. Lex() returns Yield::NEED_MORE_DATA if more data is needed, in
295 * which case you should just return from DoDecode(). If lexing reaches a
296 * terminal state, Lex() returns TerminalState::SUCCESS or
297 * TerminalState::FAILURE, and you can check which one to determine if lexing
298 * succeeded or failed and do any necessary cleanup.
300 * Sometimes, the input data is truncated. StreamingLexer will notify you when
301 * this happens by invoking the truncated data state you passed to the
302 * constructor. At this point you can attempt to recover and return
303 * TerminalState::SUCCESS or TerminalState::FAILURE, depending on whether you
304 * were successful. Note that you can't return anything other than a terminal
305 * state in this situation, since there's no more data to read. For the same
306 * reason, your truncated data state shouldn't require any data. (That is, the
307 * @aSize argument you pass to Transition::To() must be zero.) Violating these
308 * requirements will trigger assertions and an immediate transition to
309 * TerminalState::FAILURE.
311 * Some lexers may want to *avoid* buffering in some cases, and just process the
312 * data as it comes in. This is useful if, for example, you just want to skip
313 * over a large section of data; there's no point in buffering data you're just
314 * going to ignore.
316 * You can begin an unbuffered read with Transition::ToUnbuffered(). This works
317 * a little differently than Transition::To() in that you specify *two* states.
318 * The @aUnbufferedState argument specifies a state that will be called
319 * repeatedly with unbuffered data, as soon as it arrives. The implementation
320 * for that state should return either a transition to a terminal state, or a
321 * Transition::ContinueUnbuffered() to the same @aUnbufferedState. (From a
322 * technical perspective, it's not necessary to specify the state again, but
323 * it's helpful to human readers.) Once the amount of data requested in the
324 * original call to Transition::ToUnbuffered() has been delivered, Lex() will
325 * transition to the @aNextState state specified via Transition::ToUnbuffered().
326 * That state will be invoked with *no* data; it's just called to signal that
327 * the unbuffered read is over.
329 * It's sometimes useful for a lexer to provide incremental results, rather
330 * than simply running to completion and presenting all its output at once. For
331 * example, when decoding animated images, it may be useful to produce each
332 * frame incrementally. StreamingLexer supports this by allowing a lexer to
333 * yield.
335 * To yield back to the caller, a state implementation can simply return
336 * Transition::ToAfterYield(). ToAfterYield()'s @aNextState argument specifies
337 * the next state that the lexer should transition to, just like when using
338 * Transition::To(), but there are two differences. One is that Lex() will
339 * return to the caller before processing any more data when it encounters a
340 * yield transition. This provides an opportunity for the caller to interact with the
341 * lexer's intermediate results. The second difference is that @aNextState
342 * will be called with *the same data as the state that you returned
343 * Transition::ToAfterYield() from*. This allows a lexer to partially consume
344 * the data, return intermediate results, and then finish consuming the data
345 * when @aNextState is called.
347 * It's also possible to yield during an unbuffered read. Just return a
348 * Transition::ContinueUnbufferedAfterYield(). Just like with
349 * Transition::ContinueUnbuffered(), the @aUnbufferedState must be the same as
350 * the one originally passed to Transition::ToUnbuffered(). The second argument,
351 * @aSize, specifies the amount of data that the lexer has already consumed.
352 * When @aUnbufferedState is next invoked, it will get the same data that it
353 * received previously, except that the first @aSize bytes will be excluded.
354 * This makes it easy to consume unbuffered data incrementally.
356 * XXX(seth): We should be able to get of the |State| stuff totally once bug
357 * 1198451 lands, since we can then just return a function representing the next
358 * state directly.
360 template <typename State, size_t InlineBufferSize = 16>
361 class StreamingLexer
363 public:
364 StreamingLexer(const LexerTransition<State>& aStartState,
365 const LexerTransition<State>& aTruncatedState)
366 : mTransition(TerminalState::FAILURE)
367 , mTruncatedTransition(aTruncatedState)
369 if (!aStartState.NextStateIsTerminal() &&
370 aStartState.ControlFlow() == ControlFlowStrategy::YIELD) {
371 // Allowing a StreamingLexer to start in a yield state doesn't make sense
372 // semantically (since yield states are supposed to deliver the same data
373 // as previous states, and there's no previous state here), but more
374 // importantly, it's necessary to advance a SourceBufferIterator at least
375 // once before you can read from it, and adding the necessary checks to
376 // Lex() to avoid that issue has the potential to mask real bugs. So
377 // instead, it's better to forbid starting in a yield state.
378 MOZ_ASSERT_UNREACHABLE("Starting in a yield state");
379 return;
382 if (!aTruncatedState.NextStateIsTerminal() &&
383 (aTruncatedState.ControlFlow() == ControlFlowStrategy::YIELD ||
384 aTruncatedState.Buffering() == BufferingStrategy::UNBUFFERED ||
385 aTruncatedState.Size() != 0)) {
386 // The truncated state can't receive any data because, by definition,
387 // there is no more data to receive. That means that yielding or an
388 // unbuffered read would not make sense, and that the state must require
389 // zero bytes.
390 MOZ_ASSERT_UNREACHABLE("Truncated state makes no sense");
391 return;
394 SetTransition(aStartState);
398 * From the given SourceBufferIterator, aIterator, create a new iterator at
399 * the same position, with the given read limit, aReadLimit. The read limit
400 * applies after adjusting for the position. If the given iterator has been
401 * advanced, but required buffering inside StreamingLexer, the position
402 * of the cloned iterator will be at the beginning of buffered data; this
403 * should match the perspective of the caller.
405 Maybe<SourceBufferIterator> Clone(SourceBufferIterator& aIterator,
406 size_t aReadLimit) const
408 // In order to advance to the current position of the iterator from the
409 // perspective of the caller, we need to take into account if we are
410 // buffering data.
411 size_t pos = aIterator.Position();
412 if (!mBuffer.empty()) {
413 pos += aIterator.Length();
414 MOZ_ASSERT(pos > mBuffer.length());
415 pos -= mBuffer.length();
418 size_t readLimit = aReadLimit;
419 if (aReadLimit != SIZE_MAX) {
420 readLimit += pos;
423 SourceBufferIterator other = aIterator.Owner()->Iterator(readLimit);
425 // Since the current iterator has already advanced to this point, we
426 // know that the state can only be READY or COMPLETE. That does not mean
427 // everything is stored in a single chunk, and may require multiple Advance
428 // calls to get where we want to be.
429 SourceBufferIterator::State state;
430 do {
431 state = other.Advance(pos);
432 if (state != SourceBufferIterator::READY) {
433 // The only way we should fail to advance over data we already seen is
434 // if we hit an error while inserting data into the buffer. This will
435 // cause an early exit.
436 MOZ_ASSERT(NS_FAILED(other.CompletionStatus()));
437 return Nothing();
439 MOZ_ASSERT(pos >= other.Length());
440 pos -= other.Length();
441 } while (pos > 0);
443 // Force the data pointer to be where we expect it to be.
444 state = other.Advance(0);
445 if (state != SourceBufferIterator::READY) {
446 // The current position could be the end of the buffer, in which case
447 // there is no point cloning with no more data to read.
448 MOZ_ASSERT(state == SourceBufferIterator::COMPLETE);
449 return Nothing();
451 return Some(Move(other));
454 template <typename Func>
455 LexerResult Lex(SourceBufferIterator& aIterator,
456 IResumable* aOnResume,
457 Func aFunc)
459 if (mTransition.NextStateIsTerminal()) {
460 // We've already reached a terminal state. We never deliver any more data
461 // in this case; just return the terminal state again immediately.
462 return LexerResult(mTransition.NextStateAsTerminal());
465 Maybe<LexerResult> result;
467 // If the lexer requested a yield last time, we deliver the same data again
468 // before we read anything else from |aIterator|. Note that although to the
469 // callers of Lex(), Yield::NEED_MORE_DATA is just another type of yield,
470 // internally they're different in that we don't redeliver the same data in
471 // the Yield::NEED_MORE_DATA case, and |mYieldingToState| is not set. This
472 // means that for Yield::NEED_MORE_DATA, we go directly to the loop below.
473 if (mYieldingToState) {
474 result = mTransition.Buffering() == BufferingStrategy::UNBUFFERED
475 ? UnbufferedReadAfterYield(aIterator, aFunc)
476 : BufferedReadAfterYield(aIterator, aFunc);
479 while (!result) {
480 MOZ_ASSERT_IF(mTransition.Buffering() == BufferingStrategy::UNBUFFERED,
481 mUnbufferedState);
483 // Figure out how much we need to read.
484 const size_t toRead = mTransition.Buffering() == BufferingStrategy::UNBUFFERED
485 ? mUnbufferedState->mBytesRemaining
486 : mTransition.Size() - mBuffer.length();
488 // Attempt to advance the iterator by |toRead| bytes.
489 switch (aIterator.AdvanceOrScheduleResume(toRead, aOnResume)) {
490 case SourceBufferIterator::WAITING:
491 // We can't continue because the rest of the data hasn't arrived from
492 // the network yet. We don't have to do anything special; the
493 // SourceBufferIterator will ensure that |aOnResume| gets called when
494 // more data is available.
495 result = Some(LexerResult(Yield::NEED_MORE_DATA));
496 break;
498 case SourceBufferIterator::COMPLETE:
499 // The data is truncated; if not, the lexer would've reached a
500 // terminal state by now. We only get to
501 // SourceBufferIterator::COMPLETE after every byte of data has been
502 // delivered to the lexer.
503 result = Truncated(aIterator, aFunc);
504 break;
506 case SourceBufferIterator::READY:
507 // Process the new data that became available.
508 MOZ_ASSERT(aIterator.Data());
510 result = mTransition.Buffering() == BufferingStrategy::UNBUFFERED
511 ? UnbufferedRead(aIterator, aFunc)
512 : BufferedRead(aIterator, aFunc);
513 break;
515 default:
516 MOZ_ASSERT_UNREACHABLE("Unknown SourceBufferIterator state");
517 result = SetTransition(Transition::TerminateFailure());
521 return *result;
524 private:
525 template <typename Func>
526 Maybe<LexerResult> UnbufferedRead(SourceBufferIterator& aIterator, Func aFunc)
528 MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::UNBUFFERED);
529 MOZ_ASSERT(mUnbufferedState);
530 MOZ_ASSERT(!mYieldingToState);
531 MOZ_ASSERT(mBuffer.empty(),
532 "Buffered read at the same time as unbuffered read?");
533 MOZ_ASSERT(aIterator.Length() <= mUnbufferedState->mBytesRemaining,
534 "Read too much data during unbuffered read?");
535 MOZ_ASSERT(mUnbufferedState->mBytesConsumedInCurrentChunk == 0,
536 "Already consumed data in the current chunk, but not yielding?");
538 if (mUnbufferedState->mBytesRemaining == 0) {
539 // We're done with the unbuffered read, so transition to the next state.
540 return SetTransition(aFunc(mTransition.NextState(), nullptr, 0));
543 return ContinueUnbufferedRead(aIterator.Data(), aIterator.Length(),
544 aIterator.Length(), aFunc);
547 template <typename Func>
548 Maybe<LexerResult> UnbufferedReadAfterYield(SourceBufferIterator& aIterator, Func aFunc)
550 MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::UNBUFFERED);
551 MOZ_ASSERT(mUnbufferedState);
552 MOZ_ASSERT(mYieldingToState);
553 MOZ_ASSERT(mBuffer.empty(),
554 "Buffered read at the same time as unbuffered read?");
555 MOZ_ASSERT(aIterator.Length() <= mUnbufferedState->mBytesRemaining,
556 "Read too much data during unbuffered read?");
557 MOZ_ASSERT(mUnbufferedState->mBytesConsumedInCurrentChunk <= aIterator.Length(),
558 "Consumed more data than the current chunk holds?");
559 MOZ_ASSERT(mTransition.UnbufferedState() == *mYieldingToState);
561 mYieldingToState = Nothing();
563 if (mUnbufferedState->mBytesRemaining == 0) {
564 // We're done with the unbuffered read, so transition to the next state.
565 return SetTransition(aFunc(mTransition.NextState(), nullptr, 0));
568 // Since we've yielded, we may have already consumed some data in this
569 // chunk. Make the necessary adjustments. (Note that the std::min call is
570 // just belt-and-suspenders to keep this code memory safe even if there's
571 // a bug somewhere.)
572 const size_t toSkip =
573 std::min(mUnbufferedState->mBytesConsumedInCurrentChunk, aIterator.Length());
574 const char* data = aIterator.Data() + toSkip;
575 const size_t length = aIterator.Length() - toSkip;
577 // If |length| is zero, we've hit the end of the current chunk. This only
578 // happens if we yield right at the end of a chunk. Rather than call |aFunc|
579 // with a |length| of zero bytes (which seems potentially surprising to
580 // decoder authors), we go ahead and read more data.
581 if (length == 0) {
582 return FinishCurrentChunkOfUnbufferedRead(aIterator.Length());
585 return ContinueUnbufferedRead(data, length, aIterator.Length(), aFunc);
588 template <typename Func>
589 Maybe<LexerResult> ContinueUnbufferedRead(const char* aData,
590 size_t aLength,
591 size_t aChunkLength,
592 Func aFunc)
594 // Call aFunc with the unbuffered state to indicate that we're in the
595 // middle of an unbuffered read. We enforce that any state transition
596 // passed back to us is either a terminal state or takes us back to the
597 // unbuffered state.
598 LexerTransition<State> unbufferedTransition =
599 aFunc(mTransition.UnbufferedState(), aData, aLength);
601 // If we reached a terminal state, we're done.
602 if (unbufferedTransition.NextStateIsTerminal()) {
603 return SetTransition(unbufferedTransition);
606 MOZ_ASSERT(mTransition.UnbufferedState() ==
607 unbufferedTransition.NextState());
609 // Perform bookkeeping.
610 if (unbufferedTransition.ControlFlow() == ControlFlowStrategy::YIELD) {
611 mUnbufferedState->mBytesConsumedInCurrentChunk += unbufferedTransition.Size();
612 return SetTransition(unbufferedTransition);
615 MOZ_ASSERT(unbufferedTransition.Size() == 0);
616 return FinishCurrentChunkOfUnbufferedRead(aChunkLength);
619 Maybe<LexerResult> FinishCurrentChunkOfUnbufferedRead(size_t aChunkLength)
621 // We've finished an unbuffered read of a chunk of length |aChunkLength|, so
622 // update |myBytesRemaining| to reflect that we're |aChunkLength| closer to
623 // the end of the unbuffered read. (The std::min call is just
624 // belt-and-suspenders to keep this code memory safe even if there's a bug
625 // somewhere.)
626 mUnbufferedState->mBytesRemaining -=
627 std::min(mUnbufferedState->mBytesRemaining, aChunkLength);
629 // Since we're moving on to a new chunk, we can forget about the count of
630 // bytes consumed by yielding in the current chunk.
631 mUnbufferedState->mBytesConsumedInCurrentChunk = 0;
633 return Nothing(); // Keep processing.
636 template <typename Func>
637 Maybe<LexerResult> BufferedRead(SourceBufferIterator& aIterator, Func aFunc)
639 MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
640 MOZ_ASSERT(!mYieldingToState);
641 MOZ_ASSERT(!mUnbufferedState,
642 "Buffered read at the same time as unbuffered read?");
643 MOZ_ASSERT(mBuffer.length() < mTransition.Size() ||
644 (mBuffer.length() == 0 && mTransition.Size() == 0),
645 "Buffered more than we needed?");
647 // If we have all the data, we don't actually need to buffer anything.
648 if (mBuffer.empty() && aIterator.Length() == mTransition.Size()) {
649 return SetTransition(aFunc(mTransition.NextState(),
650 aIterator.Data(),
651 aIterator.Length()));
654 // We do need to buffer, so make sure the buffer has enough capacity. We
655 // deliberately wait until we know for sure we need to buffer to call
656 // reserve() since it could require memory allocation.
657 if (!mBuffer.reserve(mTransition.Size())) {
658 return SetTransition(Transition::TerminateFailure());
661 // Append the new data we just got to the buffer.
662 if (!mBuffer.append(aIterator.Data(), aIterator.Length())) {
663 return SetTransition(Transition::TerminateFailure());
666 if (mBuffer.length() != mTransition.Size()) {
667 return Nothing(); // Keep processing.
670 // We've buffered everything, so transition to the next state.
671 return SetTransition(aFunc(mTransition.NextState(),
672 mBuffer.begin(),
673 mBuffer.length()));
676 template <typename Func>
677 Maybe<LexerResult> BufferedReadAfterYield(SourceBufferIterator& aIterator,
678 Func aFunc)
680 MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
681 MOZ_ASSERT(mYieldingToState);
682 MOZ_ASSERT(!mUnbufferedState,
683 "Buffered read at the same time as unbuffered read?");
684 MOZ_ASSERT(mBuffer.length() <= mTransition.Size(),
685 "Buffered more than we needed?");
687 State nextState = Move(*mYieldingToState);
689 // After a yield, we need to take the same data that we delivered to the
690 // last state, and deliver it again to the new state. We know that this is
691 // happening right at a state transition, and that the last state was a
692 // buffered read, so there are two cases:
694 // 1. We got the data from the SourceBufferIterator directly.
695 if (mBuffer.empty() && aIterator.Length() == mTransition.Size()) {
696 return SetTransition(aFunc(nextState,
697 aIterator.Data(),
698 aIterator.Length()));
701 // 2. We got the data from the buffer.
702 if (mBuffer.length() == mTransition.Size()) {
703 return SetTransition(aFunc(nextState,
704 mBuffer.begin(),
705 mBuffer.length()));
708 // Anything else indicates a bug.
709 MOZ_ASSERT_UNREACHABLE("Unexpected state encountered during yield");
710 return SetTransition(Transition::TerminateFailure());
713 template <typename Func>
714 Maybe<LexerResult> Truncated(SourceBufferIterator& aIterator,
715 Func aFunc)
717 // The data is truncated. Let the lexer clean up and decide which terminal
718 // state we should end up in.
719 LexerTransition<State> transition
720 = mTruncatedTransition.NextStateIsTerminal()
721 ? mTruncatedTransition
722 : aFunc(mTruncatedTransition.NextState(), nullptr, 0);
724 if (!transition.NextStateIsTerminal()) {
725 MOZ_ASSERT_UNREACHABLE("Truncated state didn't lead to terminal state?");
726 return SetTransition(Transition::TerminateFailure());
729 // If the SourceBuffer was completed with a failing state, we end in
730 // TerminalState::FAILURE no matter what. This only happens in exceptional
731 // situations like SourceBuffer itself encountering a failure due to OOM.
732 if (NS_FAILED(aIterator.CompletionStatus())) {
733 return SetTransition(Transition::TerminateFailure());
736 return SetTransition(transition);
739 Maybe<LexerResult> SetTransition(const LexerTransition<State>& aTransition)
741 // There should be no transitions while we're buffering for a buffered read
742 // unless they're to terminal states. (The terminal state transitions would
743 // generally be triggered by error handling code.)
744 MOZ_ASSERT_IF(!mBuffer.empty(),
745 aTransition.NextStateIsTerminal() ||
746 mBuffer.length() == mTransition.Size());
748 // Similarly, the only transitions allowed in the middle of an unbuffered
749 // read are to a terminal state, or a yield to the same state. Otherwise, we
750 // should remain in the same state until the unbuffered read completes.
751 MOZ_ASSERT_IF(mUnbufferedState,
752 aTransition.NextStateIsTerminal() ||
753 (aTransition.ControlFlow() == ControlFlowStrategy::YIELD &&
754 aTransition.NextState() == mTransition.UnbufferedState()) ||
755 mUnbufferedState->mBytesRemaining == 0);
757 // If this transition is a yield, save the next state and return. We'll
758 // handle the rest when Lex() gets called again.
759 if (!aTransition.NextStateIsTerminal() &&
760 aTransition.ControlFlow() == ControlFlowStrategy::YIELD) {
761 mYieldingToState = Some(aTransition.NextState());
762 return Some(LexerResult(Yield::OUTPUT_AVAILABLE));
765 // Update our transition.
766 mTransition = aTransition;
768 // Get rid of anything left over from the previous state.
769 mBuffer.clear();
770 mYieldingToState = Nothing();
771 mUnbufferedState = Nothing();
773 // If we reached a terminal state, let the caller know.
774 if (mTransition.NextStateIsTerminal()) {
775 return Some(LexerResult(mTransition.NextStateAsTerminal()));
778 // If we're entering an unbuffered state, record how long we'll stay in it.
779 if (mTransition.Buffering() == BufferingStrategy::UNBUFFERED) {
780 mUnbufferedState.emplace(mTransition.Size());
783 return Nothing(); // Keep processing.
786 // State that tracks our position within an unbuffered read.
787 struct UnbufferedState
789 explicit UnbufferedState(size_t aBytesRemaining)
790 : mBytesRemaining(aBytesRemaining)
791 , mBytesConsumedInCurrentChunk(0)
794 size_t mBytesRemaining;
795 size_t mBytesConsumedInCurrentChunk;
798 Vector<char, InlineBufferSize> mBuffer;
799 LexerTransition<State> mTransition;
800 const LexerTransition<State> mTruncatedTransition;
801 Maybe<State> mYieldingToState;
802 Maybe<UnbufferedState> mUnbufferedState;
805 } // namespace image
806 } // namespace mozilla
808 #endif // mozilla_image_StreamingLexer_h