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