mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
d0775d3cfb
Summary: This is motivated by D63591, where we realized that there isn't a really good way of telling whether a DataExtractor is reading actual data, or is it just returning default values because it reached the end of the buffer. This patch resolves that by providing a new "Cursor" class. A Cursor object encapsulates two things: - the current position/offset in the DataExtractor - an error object Storing the error object inside the Cursor enables one to use the same pattern as the std::{io}stream API, where one can blindly perform a sequence of reads and only check for errors once at the end of the operation. Similarly to the stream API, as soon as we encounter one error, all of the subsequent operations are skipped (return default values) too, even if the would suceed with clear error state. Unlike the std::stream API (but in line with other llvm APIs), we force the error state to be checked through usage of llvm::Error. Reviewers: probinson, dblaikie, JDevlieghere, aprantl, echristo Subscribers: kristina, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D63713 llvm-svn: 370042
219 lines
6.7 KiB
C++
219 lines
6.7 KiB
C++
//===-- DataExtractor.cpp -------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/DataExtractor.h"
|
|
#include "llvm/Support/Errc.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/Host.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
#include "llvm/Support/SwapByteOrder.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static void unexpectedEndReached(Error *E) {
|
|
if (E)
|
|
*E = createStringError(errc::illegal_byte_sequence,
|
|
"unexpected end of data");
|
|
}
|
|
|
|
static bool isError(Error *E) { return E && *E; }
|
|
|
|
template <typename T>
|
|
static T getU(uint64_t *offset_ptr, const DataExtractor *de,
|
|
bool isLittleEndian, const char *Data, llvm::Error *Err) {
|
|
ErrorAsOutParameter ErrAsOut(Err);
|
|
T val = 0;
|
|
if (isError(Err))
|
|
return val;
|
|
|
|
uint64_t offset = *offset_ptr;
|
|
if (!de->isValidOffsetForDataOfSize(offset, sizeof(T))) {
|
|
unexpectedEndReached(Err);
|
|
return val;
|
|
}
|
|
std::memcpy(&val, &Data[offset], sizeof(val));
|
|
if (sys::IsLittleEndianHost != isLittleEndian)
|
|
sys::swapByteOrder(val);
|
|
|
|
// Advance the offset
|
|
*offset_ptr += sizeof(val);
|
|
return val;
|
|
}
|
|
|
|
template <typename T>
|
|
static T *getUs(uint64_t *offset_ptr, T *dst, uint32_t count,
|
|
const DataExtractor *de, bool isLittleEndian, const char *Data,
|
|
llvm::Error *Err) {
|
|
ErrorAsOutParameter ErrAsOut(Err);
|
|
if (isError(Err))
|
|
return nullptr;
|
|
|
|
uint64_t offset = *offset_ptr;
|
|
|
|
if (!de->isValidOffsetForDataOfSize(offset, sizeof(*dst) * count)) {
|
|
unexpectedEndReached(Err);
|
|
return nullptr;
|
|
}
|
|
for (T *value_ptr = dst, *end = dst + count; value_ptr != end;
|
|
++value_ptr, offset += sizeof(*dst))
|
|
*value_ptr = getU<T>(offset_ptr, de, isLittleEndian, Data, Err);
|
|
// Advance the offset
|
|
*offset_ptr = offset;
|
|
// Return a non-NULL pointer to the converted data as an indicator of
|
|
// success
|
|
return dst;
|
|
}
|
|
|
|
uint8_t DataExtractor::getU8(uint64_t *offset_ptr, llvm::Error *Err) const {
|
|
return getU<uint8_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
|
|
}
|
|
|
|
uint8_t *
|
|
DataExtractor::getU8(uint64_t *offset_ptr, uint8_t *dst, uint32_t count) const {
|
|
return getUs<uint8_t>(offset_ptr, dst, count, this, IsLittleEndian,
|
|
Data.data(), nullptr);
|
|
}
|
|
|
|
uint8_t *DataExtractor::getU8(Cursor &C, uint8_t *Dst, uint32_t Count) const {
|
|
return getUs<uint8_t>(&C.Offset, Dst, Count, this, IsLittleEndian,
|
|
Data.data(), &C.Err);
|
|
}
|
|
|
|
uint16_t DataExtractor::getU16(uint64_t *offset_ptr, llvm::Error *Err) const {
|
|
return getU<uint16_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
|
|
}
|
|
|
|
uint16_t *DataExtractor::getU16(uint64_t *offset_ptr, uint16_t *dst,
|
|
uint32_t count) const {
|
|
return getUs<uint16_t>(offset_ptr, dst, count, this, IsLittleEndian,
|
|
Data.data(), nullptr);
|
|
}
|
|
|
|
uint32_t DataExtractor::getU24(uint64_t *offset_ptr) const {
|
|
uint24_t ExtractedVal =
|
|
getU<uint24_t>(offset_ptr, this, IsLittleEndian, Data.data(), nullptr);
|
|
// The 3 bytes are in the correct byte order for the host.
|
|
return ExtractedVal.getAsUint32(sys::IsLittleEndianHost);
|
|
}
|
|
|
|
uint32_t DataExtractor::getU32(uint64_t *offset_ptr, llvm::Error *Err) const {
|
|
return getU<uint32_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
|
|
}
|
|
|
|
uint32_t *DataExtractor::getU32(uint64_t *offset_ptr, uint32_t *dst,
|
|
uint32_t count) const {
|
|
return getUs<uint32_t>(offset_ptr, dst, count, this, IsLittleEndian,
|
|
Data.data(), nullptr);
|
|
}
|
|
|
|
uint64_t DataExtractor::getU64(uint64_t *offset_ptr, llvm::Error *Err) const {
|
|
return getU<uint64_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
|
|
}
|
|
|
|
uint64_t *DataExtractor::getU64(uint64_t *offset_ptr, uint64_t *dst,
|
|
uint32_t count) const {
|
|
return getUs<uint64_t>(offset_ptr, dst, count, this, IsLittleEndian,
|
|
Data.data(), nullptr);
|
|
}
|
|
|
|
uint64_t DataExtractor::getUnsigned(uint64_t *offset_ptr, uint32_t byte_size,
|
|
llvm::Error *Err) const {
|
|
switch (byte_size) {
|
|
case 1:
|
|
return getU8(offset_ptr, Err);
|
|
case 2:
|
|
return getU16(offset_ptr, Err);
|
|
case 4:
|
|
return getU32(offset_ptr, Err);
|
|
case 8:
|
|
return getU64(offset_ptr, Err);
|
|
}
|
|
llvm_unreachable("getUnsigned unhandled case!");
|
|
}
|
|
|
|
int64_t
|
|
DataExtractor::getSigned(uint64_t *offset_ptr, uint32_t byte_size) const {
|
|
switch (byte_size) {
|
|
case 1:
|
|
return (int8_t)getU8(offset_ptr);
|
|
case 2:
|
|
return (int16_t)getU16(offset_ptr);
|
|
case 4:
|
|
return (int32_t)getU32(offset_ptr);
|
|
case 8:
|
|
return (int64_t)getU64(offset_ptr);
|
|
}
|
|
llvm_unreachable("getSigned unhandled case!");
|
|
}
|
|
|
|
const char *DataExtractor::getCStr(uint64_t *offset_ptr) const {
|
|
uint64_t offset = *offset_ptr;
|
|
StringRef::size_type pos = Data.find('\0', offset);
|
|
if (pos != StringRef::npos) {
|
|
*offset_ptr = pos + 1;
|
|
return Data.data() + offset;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
StringRef DataExtractor::getCStrRef(uint64_t *offset_ptr) const {
|
|
uint64_t Start = *offset_ptr;
|
|
StringRef::size_type Pos = Data.find('\0', Start);
|
|
if (Pos != StringRef::npos) {
|
|
*offset_ptr = Pos + 1;
|
|
return StringRef(Data.data() + Start, Pos - Start);
|
|
}
|
|
return StringRef();
|
|
}
|
|
|
|
uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr,
|
|
llvm::Error *Err) const {
|
|
assert(*offset_ptr <= Data.size());
|
|
ErrorAsOutParameter ErrAsOut(Err);
|
|
if (isError(Err))
|
|
return 0;
|
|
|
|
const char *error;
|
|
unsigned bytes_read;
|
|
uint64_t result = decodeULEB128(
|
|
reinterpret_cast<const uint8_t *>(Data.data() + *offset_ptr), &bytes_read,
|
|
reinterpret_cast<const uint8_t *>(Data.data() + Data.size()), &error);
|
|
if (error) {
|
|
if (Err)
|
|
*Err = createStringError(errc::illegal_byte_sequence, error);
|
|
return 0;
|
|
}
|
|
*offset_ptr += bytes_read;
|
|
return result;
|
|
}
|
|
|
|
int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr) const {
|
|
assert(*offset_ptr <= Data.size());
|
|
|
|
const char *error;
|
|
unsigned bytes_read;
|
|
int64_t result = decodeSLEB128(
|
|
reinterpret_cast<const uint8_t *>(Data.data() + *offset_ptr), &bytes_read,
|
|
reinterpret_cast<const uint8_t *>(Data.data() + Data.size()), &error);
|
|
if (error)
|
|
return 0;
|
|
*offset_ptr += bytes_read;
|
|
return result;
|
|
}
|
|
|
|
void DataExtractor::skip(Cursor &C, uint64_t Length) const {
|
|
ErrorAsOutParameter ErrAsOut(&C.Err);
|
|
if (isError(&C.Err))
|
|
return;
|
|
|
|
if (isValidOffsetForDataOfSize(C.Offset, Length))
|
|
C.Offset += Length;
|
|
else
|
|
unexpectedEndReached(&C.Err);
|
|
}
|