Skip to content

Commit c1106c9

Browse files
committed
[Support] Add LEB128 support to BinaryStreamReader/Writer.
Summary: This patch adds support for ULEB128 and SLEB128 encoding and decoding to BinaryStreamWriter and BinaryStreamReader respectively. Support for ULEB128/SLEB128 will be used for eh-frame parsing in the JITLink library currently under development (see https://reviews.llvm.org/D58704). Reviewers: zturner, dblaikie Subscribers: kristina, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D60810 llvm-svn: 358584
1 parent 258a425 commit c1106c9

File tree

5 files changed

+142
-1
lines changed

5 files changed

+142
-1
lines changed

llvm/include/llvm/Support/BinaryStreamReader.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,18 @@ class BinaryStreamReader {
9696
return Error::success();
9797
}
9898

99+
/// Read an unsigned LEB128 encoded value.
100+
///
101+
/// \returns a success error code if the data was successfully read, otherwise
102+
/// returns an appropriate error code.
103+
Error readULEB128(uint64_t &Dest);
104+
105+
/// Read a signed LEB128 encoded value.
106+
///
107+
/// \returns a success error code if the data was successfully read, otherwise
108+
/// returns an appropriate error code.
109+
Error readSLEB128(int64_t &Dest);
110+
99111
/// Read a null terminated string from \p Dest. Whether a copy occurs depends
100112
/// on the implementation of the underlying stream. Updates the stream's
101113
/// offset to point after the newly read data.

llvm/include/llvm/Support/BinaryStreamWriter.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,20 @@ class BinaryStreamWriter {
7979
return writeInteger<U>(static_cast<U>(Num));
8080
}
8181

82+
/// Write the unsigned integer Value to the underlying stream using ULEB128
83+
/// encoding.
84+
///
85+
/// \returns a success error code if the data was successfully written,
86+
/// otherwise returns an appropriate error code.
87+
Error writeULEB128(uint64_t Value);
88+
89+
/// Write the unsigned integer Value to the underlying stream using ULEB128
90+
/// encoding.
91+
///
92+
/// \returns a success error code if the data was successfully written,
93+
/// otherwise returns an appropriate error code.
94+
Error writeSLEB128(int64_t Value);
95+
8296
/// Write the string \p Str to the underlying stream followed by a null
8397
/// terminator. On success, updates the offset so that subsequent writes
8498
/// occur at the next unwritten position. \p Str need not be null terminated

llvm/lib/Support/BinaryStreamReader.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "llvm/Support/BinaryStreamError.h"
1212
#include "llvm/Support/BinaryStreamRef.h"
13+
#include "llvm/Support/LEB128.h"
1314

1415
using namespace llvm;
1516
using endianness = llvm::support::endianness;
@@ -40,6 +41,36 @@ Error BinaryStreamReader::readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size) {
4041
return Error::success();
4142
}
4243

44+
Error BinaryStreamReader::readULEB128(uint64_t &Dest) {
45+
SmallVector<uint8_t, 10> EncodedBytes;
46+
ArrayRef<uint8_t> NextByte;
47+
48+
// Copy the encoded ULEB into the buffer.
49+
do {
50+
if (auto Err = readBytes(NextByte, 1))
51+
return Err;
52+
EncodedBytes.push_back(NextByte[0]);
53+
} while (NextByte[0] & 0x80);
54+
55+
Dest = decodeULEB128(EncodedBytes.begin(), nullptr, EncodedBytes.end());
56+
return Error::success();
57+
}
58+
59+
Error BinaryStreamReader::readSLEB128(int64_t &Dest) {
60+
SmallVector<uint8_t, 10> EncodedBytes;
61+
ArrayRef<uint8_t> NextByte;
62+
63+
// Copy the encoded ULEB into the buffer.
64+
do {
65+
if (auto Err = readBytes(NextByte, 1))
66+
return Err;
67+
EncodedBytes.push_back(NextByte[0]);
68+
} while (NextByte[0] & 0x80);
69+
70+
Dest = decodeSLEB128(EncodedBytes.begin(), nullptr, EncodedBytes.end());
71+
return Error::success();
72+
}
73+
4374
Error BinaryStreamReader::readCString(StringRef &Dest) {
4475
uint32_t OriginalOffset = getOffset();
4576
uint32_t FoundOffset = 0;
@@ -145,4 +176,4 @@ BinaryStreamReader::split(uint32_t Off) const {
145176
BinaryStreamReader W1{First};
146177
BinaryStreamReader W2{Second};
147178
return std::make_pair(W1, W2);
148-
}
179+
}

