Bug 1796551 [wpt PR 36570] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / image / StreamingLexer.h
blob06dbabde1c1a36490e243ae01277d8290f74a168
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 <utility>
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"
27 namespace mozilla {
28 namespace image {
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.
46 enum class Yield {
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;
54 /**
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
58 * for execution.
60 template <typename State>
61 class LexerTransition {
62 public:
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;
86 size_t Size() const {
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;
98 private:
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 {
109 State mState;
110 Maybe<State> mUnbufferedState;
111 size_t mSize;
112 BufferingStrategy mBufferingStrategy;
113 ControlFlowStrategy mControlFlowStrategy;
115 NonTerminalState(State aState, const Maybe<State>& aUnbufferedState,
116 size_t aSize, BufferingStrategy aBufferingStrategy,
117 ControlFlowStrategy aControlFlowStrategy)
118 : mState(aState),
119 mUnbufferedState(aUnbufferedState),
120 mSize(aSize),
121 mBufferingStrategy(aBufferingStrategy),
122 mControlFlowStrategy(aControlFlowStrategy) {
123 MOZ_ASSERT_IF(mBufferingStrategy == BufferingStrategy::UNBUFFERED,
124 mUnbufferedState);
125 MOZ_ASSERT_IF(mUnbufferedState,
126 mBufferingStrategy == BufferingStrategy::UNBUFFERED);
130 Variant<NonTerminalState, TerminalState> mNextState;
133 struct Transition {
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
144 /// delivered again.
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
154 * data unbuffered.
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,
165 size_t aSize) {
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
174 * ToUnbuffered().
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
206 * as needed.)
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
215 * as needed.)
217 * No more data will be delivered after this function is used.
219 static TerminalState TerminateFailure() { return TerminalState::FAILURE; }
221 private:
222 Transition();
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
228 * from the network.
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
234 * read.
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
246 * return in turn.
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
276 * going to ignore.
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
295 * yield.
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
320 * state directly.
322 template <typename State, size_t InlineBufferSize = 16>
323 class StreamingLexer {
324 public:
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");
339 return;
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
349 // zero bytes.
350 MOZ_ASSERT_UNREACHABLE("Truncated state makes no sense");
351 return;
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
369 // buffering data.
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) {
379 readLimit += pos;
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;
389 do {
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()));
396 return Nothing();
398 MOZ_ASSERT(pos >= other.Length());
399 pos -= other.Length();
400 } while (pos > 0);
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);
408 return Nothing();
410 return Some(std::move(other));
413 template <typename Func>
414 LexerResult Lex(SourceBufferIterator& aIterator, IResumable* aOnResume,
415 Func aFunc) {
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);
436 while (!result) {
437 MOZ_ASSERT_IF(mTransition.Buffering() == BufferingStrategy::UNBUFFERED,
438 mUnbufferedState);
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));
454 break;
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);
462 break;
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);
471 break;
473 default:
474 MOZ_ASSERT_UNREACHABLE("Unknown SourceBufferIterator state");
475 result = SetTransition(Transition::TerminateFailure());
479 return *result;
482 private:
483 template <typename Func>
484 Maybe<LexerResult> UnbufferedRead(SourceBufferIterator& aIterator,
485 Func aFunc) {
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,
507 Func aFunc) {
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?");
515 MOZ_ASSERT(
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
530 // a bug somewhere.)
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.
540 if (length == 0) {
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
553 // unbuffered state.
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
581 // somewhere.)
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,
631 Func aFunc) {
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.
696 MOZ_ASSERT_IF(
697 mUnbufferedState,
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.
715 mBuffer.clear();
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;
748 } // namespace image
749 } // namespace mozilla
751 #endif // mozilla_image_StreamingLexer_h