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:
Mike Hommey 2024-05-07 00:43:37 +00:00
parent 54ee806bca
commit d069b8990a
2 changed files with 120 additions and 0 deletions

View file

@ -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

View file

@ -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");