forked from mirrors/gecko-dev
Bug 1894170 - Add a Tokenizer helper to read hexadecimal. r=xpcom-reviewers,emilio
Differential Revision: https://phabricator.services.mozilla.com/D209185
This commit is contained in:
parent
54ee806bca
commit
d069b8990a
2 changed files with 120 additions and 0 deletions
|
|
@ -438,6 +438,66 @@ class TTokenizer : public TokenizerBase<TChar> {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an hexadecimal read helper. It returns false and doesn't move the
|
||||
* read cursor when any of the following happens:
|
||||
* - the token at the read cursor is not 0, and it's not followed by x
|
||||
* - the token(s) that follow don't make a valid hexadecimal number
|
||||
* - the final number doesn't fit the T type
|
||||
* Otherwise true is returned, aValue is filled with the integral number
|
||||
* and the cursor is moved forward.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] bool ReadHexadecimal(T* aValue, bool aPrefixed = true) {
|
||||
MOZ_RELEASE_ASSERT(aValue);
|
||||
|
||||
typename base::TAString::const_char_iterator rollback = mRollback;
|
||||
typename base::TAString::const_char_iterator cursor = base::mCursor;
|
||||
auto revert = MakeScopeExit([&] {
|
||||
// Move to a state as if Check() call has failed
|
||||
mRollback = rollback;
|
||||
base::mCursor = cursor;
|
||||
base::mHasFailed = true;
|
||||
});
|
||||
|
||||
if (aPrefixed) {
|
||||
typename base::Token t;
|
||||
if (!Check(base::TOKEN_INTEGER, t) && t.AsInteger() != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CheckChar([](const TChar aChar) { return aChar == 'x'; })) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TChar c = 'z';
|
||||
mozilla::CheckedInt<T> resultingNumber = 0;
|
||||
while (ReadChar(
|
||||
[](const TChar aChar) {
|
||||
return (aChar >= '0' && aChar <= '9') ||
|
||||
(aChar >= 'A' && aChar <= 'F') ||
|
||||
(aChar >= 'a' && aChar <= 'f');
|
||||
},
|
||||
&c)) {
|
||||
resultingNumber *= 16;
|
||||
if (c <= '9') {
|
||||
resultingNumber += static_cast<uint64_t>(c - '0');
|
||||
} else if (c <= 'F') {
|
||||
resultingNumber += static_cast<uint64_t>(c - 'A') + 0xa;
|
||||
} else {
|
||||
resultingNumber += static_cast<uint64_t>(c - 'a') + 0xa;
|
||||
}
|
||||
}
|
||||
if (c == 'z' || !resultingNumber.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*aValue = resultingNumber.value();
|
||||
revert.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the read cursor position back as it was before the last call of any
|
||||
* parsing method of TTokenizer (Next, Check*, Skip*, Read*) so that the last
|
||||
|
|
|
|||
|
|
@ -1421,6 +1421,66 @@ TEST(Tokenizer, ReadIntegers)
|
|||
EXPECT_TRUE(t.CheckEOF());
|
||||
}
|
||||
|
||||
TEST(Tokenizer, ReadHexadecimals)
|
||||
{
|
||||
Tokenizer t("0x100,0x0a,0xFe02dXXX,0a,0xX,0xffffffff,0x7fffffff,0x100000000");
|
||||
|
||||
uint32_t value32;
|
||||
int32_t signed_value32;
|
||||
uint64_t value64;
|
||||
|
||||
// "0x100,"
|
||||
EXPECT_TRUE(t.ReadHexadecimal(&value32));
|
||||
EXPECT_TRUE(value32 == 0x100);
|
||||
EXPECT_TRUE(t.CheckChar(','));
|
||||
|
||||
// "0x0a,"
|
||||
EXPECT_TRUE(t.ReadHexadecimal(&value32));
|
||||
EXPECT_TRUE(value32 == 0xa);
|
||||
EXPECT_TRUE(t.CheckChar(','));
|
||||
|
||||
// "0xFe02dX,"
|
||||
EXPECT_TRUE(t.ReadHexadecimal(&value32));
|
||||
EXPECT_TRUE(value32 == 0xfe02d);
|
||||
EXPECT_TRUE(t.CheckWord("XXX"));
|
||||
EXPECT_TRUE(t.CheckChar(','));
|
||||
|
||||
// "0a,"
|
||||
EXPECT_FALSE(t.ReadHexadecimal(&value32));
|
||||
EXPECT_TRUE(t.ReadHexadecimal(&value32, /* aPrefixed = */ false));
|
||||
EXPECT_TRUE(value32 == 0xa);
|
||||
EXPECT_TRUE(t.CheckChar(','));
|
||||
|
||||
// "0xX,"
|
||||
EXPECT_FALSE(t.ReadHexadecimal(&value32));
|
||||
EXPECT_TRUE(t.Check(Tokenizer::Token::Number(0)));
|
||||
EXPECT_TRUE(t.CheckWord("xX"));
|
||||
EXPECT_TRUE(t.CheckChar(','));
|
||||
|
||||
// "0xffffffff,"
|
||||
// there is a case to be made that maybe this should be parsed as -1,
|
||||
// but for now, this is not supported.
|
||||
EXPECT_FALSE(t.ReadHexadecimal(&signed_value32));
|
||||
EXPECT_FALSE(t.CheckChar(','));
|
||||
|
||||
EXPECT_TRUE(t.ReadHexadecimal(&value32));
|
||||
EXPECT_TRUE(value32 == std::numeric_limits<uint32_t>::max());
|
||||
EXPECT_TRUE(t.CheckChar(','));
|
||||
|
||||
// "0x7fffffff,"
|
||||
EXPECT_TRUE(t.ReadHexadecimal(&signed_value32));
|
||||
EXPECT_TRUE(signed_value32 == std::numeric_limits<int32_t>::max());
|
||||
EXPECT_TRUE(t.CheckChar(','));
|
||||
|
||||
// "0x100000000,"
|
||||
EXPECT_FALSE(t.ReadHexadecimal(&value32));
|
||||
EXPECT_FALSE(t.CheckEOF());
|
||||
EXPECT_FALSE(t.CheckChar(','));
|
||||
|
||||
EXPECT_TRUE(t.ReadHexadecimal(&value64));
|
||||
EXPECT_TRUE(t.CheckEOF());
|
||||
}
|
||||
|
||||
TEST(Tokenizer, CheckPhrase)
|
||||
{
|
||||
Tokenizer t("foo bar baz");
|
||||
|
|
|
|||
Loading…
Reference in a new issue