diff --git a/llvm/include/llvm/Support/DataExtractor.h b/llvm/include/llvm/Support/DataExtractor.h --- a/llvm/include/llvm/Support/DataExtractor.h +++ b/llvm/include/llvm/Support/DataExtractor.h @@ -217,11 +217,25 @@ /// The number of bytes to extract. If there are not enough bytes in the /// data to extract all of the bytes, the offset will be left unmodified. /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// /// \return /// A StringRef for the extracted bytes. If the offset pointed to by /// \a OffsetPtr is out of bounds, or if the offset plus the length /// is out of bounds, a default-initialized StringRef will be returned. - StringRef getBytes(uint64_t *OffsetPtr, uint64_t Length) const; + StringRef getBytes(uint64_t *OffsetPtr, uint64_t Length, + Error *Err = nullptr) const; + + /// Extract a fixed number of bytes from the location given by the cursor. In + /// case of an extraction error, or if the cursor is already in an error + /// state, a default-initialized StringRef is returned. + StringRef getBytes(Cursor &C, uint64_t Length) { + return getBytes(&C.Offset, Length, &C.Err); + } /// Extract an unsigned integer of size \a byte_size from \a /// *offset_ptr. @@ -453,9 +467,20 @@ /// is out of bounds or there are not enough bytes to extract this value, /// the offset will be left unmodified. /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// /// @return /// The extracted 24-bit value represented in a uint32_t. - uint32_t getU24(uint64_t *offset_ptr) const; + uint32_t getU24(uint64_t *OffsetPtr, Error *Err = nullptr) const; + + /// Extract a single 24-bit unsigned value from the location given by the + /// cursor. In case of an extraction error, or if the cursor is already in an + /// error state, zero is returned. + uint32_t getU24(Cursor &C) const { return getU24(&C.Offset, &C.Err); } /// Extract a uint32_t value from \a *offset_ptr. /// @@ -575,9 +600,20 @@ /// enough bytes to extract this value, the offset will be left /// unmodified. /// + /// @param[in,out] Err + /// A pointer to an Error object. Upon return the Error object is set to + /// indicate the result (success/failure) of the function. If the Error + /// object is already set when calling this function, no extraction is + /// performed. + /// /// @return /// The extracted signed integer value. - int64_t getSLEB128(uint64_t *offset_ptr) const; + int64_t getSLEB128(uint64_t *OffsetPtr, Error *Err = nullptr) const; + + /// Extract an signed LEB128 value from the location given by the cursor. + /// In case of an extraction error, or if the cursor is already in an error + /// state, zero is returned. + int64_t getSLEB128(Cursor &C) const { return getSLEB128(&C.Offset, &C.Err); } /// Extract a unsigned LEB128 value from \a *offset_ptr. /// @@ -603,7 +639,7 @@ /// The extracted unsigned integer value. uint64_t getULEB128(uint64_t *offset_ptr, llvm::Error *Err = nullptr) const; - /// Extract an unsigned ULEB128 value from the location given by the cursor. + /// Extract an unsigned LEB128 value from the location given by the cursor. /// In case of an extraction error, or if the cursor is already in an error /// state, zero is returned. uint64_t getULEB128(Cursor &C) const { return getULEB128(&C.Offset, &C.Err); } diff --git a/llvm/lib/Support/DataExtractor.cpp b/llvm/lib/Support/DataExtractor.cpp --- a/llvm/lib/Support/DataExtractor.cpp +++ b/llvm/lib/Support/DataExtractor.cpp @@ -95,9 +95,9 @@ Data.data(), nullptr); } -uint32_t DataExtractor::getU24(uint64_t *offset_ptr) const { +uint32_t DataExtractor::getU24(uint64_t *OffsetPtr, Error *Err) const { uint24_t ExtractedVal = - getU(offset_ptr, this, IsLittleEndian, Data.data(), nullptr); + getU(OffsetPtr, this, IsLittleEndian, Data.data(), Err); // The 3 bytes are in the correct byte order for the host. return ExtractedVal.getAsUint32(sys::IsLittleEndianHost); } @@ -174,47 +174,51 @@ return Bytes.trim(TrimChars); } -StringRef DataExtractor::getBytes(uint64_t *OffsetPtr, uint64_t Length) const { - if (!isValidOffsetForDataOfSize(*OffsetPtr, Length)) +StringRef DataExtractor::getBytes(uint64_t *OffsetPtr, uint64_t Length, + Error *Err) const { + ErrorAsOutParameter ErrAsOut(Err); + if (isError(Err)) + return StringRef(); + + if (!isValidOffsetForDataOfSize(*OffsetPtr, Length)) { + unexpectedEndReached(Err, *OffsetPtr); return StringRef(); + } + StringRef Result = Data.substr(*OffsetPtr, Length); *OffsetPtr += Length; return Result; } -uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr, - llvm::Error *Err) const { - assert(*offset_ptr <= Data.size()); +template +static T getLEB128(StringRef Data, uint64_t *OffsetPtr, Error *Err, + T (&Decoder)(const uint8_t *p, unsigned *n, + const uint8_t *end, const char **error)) { + ArrayRef Bytes = arrayRefFromStringRef(Data); + assert(*OffsetPtr <= Bytes.size()); ErrorAsOutParameter ErrAsOut(Err); if (isError(Err)) - return 0; + return T(); const char *error; unsigned bytes_read; - uint64_t result = decodeULEB128( - reinterpret_cast(Data.data() + *offset_ptr), &bytes_read, - reinterpret_cast(Data.data() + Data.size()), &error); + T result = + Decoder(Bytes.data() + *OffsetPtr, &bytes_read, Bytes.end(), &error); if (error) { if (Err) *Err = createStringError(errc::illegal_byte_sequence, error); - return 0; + return T(); } - *offset_ptr += bytes_read; + *OffsetPtr += bytes_read; return result; } -int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr) const { - assert(*offset_ptr <= Data.size()); +uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr, Error *Err) const { + return getLEB128(Data, offset_ptr, Err, decodeULEB128); +} - const char *error; - unsigned bytes_read; - int64_t result = decodeSLEB128( - reinterpret_cast(Data.data() + *offset_ptr), &bytes_read, - reinterpret_cast(Data.data() + Data.size()), &error); - if (error) - return 0; - *offset_ptr += bytes_read; - return result; +int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr, Error *Err) const { + return getLEB128(Data, offset_ptr, Err, decodeSLEB128); } void DataExtractor::skip(Cursor &C, uint64_t Length) const { diff --git a/llvm/unittests/Support/DataExtractorTest.cpp b/llvm/unittests/Support/DataExtractorTest.cpp --- a/llvm/unittests/Support/DataExtractorTest.cpp +++ b/llvm/unittests/Support/DataExtractorTest.cpp @@ -134,6 +134,14 @@ Offset = 0; EXPECT_EQ(0U, DE.getSLEB128(&Offset)); EXPECT_EQ(0U, Offset); + + DataExtractor::Cursor C(0); + EXPECT_EQ(0U, DE.getULEB128(C)); + EXPECT_THAT_ERROR(C.takeError(), Failed()); + + C = DataExtractor::Cursor(0); + EXPECT_EQ(0U, DE.getSLEB128(C)); + EXPECT_THAT_ERROR(C.takeError(), Failed()); } TEST(DataExtractorTest, Cursor_tell) { @@ -250,6 +258,16 @@ EXPECT_EQ("AB", toStringRef(S)); } +TEST(DataExtractorTest, getU24) { + DataExtractor DE(StringRef("ABCD"), false, 8); + DataExtractor::Cursor C(0); + + EXPECT_EQ(0x414243u, DE.getU24(C)); + EXPECT_EQ(0u, DE.getU24(C)); + EXPECT_EQ(3u, C.tell()); + EXPECT_THAT_ERROR(C.takeError(), Failed()); +} + TEST(DataExtractorTest, skip) { DataExtractor DE(StringRef("AB"), false, 8); DataExtractor::Cursor C(0); @@ -331,6 +349,14 @@ EXPECT_EQ(Offset, 4u); EXPECT_EQ(Str.size(), 4u); EXPECT_EQ(Str, Bytes); + + DataExtractor::Cursor C(0); + EXPECT_EQ(StringRef("\x01\x02"), DE.getBytes(C, 2)); + EXPECT_EQ(StringRef("\x00\x04", 2), DE.getBytes(C, 2)); + EXPECT_EQ(StringRef(), DE.getBytes(C, 2)); + EXPECT_EQ(StringRef(), DE.getBytes(C, 2)); + EXPECT_EQ(4u, C.tell()); + EXPECT_THAT_ERROR(C.takeError(), Failed()); } }