Bug 1572460 - Refactor `selection` out of the `InspectorFront`. r=yulia
[gecko.git] / image / StreamingLexer.h
blobe59c6315e5d537dd92d47f8683592bc70540f9e0
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 {
31 BUFFERED, // Data will be buffered and processed in one chunk.
32 UNBUFFERED // Data will be processed as it arrives, in multiple chunks.
35 /// Control flow behaviors for StreamingLexer transitions.
36 enum class ControlFlowStrategy {
37 CONTINUE, // If there's enough data, proceed to the next state immediately.
38 YIELD // Yield to the caller before proceeding to the next state.
41 /// Possible terminal states for the lexer.
42 enum class TerminalState { SUCCESS, FAILURE };
44 /// Possible yield reasons for the lexer.
45 enum class Yield {
46 NEED_MORE_DATA, // The lexer cannot continue without more data.
47 OUTPUT_AVAILABLE // There is output available for the caller to consume.
50 /// The result of a call to StreamingLexer::Lex().
51 typedef Variant<TerminalState, Yield> LexerResult;
53 /**
54 * LexerTransition is a type used to give commands to the lexing framework.
55 * Code that uses StreamingLexer can create LexerTransition values using the
56 * static methods on Transition, and then return them to the lexing framework
57 * for execution.
59 template <typename State>
60 class LexerTransition {
61 public:
62 // This is implicit so that Terminate{Success,Failure}() can return a
63 // TerminalState and have it implicitly converted to a
64 // LexerTransition<State>, which avoids the need for a "<State>"
65 // qualification to the Terminate{Success,Failure}() callsite.
66 MOZ_IMPLICIT LexerTransition(TerminalState aFinalState)
67 : mNextState(aFinalState) {}
69 bool NextStateIsTerminal() const {
70 return mNextState.template is<TerminalState>();
73 TerminalState NextStateAsTerminal() const {
74 return mNextState.template as<TerminalState>();
77 State NextState() const {
78 return mNextState.template as<NonTerminalState>().mState;
81 State UnbufferedState() const {
82 return *mNextState.template as<NonTerminalState>().mUnbufferedState;
85 size_t Size() const {
86 return mNextState.template as<NonTerminalState>().mSize;
89 BufferingStrategy Buffering() const {
90 return mNextState.template as<NonTerminalState>().mBufferingStrategy;
93 ControlFlowStrategy ControlFlow() const {
94 return mNextState.template as<NonTerminalState>().mControlFlowStrategy;
97 private:
98 friend struct Transition;
100 LexerTransition(State aNextState, const Maybe<State>& aUnbufferedState,
101 size_t aSize, BufferingStrategy aBufferingStrategy,
102 ControlFlowStrategy aControlFlowStrategy)
103 : mNextState(NonTerminalState(aNextState, aUnbufferedState, aSize,
104 aBufferingStrategy, aControlFlowStrategy)) {
107 struct NonTerminalState {
108 State mState;
109 Maybe<State> mUnbufferedState;
110 size_t mSize;
111 BufferingStrategy mBufferingStrategy;
112 ControlFlowStrategy mControlFlowStrategy;
114 NonTerminalState(State aState, const Maybe<State>& aUnbufferedState,
115 size_t aSize, BufferingStrategy aBufferingStrategy,
116 ControlFlowStrategy aControlFlowStrategy)
117 : mState(aState),
118 mUnbufferedState(aUnbufferedState),
119 mSize(aSize),
120 mBufferingStrategy(aBufferingStrategy),
121 mControlFlowStrategy(aControlFlowStrategy) {
122 MOZ_ASSERT_IF(mBufferingStrategy == BufferingStrategy::UNBUFFERED,
123 mUnbufferedState);
124 MOZ_ASSERT_IF(mUnbufferedState,
125 mBufferingStrategy == BufferingStrategy::UNBUFFERED);
129 Variant<NonTerminalState, TerminalState> mNextState;
132 struct Transition {
133 /// Transition to @aNextState, buffering @aSize bytes of data.
134 template <typename State>
135 static LexerTransition<State> To(const State& aNextState, size_t aSize) {
136 return LexerTransition<State>(aNextState, Nothing(), aSize,
137 BufferingStrategy::BUFFERED,
138 ControlFlowStrategy::CONTINUE);
141 /// Yield to the caller, transitioning to @aNextState when Lex() is next
142 /// invoked. The same data that was delivered for the current state will be
143 /// delivered again.
144 template <typename State>
145 static LexerTransition<State> ToAfterYield(const State& aNextState) {
146 return LexerTransition<State>(aNextState, Nothing(), 0,
147 BufferingStrategy::BUFFERED,
148 ControlFlowStrategy::YIELD);
152 * Transition to @aNextState via @aUnbufferedState, reading @aSize bytes of
153 * data unbuffered.
155 * The unbuffered data will be delivered in state @aUnbufferedState, which may
156 * be invoked repeatedly until all @aSize bytes have been delivered. Then,
157 * @aNextState will be invoked with no data. No state transitions are allowed
158 * from @aUnbufferedState except for transitions to a terminal state, so
159 * @aNextState will always be reached unless lexing terminates early.
161 template <typename State>
162 static LexerTransition<State> ToUnbuffered(const State& aNextState,
163 const State& aUnbufferedState,
164 size_t aSize) {
165 return LexerTransition<State>(aNextState, Some(aUnbufferedState), aSize,
166 BufferingStrategy::UNBUFFERED,
167 ControlFlowStrategy::CONTINUE);
171 * Continue receiving unbuffered data. @aUnbufferedState should be the same
172 * state as the @aUnbufferedState specified in the preceding call to
173 * ToUnbuffered().
175 * This should be used during an unbuffered read initiated by ToUnbuffered().
177 template <typename State>
178 static LexerTransition<State> ContinueUnbuffered(
179 const State& aUnbufferedState) {
180 return LexerTransition<State>(aUnbufferedState, Nothing(), 0,
181 BufferingStrategy::BUFFERED,
182 ControlFlowStrategy::CONTINUE);
186 * Continue receiving unbuffered data. @aUnbufferedState should be the same
187 * state as the @aUnbufferedState specified in the preceding call to
188 * ToUnbuffered(). @aSize indicates the amount of data that has already been
189 * consumed; the next state will receive the same data that was delivered to
190 * the current state, without the first @aSize bytes.
192 * This should be used during an unbuffered read initiated by ToUnbuffered().
194 template <typename State>
195 static LexerTransition<State> ContinueUnbufferedAfterYield(
196 const State& aUnbufferedState, size_t aSize) {
197 return LexerTransition<State>(aUnbufferedState, Nothing(), aSize,
198 BufferingStrategy::BUFFERED,
199 ControlFlowStrategy::YIELD);
203 * Terminate lexing, ending up in terminal state SUCCESS. (The implicit
204 * LexerTransition constructor will convert the result to a LexerTransition
205 * as needed.)
207 * No more data will be delivered after this function is used.
209 static TerminalState TerminateSuccess() { return TerminalState::SUCCESS; }
212 * Terminate lexing, ending up in terminal state FAILURE. (The implicit
213 * LexerTransition constructor will convert the result to a LexerTransition
214 * as needed.)
216 * No more data will be delivered after this function is used.
218 static TerminalState TerminateFailure() { return TerminalState::FAILURE; }
220 private:
221 Transition();
225 * StreamingLexer is a lexing framework designed to make it simple to write
226 * image decoders without worrying about the details of how the data is arriving
227 * from the network.
229 * To use StreamingLexer:
231 * - Create a State type. This should be an |enum class| listing all of the
232 * states that you can be in while lexing the image format you're trying to
233 * read.
235 * - Add an instance of StreamingLexer<State> to your decoder class. Initialize
236 * it with a Transition::To() the state that you want to start lexing in, and
237 * a Transition::To() the state you'd like to use to handle truncated data.
239 * - In your decoder's DoDecode() method, call Lex(), passing in the input
240 * data and length that are passed to DoDecode(). You also need to pass
241 * a lambda which dispatches to lexing code for each state based on the State
242 * value that's passed in. The lambda generally should just continue a
243 * |switch| statement that calls different methods for each State value. Each
244 * method should return a LexerTransition<State>, which the lambda should
245 * return in turn.
247 * - Write the methods that actually implement lexing for your image format.
248 * These methods should return either Transition::To(), to move on to another
249 * state, or Transition::Terminate{Success,Failure}(), if lexing has
250 * terminated in either success or failure. (There are also additional
251 * transitions for unbuffered reads; see below.)
253 * That's the basics. The StreamingLexer will track your position in the input
254 * and buffer enough data so that your lexing methods can process everything in
255 * one pass. Lex() returns Yield::NEED_MORE_DATA if more data is needed, in
256 * which case you should just return from DoDecode(). If lexing reaches a
257 * terminal state, Lex() returns TerminalState::SUCCESS or
258 * TerminalState::FAILURE, and you can check which one to determine if lexing
259 * succeeded or failed and do any necessary cleanup.
261 * Sometimes, the input data is truncated. StreamingLexer will notify you when
262 * this happens by invoking the truncated data state you passed to the
263 * constructor. At this point you can attempt to recover and return
264 * TerminalState::SUCCESS or TerminalState::FAILURE, depending on whether you
265 * were successful. Note that you can't return anything other than a terminal
266 * state in this situation, since there's no more data to read. For the same
267 * reason, your truncated data state shouldn't require any data. (That is, the
268 * @aSize argument you pass to Transition::To() must be zero.) Violating these
269 * requirements will trigger assertions and an immediate transition to
270 * TerminalState::FAILURE.
272 * Some lexers may want to *avoid* buffering in some cases, and just process the
273 * data as it comes in. This is useful if, for example, you just want to skip
274 * over a large section of data; there's no point in buffering data you're just
275 * going to ignore.
277 * You can begin an unbuffered read with Transition::ToUnbuffered(). This works
278 * a little differently than Transition::To() in that you specify *two* states.
279 * The @aUnbufferedState argument specifies a state that will be called
280 * repeatedly with unbuffered data, as soon as it arrives. The implementation
281 * for that state should return either a transition to a terminal state, or a
282 * Transition::ContinueUnbuffered() to the same @aUnbufferedState. (From a
283 * technical perspective, it's not necessary to specify the state again, but
284 * it's helpful to human readers.) Once the amount of data requested in the
285 * original call to Transition::ToUnbuffered() has been delivered, Lex() will
286 * transition to the @aNextState state specified via Transition::ToUnbuffered().
287 * That state will be invoked with *no* data; it's just called to signal that
288 * the unbuffered read is over.
290 * It's sometimes useful for a lexer to provide incremental results, rather
291 * than simply running to completion and presenting all its output at once. For
292 * example, when decoding animated images, it may be useful to produce each
293 * frame incrementally. StreamingLexer supports this by allowing a lexer to
294 * yield.
296 * To yield back to the caller, a state implementation can simply return
297 * Transition::ToAfterYield(). ToAfterYield()'s @aNextState argument specifies
298 * the next state that the lexer should transition to, just like when using
299 * Transition::To(), but there are two differences. One is that Lex() will
300 * return to the caller before processing any more data when it encounters a
301 * yield transition. This provides an opportunity for the caller to interact
302 * with the lexer's intermediate results. The second difference is that
303 * @aNextState will be called with *the same data as the state that you returned
304 * Transition::ToAfterYield() from*. This allows a lexer to partially consume
305 * the data, return intermediate results, and then finish consuming the data
306 * when @aNextState is called.
308 * It's also possible to yield during an unbuffered read. Just return a
309 * Transition::ContinueUnbufferedAfterYield(). Just like with
310 * Transition::ContinueUnbuffered(), the @aUnbufferedState must be the same as
311 * the one originally passed to Transition::ToUnbuffered(). The second argument,
312 * @aSize, specifies the amount of data that the lexer has already consumed.
313 * When @aUnbufferedState is next invoked, it will get the same data that it
314 * received previously, except that the first @aSize bytes will be excluded.
315 * This makes it easy to consume unbuffered data incrementally.
317 * XXX(seth): We should be able to get of the |State| stuff totally once bug
318 * 1198451 lands, since we can then just return a function representing the next
319 * state directly.
321 template <typename State, size_t InlineBufferSize = 16>
322 class StreamingLexer {
323 public:
324 StreamingLexer(const LexerTransition<State>& aStartState,
325 const LexerTransition<State>& aTruncatedState)
326 : mTransition(TerminalState::FAILURE),
327 mTruncatedTransition(aTruncatedState) {
328 if (!aStartState.NextStateIsTerminal() &&
329 aStartState.ControlFlow() == ControlFlowStrategy::YIELD) {
330 // Allowing a StreamingLexer to start in a yield state doesn't make sense
331 // semantically (since yield states are supposed to deliver the same data
332 // as previous states, and there's no previous state here), but more
333 // importantly, it's necessary to advance a SourceBufferIterator at least
334 // once before you can read from it, and adding the necessary checks to
335 // Lex() to avoid that issue has the potential to mask real bugs. So
336 // instead, it's better to forbid starting in a yield state.
337 MOZ_ASSERT_UNREACHABLE("Starting in a yield state");
338 return;
341 if (!aTruncatedState.NextStateIsTerminal() &&
342 (aTruncatedState.ControlFlow() == ControlFlowStrategy::YIELD ||
343 aTruncatedState.Buffering() == BufferingStrategy::UNBUFFERED ||
344 aTruncatedState.Size() != 0)) {
345 // The truncated state can't receive any data because, by definition,
346 // there is no more data to receive. That means that yielding or an
347 // unbuffered read would not make sense, and that the state must require
348 // zero bytes.
349 MOZ_ASSERT_UNREACHABLE("Truncated state makes no sense");
350 return;
353 SetTransition(aStartState);
357 * From the given SourceBufferIterator, aIterator, create a new iterator at
358 * the same position, with the given read limit, aReadLimit. The read limit
359 * applies after adjusting for the position. If the given iterator has been
360 * advanced, but required buffering inside StreamingLexer, the position
361 * of the cloned iterator will be at the beginning of buffered data; this
362 * should match the perspective of the caller.
364 Maybe<SourceBufferIterator> Clone(SourceBufferIterator& aIterator,
365 size_t aReadLimit) const {
366 // In order to advance to the current position of the iterator from the
367 // perspective of the caller, we need to take into account if we are
368 // buffering data.
369 size_t pos = aIterator.Position();
370 if (!mBuffer.empty()) {
371 pos += aIterator.Length();
372 MOZ_ASSERT(pos > mBuffer.length());
373 pos -= mBuffer.length();
376 size_t readLimit = aReadLimit;
377 if (aReadLimit != SIZE_MAX) {
378 readLimit += pos;
381 SourceBufferIterator other = aIterator.Owner()->Iterator(readLimit);
383 // Since the current iterator has already advanced to this point, we
384 // know that the state can only be READY or COMPLETE. That does not mean
385 // everything is stored in a single chunk, and may require multiple Advance
386 // calls to get where we want to be.
387 SourceBufferIterator::State state;
388 do {
389 state = other.Advance(pos);
390 if (state != SourceBufferIterator::READY) {
391 // The only way we should fail to advance over data we already seen is
392 // if we hit an error while inserting data into the buffer. This will
393 // cause an early exit.
394 MOZ_ASSERT(NS_FAILED(other.CompletionStatus()));
395 return Nothing();
397 MOZ_ASSERT(pos >= other.Length());
398 pos -= other.Length();
399 } while (pos > 0);
401 // Force the data pointer to be where we expect it to be.
402 state = other.Advance(0);
403 if (state != SourceBufferIterator::READY) {
404 // The current position could be the end of the buffer, in which case
405 // there is no point cloning with no more data to read.
406 MOZ_ASSERT(state == SourceBufferIterator::COMPLETE);
407 return Nothing();
409 return Some(std::move(other));
412 template <typename Func>
413 LexerResult Lex(SourceBufferIterator& aIterator, IResumable* aOnResume,
414 Func aFunc) {
415 if (mTransition.NextStateIsTerminal()) {
416 // We've already reached a terminal state. We never deliver any more data
417 // in this case; just return the terminal state again immediately.
418 return LexerResult(mTransition.NextStateAsTerminal());
421 Maybe<LexerResult> result;
423 // If the lexer requested a yield last time, we deliver the same data again
424 // before we read anything else from |aIterator|. Note that although to the
425 // callers of Lex(), Yield::NEED_MORE_DATA is just another type of yield,
426 // internally they're different in that we don't redeliver the same data in
427 // the Yield::NEED_MORE_DATA case, and |mYieldingToState| is not set. This
428 // means that for Yield::NEED_MORE_DATA, we go directly to the loop below.
429 if (mYieldingToState) {
430 result = mTransition.Buffering() == BufferingStrategy::UNBUFFERED
431 ? UnbufferedReadAfterYield(aIterator, aFunc)
432 : BufferedReadAfterYield(aIterator, aFunc);
435 while (!result) {
436 MOZ_ASSERT_IF(mTransition.Buffering() == BufferingStrategy::UNBUFFERED,
437 mUnbufferedState);
439 // Figure out how much we need to read.
440 const size_t toRead =
441 mTransition.Buffering() == BufferingStrategy::UNBUFFERED
442 ? mUnbufferedState->mBytesRemaining
443 : mTransition.Size() - mBuffer.length();
445 // Attempt to advance the iterator by |toRead| bytes.
446 switch (aIterator.AdvanceOrScheduleResume(toRead, aOnResume)) {
447 case SourceBufferIterator::WAITING:
448 // We can't continue because the rest of the data hasn't arrived from
449 // the network yet. We don't have to do anything special; the
450 // SourceBufferIterator will ensure that |aOnResume| gets called when
451 // more data is available.
452 result = Some(LexerResult(Yield::NEED_MORE_DATA));
453 break;
455 case SourceBufferIterator::COMPLETE:
456 // The data is truncated; if not, the lexer would've reached a
457 // terminal state by now. We only get to
458 // SourceBufferIterator::COMPLETE after every byte of data has been
459 // delivered to the lexer.
460 result = Truncated(aIterator, aFunc);
461 break;
463 case SourceBufferIterator::READY:
464 // Process the new data that became available.
465 MOZ_ASSERT(aIterator.Data());
467 result = mTransition.Buffering() == BufferingStrategy::UNBUFFERED
468 ? UnbufferedRead(aIterator, aFunc)
469 : BufferedRead(aIterator, aFunc);
470 break;
472 default:
473 MOZ_ASSERT_UNREACHABLE("Unknown SourceBufferIterator state");
474 result = SetTransition(Transition::TerminateFailure());
478 return *result;
481 private:
482 template <typename Func>
483 Maybe<LexerResult> UnbufferedRead(SourceBufferIterator& aIterator,
484 Func aFunc) {
485 MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::UNBUFFERED);
486 MOZ_ASSERT(mUnbufferedState);
487 MOZ_ASSERT(!mYieldingToState);
488 MOZ_ASSERT(mBuffer.empty(),
489 "Buffered read at the same time as unbuffered read?");
490 MOZ_ASSERT(aIterator.Length() <= mUnbufferedState->mBytesRemaining,
491 "Read too much data during unbuffered read?");
492 MOZ_ASSERT(mUnbufferedState->mBytesConsumedInCurrentChunk == 0,
493 "Already consumed data in the current chunk, but not yielding?");
495 if (mUnbufferedState->mBytesRemaining == 0) {
496 // We're done with the unbuffered read, so transition to the next state.
497 return SetTransition(aFunc(mTransition.NextState(), nullptr, 0));
500 return ContinueUnbufferedRead(aIterator.Data(), aIterator.Length(),
501 aIterator.Length(), aFunc);
504 template <typename Func>
505 Maybe<LexerResult> UnbufferedReadAfterYield(SourceBufferIterator& aIterator,
506 Func aFunc) {
507 MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::UNBUFFERED);
508 MOZ_ASSERT(mUnbufferedState);
509 MOZ_ASSERT(mYieldingToState);
510 MOZ_ASSERT(mBuffer.empty(),
511 "Buffered read at the same time as unbuffered read?");
512 MOZ_ASSERT(aIterator.Length() <= mUnbufferedState->mBytesRemaining,
513 "Read too much data during unbuffered read?");
514 MOZ_ASSERT(
515 mUnbufferedState->mBytesConsumedInCurrentChunk <= aIterator.Length(),
516 "Consumed more data than the current chunk holds?");
517 MOZ_ASSERT(mTransition.UnbufferedState() == *mYieldingToState);
519 mYieldingToState = Nothing();
521 if (mUnbufferedState->mBytesRemaining == 0) {
522 // We're done with the unbuffered read, so transition to the next state.
523 return SetTransition(aFunc(mTransition.NextState(), nullptr, 0));
526 // Since we've yielded, we may have already consumed some data in this
527 // chunk. Make the necessary adjustments. (Note that the std::min call is
528 // just belt-and-suspenders to keep this code memory safe even if there's
529 // a bug somewhere.)
530 const size_t toSkip = std::min(
531 mUnbufferedState->mBytesConsumedInCurrentChunk, aIterator.Length());
532 const char* data = aIterator.Data() + toSkip;
533 const size_t length = aIterator.Length() - toSkip;
535 // If |length| is zero, we've hit the end of the current chunk. This only
536 // happens if we yield right at the end of a chunk. Rather than call |aFunc|
537 // with a |length| of zero bytes (which seems potentially surprising to
538 // decoder authors), we go ahead and read more data.
539 if (length == 0) {
540 return FinishCurrentChunkOfUnbufferedRead(aIterator.Length());
543 return ContinueUnbufferedRead(data, length, aIterator.Length(), aFunc);
546 template <typename Func>
547 Maybe<LexerResult> ContinueUnbufferedRead(const char* aData, size_t aLength,
548 size_t aChunkLength, Func aFunc) {
549 // Call aFunc with the unbuffered state to indicate that we're in the
550 // middle of an unbuffered read. We enforce that any state transition
551 // passed back to us is either a terminal state or takes us back to the
552 // unbuffered state.
553 LexerTransition<State> unbufferedTransition =
554 aFunc(mTransition.UnbufferedState(), aData, aLength);
556 // If we reached a terminal state, we're done.
557 if (unbufferedTransition.NextStateIsTerminal()) {
558 return SetTransition(unbufferedTransition);
561 MOZ_ASSERT(mTransition.UnbufferedState() ==
562 unbufferedTransition.NextState());
564 // Perform bookkeeping.
565 if (unbufferedTransition.ControlFlow() == ControlFlowStrategy::YIELD) {
566 mUnbufferedState->mBytesConsumedInCurrentChunk +=
567 unbufferedTransition.Size();
568 return SetTransition(unbufferedTransition);
571 MOZ_ASSERT(unbufferedTransition.Size() == 0);
572 return FinishCurrentChunkOfUnbufferedRead(aChunkLength);
575 Maybe<LexerResult> FinishCurrentChunkOfUnbufferedRead(size_t aChunkLength) {
576 // We've finished an unbuffered read of a chunk of length |aChunkLength|, so
577 // update |myBytesRemaining| to reflect that we're |aChunkLength| closer to
578 // the end of the unbuffered read. (The std::min call is just
579 // belt-and-suspenders to keep this code memory safe even if there's a bug
580 // somewhere.)
581 mUnbufferedState->mBytesRemaining -=
582 std::min(mUnbufferedState->mBytesRemaining, aChunkLength);
584 // Since we're moving on to a new chunk, we can forget about the count of
585 // bytes consumed by yielding in the current chunk.
586 mUnbufferedState->mBytesConsumedInCurrentChunk = 0;
588 return Nothing(); // Keep processing.
591 template <typename Func>
592 Maybe<LexerResult> BufferedRead(SourceBufferIterator& aIterator, Func aFunc) {
593 MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
594 MOZ_ASSERT(!mYieldingToState);
595 MOZ_ASSERT(!mUnbufferedState,
596 "Buffered read at the same time as unbuffered read?");
597 MOZ_ASSERT(mBuffer.length() < mTransition.Size() ||
598 (mBuffer.length() == 0 && mTransition.Size() == 0),
599 "Buffered more than we needed?");
601 // If we have all the data, we don't actually need to buffer anything.
602 if (mBuffer.empty() && aIterator.Length() == mTransition.Size()) {
603 return SetTransition(
604 aFunc(mTransition.NextState(), aIterator.Data(), aIterator.Length()));
607 // We do need to buffer, so make sure the buffer has enough capacity. We
608 // deliberately wait until we know for sure we need to buffer to call
609 // reserve() since it could require memory allocation.
610 if (!mBuffer.reserve(mTransition.Size())) {
611 return SetTransition(Transition::TerminateFailure());
614 // Append the new data we just got to the buffer.
615 if (!mBuffer.append(aIterator.Data(), aIterator.Length())) {
616 return SetTransition(Transition::TerminateFailure());
619 if (mBuffer.length() != mTransition.Size()) {
620 return Nothing(); // Keep processing.
623 // We've buffered everything, so transition to the next state.
624 return SetTransition(
625 aFunc(mTransition.NextState(), mBuffer.begin(), mBuffer.length()));
628 template <typename Func>
629 Maybe<LexerResult> BufferedReadAfterYield(SourceBufferIterator& aIterator,
630 Func aFunc) {
631 MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
632 MOZ_ASSERT(mYieldingToState);
633 MOZ_ASSERT(!mUnbufferedState,
634 "Buffered read at the same time as unbuffered read?");
635 MOZ_ASSERT(mBuffer.length() <= mTransition.Size(),
636 "Buffered more than we needed?");
638 State nextState = std::move(*mYieldingToState);
640 // After a yield, we need to take the same data that we delivered to the
641 // last state, and deliver it again to the new state. We know that this is
642 // happening right at a state transition, and that the last state was a
643 // buffered read, so there are two cases:
645 // 1. We got the data from the SourceBufferIterator directly.
646 if (mBuffer.empty() && aIterator.Length() == mTransition.Size()) {
647 return SetTransition(
648 aFunc(nextState, aIterator.Data(), aIterator.Length()));
651 // 2. We got the data from the buffer.
652 if (mBuffer.length() == mTransition.Size()) {
653 return SetTransition(aFunc(nextState, mBuffer.begin(), mBuffer.length()));
656 // Anything else indicates a bug.
657 MOZ_ASSERT_UNREACHABLE("Unexpected state encountered during yield");
658 return SetTransition(Transition::TerminateFailure());
661 template <typename Func>
662 Maybe<LexerResult> Truncated(SourceBufferIterator& aIterator, Func aFunc) {
663 // The data is truncated. Let the lexer clean up and decide which terminal
664 // state we should end up in.
665 LexerTransition<State> transition =
666 mTruncatedTransition.NextStateIsTerminal()
667 ? mTruncatedTransition
668 : aFunc(mTruncatedTransition.NextState(), nullptr, 0);
670 if (!transition.NextStateIsTerminal()) {
671 MOZ_ASSERT_UNREACHABLE("Truncated state didn't lead to terminal state?");
672 return SetTransition(Transition::TerminateFailure());
675 // If the SourceBuffer was completed with a failing state, we end in
676 // TerminalState::FAILURE no matter what. This only happens in exceptional
677 // situations like SourceBuffer itself encountering a failure due to OOM.
678 if (NS_FAILED(aIterator.CompletionStatus())) {
679 return SetTransition(Transition::TerminateFailure());
682 return SetTransition(transition);
685 Maybe<LexerResult> SetTransition(const LexerTransition<State>& aTransition) {
686 // There should be no transitions while we're buffering for a buffered read
687 // unless they're to terminal states. (The terminal state transitions would
688 // generally be triggered by error handling code.)
689 MOZ_ASSERT_IF(!mBuffer.empty(), aTransition.NextStateIsTerminal() ||
690 mBuffer.length() == mTransition.Size());
692 // Similarly, the only transitions allowed in the middle of an unbuffered
693 // read are to a terminal state, or a yield to the same state. Otherwise, we
694 // should remain in the same state until the unbuffered read completes.
695 MOZ_ASSERT_IF(
696 mUnbufferedState,
697 aTransition.NextStateIsTerminal() ||
698 (aTransition.ControlFlow() == ControlFlowStrategy::YIELD &&
699 aTransition.NextState() == mTransition.UnbufferedState()) ||
700 mUnbufferedState->mBytesRemaining == 0);
702 // If this transition is a yield, save the next state and return. We'll
703 // handle the rest when Lex() gets called again.
704 if (!aTransition.NextStateIsTerminal() &&
705 aTransition.ControlFlow() == ControlFlowStrategy::YIELD) {
706 mYieldingToState = Some(aTransition.NextState());
707 return Some(LexerResult(Yield::OUTPUT_AVAILABLE));
710 // Update our transition.
711 mTransition = aTransition;
713 // Get rid of anything left over from the previous state.
714 mBuffer.clear();
715 mYieldingToState = Nothing();
716 mUnbufferedState = Nothing();
718 // If we reached a terminal state, let the caller know.
719 if (mTransition.NextStateIsTerminal()) {
720 return Some(LexerResult(mTransition.NextStateAsTerminal()));
723 // If we're entering an unbuffered state, record how long we'll stay in it.
724 if (mTransition.Buffering() == BufferingStrategy::UNBUFFERED) {
725 mUnbufferedState.emplace(mTransition.Size());
728 return Nothing(); // Keep processing.
731 // State that tracks our position within an unbuffered read.
732 struct UnbufferedState {
733 explicit UnbufferedState(size_t aBytesRemaining)
734 : mBytesRemaining(aBytesRemaining), mBytesConsumedInCurrentChunk(0) {}
736 size_t mBytesRemaining;
737 size_t mBytesConsumedInCurrentChunk;
740 Vector<char, InlineBufferSize> mBuffer;
741 LexerTransition<State> mTransition;
742 const LexerTransition<State> mTruncatedTransition;
743 Maybe<State> mYieldingToState;
744 Maybe<UnbufferedState> mUnbufferedState;
747 } // namespace image
748 } // namespace mozilla
750 #endif // mozilla_image_StreamingLexer_h