2017-02-28 06:11:43 +08:00
|
|
|
//===- BinaryStreamReader.cpp - Reads objects from a binary stream --------===//
|
2016-04-30 01:22:58 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2017-03-03 04:52:51 +08:00
|
|
|
#include "llvm/Support/BinaryStreamReader.h"
|
2016-05-26 04:37:03 +08:00
|
|
|
|
2017-03-03 04:52:51 +08:00
|
|
|
#include "llvm/Support/BinaryStreamError.h"
|
|
|
|
#include "llvm/Support/BinaryStreamRef.h"
|
2016-04-30 01:22:58 +08:00
|
|
|
|
|
|
|
using namespace llvm;
|
[BinaryStream] Reduce the amount of boiler plate needed to use.
Often you have an array and you just want to use it. With the current
design, you have to first construct a `BinaryByteStream`, and then create
a `BinaryStreamRef` from it. Worse, the `BinaryStreamRef` holds a pointer
to the `BinaryByteStream`, so you can't just create a temporary one to
appease the compiler, you have to actually hold onto both the `ArrayRef`
as well as the `BinaryByteStream` *AND* the `BinaryStreamReader` on top of
that. This makes for very cumbersome code, often requiring one to store a
`BinaryByteStream` in a class just to circumvent this.
At the cost of some added complexity (not exposed to users, but internal
to the library), we can do better than this. This patch allows us to
construct `BinaryStreamReaders` and `BinaryStreamWriters` directly from
source data (e.g. `StringRef`, `MutableArrayRef<uint8_t>`, etc). Not only
does this reduce the amount of code you have to type and make it more
obvious how to use it, but it solves real lifetime issues when it's
inconvenient to hold onto a `BinaryByteStream` for a long time.
The additional complexity is in the form of an added layer of indirection.
Whereas before we simply stored a `BinaryStream*` in the ref, we now store
both a `BinaryStream*` **and** a `std::shared_ptr<BinaryStream>`. When
the user wants to construct a `BinaryStreamRef` directly from an
`ArrayRef` etc, we allocate an internal object that holds ownership over a
`BinaryByteStream` and forwards all calls, and store this in the
`shared_ptr<>`. This also maintains the ref semantics, as you can copy it
by value and references refer to the same underlying stream -- the one
being held in the object stored in the `shared_ptr`.
Differential Revision: https://reviews.llvm.org/D33293
llvm-svn: 303294
2017-05-18 04:23:31 +08:00
|
|
|
using endianness = llvm::support::endianness;
|
2016-04-30 01:22:58 +08:00
|
|
|
|
[BinaryStream] Reduce the amount of boiler plate needed to use.
Often you have an array and you just want to use it. With the current
design, you have to first construct a `BinaryByteStream`, and then create
a `BinaryStreamRef` from it. Worse, the `BinaryStreamRef` holds a pointer
to the `BinaryByteStream`, so you can't just create a temporary one to
appease the compiler, you have to actually hold onto both the `ArrayRef`
as well as the `BinaryByteStream` *AND* the `BinaryStreamReader` on top of
that. This makes for very cumbersome code, often requiring one to store a
`BinaryByteStream` in a class just to circumvent this.
At the cost of some added complexity (not exposed to users, but internal
to the library), we can do better than this. This patch allows us to
construct `BinaryStreamReaders` and `BinaryStreamWriters` directly from
source data (e.g. `StringRef`, `MutableArrayRef<uint8_t>`, etc). Not only
does this reduce the amount of code you have to type and make it more
obvious how to use it, but it solves real lifetime issues when it's
inconvenient to hold onto a `BinaryByteStream` for a long time.
The additional complexity is in the form of an added layer of indirection.
Whereas before we simply stored a `BinaryStream*` in the ref, we now store
both a `BinaryStream*` **and** a `std::shared_ptr<BinaryStream>`. When
the user wants to construct a `BinaryStreamRef` directly from an
`ArrayRef` etc, we allocate an internal object that holds ownership over a
`BinaryByteStream` and forwards all calls, and store this in the
`shared_ptr<>`. This also maintains the ref semantics, as you can copy it
by value and references refer to the same underlying stream -- the one
being held in the object stored in the `shared_ptr`.
Differential Revision: https://reviews.llvm.org/D33293
llvm-svn: 303294
2017-05-18 04:23:31 +08:00
|
|
|
BinaryStreamReader::BinaryStreamReader(BinaryStreamRef Ref) : Stream(Ref) {}
|
|
|
|
|
|
|
|
BinaryStreamReader::BinaryStreamReader(BinaryStream &Stream) : Stream(Stream) {}
|
|
|
|
|
|
|
|
BinaryStreamReader::BinaryStreamReader(ArrayRef<uint8_t> Data,
|
|
|
|
endianness Endian)
|
|
|
|
: Stream(Data, Endian) {}
|
|
|
|
|
|
|
|
BinaryStreamReader::BinaryStreamReader(StringRef Data, endianness Endian)
|
|
|
|
: Stream(Data, Endian) {}
|
2016-04-30 01:22:58 +08:00
|
|
|
|
2017-02-28 06:11:43 +08:00
|
|
|
Error BinaryStreamReader::readLongestContiguousChunk(
|
|
|
|
ArrayRef<uint8_t> &Buffer) {
|
2016-06-10 13:09:12 +08:00
|
|
|
if (auto EC = Stream.readLongestContiguousChunk(Offset, Buffer))
|
|
|
|
return EC;
|
|
|
|
Offset += Buffer.size();
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-02-28 06:11:43 +08:00
|
|
|
Error BinaryStreamReader::readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size) {
|
2016-05-27 09:54:44 +08:00
|
|
|
if (auto EC = Stream.readBytes(Offset, Size, Buffer))
|
|
|
|
return EC;
|
|
|
|
Offset += Size;
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-02-28 06:11:43 +08:00
|
|
|
Error BinaryStreamReader::readCString(StringRef &Dest) {
|
2016-05-27 09:54:44 +08:00
|
|
|
uint32_t OriginalOffset = getOffset();
|
2017-05-26 05:12:27 +08:00
|
|
|
uint32_t FoundOffset = 0;
|
2017-02-28 06:11:43 +08:00
|
|
|
while (true) {
|
2017-05-26 05:12:27 +08:00
|
|
|
uint32_t ThisOffset = getOffset();
|
|
|
|
ArrayRef<uint8_t> Buffer;
|
|
|
|
if (auto EC = readLongestContiguousChunk(Buffer))
|
2016-05-07 04:51:57 +08:00
|
|
|
return EC;
|
2017-05-26 05:12:27 +08:00
|
|
|
StringRef S(reinterpret_cast<const char *>(Buffer.begin()), Buffer.size());
|
|
|
|
size_t Pos = S.find_first_of('\0');
|
|
|
|
if (LLVM_LIKELY(Pos != StringRef::npos)) {
|
|
|
|
FoundOffset = Pos + ThisOffset;
|
2017-02-28 06:11:43 +08:00
|
|
|
break;
|
2017-05-26 05:12:27 +08:00
|
|
|
}
|
2017-02-28 06:11:43 +08:00
|
|
|
}
|
2017-05-26 05:12:27 +08:00
|
|
|
assert(FoundOffset >= OriginalOffset);
|
|
|
|
|
2016-05-27 09:54:44 +08:00
|
|
|
setOffset(OriginalOffset);
|
2017-05-26 05:12:27 +08:00
|
|
|
size_t Length = FoundOffset - OriginalOffset;
|
2016-05-27 09:54:44 +08:00
|
|
|
|
2017-02-28 06:11:43 +08:00
|
|
|
if (auto EC = readFixedString(Dest, Length))
|
2016-05-27 09:54:44 +08:00
|
|
|
return EC;
|
|
|
|
|
2017-05-26 05:12:27 +08:00
|
|
|
// Now set the offset back to after the null terminator.
|
|
|
|
setOffset(FoundOffset + 1);
|
2016-05-07 04:51:57 +08:00
|
|
|
return Error::success();
|
2016-04-30 01:22:58 +08:00
|
|
|
}
|
2016-05-03 08:28:21 +08:00
|
|
|
|
2017-05-31 02:19:06 +08:00
|
|
|
Error BinaryStreamReader::readWideString(ArrayRef<UTF16> &Dest) {
|
|
|
|
uint32_t Length = 0;
|
|
|
|
uint32_t OriginalOffset = getOffset();
|
|
|
|
const UTF16 *C;
|
|
|
|
while (true) {
|
|
|
|
if (auto EC = readObject(C))
|
|
|
|
return EC;
|
|
|
|
if (*C == 0x0000)
|
|
|
|
break;
|
|
|
|
++Length;
|
|
|
|
}
|
|
|
|
uint32_t NewOffset = getOffset();
|
|
|
|
setOffset(OriginalOffset);
|
|
|
|
|
|
|
|
if (auto EC = readArray(Dest, Length))
|
|
|
|
return EC;
|
|
|
|
setOffset(NewOffset);
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-02-28 06:11:43 +08:00
|
|
|
Error BinaryStreamReader::readFixedString(StringRef &Dest, uint32_t Length) {
|
2016-05-27 09:54:44 +08:00
|
|
|
ArrayRef<uint8_t> Bytes;
|
2016-05-28 13:21:57 +08:00
|
|
|
if (auto EC = readBytes(Bytes, Length))
|
2016-05-03 08:28:21 +08:00
|
|
|
return EC;
|
2016-05-27 09:54:44 +08:00
|
|
|
Dest = StringRef(reinterpret_cast<const char *>(Bytes.begin()), Bytes.size());
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-02-28 06:11:43 +08:00
|
|
|
Error BinaryStreamReader::readStreamRef(BinaryStreamRef &Ref) {
|
2016-05-27 09:54:44 +08:00
|
|
|
return readStreamRef(Ref, bytesRemaining());
|
|
|
|
}
|
|
|
|
|
2017-02-28 06:11:43 +08:00
|
|
|
Error BinaryStreamReader::readStreamRef(BinaryStreamRef &Ref, uint32_t Length) {
|
2016-05-27 09:54:44 +08:00
|
|
|
if (bytesRemaining() < Length)
|
2017-03-01 01:49:34 +08:00
|
|
|
return make_error<BinaryStreamError>(stream_error_code::stream_too_short);
|
[codeview] Fix a nasty use after free.
StreamRef was designed to be a thin wrapper over an abstract
stream interface that could itself be treated the same as any
other stream interface. For this reason, it inherited publicly
from StreamInterface, and stored a StreamInterface* internally.
But StreamRef was also designed to be lightweight and easily
copyable, similar to ArrayRef. This led to two misuses of
the classes.
1) When creating a StreamRef A from another StreamRef B, it was
possible to end up with A storing a pointer to B, even when
B was a temporary object, leading to use after free.
2) The above situation could be repeated ad nauseum, so that
A stores a pointer to B, which itself stores a pointer to
another StreamRef C, and so on and so on, creating an
unnecessarily level of nesting depth.
This patch removes the public inheritance relationship between
StreamRef and StreamInterface, making it so that we can never
accidentally convert a StreamRef to a StreamInterface.
llvm-svn: 271570
2016-06-03 03:51:48 +08:00
|
|
|
Ref = Stream.slice(Offset, Length);
|
2016-05-03 08:28:21 +08:00
|
|
|
Offset += Length;
|
2016-05-07 04:51:57 +08:00
|
|
|
return Error::success();
|
2016-05-03 08:28:21 +08:00
|
|
|
}
|
2016-10-21 02:31:19 +08:00
|
|
|
|
2017-06-24 00:38:40 +08:00
|
|
|
Error BinaryStreamReader::readSubstream(BinarySubstreamRef &Stream,
|
|
|
|
uint32_t Size) {
|
|
|
|
Stream.Offset = getOffset();
|
|
|
|
return readStreamRef(Stream.StreamData, Size);
|
|
|
|
}
|
|
|
|
|
2017-02-28 06:11:43 +08:00
|
|
|
Error BinaryStreamReader::skip(uint32_t Amount) {
|
2016-10-21 02:31:19 +08:00
|
|
|
if (Amount > bytesRemaining())
|
2017-03-01 01:49:34 +08:00
|
|
|
return make_error<BinaryStreamError>(stream_error_code::stream_too_short);
|
2016-10-21 02:31:19 +08:00
|
|
|
Offset += Amount;
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-05-18 04:42:52 +08:00
|
|
|
Error BinaryStreamReader::padToAlignment(uint32_t Align) {
|
|
|
|
uint32_t NewOffset = alignTo(Offset, Align);
|
|
|
|
return skip(NewOffset - Offset);
|
|
|
|
}
|
|
|
|
|
2017-02-28 06:11:43 +08:00
|
|
|
uint8_t BinaryStreamReader::peek() const {
|
2016-10-21 02:31:19 +08:00
|
|
|
ArrayRef<uint8_t> Buffer;
|
|
|
|
auto EC = Stream.readBytes(Offset, 1, Buffer);
|
|
|
|
assert(!EC && "Cannot peek an empty buffer!");
|
|
|
|
llvm::consumeError(std::move(EC));
|
|
|
|
return Buffer[0];
|
|
|
|
}
|
2017-05-03 23:58:37 +08:00
|
|
|
|
|
|
|
std::pair<BinaryStreamReader, BinaryStreamReader>
|
|
|
|
BinaryStreamReader::split(uint32_t Off) const {
|
|
|
|
assert(getLength() >= Off);
|
|
|
|
|
|
|
|
BinaryStreamRef First = Stream.drop_front(Offset);
|
|
|
|
|
|
|
|
BinaryStreamRef Second = First.drop_front(Off);
|
|
|
|
First = First.keep_front(Off);
|
|
|
|
BinaryStreamReader W1{First};
|
|
|
|
BinaryStreamReader W2{Second};
|
|
|
|
return std::make_pair(W1, W2);
|
|
|
|
}
|