mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1210291 - Streamline StreamingLexer's handling of terminal states. r=seth.
This patch introduces TerminalState and changes LexerTransition::mNextState to be a Variant<State, TerminalState>. This means that SUCCESS and FAILURE no longer need to be part of State. Some things to note: - This simplifies the handling of Lex()'s return value, which is nice. - The patch splits Terminate() into TerminateSuccess() and TerminateFailure(). - |const State& aNextState| wouldn't work for the first arg to LexerTransition's ctor due to errors in Variant construction that I didn't understand. I had to change it to |State aNextState|.
This commit is contained in:
parent
c78a9e3e4c
commit
28bfe1d2d9
@ -1,4 +1,5 @@
|
|||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
@ -14,7 +15,9 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
|
#include "mozilla/Variant.h"
|
||||||
#include "mozilla/Vector.h"
|
#include "mozilla/Vector.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
@ -27,13 +30,12 @@ enum class BufferingStrategy
|
|||||||
UNBUFFERED // Data will be processed as it arrives, in multiple chunks.
|
UNBUFFERED // Data will be processed as it arrives, in multiple chunks.
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @return true if @aState is a terminal state.
|
/// The result of a call to StreamingLexer::Lex().
|
||||||
template <typename State>
|
enum class TerminalState
|
||||||
bool IsTerminalState(State aState)
|
|
||||||
{
|
{
|
||||||
return aState == State::SUCCESS ||
|
SUCCESS,
|
||||||
aState == State::FAILURE;
|
FAILURE
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LexerTransition is a type used to give commands to the lexing framework.
|
* LexerTransition is a type used to give commands to the lexing framework.
|
||||||
@ -45,33 +47,78 @@ template <typename State>
|
|||||||
class LexerTransition
|
class LexerTransition
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
State NextState() const { return mNextState; }
|
// This is implicit so that Terminate{Success,Failure}() can return a
|
||||||
State UnbufferedState() const { return *mUnbufferedState; }
|
// TerminalState and have it implicitly converted to a
|
||||||
size_t Size() const { return mSize; }
|
// LexerTransition<State>, which avoids the need for a "<State>"
|
||||||
BufferingStrategy Buffering() const { return mBufferingStrategy; }
|
// qualification to the Terminate{Success,Failure}() callsite.
|
||||||
|
MOZ_IMPLICIT LexerTransition(TerminalState aFinalState)
|
||||||
|
: mNextState(aFinalState)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool NextStateIsTerminal() const
|
||||||
|
{
|
||||||
|
return mNextState.template is<TerminalState>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalState NextStateAsTerminal() const
|
||||||
|
{
|
||||||
|
return mNextState.template as<TerminalState>();
|
||||||
|
}
|
||||||
|
|
||||||
|
State NextState() const
|
||||||
|
{
|
||||||
|
return mNextState.template as<NonTerminalState>().mState;
|
||||||
|
}
|
||||||
|
|
||||||
|
State UnbufferedState() const
|
||||||
|
{
|
||||||
|
return *mNextState.template as<NonTerminalState>().mUnbufferedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Size() const
|
||||||
|
{
|
||||||
|
return mNextState.template as<NonTerminalState>().mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferingStrategy Buffering() const
|
||||||
|
{
|
||||||
|
return mNextState.template as<NonTerminalState>().mBufferingStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct Transition;
|
friend struct Transition;
|
||||||
|
|
||||||
LexerTransition(const State& aNextState,
|
LexerTransition(State aNextState,
|
||||||
const Maybe<State>& aUnbufferedState,
|
const Maybe<State>& aUnbufferedState,
|
||||||
size_t aSize,
|
size_t aSize,
|
||||||
BufferingStrategy aBufferingStrategy)
|
BufferingStrategy aBufferingStrategy)
|
||||||
: mNextState(aNextState)
|
: mNextState(NonTerminalState(aNextState, aUnbufferedState, aSize,
|
||||||
, mUnbufferedState(aUnbufferedState)
|
aBufferingStrategy))
|
||||||
, mSize(aSize)
|
{}
|
||||||
, mBufferingStrategy(aBufferingStrategy)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT_IF(mBufferingStrategy == BufferingStrategy::UNBUFFERED,
|
|
||||||
mUnbufferedState);
|
|
||||||
MOZ_ASSERT_IF(mUnbufferedState,
|
|
||||||
mBufferingStrategy == BufferingStrategy::UNBUFFERED);
|
|
||||||
}
|
|
||||||
|
|
||||||
State mNextState;
|
struct NonTerminalState
|
||||||
Maybe<State> mUnbufferedState;
|
{
|
||||||
size_t mSize;
|
State mState;
|
||||||
BufferingStrategy mBufferingStrategy;
|
Maybe<State> mUnbufferedState;
|
||||||
|
size_t mSize;
|
||||||
|
BufferingStrategy mBufferingStrategy;
|
||||||
|
|
||||||
|
NonTerminalState(State aState,
|
||||||
|
const Maybe<State>& aUnbufferedState,
|
||||||
|
size_t aSize,
|
||||||
|
BufferingStrategy aBufferingStrategy)
|
||||||
|
: mState(aState)
|
||||||
|
, mUnbufferedState(aUnbufferedState)
|
||||||
|
, mSize(aSize)
|
||||||
|
, mBufferingStrategy(aBufferingStrategy)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT_IF(mBufferingStrategy == BufferingStrategy::UNBUFFERED,
|
||||||
|
mUnbufferedState);
|
||||||
|
MOZ_ASSERT_IF(mUnbufferedState,
|
||||||
|
mBufferingStrategy == BufferingStrategy::UNBUFFERED);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Variant<NonTerminalState, TerminalState> mNextState;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Transition
|
struct Transition
|
||||||
@ -81,7 +128,6 @@ struct Transition
|
|||||||
static LexerTransition<State>
|
static LexerTransition<State>
|
||||||
To(const State& aNextState, size_t aSize)
|
To(const State& aNextState, size_t aSize)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!IsTerminalState(aNextState));
|
|
||||||
return LexerTransition<State>(aNextState, Nothing(), aSize,
|
return LexerTransition<State>(aNextState, Nothing(), aSize,
|
||||||
BufferingStrategy::BUFFERED);
|
BufferingStrategy::BUFFERED);
|
||||||
}
|
}
|
||||||
@ -102,8 +148,6 @@ struct Transition
|
|||||||
const State& aUnbufferedState,
|
const State& aUnbufferedState,
|
||||||
size_t aSize)
|
size_t aSize)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!IsTerminalState(aNextState));
|
|
||||||
MOZ_ASSERT(!IsTerminalState(aUnbufferedState));
|
|
||||||
return LexerTransition<State>(aNextState, Some(aUnbufferedState), aSize,
|
return LexerTransition<State>(aNextState, Some(aUnbufferedState), aSize,
|
||||||
BufferingStrategy::UNBUFFERED);
|
BufferingStrategy::UNBUFFERED);
|
||||||
}
|
}
|
||||||
@ -119,23 +163,34 @@ struct Transition
|
|||||||
static LexerTransition<State>
|
static LexerTransition<State>
|
||||||
ContinueUnbuffered(const State& aUnbufferedState)
|
ContinueUnbuffered(const State& aUnbufferedState)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!IsTerminalState(aUnbufferedState));
|
|
||||||
return LexerTransition<State>(aUnbufferedState, Nothing(), 0,
|
return LexerTransition<State>(aUnbufferedState, Nothing(), 0,
|
||||||
BufferingStrategy::BUFFERED);
|
BufferingStrategy::BUFFERED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminate lexing, ending up in terminal state @aFinalState.
|
* Terminate lexing, ending up in terminal state SUCCESS. (The implicit
|
||||||
|
* LexerTransition constructor will convert the result to a LexerTransition
|
||||||
|
* as needed.)
|
||||||
*
|
*
|
||||||
* No more data will be delivered after Terminate() is used.
|
* No more data will be delivered after this function is used.
|
||||||
*/
|
*/
|
||||||
template <typename State>
|
static TerminalState
|
||||||
static LexerTransition<State>
|
TerminateSuccess()
|
||||||
Terminate(const State& aFinalState)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsTerminalState(aFinalState));
|
return TerminalState::SUCCESS;
|
||||||
return LexerTransition<State>(aFinalState, Nothing(), 0,
|
}
|
||||||
BufferingStrategy::BUFFERED);
|
|
||||||
|
/**
|
||||||
|
* Terminate lexing, ending up in terminal state FAILURE. (The implicit
|
||||||
|
* LexerTransition constructor will convert the result to a LexerTransition
|
||||||
|
* as needed.)
|
||||||
|
*
|
||||||
|
* No more data will be delivered after this function is used.
|
||||||
|
*/
|
||||||
|
static TerminalState
|
||||||
|
TerminateFailure()
|
||||||
|
{
|
||||||
|
return TerminalState::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -151,7 +206,7 @@ private:
|
|||||||
*
|
*
|
||||||
* - Create a State type. This should be an |enum class| listing all of the
|
* - Create a State type. This should be an |enum class| listing all of the
|
||||||
* states that you can be in while lexing the image format you're trying to
|
* states that you can be in while lexing the image format you're trying to
|
||||||
* read. It must contain the two terminal states SUCCESS and FAILURE.
|
* read.
|
||||||
*
|
*
|
||||||
* - Add an instance of StreamingLexer<State> to your decoder class. Initialize
|
* - Add an instance of StreamingLexer<State> to your decoder class. Initialize
|
||||||
* it with a Transition::To() the state that you want to start lexing in.
|
* it with a Transition::To() the state that you want to start lexing in.
|
||||||
@ -166,9 +221,9 @@ private:
|
|||||||
*
|
*
|
||||||
* - Write the methods that actually implement lexing for your image format.
|
* - Write the methods that actually implement lexing for your image format.
|
||||||
* These methods should return either Transition::To(), to move on to another
|
* These methods should return either Transition::To(), to move on to another
|
||||||
* state, or Transition::Terminate(), if lexing has terminated in either
|
* state, or Transition::Terminate{Success,Failure}(), if lexing has
|
||||||
* success or failure. (There are also additional transitions for unbuffered
|
* terminated in either success or failure. (There are also additional
|
||||||
* reads; see below.)
|
* transitions for unbuffered reads; see below.)
|
||||||
*
|
*
|
||||||
* That's all there is to it. The StreamingLexer will track your position in the
|
* That's all there is to it. The StreamingLexer will track your position in the
|
||||||
* input and buffer enough data so that your lexing methods can process
|
* input and buffer enough data so that your lexing methods can process
|
||||||
@ -208,12 +263,12 @@ public:
|
|||||||
{ }
|
{ }
|
||||||
|
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
Maybe<State> Lex(const char* aInput, size_t aLength, Func aFunc)
|
Maybe<TerminalState> Lex(const char* aInput, size_t aLength, Func aFunc)
|
||||||
{
|
{
|
||||||
if (IsTerminalState(mTransition.NextState())) {
|
if (mTransition.NextStateIsTerminal()) {
|
||||||
// We've already reached a terminal state. We never deliver any more data
|
// We've already reached a terminal state. We never deliver any more data
|
||||||
// in this case; just return the terminal state again immediately.
|
// in this case; just return the terminal state again immediately.
|
||||||
return Some(mTransition.NextState());
|
return Some(mTransition.NextStateAsTerminal());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mToReadUnbuffered > 0) {
|
if (mToReadUnbuffered > 0) {
|
||||||
@ -225,15 +280,15 @@ public:
|
|||||||
|
|
||||||
size_t toRead = std::min(mToReadUnbuffered, aLength);
|
size_t toRead = std::min(mToReadUnbuffered, aLength);
|
||||||
|
|
||||||
// Call aFunc with the unbuffered state to indicate that we're in the middle
|
// Call aFunc with the unbuffered state to indicate that we're in the
|
||||||
// of an unbuffered read. We enforce that any state transition passed back
|
// middle of an unbuffered read. We enforce that any state transition
|
||||||
// to us is either a terminal states or takes us back to the unbuffered
|
// passed back to us is either a terminal state or takes us back to the
|
||||||
// state.
|
// unbuffered state.
|
||||||
LexerTransition<State> unbufferedTransition =
|
LexerTransition<State> unbufferedTransition =
|
||||||
aFunc(mTransition.UnbufferedState(), aInput, toRead);
|
aFunc(mTransition.UnbufferedState(), aInput, toRead);
|
||||||
if (IsTerminalState(unbufferedTransition.NextState())) {
|
if (unbufferedTransition.NextStateIsTerminal()) {
|
||||||
mTransition = unbufferedTransition;
|
mTransition = unbufferedTransition;
|
||||||
return Some(mTransition.NextState()); // Done!
|
return Some(mTransition.NextStateAsTerminal()); // Done!
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(mTransition.UnbufferedState() ==
|
MOZ_ASSERT(mTransition.UnbufferedState() ==
|
||||||
unbufferedTransition.NextState());
|
unbufferedTransition.NextState());
|
||||||
@ -247,8 +302,8 @@ public:
|
|||||||
|
|
||||||
// We're done with the unbuffered read, so transition to the next state.
|
// We're done with the unbuffered read, so transition to the next state.
|
||||||
mTransition = aFunc(mTransition.NextState(), nullptr, 0);
|
mTransition = aFunc(mTransition.NextState(), nullptr, 0);
|
||||||
if (IsTerminalState(mTransition.NextState())) {
|
if (mTransition.NextStateIsTerminal()) {
|
||||||
return Some(mTransition.NextState()); // Done!
|
return Some(mTransition.NextStateAsTerminal()); // Done!
|
||||||
}
|
}
|
||||||
} else if (0 < mBuffer.length()) {
|
} else if (0 < mBuffer.length()) {
|
||||||
// We're continuing a buffered read.
|
// We're continuing a buffered read.
|
||||||
@ -272,8 +327,8 @@ public:
|
|||||||
mTransition =
|
mTransition =
|
||||||
aFunc(mTransition.NextState(), mBuffer.begin(), mBuffer.length());
|
aFunc(mTransition.NextState(), mBuffer.begin(), mBuffer.length());
|
||||||
mBuffer.clear();
|
mBuffer.clear();
|
||||||
if (IsTerminalState(mTransition.NextState())) {
|
if (mTransition.NextStateIsTerminal()) {
|
||||||
return Some(mTransition.NextState()); // Done!
|
return Some(mTransition.NextStateAsTerminal()); // Done!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,13 +346,13 @@ public:
|
|||||||
|
|
||||||
// Call aFunc with the unbuffered state to indicate that we're in the
|
// Call aFunc with the unbuffered state to indicate that we're in the
|
||||||
// middle of an unbuffered read. We enforce that any state transition
|
// middle of an unbuffered read. We enforce that any state transition
|
||||||
// passed back to us is either a terminal states or takes us back to the
|
// passed back to us is either a terminal state or takes us back to the
|
||||||
// unbuffered state.
|
// unbuffered state.
|
||||||
LexerTransition<State> unbufferedTransition =
|
LexerTransition<State> unbufferedTransition =
|
||||||
aFunc(mTransition.UnbufferedState(), aInput, toRead);
|
aFunc(mTransition.UnbufferedState(), aInput, toRead);
|
||||||
if (IsTerminalState(unbufferedTransition.NextState())) {
|
if (unbufferedTransition.NextStateIsTerminal()) {
|
||||||
mTransition = unbufferedTransition;
|
mTransition = unbufferedTransition;
|
||||||
return Some(mTransition.NextState()); // Done!
|
return Some(mTransition.NextStateAsTerminal()); // Done!
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(mTransition.UnbufferedState() ==
|
MOZ_ASSERT(mTransition.UnbufferedState() ==
|
||||||
unbufferedTransition.NextState());
|
unbufferedTransition.NextState());
|
||||||
@ -309,8 +364,8 @@ public:
|
|||||||
aInput += toRead;
|
aInput += toRead;
|
||||||
aLength -= toRead;
|
aLength -= toRead;
|
||||||
|
|
||||||
if (IsTerminalState(mTransition.NextState())) {
|
if (mTransition.NextStateIsTerminal()) {
|
||||||
return Some(mTransition.NextState()); // Done!
|
return Some(mTransition.NextStateAsTerminal()); // Done!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,9 +378,9 @@ public:
|
|||||||
if (mTransition.Buffering() == BufferingStrategy::UNBUFFERED) {
|
if (mTransition.Buffering() == BufferingStrategy::UNBUFFERED) {
|
||||||
LexerTransition<State> unbufferedTransition =
|
LexerTransition<State> unbufferedTransition =
|
||||||
aFunc(mTransition.UnbufferedState(), aInput, aLength);
|
aFunc(mTransition.UnbufferedState(), aInput, aLength);
|
||||||
if (IsTerminalState(unbufferedTransition.NextState())) {
|
if (unbufferedTransition.NextStateIsTerminal()) {
|
||||||
mTransition = unbufferedTransition;
|
mTransition = unbufferedTransition;
|
||||||
return Some(mTransition.NextState()); // Done!
|
return Some(mTransition.NextStateAsTerminal()); // Done!
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(mTransition.UnbufferedState() ==
|
MOZ_ASSERT(mTransition.UnbufferedState() ==
|
||||||
unbufferedTransition.NextState());
|
unbufferedTransition.NextState());
|
||||||
@ -333,11 +388,11 @@ public:
|
|||||||
mToReadUnbuffered = mTransition.Size() - aLength;
|
mToReadUnbuffered = mTransition.Size() - aLength;
|
||||||
return Nothing(); // Need more input.
|
return Nothing(); // Need more input.
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the next state is buffered, buffer what we can and then wait.
|
// If the next state is buffered, buffer what we can and then wait.
|
||||||
MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
|
MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
|
||||||
if (!mBuffer.reserve(mTransition.Size())) {
|
if (!mBuffer.reserve(mTransition.Size())) {
|
||||||
return Some(State::FAILURE); // Done due to allocation failure.
|
return Some(TerminalState::FAILURE); // Done due to allocation failure.
|
||||||
}
|
}
|
||||||
mBuffer.append(aInput, aLength);
|
mBuffer.append(aInput, aLength);
|
||||||
return Nothing(); // Need more input.
|
return Nothing(); // Need more input.
|
||||||
|
@ -437,7 +437,7 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|||||||
MOZ_ASSERT(aBuffer);
|
MOZ_ASSERT(aBuffer);
|
||||||
MOZ_ASSERT(aCount > 0);
|
MOZ_ASSERT(aCount > 0);
|
||||||
|
|
||||||
Maybe<State> terminalState =
|
Maybe<TerminalState> terminalState =
|
||||||
mLexer.Lex(aBuffer, aCount, [=](State aState,
|
mLexer.Lex(aBuffer, aCount, [=](State aState,
|
||||||
const char* aData, size_t aLength) {
|
const char* aData, size_t aLength) {
|
||||||
switch (aState) {
|
switch (aState) {
|
||||||
@ -452,23 +452,13 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|||||||
case State::RLE_DELTA: return ReadRLEDelta(aData);
|
case State::RLE_DELTA: return ReadRLEDelta(aData);
|
||||||
case State::RLE_ABSOLUTE: return ReadRLEAbsolute(aData, aLength);
|
case State::RLE_ABSOLUTE: return ReadRLEAbsolute(aData, aLength);
|
||||||
default:
|
default:
|
||||||
MOZ_ASSERT_UNREACHABLE("Unknown State");
|
MOZ_CRASH("Unknown State");
|
||||||
return Transition::Terminate(State::FAILURE);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!terminalState) {
|
if (terminalState == Some(TerminalState::FAILURE)) {
|
||||||
return; // Need more data.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*terminalState == State::FAILURE) {
|
|
||||||
PostDataError();
|
PostDataError();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(*terminalState == State::SUCCESS);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LexerTransition<nsBMPDecoder::State>
|
LexerTransition<nsBMPDecoder::State>
|
||||||
@ -479,7 +469,7 @@ nsBMPDecoder::ReadFileHeader(const char* aData, size_t aLength)
|
|||||||
bool signatureOk = aData[0] == 'B' && aData[1] == 'M';
|
bool signatureOk = aData[0] == 'B' && aData[1] == 'M';
|
||||||
if (!signatureOk) {
|
if (!signatureOk) {
|
||||||
PostDataError();
|
PostDataError();
|
||||||
return Transition::Terminate(State::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We ignore the filesize (aData + 2) and reserved (aData + 6) fields.
|
// We ignore the filesize (aData + 2) and reserved (aData + 6) fields.
|
||||||
@ -506,7 +496,7 @@ nsBMPDecoder::ReadInfoHeaderSize(const char* aData, size_t aLength)
|
|||||||
mH.mBIHSize <= InfoHeaderLength::OS2_V2_MAX);
|
mH.mBIHSize <= InfoHeaderLength::OS2_V2_MAX);
|
||||||
if (!bihSizeOk) {
|
if (!bihSizeOk) {
|
||||||
PostDataError();
|
PostDataError();
|
||||||
return Transition::Terminate(State::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
// ICO BMPs must have a WinVMPv3 header. nsICODecoder should have already
|
// ICO BMPs must have a WinVMPv3 header. nsICODecoder should have already
|
||||||
// terminated decoding if this isn't the case.
|
// terminated decoding if this isn't the case.
|
||||||
@ -561,7 +551,7 @@ nsBMPDecoder::ReadInfoHeaderRest(const char* aData, size_t aLength)
|
|||||||
mH.mHeight != INT_MIN;
|
mH.mHeight != INT_MIN;
|
||||||
if (!sizeOk) {
|
if (!sizeOk) {
|
||||||
PostDataError();
|
PostDataError();
|
||||||
return Transition::Terminate(State::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check mBpp and mCompression.
|
// Check mBpp and mCompression.
|
||||||
@ -575,7 +565,7 @@ nsBMPDecoder::ReadInfoHeaderRest(const char* aData, size_t aLength)
|
|||||||
(mH.mBpp == 16 || mH.mBpp == 32));
|
(mH.mBpp == 16 || mH.mBpp == 32));
|
||||||
if (!bppCompressionOk) {
|
if (!bppCompressionOk) {
|
||||||
PostDataError();
|
PostDataError();
|
||||||
return Transition::Terminate(State::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post our size to the superclass.
|
// Post our size to the superclass.
|
||||||
@ -652,7 +642,7 @@ nsBMPDecoder::ReadBitfields(const char* aData, size_t aLength)
|
|||||||
// We've now read all the headers. If we're doing a metadata decode, we're
|
// We've now read all the headers. If we're doing a metadata decode, we're
|
||||||
// done.
|
// done.
|
||||||
if (IsMetadataDecode()) {
|
if (IsMetadataDecode()) {
|
||||||
return Transition::Terminate(State::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the color table, if present; it'll be filled in by ReadColorTable().
|
// Set up the color table, if present; it'll be filled in by ReadColorTable().
|
||||||
@ -677,7 +667,7 @@ nsBMPDecoder::ReadBitfields(const char* aData, size_t aLength)
|
|||||||
IntRect(IntPoint(), targetSize),
|
IntRect(IntPoint(), targetSize),
|
||||||
SurfaceFormat::B8G8R8A8);
|
SurfaceFormat::B8G8R8A8);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return Transition::Terminate(State::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
||||||
|
|
||||||
@ -688,7 +678,7 @@ nsBMPDecoder::ReadBitfields(const char* aData, size_t aLength)
|
|||||||
mImageData, mMayHaveTransparency,
|
mImageData, mMayHaveTransparency,
|
||||||
/* aFlipVertically = */ true);
|
/* aFlipVertically = */ true);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return Transition::Terminate(State::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,7 +709,7 @@ nsBMPDecoder::ReadColorTable(const char* aData, size_t aLength)
|
|||||||
// we give up.
|
// we give up.
|
||||||
if (mPreGapLength > mH.mDataOffset) {
|
if (mPreGapLength > mH.mDataOffset) {
|
||||||
PostDataError();
|
PostDataError();
|
||||||
return Transition::Terminate(State::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
uint32_t gapLength = mH.mDataOffset - mPreGapLength;
|
uint32_t gapLength = mH.mDataOffset - mPreGapLength;
|
||||||
return Transition::To(State::GAP, gapLength);
|
return Transition::To(State::GAP, gapLength);
|
||||||
@ -866,7 +856,7 @@ nsBMPDecoder::ReadPixelRow(const char* aData)
|
|||||||
|
|
||||||
FinishRow();
|
FinishRow();
|
||||||
return mCurrentRow == 0
|
return mCurrentRow == 0
|
||||||
? Transition::Terminate(State::SUCCESS)
|
? Transition::TerminateSuccess()
|
||||||
: Transition::To(State::PIXEL_ROW, mPixelRowSize);
|
: Transition::To(State::PIXEL_ROW, mPixelRowSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -874,7 +864,7 @@ LexerTransition<nsBMPDecoder::State>
|
|||||||
nsBMPDecoder::ReadRLESegment(const char* aData)
|
nsBMPDecoder::ReadRLESegment(const char* aData)
|
||||||
{
|
{
|
||||||
if (mCurrentRow == 0) {
|
if (mCurrentRow == 0) {
|
||||||
return Transition::Terminate(State::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t byte1 = uint8_t(aData[0]);
|
uint8_t byte1 = uint8_t(aData[0]);
|
||||||
@ -909,12 +899,12 @@ nsBMPDecoder::ReadRLESegment(const char* aData)
|
|||||||
mCurrentPos = 0;
|
mCurrentPos = 0;
|
||||||
FinishRow();
|
FinishRow();
|
||||||
return mCurrentRow == 0
|
return mCurrentRow == 0
|
||||||
? Transition::Terminate(State::SUCCESS)
|
? Transition::TerminateSuccess()
|
||||||
: Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
|
: Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byte2 == RLE::ESCAPE_EOF) {
|
if (byte2 == RLE::ESCAPE_EOF) {
|
||||||
return Transition::Terminate(State::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byte2 == RLE::ESCAPE_DELTA) {
|
if (byte2 == RLE::ESCAPE_DELTA) {
|
||||||
@ -972,7 +962,7 @@ nsBMPDecoder::ReadRLEDelta(const char* aData)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return mCurrentRow == 0
|
return mCurrentRow == 0
|
||||||
? Transition::Terminate(State::SUCCESS)
|
? Transition::TerminateSuccess()
|
||||||
: Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
|
: Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -985,7 +975,7 @@ nsBMPDecoder::ReadRLEAbsolute(const char* aData, size_t aLength)
|
|||||||
if (mCurrentPos + n > uint32_t(mH.mWidth)) {
|
if (mCurrentPos + n > uint32_t(mH.mWidth)) {
|
||||||
// Bad data. Stop decoding; at least part of the image may have been
|
// Bad data. Stop decoding; at least part of the image may have been
|
||||||
// decoded.
|
// decoded.
|
||||||
return Transition::Terminate(State::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
// In absolute mode, n represents the number of pixels that follow, each of
|
// In absolute mode, n represents the number of pixels that follow, each of
|
||||||
|
@ -167,9 +167,7 @@ private:
|
|||||||
PIXEL_ROW,
|
PIXEL_ROW,
|
||||||
RLE_SEGMENT,
|
RLE_SEGMENT,
|
||||||
RLE_DELTA,
|
RLE_DELTA,
|
||||||
RLE_ABSOLUTE,
|
RLE_ABSOLUTE
|
||||||
SUCCESS,
|
|
||||||
FAILURE
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is the constructor used by DecoderFactory.
|
// This is the constructor used by DecoderFactory.
|
||||||
|
@ -170,14 +170,14 @@ nsICODecoder::ReadHeader(const char* aData)
|
|||||||
{
|
{
|
||||||
// If the third byte is 1, this is an icon. If 2, a cursor.
|
// If the third byte is 1, this is an icon. If 2, a cursor.
|
||||||
if ((aData[2] != 1) && (aData[2] != 2)) {
|
if ((aData[2] != 1) && (aData[2] != 2)) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
mIsCursor = (aData[2] == 2);
|
mIsCursor = (aData[2] == 2);
|
||||||
|
|
||||||
// The fifth and sixth bytes specify the number of resources in the file.
|
// The fifth and sixth bytes specify the number of resources in the file.
|
||||||
mNumIcons = LittleEndian::readUint16(aData + 4);
|
mNumIcons = LittleEndian::readUint16(aData + 4);
|
||||||
if (mNumIcons == 0) {
|
if (mNumIcons == 0) {
|
||||||
return Transition::Terminate(ICOState::SUCCESS); // Nothing to do.
|
return Transition::TerminateSuccess(); // Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Downscale-during-decode can end up decoding different resources in the ICO
|
// Downscale-during-decode can end up decoding different resources in the ICO
|
||||||
@ -257,7 +257,7 @@ nsICODecoder::ReadDirEntry(const char* aData)
|
|||||||
if (mCurrIcon == mNumIcons) {
|
if (mCurrIcon == mNumIcons) {
|
||||||
// Ensure the resource we selected has an offset past the ICO headers.
|
// Ensure the resource we selected has an offset past the ICO headers.
|
||||||
if (mDirEntry.mImageOffset < FirstResourceOffset()) {
|
if (mDirEntry.mImageOffset < FirstResourceOffset()) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a cursor, set the hotspot. We use the hotspot from the biggest
|
// If this is a cursor, set the hotspot. We use the hotspot from the biggest
|
||||||
@ -272,7 +272,7 @@ nsICODecoder::ReadDirEntry(const char* aData)
|
|||||||
// attempt to *upscale* while decoding.
|
// attempt to *upscale* while decoding.
|
||||||
PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
|
PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
|
||||||
if (IsMetadataDecode()) {
|
if (IsMetadataDecode()) {
|
||||||
return Transition::Terminate(ICOState::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the resource we selected matches the downscaler's target size
|
// If the resource we selected matches the downscaler's target size
|
||||||
@ -309,11 +309,11 @@ nsICODecoder::SniffResource(const char* aData)
|
|||||||
mContainedDecoder->Init();
|
mContainedDecoder->Init();
|
||||||
|
|
||||||
if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
|
if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDirEntry.mBytesInRes <= PNGSIGNATURESIZE) {
|
if (mDirEntry.mBytesInRes <= PNGSIGNATURESIZE) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read in the rest of the PNG unbuffered.
|
// Read in the rest of the PNG unbuffered.
|
||||||
@ -325,7 +325,7 @@ nsICODecoder::SniffResource(const char* aData)
|
|||||||
// Make sure we have a sane size for the bitmap information header.
|
// Make sure we have a sane size for the bitmap information header.
|
||||||
int32_t bihSize = LittleEndian::readUint32(aData);
|
int32_t bihSize = LittleEndian::readUint32(aData);
|
||||||
if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
|
if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buffer the first part of the bitmap information header.
|
// Buffer the first part of the bitmap information header.
|
||||||
@ -341,13 +341,13 @@ LexerTransition<ICOState>
|
|||||||
nsICODecoder::ReadPNG(const char* aData, uint32_t aLen)
|
nsICODecoder::ReadPNG(const char* aData, uint32_t aLen)
|
||||||
{
|
{
|
||||||
if (!WriteToContainedDecoder(aData, aLen)) {
|
if (!WriteToContainedDecoder(aData, aLen)) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raymond Chen says that 32bpp only are valid PNG ICOs
|
// Raymond Chen says that 32bpp only are valid PNG ICOs
|
||||||
// http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
|
// http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
|
||||||
if (!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
|
if (!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Transition::ContinueUnbuffered(ICOState::READ_PNG);
|
return Transition::ContinueUnbuffered(ICOState::READ_PNG);
|
||||||
@ -372,7 +372,7 @@ nsICODecoder::ReadBIH(const char* aData)
|
|||||||
// The color table is present only if BPP is <= 8.
|
// The color table is present only if BPP is <= 8.
|
||||||
uint16_t numColors = GetNumColors();
|
uint16_t numColors = GetNumColors();
|
||||||
if (numColors == (uint16_t)-1) {
|
if (numColors == (uint16_t)-1) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
dataOffset += 4 * numColors;
|
dataOffset += 4 * numColors;
|
||||||
}
|
}
|
||||||
@ -393,23 +393,23 @@ nsICODecoder::ReadBIH(const char* aData)
|
|||||||
// will understand, because the BMP decoder doesn't expect the alpha mask that
|
// will understand, because the BMP decoder doesn't expect the alpha mask that
|
||||||
// follows the BMP data in an ICO.
|
// follows the BMP data in an ICO.
|
||||||
if (!FixBitmapHeight(reinterpret_cast<int8_t*>(mBIHraw))) {
|
if (!FixBitmapHeight(reinterpret_cast<int8_t*>(mBIHraw))) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix the ICO width from the BIH.
|
// Fix the ICO width from the BIH.
|
||||||
if (!FixBitmapWidth(reinterpret_cast<int8_t*>(mBIHraw))) {
|
if (!FixBitmapWidth(reinterpret_cast<int8_t*>(mBIHraw))) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write out the BMP's bitmap info header.
|
// Write out the BMP's bitmap info header.
|
||||||
if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw))) {
|
if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw))) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to make sure we have valid color settings.
|
// Check to make sure we have valid color settings.
|
||||||
uint16_t numColors = GetNumColors();
|
uint16_t numColors = GetNumColors();
|
||||||
if (numColors == uint16_t(-1)) {
|
if (numColors == uint16_t(-1)) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we have an AND mask on this BMP? If so, we need to read it after we read
|
// Do we have an AND mask on this BMP? If so, we need to read it after we read
|
||||||
@ -429,7 +429,7 @@ LexerTransition<ICOState>
|
|||||||
nsICODecoder::ReadBMP(const char* aData, uint32_t aLen)
|
nsICODecoder::ReadBMP(const char* aData, uint32_t aLen)
|
||||||
{
|
{
|
||||||
if (!WriteToContainedDecoder(aData, aLen)) {
|
if (!WriteToContainedDecoder(aData, aLen)) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Transition::ContinueUnbuffered(ICOState::READ_BMP);
|
return Transition::ContinueUnbuffered(ICOState::READ_BMP);
|
||||||
@ -466,7 +466,7 @@ nsICODecoder::PrepareForMask()
|
|||||||
// we must have a truncated (and therefore corrupt) AND mask.
|
// we must have a truncated (and therefore corrupt) AND mask.
|
||||||
uint32_t expectedLength = mMaskRowSize * GetRealHeight();
|
uint32_t expectedLength = mMaskRowSize * GetRealHeight();
|
||||||
if (maskLength < expectedLength) {
|
if (maskLength < expectedLength) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're downscaling, the mask is the wrong size for the surface we've
|
// If we're downscaling, the mask is the wrong size for the surface we've
|
||||||
@ -483,7 +483,7 @@ nsICODecoder::PrepareForMask()
|
|||||||
/* aHasAlpha = */ true,
|
/* aHasAlpha = */ true,
|
||||||
/* aFlipVertically = */ true);
|
/* aFlipVertically = */ true);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,7 +516,7 @@ nsICODecoder::ReadMaskRow(const char* aData)
|
|||||||
static_cast<nsBMPDecoder*>(mContainedDecoder.get());
|
static_cast<nsBMPDecoder*>(mContainedDecoder.get());
|
||||||
uint32_t* imageData = bmpDecoder->GetImageData();
|
uint32_t* imageData = bmpDecoder->GetImageData();
|
||||||
if (!imageData) {
|
if (!imageData) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
decoded = imageData + mCurrMaskLine * GetRealWidth();
|
decoded = imageData + mCurrMaskLine * GetRealWidth();
|
||||||
@ -566,7 +566,7 @@ nsICODecoder::FinishMask()
|
|||||||
static_cast<nsBMPDecoder*>(mContainedDecoder.get());
|
static_cast<nsBMPDecoder*>(mContainedDecoder.get());
|
||||||
uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
|
uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
|
||||||
if (!imageData) {
|
if (!imageData) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through the alpha values, copying from mask to image.
|
// Iterate through the alpha values, copying from mask to image.
|
||||||
@ -596,10 +596,10 @@ nsICODecoder::FinishResource()
|
|||||||
// entry. If not, we consider the image corrupt.
|
// entry. If not, we consider the image corrupt.
|
||||||
if (mContainedDecoder->HasSize() &&
|
if (mContainedDecoder->HasSize() &&
|
||||||
mContainedDecoder->GetSize() != GetRealSize()) {
|
mContainedDecoder->GetSize() != GetRealSize()) {
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Transition::Terminate(ICOState::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -609,7 +609,7 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|||||||
MOZ_ASSERT(aBuffer);
|
MOZ_ASSERT(aBuffer);
|
||||||
MOZ_ASSERT(aCount > 0);
|
MOZ_ASSERT(aCount > 0);
|
||||||
|
|
||||||
Maybe<ICOState> terminalState =
|
Maybe<TerminalState> terminalState =
|
||||||
mLexer.Lex(aBuffer, aCount,
|
mLexer.Lex(aBuffer, aCount,
|
||||||
[=](ICOState aState, const char* aData, size_t aLength) {
|
[=](ICOState aState, const char* aData, size_t aLength) {
|
||||||
switch (aState) {
|
switch (aState) {
|
||||||
@ -640,21 +640,13 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|||||||
case ICOState::FINISHED_RESOURCE:
|
case ICOState::FINISHED_RESOURCE:
|
||||||
return FinishResource();
|
return FinishResource();
|
||||||
default:
|
default:
|
||||||
MOZ_ASSERT_UNREACHABLE("Unknown ICOState");
|
MOZ_CRASH("Unknown ICOState");
|
||||||
return Transition::Terminate(ICOState::FAILURE);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!terminalState) {
|
if (terminalState == Some(TerminalState::FAILURE)) {
|
||||||
return; // Need more data.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*terminalState == ICOState::FAILURE) {
|
|
||||||
PostDataError();
|
PostDataError();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(*terminalState == ICOState::SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -21,8 +21,6 @@ class RasterImage;
|
|||||||
|
|
||||||
enum class ICOState
|
enum class ICOState
|
||||||
{
|
{
|
||||||
SUCCESS,
|
|
||||||
FAILURE,
|
|
||||||
HEADER,
|
HEADER,
|
||||||
DIR_ENTRY,
|
DIR_ENTRY,
|
||||||
SKIP_TO_RESOURCE,
|
SKIP_TO_RESOURCE,
|
||||||
|
@ -42,7 +42,7 @@ nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|||||||
MOZ_ASSERT(aBuffer);
|
MOZ_ASSERT(aBuffer);
|
||||||
MOZ_ASSERT(aCount > 0);
|
MOZ_ASSERT(aCount > 0);
|
||||||
|
|
||||||
Maybe<State> terminalState =
|
Maybe<TerminalState> terminalState =
|
||||||
mLexer.Lex(aBuffer, aCount, [=](State aState,
|
mLexer.Lex(aBuffer, aCount, [=](State aState,
|
||||||
const char* aData, size_t aLength) {
|
const char* aData, size_t aLength) {
|
||||||
switch (aState) {
|
switch (aState) {
|
||||||
@ -53,21 +53,13 @@ nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|||||||
case State::FINISH:
|
case State::FINISH:
|
||||||
return Finish();
|
return Finish();
|
||||||
default:
|
default:
|
||||||
MOZ_ASSERT_UNREACHABLE("Unknown State");
|
MOZ_CRASH("Unknown State");
|
||||||
return Transition::Terminate(State::FAILURE);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!terminalState) {
|
if (terminalState == Some(TerminalState::FAILURE)) {
|
||||||
return; // Need more data.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*terminalState == State::FAILURE) {
|
|
||||||
PostDataError();
|
PostDataError();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(*terminalState == State::SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LexerTransition<nsIconDecoder::State>
|
LexerTransition<nsIconDecoder::State>
|
||||||
@ -88,7 +80,7 @@ nsIconDecoder::ReadHeader(const char* aData)
|
|||||||
|
|
||||||
// If we're doing a metadata decode, we're done.
|
// If we're doing a metadata decode, we're done.
|
||||||
if (IsMetadataDecode()) {
|
if (IsMetadataDecode()) {
|
||||||
return Transition::Terminate(State::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
||||||
@ -97,7 +89,7 @@ nsIconDecoder::ReadHeader(const char* aData)
|
|||||||
IntRect(IntPoint(), targetSize),
|
IntRect(IntPoint(), targetSize),
|
||||||
gfx::SurfaceFormat::B8G8R8A8);
|
gfx::SurfaceFormat::B8G8R8A8);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return Transition::Terminate(State::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
||||||
|
|
||||||
@ -105,7 +97,7 @@ nsIconDecoder::ReadHeader(const char* aData)
|
|||||||
nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
|
nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
|
||||||
mImageData, /* aHasAlpha = */ true);
|
mImageData, /* aHasAlpha = */ true);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return Transition::Terminate(State::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +134,7 @@ nsIconDecoder::Finish()
|
|||||||
PostFrameStop();
|
PostFrameStop();
|
||||||
PostDecodeDone();
|
PostDecodeDone();
|
||||||
|
|
||||||
return Transition::Terminate(State::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace image
|
} // namespace image
|
||||||
|
@ -50,9 +50,7 @@ private:
|
|||||||
enum class State {
|
enum class State {
|
||||||
HEADER,
|
HEADER,
|
||||||
ROW_OF_PIXELS,
|
ROW_OF_PIXELS,
|
||||||
FINISH,
|
FINISH
|
||||||
SUCCESS,
|
|
||||||
FAILURE
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LexerTransition<State> ReadHeader(const char* aData);
|
LexerTransition<State> ReadHeader(const char* aData);
|
||||||
|
@ -15,9 +15,7 @@ enum class TestState
|
|||||||
ONE,
|
ONE,
|
||||||
TWO,
|
TWO,
|
||||||
THREE,
|
THREE,
|
||||||
UNBUFFERED,
|
UNBUFFERED
|
||||||
SUCCESS,
|
|
||||||
FAILURE
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -41,10 +39,9 @@ DoLex(TestState aState, const char* aData, size_t aLength)
|
|||||||
return Transition::To(TestState::THREE, 3);
|
return Transition::To(TestState::THREE, 3);
|
||||||
case TestState::THREE:
|
case TestState::THREE:
|
||||||
CheckData(aData, aLength);
|
CheckData(aData, aLength);
|
||||||
return Transition::Terminate(TestState::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
default:
|
default:
|
||||||
EXPECT_TRUE(false); // Shouldn't get here.
|
MOZ_CRASH("Unknown TestState");
|
||||||
return Transition::Terminate(TestState::FAILURE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,10 +62,9 @@ DoLexWithUnbuffered(TestState aState, const char* aData, size_t aLength,
|
|||||||
return Transition::To(TestState::THREE, 3);
|
return Transition::To(TestState::THREE, 3);
|
||||||
case TestState::THREE:
|
case TestState::THREE:
|
||||||
CheckData(aData, aLength);
|
CheckData(aData, aLength);
|
||||||
return Transition::Terminate(TestState::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
default:
|
default:
|
||||||
EXPECT_TRUE(false);
|
MOZ_CRASH("Unknown TestState");
|
||||||
return Transition::Terminate(TestState::FAILURE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,10 +76,9 @@ DoLexWithUnbufferedTerminate(TestState aState, const char* aData, size_t aLength
|
|||||||
CheckData(aData, aLength);
|
CheckData(aData, aLength);
|
||||||
return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3);
|
return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3);
|
||||||
case TestState::UNBUFFERED:
|
case TestState::UNBUFFERED:
|
||||||
return Transition::Terminate(TestState::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
default:
|
default:
|
||||||
EXPECT_TRUE(false);
|
MOZ_CRASH("Unknown TestState");
|
||||||
return Transition::Terminate(TestState::FAILURE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,9 +88,9 @@ TEST(ImageStreamingLexer, SingleChunk)
|
|||||||
char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
|
char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
|
||||||
|
|
||||||
// Test delivering all the data at once.
|
// Test delivering all the data at once.
|
||||||
Maybe<TestState> result = lexer.Lex(data, sizeof(data), DoLex);
|
Maybe<TerminalState> result = lexer.Lex(data, sizeof(data), DoLex);
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::SUCCESS, *result);
|
EXPECT_EQ(Some(TerminalState::SUCCESS), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ImageStreamingLexer, SingleChunkWithUnbuffered)
|
TEST(ImageStreamingLexer, SingleChunkWithUnbuffered)
|
||||||
@ -105,13 +100,13 @@ TEST(ImageStreamingLexer, SingleChunkWithUnbuffered)
|
|||||||
Vector<char> unbufferedVector;
|
Vector<char> unbufferedVector;
|
||||||
|
|
||||||
// Test delivering all the data at once.
|
// Test delivering all the data at once.
|
||||||
Maybe<TestState> result =
|
Maybe<TerminalState> result =
|
||||||
lexer.Lex(data, sizeof(data),
|
lexer.Lex(data, sizeof(data),
|
||||||
[&](TestState aState, const char* aData, size_t aLength) {
|
[&](TestState aState, const char* aData, size_t aLength) {
|
||||||
return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
|
return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
|
||||||
});
|
});
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::SUCCESS, *result);
|
EXPECT_EQ(Some(TerminalState::SUCCESS), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ImageStreamingLexer, ChunkPerState)
|
TEST(ImageStreamingLexer, ChunkPerState)
|
||||||
@ -121,11 +116,11 @@ TEST(ImageStreamingLexer, ChunkPerState)
|
|||||||
|
|
||||||
// Test delivering in perfectly-sized chunks, one per state.
|
// Test delivering in perfectly-sized chunks, one per state.
|
||||||
for (unsigned i = 0 ; i < 3 ; ++i) {
|
for (unsigned i = 0 ; i < 3 ; ++i) {
|
||||||
Maybe<TestState> result = lexer.Lex(data + 3 * i, 3, DoLex);
|
Maybe<TerminalState> result = lexer.Lex(data + 3 * i, 3, DoLex);
|
||||||
|
|
||||||
if (i == 2) {
|
if (i == 2) {
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::SUCCESS, *result);
|
EXPECT_EQ(Some(TerminalState::SUCCESS), result);
|
||||||
} else {
|
} else {
|
||||||
EXPECT_TRUE(result.isNothing());
|
EXPECT_TRUE(result.isNothing());
|
||||||
}
|
}
|
||||||
@ -140,7 +135,7 @@ TEST(ImageStreamingLexer, ChunkPerStateWithUnbuffered)
|
|||||||
|
|
||||||
// Test delivering in perfectly-sized chunks, one per state.
|
// Test delivering in perfectly-sized chunks, one per state.
|
||||||
for (unsigned i = 0 ; i < 3 ; ++i) {
|
for (unsigned i = 0 ; i < 3 ; ++i) {
|
||||||
Maybe<TestState> result =
|
Maybe<TerminalState> result =
|
||||||
lexer.Lex(data + 3 * i, 3,
|
lexer.Lex(data + 3 * i, 3,
|
||||||
[&](TestState aState, const char* aData, size_t aLength) {
|
[&](TestState aState, const char* aData, size_t aLength) {
|
||||||
return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
|
return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
|
||||||
@ -148,7 +143,7 @@ TEST(ImageStreamingLexer, ChunkPerStateWithUnbuffered)
|
|||||||
|
|
||||||
if (i == 2) {
|
if (i == 2) {
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::SUCCESS, *result);
|
EXPECT_EQ(Some(TerminalState::SUCCESS), result);
|
||||||
} else {
|
} else {
|
||||||
EXPECT_TRUE(result.isNothing());
|
EXPECT_TRUE(result.isNothing());
|
||||||
}
|
}
|
||||||
@ -162,11 +157,11 @@ TEST(ImageStreamingLexer, OneByteChunks)
|
|||||||
|
|
||||||
// Test delivering in one byte chunks.
|
// Test delivering in one byte chunks.
|
||||||
for (unsigned i = 0 ; i < 9 ; ++i) {
|
for (unsigned i = 0 ; i < 9 ; ++i) {
|
||||||
Maybe<TestState> result = lexer.Lex(data + i, 1, DoLex);
|
Maybe<TerminalState> result = lexer.Lex(data + i, 1, DoLex);
|
||||||
|
|
||||||
if (i == 8) {
|
if (i == 8) {
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::SUCCESS, *result);
|
EXPECT_EQ(Some(TerminalState::SUCCESS), result);
|
||||||
} else {
|
} else {
|
||||||
EXPECT_TRUE(result.isNothing());
|
EXPECT_TRUE(result.isNothing());
|
||||||
}
|
}
|
||||||
@ -181,7 +176,7 @@ TEST(ImageStreamingLexer, OneByteChunksWithUnbuffered)
|
|||||||
|
|
||||||
// Test delivering in one byte chunks.
|
// Test delivering in one byte chunks.
|
||||||
for (unsigned i = 0 ; i < 9 ; ++i) {
|
for (unsigned i = 0 ; i < 9 ; ++i) {
|
||||||
Maybe<TestState> result =
|
Maybe<TerminalState> result =
|
||||||
lexer.Lex(data + i, 1,
|
lexer.Lex(data + i, 1,
|
||||||
[&](TestState aState, const char* aData, size_t aLength) {
|
[&](TestState aState, const char* aData, size_t aLength) {
|
||||||
return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
|
return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
|
||||||
@ -189,7 +184,7 @@ TEST(ImageStreamingLexer, OneByteChunksWithUnbuffered)
|
|||||||
|
|
||||||
if (i == 8) {
|
if (i == 8) {
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::SUCCESS, *result);
|
EXPECT_EQ(Some(TerminalState::SUCCESS), result);
|
||||||
} else {
|
} else {
|
||||||
EXPECT_TRUE(result.isNothing());
|
EXPECT_TRUE(result.isNothing());
|
||||||
}
|
}
|
||||||
@ -202,23 +197,23 @@ TEST(ImageStreamingLexer, TerminateSuccess)
|
|||||||
char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
|
char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
|
||||||
|
|
||||||
// Test that Terminate is "sticky".
|
// Test that Terminate is "sticky".
|
||||||
Maybe<TestState> result =
|
Maybe<TerminalState> result =
|
||||||
lexer.Lex(data, sizeof(data),
|
lexer.Lex(data, sizeof(data),
|
||||||
[&](TestState aState, const char* aData, size_t aLength) {
|
[&](TestState aState, const char* aData, size_t aLength) {
|
||||||
EXPECT_TRUE(aState == TestState::ONE);
|
EXPECT_TRUE(aState == TestState::ONE);
|
||||||
return Transition::Terminate(TestState::SUCCESS);
|
return Transition::TerminateSuccess();
|
||||||
});
|
});
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::SUCCESS, *result);
|
EXPECT_EQ(Some(TerminalState::SUCCESS), result);
|
||||||
|
|
||||||
result =
|
result =
|
||||||
lexer.Lex(data, sizeof(data),
|
lexer.Lex(data, sizeof(data),
|
||||||
[&](TestState aState, const char* aData, size_t aLength) {
|
[&](TestState aState, const char* aData, size_t aLength) {
|
||||||
EXPECT_TRUE(false); // Shouldn't get here.
|
EXPECT_TRUE(false); // Shouldn't get here.
|
||||||
return Transition::Terminate(TestState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
});
|
});
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::SUCCESS, *result);
|
EXPECT_EQ(Some(TerminalState::SUCCESS), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ImageStreamingLexer, TerminateFailure)
|
TEST(ImageStreamingLexer, TerminateFailure)
|
||||||
@ -227,23 +222,23 @@ TEST(ImageStreamingLexer, TerminateFailure)
|
|||||||
char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
|
char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
|
||||||
|
|
||||||
// Test that Terminate is "sticky".
|
// Test that Terminate is "sticky".
|
||||||
Maybe<TestState> result =
|
Maybe<TerminalState> result =
|
||||||
lexer.Lex(data, sizeof(data),
|
lexer.Lex(data, sizeof(data),
|
||||||
[&](TestState aState, const char* aData, size_t aLength) {
|
[&](TestState aState, const char* aData, size_t aLength) {
|
||||||
EXPECT_TRUE(aState == TestState::ONE);
|
EXPECT_TRUE(aState == TestState::ONE);
|
||||||
return Transition::Terminate(TestState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
});
|
});
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::FAILURE, *result);
|
EXPECT_EQ(Some(TerminalState::FAILURE), result);
|
||||||
|
|
||||||
result =
|
result =
|
||||||
lexer.Lex(data, sizeof(data),
|
lexer.Lex(data, sizeof(data),
|
||||||
[&](TestState aState, const char* aData, size_t aLength) {
|
[&](TestState aState, const char* aData, size_t aLength) {
|
||||||
EXPECT_TRUE(false); // Shouldn't get here.
|
EXPECT_TRUE(false); // Shouldn't get here.
|
||||||
return Transition::Terminate(TestState::FAILURE);
|
return Transition::TerminateFailure();
|
||||||
});
|
});
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::FAILURE, *result);
|
EXPECT_EQ(Some(TerminalState::FAILURE), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ImageStreamingLexer, TerminateUnbuffered)
|
TEST(ImageStreamingLexer, TerminateUnbuffered)
|
||||||
@ -253,12 +248,12 @@ TEST(ImageStreamingLexer, TerminateUnbuffered)
|
|||||||
|
|
||||||
// Test that Terminate works during an unbuffered read.
|
// Test that Terminate works during an unbuffered read.
|
||||||
for (unsigned i = 0 ; i < 9 ; ++i) {
|
for (unsigned i = 0 ; i < 9 ; ++i) {
|
||||||
Maybe<TestState> result =
|
Maybe<TerminalState> result =
|
||||||
lexer.Lex(data + i, 1, DoLexWithUnbufferedTerminate);
|
lexer.Lex(data + i, 1, DoLexWithUnbufferedTerminate);
|
||||||
|
|
||||||
if (i > 2) {
|
if (i > 2) {
|
||||||
EXPECT_TRUE(result.isSome());
|
EXPECT_TRUE(result.isSome());
|
||||||
EXPECT_EQ(TestState::SUCCESS, *result);
|
EXPECT_EQ(Some(TerminalState::SUCCESS), result);
|
||||||
} else {
|
} else {
|
||||||
EXPECT_TRUE(result.isNothing());
|
EXPECT_TRUE(result.isNothing());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user