Skip to content

Commit

Permalink
Bug 1616298 - ULEB128Reader - r=canaltinova
Browse files Browse the repository at this point in the history
Unsigned LEB128 parser class that can be fed bytes one by one.

Differential Revision: https://phabricator.services.mozilla.com/D63231

--HG--
extra : moz-landing-system : lando
  • Loading branch information
squelart committed Feb 25, 2020
1 parent 8f7a1f5 commit bd615e6
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
57 changes: 57 additions & 0 deletions mozglue/baseprofiler/public/leb128iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,63 @@ T ReadULEB128(It& aIterator) {
}
}

// constexpr ULEB128 reader class.
// Mostly useful when dealing with non-trivial byte feeds.
template <typename T>
class ULEB128Reader {
static_assert(!std::numeric_limits<T>::is_signed,
"ULEB128Reader must handle an unsigned type");

public:
constexpr ULEB128Reader() = default;

// Don't allow copy/assignment, it doesn't make sense for a stateful parser.
constexpr ULEB128Reader(const ULEB128Reader&) = delete;
constexpr ULEB128Reader& operator=(const ULEB128Reader&) = delete;

// Feed a byte into the parser.
// Returns true if this was the last byte.
constexpr MOZ_MUST_USE bool FeedByteIsComplete(unsigned aByte) {
MOZ_ASSERT(!IsComplete());
// Extract the 7 bits of value, and shift them in place into the value.
mValue |= static_cast<T>(aByte & 0x7fu) << mShift;
// If the 8th bit is *not* set, this was the last byte.
// Expecting small values, so it should be more likely that the bit is off.
if (MOZ_LIKELY((aByte & 0x80u) == 0)) {
mShift = mCompleteShift;
return true;
}
// There are more bytes to read.
// Next byte will contain more significant bits above the past 7.
mShift += 7;
// Safety check that we're not going to shift by >= than the type size,
// which is Undefined Behavior in C++.
MOZ_ASSERT(mShift < CHAR_BIT * sizeof(T));
return false;
}

constexpr void Reset() {
mValue = 0;
mShift = 0;
}

constexpr MOZ_MUST_USE bool IsComplete() const {
return mShift == mCompleteShift;
}

constexpr MOZ_MUST_USE T Value() const {
MOZ_ASSERT(IsComplete());
return mValue;
}

private:
// Special value of `mShift` indicating that parsing is complete.
constexpr static unsigned mCompleteShift = 0x10000u;

T mValue = 0;
unsigned mShift = 0;
};

} // namespace mozilla

#endif // leb128iterator_h
105 changes: 105 additions & 0 deletions mozglue/tests/TestBaseProfiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ void TestLEB128() {
for (unsigned i = 0; i < test.mSize; ++i) {
MOZ_RELEASE_ASSERT(buffer[i] == uint8_t(test.mBytes[i]));
}

// Move pointer (iterator) back to start of buffer.
p = buffer;
// And read the LEB128 we wrote above.
Expand All @@ -254,11 +255,115 @@ void TestLEB128() {
MOZ_RELEASE_ASSERT(p == buffer + test.mSize);
// And check the read value.
MOZ_RELEASE_ASSERT(read == test.mValue);

// Testing ULEB128 reader.
ULEB128Reader<uint64_t> reader;
MOZ_RELEASE_ASSERT(!reader.IsComplete());
// Move pointer back to start of buffer.
p = buffer;
for (;;) {
// Read a byte and feed it to the reader.
if (reader.FeedByteIsComplete(*p++)) {
break;
}
// Not complete yet, we shouldn't have reached the end pointer.
MOZ_RELEASE_ASSERT(!reader.IsComplete());
MOZ_RELEASE_ASSERT(p < buffer + test.mSize);
}
MOZ_RELEASE_ASSERT(reader.IsComplete());
// Pointer should have advanced just past the expected LEB128 size.
MOZ_RELEASE_ASSERT(p == buffer + test.mSize);
// And check the read value.
MOZ_RELEASE_ASSERT(reader.Value() == test.mValue);

// And again after a Reset.
reader.Reset();
MOZ_RELEASE_ASSERT(!reader.IsComplete());
p = buffer;
for (;;) {
if (reader.FeedByteIsComplete(*p++)) {
break;
}
MOZ_RELEASE_ASSERT(!reader.IsComplete());
MOZ_RELEASE_ASSERT(p < buffer + test.mSize);
}
MOZ_RELEASE_ASSERT(reader.IsComplete());
MOZ_RELEASE_ASSERT(p == buffer + test.mSize);
MOZ_RELEASE_ASSERT(reader.Value() == test.mValue);
}

printf("TestLEB128 done\n");
}

template <uint8_t byte, uint8_t... tail>
constexpr bool TestConstexprULEB128Reader(ULEB128Reader<uint64_t>& aReader) {
if (aReader.IsComplete()) {
return false;
}
const bool isComplete = aReader.FeedByteIsComplete(byte);
if (aReader.IsComplete() != isComplete) {
return false;
}
if constexpr (sizeof...(tail) == 0) {
return isComplete;
} else {
if (isComplete) {
return false;
}
return TestConstexprULEB128Reader<tail...>(aReader);
}
}

template <uint64_t expected, uint8_t... bytes>
constexpr bool TestConstexprULEB128Reader() {
ULEB128Reader<uint64_t> reader;
if (!TestConstexprULEB128Reader<bytes...>(reader)) {
return false;
}
if (!reader.IsComplete()) {
return false;
}
if (reader.Value() != expected) {
return false;
}

reader.Reset();
if (!TestConstexprULEB128Reader<bytes...>(reader)) {
return false;
}
if (!reader.IsComplete()) {
return false;
}
if (reader.Value() != expected) {
return false;
}

return true;
}

static_assert(TestConstexprULEB128Reader<0x0u, 0x0u>());
static_assert(!TestConstexprULEB128Reader<0x0u, 0x0u, 0x0u>());
static_assert(TestConstexprULEB128Reader<0x1u, 0x1u>());
static_assert(TestConstexprULEB128Reader<0x7Fu, 0x7Fu>());
static_assert(TestConstexprULEB128Reader<0x80u, 0x80u, 0x01u>());
static_assert(!TestConstexprULEB128Reader<0x80u, 0x80u>());
static_assert(!TestConstexprULEB128Reader<0x80u, 0x01u>());
static_assert(TestConstexprULEB128Reader<0x81u, 0x81u, 0x01u>());
static_assert(TestConstexprULEB128Reader<0xFFu, 0xFFu, 0x01u>());
static_assert(TestConstexprULEB128Reader<0x100u, 0x80u, 0x02u>());
static_assert(TestConstexprULEB128Reader<0xFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu,
0xFFu, 0x0Fu>());
static_assert(
!TestConstexprULEB128Reader<0xFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu>());
static_assert(!TestConstexprULEB128Reader<0xFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu,
0xFFu, 0xFFu, 0x0Fu>());
static_assert(
TestConstexprULEB128Reader<0xFFFFFFFFFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0x01u>());
static_assert(
!TestConstexprULEB128Reader<0xFFFFFFFFFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu>());

static void TestModuloBuffer(ModuloBuffer<>& mb, uint32_t MBSize) {
using MB = ModuloBuffer<>;

Expand Down

0 comments on commit bd615e6

Please sign in to comment.