llvm/lib/Support/BinaryStreamWriter.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "llvm/Support/BinaryStreamError.h"
1212
#include "llvm/Support/BinaryStreamReader.h"
1313
#include "llvm/Support/BinaryStreamRef.h"
14+
#include "llvm/Support/LEB128.h"
1415

1516
using namespace llvm;
1617

@@ -31,6 +32,18 @@ Error BinaryStreamWriter::writeBytes(ArrayRef<uint8_t> Buffer) {
3132
return Error::success();
3233
}
3334

35+
Error BinaryStreamWriter::writeULEB128(uint64_t Value) {
36+
uint8_t EncodedBytes[10] = {0};
37+
unsigned Size = encodeULEB128(Value, &EncodedBytes[0]);
38+
return writeBytes({EncodedBytes, Size});
39+
}
40+
41+
Error BinaryStreamWriter::writeSLEB128(int64_t Value) {
42+
uint8_t EncodedBytes[10] = {0};
43+
unsigned Size = encodeSLEB128(Value, &EncodedBytes[0]);
44+
return writeBytes({EncodedBytes, Size});
45+
}
46+
3447
Error BinaryStreamWriter::writeCString(StringRef Str) {
3548
if (auto EC = writeFixedString(Str))
3649
return EC;

llvm/unittests/Support/BinaryStreamTest.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,77 @@ TEST_F(BinaryStreamTest, StreamReaderEnum) {
610610
}
611611
}
612612

613+
TEST_F(BinaryStreamTest, StreamReaderULEB128) {
614+
std::vector<uint64_t> TestValues = {
615+
0, // Zero
616+
0x7F, // One byte
617+
0xFF, // One byte, all-ones
618+
0xAAAA, // Two bytes
619+
0xAAAAAAAA, // Four bytes
620+
0xAAAAAAAAAAAAAAAA, // Eight bytes
621+
0xffffffffffffffff // Eight bytess, all-ones
622+
};
623+
624+
// Conservatively assume a 10-byte encoding for each of our LEB128s, with no
625+
// alignment requirement.
626+
initializeOutput(10 * TestValues.size(), 1);
627+
initializeInputFromOutput(1);
628+
629+
for (auto &Stream : Streams) {
630+
// Write fields.
631+
BinaryStreamWriter Writer(*Stream.Output);
632+
for (const auto &Value : TestValues)
633+
ASSERT_THAT_ERROR(Writer.writeULEB128(Value), Succeeded());
634+
635+
// Read fields.
636+
BinaryStreamReader Reader(*Stream.Input);
637+
std::vector<uint64_t> Results;
638+
Results.resize(TestValues.size());
639+
for (unsigned I = 0; I != TestValues.size(); ++I)
640+
ASSERT_THAT_ERROR(Reader.readULEB128(Results[I]), Succeeded());
641+
642+
for (unsigned I = 0; I != TestValues.size(); ++I)
643+
EXPECT_EQ(TestValues[I], Results[I]);
644+
}
645+
}
646+
647+
TEST_F(BinaryStreamTest, StreamReaderSLEB128) {
648+
std::vector<int64_t> TestValues = {
649+
0, // Zero
650+
0x7F, // One byte
651+
-0x7F, // One byte, negative
652+
0xFF, // One byte, all-ones
653+
0xAAAA, // Two bytes
654+
-0xAAAA, // Two bytes, negative
655+
0xAAAAAAAA, // Four bytes
656+
-0xAAAAAAAA, // Four bytes, negative
657+
0x2AAAAAAAAAAAAAAA, // Eight bytes
658+
-0x7ffffffffffffff // Eight bytess, negative
659+
};
660+
661+
// Conservatively assume a 10-byte encoding for each of our LEB128s, with no
662+
// alignment requirement.
663+
initializeOutput(10 * TestValues.size(), 1);
664+
initializeInputFromOutput(1);
665+
666+
for (auto &Stream : Streams) {
667+
// Write fields.
668+
BinaryStreamWriter Writer(*Stream.Output);
669+
for (const auto &Value : TestValues)
670+
ASSERT_THAT_ERROR(Writer.writeSLEB128(Value), Succeeded());
671+
672+
// Read fields.
673+
BinaryStreamReader Reader(*Stream.Input);
674+
std::vector<int64_t> Results;
675+
Results.resize(TestValues.size());
676+
for (unsigned I = 0; I != TestValues.size(); ++I)
677+
ASSERT_THAT_ERROR(Reader.readSLEB128(Results[I]), Succeeded());
678+
679+
for (unsigned I = 0; I != TestValues.size(); ++I)
680+
EXPECT_EQ(TestValues[I], Results[I]);
681+
}
682+
}
683+
613684
TEST_F(BinaryStreamTest, StreamReaderObject) {
614685
struct Foo {
615686
int X;

0 commit comments

Comments
 (0)