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;
|
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
|
* 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
|
* parsing method of TTokenizer (Next, Check*, Skip*, Read*) so that the last
|
||||||
|
|
|
||||||
|
|
@ -1421,6 +1421,66 @@ TEST(Tokenizer, ReadIntegers)
|
||||||
EXPECT_TRUE(t.CheckEOF());
|
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)
|
TEST(Tokenizer, CheckPhrase)
|
||||||
{
|
{
|
||||||
Tokenizer t("foo bar baz");
|
Tokenizer t("foo bar baz");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue