[wasm] Add support for 64-bit LEB encodings.

R=binji@chromium.org,ahaas@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1746063003

Cr-Commit-Position: refs/heads/master@{#34406}
This commit is contained in:
titzer 2016-03-01 11:36:19 -08:00 committed by Commit bot
parent 8016a2d53f
commit 616f05496e
2 changed files with 279 additions and 33 deletions

View File

@ -77,53 +77,44 @@ class Decoder {
return check(base, offset, 8, msg) ? read_u64(base + offset) : 0;
}
// Reads a variable-length unsigned integer (little endian).
uint32_t checked_read_u32v(const byte* base, int offset, int* length,
const char* msg = "expected LEB128") {
if (!check(base, offset, 1, msg)) {
*length = 0;
return 0;
}
const ptrdiff_t kMaxDiff = 5; // maximum 5 bytes.
const byte* ptr = base + offset;
const byte* end = ptr + kMaxDiff;
if (end > limit_) end = limit_;
int shift = 0;
byte b = 0;
uint32_t result = 0;
while (ptr < end) {
b = *ptr++;
result = result | ((b & 0x7F) << shift);
if ((b & 0x80) == 0) break;
shift += 7;
}
DCHECK_LE(ptr - (base + offset), kMaxDiff);
*length = static_cast<int>(ptr - (base + offset));
if (ptr == end) {
if (*length == kMaxDiff && (b & 0xF0) != 0) {
error(base, ptr, "extra bits in LEB128");
return 0;
}
if ((b & 0x80) != 0) {
error(base, ptr, msg);
return 0;
}
}
return result;
const char* msg = "expected LEB32") {
return checked_read_leb<uint32_t>(base, offset, length, msg);
}
// Reads a variable-length signed integer (little endian).
int32_t checked_read_i32v(const byte* base, int offset, int* length,
const char* msg = "expected SLEB128") {
const char* msg = "expected SLEB32") {
uint32_t result = checked_read_u32v(base, offset, length, msg);
if (*length == 5) return bit_cast<int32_t>(result);
if (*length > 0) {
int shift = 32 - 7 * *length;
// Perform sign extension.
return bit_cast<int32_t>(result << shift) >> shift;
}
return 0;
}
// Reads a variable-length unsigned integer (little endian).
uint64_t checked_read_u64v(const byte* base, int offset, int* length,
const char* msg = "expected LEB64") {
return checked_read_leb<uint64_t>(base, offset, length, msg);
}
// Reads a variable-length signed integer (little endian).
int64_t checked_read_i64v(const byte* base, int offset, int* length,
const char* msg = "expected SLEB64") {
uint64_t result = checked_read_u64v(base, offset, length, msg);
if (*length == 10) return bit_cast<int64_t>(result);
if (*length > 0) {
int shift = 64 - 7 * *length;
// Perform sign extension.
return bit_cast<int64_t>(result << shift) >> shift;
}
return 0;
}
// Reads a single 16-bit unsigned integer (little endian).
inline uint16_t read_u16(const byte* ptr) {
DCHECK(ptr >= start_ && (ptr + 2) <= end_);
@ -333,6 +324,46 @@ class Decoder {
const byte* error_pc_;
const byte* error_pt_;
base::SmartArrayPointer<char> error_msg_;
private:
template <typename IntType>
IntType checked_read_leb(const byte* base, int offset, int* length,
const char* msg) {
if (!check(base, offset, 1, msg)) {
*length = 0;
return 0;
}
const int kMaxLength = (sizeof(IntType) * 8 + 6) / 7;
const byte* ptr = base + offset;
const byte* end = ptr + kMaxLength;
if (end > limit_) end = limit_;
int shift = 0;
byte b = 0;
IntType result = 0;
while (ptr < end) {
b = *ptr++;
result = result | (static_cast<IntType>(b & 0x7F) << shift);
if ((b & 0x80) == 0) break;
shift += 7;
}
DCHECK_LE(ptr - (base + offset), kMaxLength);
*length = static_cast<int>(ptr - (base + offset));
if (ptr == end) {
// Check there are no bits set beyond the bitwidth of {IntType}.
const int kExtraBits = (1 + kMaxLength * 7) - (sizeof(IntType) * 8);
const byte kExtraBitsMask = static_cast<byte>(0xFF << (8 - kExtraBits));
if (*length == kMaxLength && (b & kExtraBitsMask) != 0) {
error(base, ptr, "extra bits in varint");
return 0;
}
if ((b & 0x80) != 0) {
error(base, ptr, msg);
return 0;
}
}
return result;
}
};
#undef TRACE

View File

@ -38,6 +38,26 @@ class DecoderTest : public TestWithZone {
EXPECT_EQ(expected_length, length); \
} while (false)
#define CHECK_UINT64V_INLINE(expected, expected_length, ...) \
do { \
const byte data[] = {__VA_ARGS__}; \
decoder.Reset(data, data + sizeof(data)); \
int length; \
EXPECT_EQ(expected, \
decoder.checked_read_u64v(decoder.start(), 0, &length)); \
EXPECT_EQ(expected_length, length); \
} while (false)
#define CHECK_INT64V_INLINE(expected, expected_length, ...) \
do { \
const byte data[] = {__VA_ARGS__}; \
decoder.Reset(data, data + sizeof(data)); \
int length; \
EXPECT_EQ(expected, \
decoder.checked_read_i64v(decoder.start(), 0, &length)); \
EXPECT_EQ(expected_length, length); \
} while (false)
TEST_F(DecoderTest, ReadU32v_OneByte) {
CHECK_UINT32V_INLINE(0, 1, 0);
CHECK_UINT32V_INLINE(5, 1, 5);
@ -408,6 +428,201 @@ TEST_F(DecoderTest, ReadU32v_extra_bits) {
}
}
TEST_F(DecoderTest, ReadU32v_Bits) {
// A more exhaustive test.
const int kMaxSize = 5;
const uint32_t kVals[] = {
0xaabbccdd, 0x11223344, 0x33445566, 0xffeeddcc, 0xF0F0F0F0, 0x0F0F0F0F,
0xEEEEEEEE, 0xAAAAAAAA, 0x12345678, 0x9abcdef0, 0x80309488, 0x729ed997,
0xc4a0cf81, 0x16c6eb85, 0x4206db8e, 0xf3b089d5, 0xaa2e223e, 0xf99e29c8,
0x4a4357d8, 0x1890b1c1, 0x8d80a085, 0xacb6ae4c, 0x1b827e10, 0xeb5c7bd9,
0xbb1bc146, 0xdf57a33l};
byte data[kMaxSize];
// foreach value in above array
for (size_t v = 0; v < arraysize(kVals); v++) {
// foreach length 1...32
for (int i = 1; i <= 32; i++) {
uint32_t val = kVals[v];
if (i < 32) val &= ((1 << i) - 1);
int length = 1 + i / 7;
for (int j = 0; j < kMaxSize; j++) {
data[j] = static_cast<byte>((val >> (7 * j)) & MASK_7);
}
for (int j = 0; j < length - 1; j++) {
data[j] |= 0x80;
}
// foreach buffer size 0...5
for (int limit = 0; limit <= kMaxSize; limit++) {
decoder.Reset(data, data + limit);
int rlen;
uint32_t result = decoder.checked_read_u32v(data, 0, &rlen);
if (limit < length) {
EXPECT_FALSE(decoder.ok());
} else {
EXPECT_TRUE(decoder.ok());
EXPECT_EQ(val, result);
EXPECT_EQ(length, rlen);
}
}
}
}
}
TEST_F(DecoderTest, ReadU64v_OneByte) {
CHECK_UINT64V_INLINE(0, 1, 0);
CHECK_UINT64V_INLINE(6, 1, 6);
CHECK_UINT64V_INLINE(8, 1, 8);
CHECK_UINT64V_INLINE(12, 1, 12);
CHECK_UINT64V_INLINE(33, 1, 33);
CHECK_UINT64V_INLINE(59, 1, 59);
CHECK_UINT64V_INLINE(110, 1, 110);
CHECK_UINT64V_INLINE(125, 1, 125);
CHECK_UINT64V_INLINE(126, 1, 126);
CHECK_UINT64V_INLINE(127, 1, 127);
}
TEST_F(DecoderTest, ReadI64v_OneByte) {
CHECK_INT64V_INLINE(0, 1, 0);
CHECK_INT64V_INLINE(4, 1, 4);
CHECK_INT64V_INLINE(6, 1, 6);
CHECK_INT64V_INLINE(9, 1, 9);
CHECK_INT64V_INLINE(33, 1, 33);
CHECK_INT64V_INLINE(61, 1, 61);
CHECK_INT64V_INLINE(63, 1, 63);
CHECK_INT64V_INLINE(-1, 1, 127);
CHECK_INT64V_INLINE(-2, 1, 126);
CHECK_INT64V_INLINE(-11, 1, 117);
CHECK_INT64V_INLINE(-62, 1, 66);
CHECK_INT64V_INLINE(-63, 1, 65);
CHECK_INT64V_INLINE(-64, 1, 64);
}
TEST_F(DecoderTest, ReadU64v_PowerOf2) {
const int kMaxSize = 10;
byte data[kMaxSize];
for (int i = 0; i < 64; i++) {
const uint64_t val = 1ull << i;
int index = i / 7;
data[index] = 1 << (i % 7);
memset(data, 0x80, index);
for (int limit = 0; limit <= kMaxSize; limit++) {
decoder.Reset(data, data + limit);
int length;
uint64_t result = decoder.checked_read_u64v(data, 0, &length);
if (limit <= index) {
EXPECT_FALSE(decoder.ok());
} else {
EXPECT_TRUE(decoder.ok());
EXPECT_EQ(val, result);
EXPECT_EQ(index + 1, length);
}
}
}
}
TEST_F(DecoderTest, ReadU64v_Bits) {
const int kMaxSize = 10;
const uint64_t kVals[] = {
0xaabbccdd11223344ull, 0x33445566ffeeddccull, 0xF0F0F0F0F0F0F0F0ull,
0x0F0F0F0F0F0F0F0Full, 0xEEEEEEEEEEEEEEEEull, 0xAAAAAAAAAAAAAAAAull,
0x123456789abcdef0ull, 0x80309488729ed997ull, 0xc4a0cf8116c6eb85ull,
0x4206db8ef3b089d5ull, 0xaa2e223ef99e29c8ull, 0x4a4357d81890b1c1ull,
0x8d80a085acb6ae4cull, 0x1b827e10eb5c7bd9ull, 0xbb1bc146df57a338ull};
byte data[kMaxSize];
// foreach value in above array
for (size_t v = 0; v < arraysize(kVals); v++) {
// foreach length 1...64
for (int i = 1; i <= 64; i++) {
uint64_t val = kVals[v];
if (i < 64) val &= ((1ull << i) - 1);
int length = 1 + i / 7;
for (int j = 0; j < kMaxSize; j++) {
data[j] = static_cast<byte>((val >> (7 * j)) & MASK_7);
}
for (int j = 0; j < length - 1; j++) {
data[j] |= 0x80;
}
// foreach buffer size 0...10
for (int limit = 0; limit <= kMaxSize; limit++) {
decoder.Reset(data, data + limit);
int rlen;
uint64_t result = decoder.checked_read_u64v(data, 0, &rlen);
if (limit < length) {
EXPECT_FALSE(decoder.ok());
} else {
EXPECT_TRUE(decoder.ok());
EXPECT_EQ(val, result);
EXPECT_EQ(length, rlen);
}
}
}
}
}
TEST_F(DecoderTest, ReadI64v_Bits) {
const int kMaxSize = 10;
// Exhaustive signedness test.
const uint64_t kVals[] = {
0xaabbccdd11223344ull, 0x33445566ffeeddccull, 0xF0F0F0F0F0F0F0F0ull,
0x0F0F0F0F0F0F0F0Full, 0xEEEEEEEEEEEEEEEEull, 0xAAAAAAAAAAAAAAAAull,
0x123456789abcdef0ull, 0x80309488729ed997ull, 0xc4a0cf8116c6eb85ull,
0x4206db8ef3b089d5ull, 0xaa2e223ef99e29c8ull, 0x4a4357d81890b1c1ull,
0x8d80a085acb6ae4cull, 0x1b827e10eb5c7bd9ull, 0xbb1bc146df57a338ull};
byte data[kMaxSize];
// foreach value in above array
for (size_t v = 0; v < arraysize(kVals); v++) {
// foreach length 1...64
for (int i = 1; i <= 64; i++) {
const int64_t val = bit_cast<int64_t>(kVals[v] << (64 - i)) >> (64 - i);
int length = 1 + i / 7;
for (int j = 0; j < kMaxSize; j++) {
const uint64_t uval = bit_cast<uint64_t>(val);
data[j] = static_cast<byte>((uval >> (7 * j)) & MASK_7);
}
for (int j = 0; j < length - 1; j++) {
data[j] |= 0x80;
}
// foreach buffer size 0...10
for (int limit = 0; limit <= kMaxSize; limit++) {
decoder.Reset(data, data + limit);
int rlen;
int64_t result = decoder.checked_read_i64v(data, 0, &rlen);
if (limit < length) {
EXPECT_FALSE(decoder.ok());
} else {
EXPECT_TRUE(decoder.ok());
EXPECT_EQ(val, result);
EXPECT_EQ(length, rlen);
}
}
}
}
}
TEST_F(DecoderTest, ReadU64v_extra_bits) {
byte data[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00};
for (int i = 1; i < 128; i++) {
data[9] = static_cast<byte>(i << 1);
int length = 0;
decoder.Reset(data, data + sizeof(data));
decoder.checked_read_u64v(decoder.start(), 0, &length);
EXPECT_EQ(10, length);
EXPECT_FALSE(decoder.ok());
}
}
} // namespace wasm
} // namespace internal
} // namespace v8