From 23a543971e6f0549e158e66a08f3631c2ccb617e Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 10 Feb 2017 00:02:58 +0000 Subject: [PATCH] [Support] Extend SLEB128 encoding support. Add support for padded SLEB128 values, and support for writing SLEB128 values to buffers rather than to ostreams, similar to the existing ULEB128 support. llvm-svn: 294675 --- llvm/include/llvm/Support/LEB128.h | 40 +++++++++++++++++++++-- llvm/unittests/Support/LEB128Test.cpp | 47 +++++++++++++++++++-------- 2 files changed, 71 insertions(+), 16 deletions(-) diff --git a/llvm/include/llvm/Support/LEB128.h b/llvm/include/llvm/Support/LEB128.h index 6a95432ca2d9..edb923bb6599 100644 --- a/llvm/include/llvm/Support/LEB128.h +++ b/llvm/include/llvm/Support/LEB128.h @@ -20,7 +20,8 @@ namespace llvm { /// Utility function to encode a SLEB128 value to an output stream. -inline void encodeSLEB128(int64_t Value, raw_ostream &OS) { +inline void encodeSLEB128(int64_t Value, raw_ostream &OS, + unsigned Padding = 0) { bool More; do { uint8_t Byte = Value & 0x7f; @@ -28,10 +29,45 @@ inline void encodeSLEB128(int64_t Value, raw_ostream &OS) { Value >>= 7; More = !((((Value == 0 ) && ((Byte & 0x40) == 0)) || ((Value == -1) && ((Byte & 0x40) != 0)))); - if (More) + if (More || Padding != 0) Byte |= 0x80; // Mark this byte to show that more bytes will follow. OS << char(Byte); } while (More); + + // Pad with 0x80 and emit a terminating byte at the end. + if (Padding != 0) { + uint8_t PadValue = Value < 0 ? 0x7f : 0x00; + for (; Padding != 1; --Padding) + OS << char(PadValue | 0x80); + OS << char(PadValue); + } +} + +/// Utility function to encode a SLEB128 value to a buffer. Returns +/// the length in bytes of the encoded value. +inline unsigned encodeSLEB128(int64_t Value, uint8_t *p, + unsigned Padding = 0) { + uint8_t *orig_p = p; + bool More; + do { + uint8_t Byte = Value & 0x7f; + // NOTE: this assumes that this signed shift is an arithmetic right shift. + Value >>= 7; + More = !((((Value == 0 ) && ((Byte & 0x40) == 0)) || + ((Value == -1) && ((Byte & 0x40) != 0)))); + if (More || Padding != 0) + Byte |= 0x80; // Mark this byte to show that more bytes will follow. + *p++ = Byte; + } while (More); + + // Pad with 0x80 and emit a terminating byte at the end. + if (Padding != 0) { + uint8_t PadValue = Value < 0 ? 0x7f : 0x00; + for (; Padding != 1; --Padding) + *p++ = (PadValue | 0x80); + *p++ = PadValue; + } + return (unsigned)(p - orig_p); } /// Utility function to encode a ULEB128 value to an output stream. diff --git a/llvm/unittests/Support/LEB128Test.cpp b/llvm/unittests/Support/LEB128Test.cpp index 76b63e5a8381..061936df1d19 100644 --- a/llvm/unittests/Support/LEB128Test.cpp +++ b/llvm/unittests/Support/LEB128Test.cpp @@ -17,26 +17,45 @@ using namespace llvm; namespace { TEST(LEB128Test, EncodeSLEB128) { -#define EXPECT_SLEB128_EQ(EXPECTED, VALUE) \ +#define EXPECT_SLEB128_EQ(EXPECTED, VALUE, PAD) \ do { \ - /* encodeSLEB128(uint64_t, raw_ostream &) */ \ std::string Expected(EXPECTED, sizeof(EXPECTED) - 1); \ - std::string Actual; \ - raw_string_ostream Stream(Actual); \ - encodeSLEB128(VALUE, Stream); \ + \ + /* encodeSLEB128(uint64_t, raw_ostream &, unsigned) */ \ + std::string Actual1; \ + raw_string_ostream Stream(Actual1); \ + encodeSLEB128(VALUE, Stream, PAD); \ Stream.flush(); \ - EXPECT_EQ(Expected, Actual); \ + EXPECT_EQ(Expected, Actual1); \ + \ + /* encodeSLEB128(uint64_t, uint8_t *, unsigned) */ \ + uint8_t Buffer[32]; \ + unsigned Size = encodeSLEB128(VALUE, Buffer, PAD); \ + std::string Actual2(reinterpret_cast(Buffer), Size); \ + EXPECT_EQ(Expected, Actual2); \ } while (0) // Encode SLEB128 - EXPECT_SLEB128_EQ("\x00", 0); - EXPECT_SLEB128_EQ("\x01", 1); - EXPECT_SLEB128_EQ("\x7f", -1); - EXPECT_SLEB128_EQ("\x3f", 63); - EXPECT_SLEB128_EQ("\x41", -63); - EXPECT_SLEB128_EQ("\x40", -64); - EXPECT_SLEB128_EQ("\xbf\x7f", -65); - EXPECT_SLEB128_EQ("\xc0\x00", 64); + EXPECT_SLEB128_EQ("\x00", 0, 0); + EXPECT_SLEB128_EQ("\x01", 1, 0); + EXPECT_SLEB128_EQ("\x7f", -1, 0); + EXPECT_SLEB128_EQ("\x3f", 63, 0); + EXPECT_SLEB128_EQ("\x41", -63, 0); + EXPECT_SLEB128_EQ("\x40", -64, 0); + EXPECT_SLEB128_EQ("\xbf\x7f", -65, 0); + EXPECT_SLEB128_EQ("\xc0\x00", 64, 0); + + // Encode SLEB128 with some extra padding bytes + EXPECT_SLEB128_EQ("\x80\x00", 0, 1); + EXPECT_SLEB128_EQ("\x80\x80\x00", 0, 2); + EXPECT_SLEB128_EQ("\xff\x80\x00", 0x7f, 1); + EXPECT_SLEB128_EQ("\xff\x80\x80\x00", 0x7f, 2); + EXPECT_SLEB128_EQ("\x80\x81\x00", 0x80, 1); + EXPECT_SLEB128_EQ("\x80\x81\x80\x00", 0x80, 2); + EXPECT_SLEB128_EQ("\xc0\x7f", -0x40, 1); + EXPECT_SLEB128_EQ("\xc0\xff\x7f", -0x40, 2); + EXPECT_SLEB128_EQ("\x80\xff\x7f", -0x80, 1); + EXPECT_SLEB128_EQ("\x80\xff\xff\x7f", -0x80, 2); #undef EXPECT_SLEB128_EQ }