diff options
Diffstat (limited to 'llvm/include/llvm/DebugInfo/MSF')
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/ByteStream.h | 160 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/IMsfFile.h | 44 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/MappedBlockStream.h | 139 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/MsfBuilder.h | 140 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/MsfCommon.h | 89 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/MsfError.h | 47 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/MsfStreamLayout.h | 35 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/StreamArray.h | 277 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/StreamInterface.h | 53 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/StreamReader.h | 110 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/StreamRef.h | 130 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/MSF/StreamWriter.h | 85 |
12 files changed, 1309 insertions, 0 deletions
diff --git a/llvm/include/llvm/DebugInfo/MSF/ByteStream.h b/llvm/include/llvm/DebugInfo/MSF/ByteStream.h new file mode 100644 index 00000000000..737693aa009 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/ByteStream.h @@ -0,0 +1,160 @@ +//===- ByteStream.h - Reads stream data from a byte sequence ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_BYTESTREAM_H +#define LLVM_DEBUGINFO_MSF_BYTESTREAM_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/MSF/MSFError.h" +#include "llvm/DebugInfo/MSF/StreamInterface.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include <cstdint> +#include <memory> +#include <type_traits> + +namespace llvm { +namespace msf { + +class ByteStream : public ReadableStream { +public: + ByteStream() {} + explicit ByteStream(ArrayRef<uint8_t> Data) : Data(Data) {} + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const override { + if (Offset > Data.size()) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + if (Data.size() < Size + Offset) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + Buffer = Data.slice(Offset, Size); + return Error::success(); + } + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const override { + if (Offset >= Data.size()) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + Buffer = Data.slice(Offset); + return Error::success(); + } + + uint32_t getLength() const override { return Data.size(); } + + ArrayRef<uint8_t> data() const { return Data; } + + StringRef str() const { + const char *CharData = reinterpret_cast<const char *>(Data.data()); + return StringRef(CharData, Data.size()); + } + +protected: + ArrayRef<uint8_t> Data; +}; + +// MemoryBufferByteStream behaves like a read-only ByteStream, but has its data +// backed by an llvm::MemoryBuffer. It also owns the underlying MemoryBuffer. +class MemoryBufferByteStream : public ByteStream { +public: + explicit MemoryBufferByteStream(std::unique_ptr<MemoryBuffer> Buffer) + : ByteStream(ArrayRef<uint8_t>(Buffer->getBuffer().bytes_begin(), + Buffer->getBuffer().bytes_end())), + MemBuffer(std::move(Buffer)) {} + + std::unique_ptr<MemoryBuffer> MemBuffer; +}; + +class MutableByteStream : public WritableStream { +public: + MutableByteStream() {} + explicit MutableByteStream(MutableArrayRef<uint8_t> Data) + : Data(Data), ImmutableStream(Data) {} + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const override { + return ImmutableStream.readBytes(Offset, Size, Buffer); + } + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const override { + return ImmutableStream.readLongestContiguousChunk(Offset, Buffer); + } + + uint32_t getLength() const override { return ImmutableStream.getLength(); } + + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Buffer) const override { + if (Data.size() < Buffer.size()) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + if (Offset > Buffer.size() - Data.size()) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + + uint8_t *DataPtr = const_cast<uint8_t *>(Data.data()); + ::memcpy(DataPtr + Offset, Buffer.data(), Buffer.size()); + return Error::success(); + } + + Error commit() const override { return Error::success(); } + + MutableArrayRef<uint8_t> data() const { return Data; } + +private: + MutableArrayRef<uint8_t> Data; + ByteStream ImmutableStream; +}; + +// A simple adapter that acts like a ByteStream but holds ownership over +// and underlying FileOutputBuffer. +class FileBufferByteStream : public WritableStream { +private: + class StreamImpl : public MutableByteStream { + public: + StreamImpl(std::unique_ptr<FileOutputBuffer> Buffer) + : MutableByteStream(MutableArrayRef<uint8_t>(Buffer->getBufferStart(), + Buffer->getBufferEnd())), + FileBuffer(std::move(Buffer)) {} + + Error commit() const override { + if (FileBuffer->commit()) + return llvm::make_error<MSFError>(msf_error_code::not_writable); + return Error::success(); + } + + private: + std::unique_ptr<FileOutputBuffer> FileBuffer; + }; + +public: + explicit FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer) + : Impl(std::move(Buffer)) {} + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const override { + return Impl.readBytes(Offset, Size, Buffer); + } + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const override { + return Impl.readLongestContiguousChunk(Offset, Buffer); + } + + uint32_t getLength() const override { return Impl.getLength(); } + + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) const override { + return Impl.writeBytes(Offset, Data); + } + Error commit() const override { return Impl.commit(); } + +private: + StreamImpl Impl; +}; + + +} // end namespace msf +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_BYTESTREAM_H diff --git a/llvm/include/llvm/DebugInfo/MSF/IMsfFile.h b/llvm/include/llvm/DebugInfo/MSF/IMsfFile.h new file mode 100644 index 00000000000..6c5754f11b9 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/IMsfFile.h @@ -0,0 +1,44 @@ +//===- IMSFFile.h - Abstract base class for an MSF file ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_IMSFFILE_H +#define LLVM_DEBUGINFO_MSF_IMSFFILE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +#include <stdint.h> + +namespace llvm { +namespace msf { + +class IMSFFile { +public: + virtual ~IMSFFile() {} + + virtual uint32_t getBlockSize() const = 0; + virtual uint32_t getBlockCount() const = 0; + + virtual uint32_t getNumStreams() const = 0; + virtual uint32_t getStreamByteSize(uint32_t StreamIndex) const = 0; + virtual ArrayRef<support::ulittle32_t> + getStreamBlockList(uint32_t StreamIndex) const = 0; + + virtual Expected<ArrayRef<uint8_t>> getBlockData(uint32_t BlockIndex, + uint32_t NumBytes) const = 0; + virtual Error setBlockData(uint32_t BlockIndex, uint32_t Offset, + ArrayRef<uint8_t> Data) const = 0; +}; +} +} + +#endif // LLVM_DEBUGINFO_MSF_IMSFFILE_H diff --git a/llvm/include/llvm/DebugInfo/MSF/MappedBlockStream.h b/llvm/include/llvm/DebugInfo/MSF/MappedBlockStream.h new file mode 100644 index 00000000000..aef6b011153 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/MappedBlockStream.h @@ -0,0 +1,139 @@ +//===- MappedBlockStream.h - Discontiguous stream data in an MSF -*- C++ +//-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_MAPPEDBLOCKSTREAM_H +#define LLVM_DEBUGINFO_MSF_MAPPEDBLOCKSTREAM_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/MSF/MSFStreamLayout.h" +#include "llvm/DebugInfo/MSF/StreamInterface.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include <cstdint> +#include <vector> + +namespace llvm { +namespace msf { + +struct MSFLayout; + +/// MappedBlockStream represents data stored in an MSF file into chunks of a +/// particular size (called the Block Size), and whose chunks may not be +/// necessarily contiguous. The arrangement of these chunks MSF the file +/// is described by some other metadata contained within the MSF file. In +/// the case of a standard MSF Stream, the layout of the stream's blocks +/// is described by the MSF "directory", but in the case of the directory +/// itself, the layout is described by an array at a fixed location within +/// the MSF. MappedBlockStream provides methods for reading from and writing +/// to one of these streams transparently, as if it were a contiguous sequence +/// of bytes. +class MappedBlockStream : public ReadableStream { + friend class WritableMappedBlockStream; + +public: + static std::unique_ptr<MappedBlockStream> + createStream(uint32_t BlockSize, uint32_t NumBlocks, + const MSFStreamLayout &Layout, const ReadableStream &MsfData); + + static std::unique_ptr<MappedBlockStream> + createIndexedStream(const MSFLayout &Layout, const ReadableStream &MsfData, + uint32_t StreamIndex); + + static std::unique_ptr<MappedBlockStream> + createDirectoryStream(const MSFLayout &Layout, const ReadableStream &MsfData); + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const override; + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const override; + + uint32_t getLength() const override; + + uint32_t getNumBytesCopied() const; + + llvm::BumpPtrAllocator &getAllocator() { return Pool; } + + void invalidateCache(); + + uint32_t getBlockSize() const { return BlockSize; } + uint32_t getNumBlocks() const { return NumBlocks; } + uint32_t getStreamLength() const { return StreamLayout.Length; } + +protected: + MappedBlockStream(uint32_t BlockSize, uint32_t NumBlocks, + const MSFStreamLayout &StreamLayout, + const ReadableStream &MsfData); + +private: + const MSFStreamLayout &getStreamLayout() const { return StreamLayout; } + void fixCacheAfterWrite(uint32_t Offset, ArrayRef<uint8_t> Data) const; + + Error readBytes(uint32_t Offset, MutableArrayRef<uint8_t> Buffer) const; + bool tryReadContiguously(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const; + + const uint32_t BlockSize; + const uint32_t NumBlocks; + const MSFStreamLayout StreamLayout; + const ReadableStream &MsfData; + + typedef MutableArrayRef<uint8_t> CacheEntry; + mutable llvm::BumpPtrAllocator Pool; + mutable DenseMap<uint32_t, std::vector<CacheEntry>> CacheMap; +}; + +class WritableMappedBlockStream : public WritableStream { +public: + static std::unique_ptr<WritableMappedBlockStream> + createStream(uint32_t BlockSize, uint32_t NumBlocks, + const MSFStreamLayout &Layout, const WritableStream &MsfData); + + static std::unique_ptr<WritableMappedBlockStream> + createIndexedStream(const MSFLayout &Layout, const WritableStream &MsfData, + uint32_t StreamIndex); + + static std::unique_ptr<WritableMappedBlockStream> + createDirectoryStream(const MSFLayout &Layout, const WritableStream &MsfData); + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const override; + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const override; + uint32_t getLength() const override; + + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Buffer) const override; + + Error commit() const override; + + const MSFStreamLayout &getStreamLayout() const { + return ReadInterface.getStreamLayout(); + } + uint32_t getBlockSize() const { return ReadInterface.getBlockSize(); } + uint32_t getNumBlocks() const { return ReadInterface.getNumBlocks(); } + uint32_t getStreamLength() const { return ReadInterface.getStreamLength(); } + +protected: + WritableMappedBlockStream(uint32_t BlockSize, uint32_t NumBlocks, + const MSFStreamLayout &StreamLayout, + const WritableStream &MsfData); + +private: + MappedBlockStream ReadInterface; + + const WritableStream &WriteInterface; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_MAPPEDBLOCKSTREAM_H diff --git a/llvm/include/llvm/DebugInfo/MSF/MsfBuilder.h b/llvm/include/llvm/DebugInfo/MSF/MsfBuilder.h new file mode 100644 index 00000000000..bf29e626339 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/MsfBuilder.h @@ -0,0 +1,140 @@ +//===- MSFBuilder.h - MSF Directory & Metadata Builder ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_MSFBUILDER_H +#define LLVM_DEBUGINFO_MSF_MSFBUILDER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/MSF/MSFCommon.h" + +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +#include <utility> +#include <vector> + +namespace llvm { +namespace msf { +class MSFBuilder { +public: + /// \brief Create a new `MSFBuilder`. + /// + /// \param BlockSize The internal block size used by the PDB file. See + /// isValidBlockSize() for a list of valid block sizes. + /// + /// \param MinBlockCount Causes the builder to reserve up front space for + /// at least `MinBlockCount` blocks. This is useful when using `MSFBuilder` + /// to read an existing MSF that you want to write back out later. The + /// original MSF file's SuperBlock contains the exact number of blocks used + /// by the file, so is a good hint as to how many blocks the new MSF file + /// will contain. Furthermore, it is actually necessary in this case. To + /// preserve stability of the file's layout, it is helpful to try to keep + /// all streams mapped to their original block numbers. To ensure that this + /// is possible, space for all blocks must be allocated beforehand so that + /// streams can be assigned to them. + /// + /// \param CanGrow If true, any operation which results in an attempt to + /// locate a free block when all available blocks have been exhausted will + /// allocate a new block, thereby growing the size of the final MSF file. + /// When false, any such attempt will result in an error. This is especially + /// useful in testing scenarios when you know your test isn't going to do + /// anything to increase the size of the file, so having an Error returned if + /// it were to happen would catch a programming error + /// + /// \returns an llvm::Error representing whether the operation succeeded or + /// failed. Currently the only way this can fail is if an invalid block size + /// is specified, or `MinBlockCount` does not leave enough room for the + /// mandatory reserved blocks required by an MSF file. + static Expected<MSFBuilder> create(BumpPtrAllocator &Allocator, + uint32_t BlockSize, + uint32_t MinBlockCount = 0, + bool CanGrow = true); + + /// Request the block map to be at a specific block address. This is useful + /// when editing a MSF and you want the layout to be as stable as possible. + Error setBlockMapAddr(uint32_t Addr); + Error setDirectoryBlocksHint(ArrayRef<uint32_t> DirBlocks); + void setFreePageMap(uint32_t Fpm); + void setUnknown1(uint32_t Unk1); + + /// Add a stream to the MSF file with the given size, occupying the given + /// list of blocks. This is useful when reading a MSF file and you want a + /// particular stream to occupy the original set of blocks. If the given + /// blocks are already allocated, or if the number of blocks specified is + /// incorrect for the given stream size, this function will return an Error. + Error addStream(uint32_t Size, ArrayRef<uint32_t> Blocks); + + /// Add a stream to the MSF file with the given size, occupying any available + /// blocks that the builder decides to use. This is useful when building a + /// new PDB file from scratch and you don't care what blocks a stream occupies + /// but you just want it to work. + Error addStream(uint32_t Size); + + /// Update the size of an existing stream. This will allocate or deallocate + /// blocks as needed to match the requested size. This can fail if `CanGrow` + /// was set to false when initializing the `MSFBuilder`. + Error setStreamSize(uint32_t Idx, uint32_t Size); + + /// Get the total number of streams in the MSF layout. This should return 1 + /// for every call to `addStream`. + uint32_t getNumStreams() const; + + /// Get the size of a stream by index. + uint32_t getStreamSize(uint32_t StreamIdx) const; + + /// Get the list of blocks allocated to a particular stream. + ArrayRef<uint32_t> getStreamBlocks(uint32_t StreamIdx) const; + + /// Get the total number of blocks that will be allocated to actual data in + /// this MSF file. + uint32_t getNumUsedBlocks() const; + + /// Get the total number of blocks that exist in the MSF file but are not + /// allocated to any valid data. + uint32_t getNumFreeBlocks() const; + + /// Get the total number of blocks in the MSF file. In practice this is equal + /// to `getNumUsedBlocks() + getNumFreeBlocks()`. + uint32_t getTotalBlockCount() const; + + /// Check whether a particular block is allocated or free. + bool isBlockFree(uint32_t Idx) const; + + /// Finalize the layout and build the headers and structures that describe the + /// MSF layout and can be written directly to the MSF file. + Expected<MSFLayout> build(); + +private: + MSFBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow, + BumpPtrAllocator &Allocator); + + Error allocateBlocks(uint32_t NumBlocks, MutableArrayRef<uint32_t> Blocks); + uint32_t computeDirectoryByteSize() const; + + typedef std::vector<uint32_t> BlockList; + + BumpPtrAllocator &Allocator; + + bool IsGrowable; + uint32_t FreePageMap; + uint32_t Unknown1; + uint32_t BlockSize; + uint32_t MininumBlocks; + uint32_t BlockMapAddr; + BitVector FreeBlocks; + std::vector<uint32_t> DirectoryBlocks; + std::vector<std::pair<uint32_t, BlockList>> StreamData; +}; +} // namespace msf +} // namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_MSFBUILDER_H diff --git a/llvm/include/llvm/DebugInfo/MSF/MsfCommon.h b/llvm/include/llvm/DebugInfo/MSF/MsfCommon.h new file mode 100644 index 00000000000..58e6a1af426 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/MsfCommon.h @@ -0,0 +1,89 @@ +//===- MSFCommon.h - Common types and functions for MSF files ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_MSFCOMMON_H +#define LLVM_DEBUGINFO_MSF_MSFCOMMON_H + +#include "llvm/ADT/ArrayRef.h" + +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" + +#include <vector> + +namespace llvm { +namespace msf { +static const char Magic[] = {'M', 'i', 'c', 'r', 'o', 's', 'o', 'f', + 't', ' ', 'C', '/', 'C', '+', '+', ' ', + 'M', 'S', 'F', ' ', '7', '.', '0', '0', + '\r', '\n', '\x1a', 'D', 'S', '\0', '\0', '\0'}; + +// The superblock is overlaid at the beginning of the file (offset 0). +// It starts with a magic header and is followed by information which +// describes the layout of the file system. +struct SuperBlock { + char MagicBytes[sizeof(Magic)]; + // The file system is split into a variable number of fixed size elements. + // These elements are referred to as blocks. The size of a block may vary + // from system to system. + support::ulittle32_t BlockSize; + // The index of the free block map. + support::ulittle32_t FreeBlockMapBlock; + // This contains the number of blocks resident in the file system. In + // practice, NumBlocks * BlockSize is equivalent to the size of the MSF + // file. + support::ulittle32_t NumBlocks; + // This contains the number of bytes which make up the directory. + support::ulittle32_t NumDirectoryBytes; + // This field's purpose is not yet known. + support::ulittle32_t Unknown1; + // This contains the block # of the block map. + support::ulittle32_t BlockMapAddr; +}; + +struct MSFLayout { + MSFLayout() : SB(nullptr) {} + const SuperBlock *SB; + ArrayRef<support::ulittle32_t> DirectoryBlocks; + ArrayRef<support::ulittle32_t> StreamSizes; + std::vector<ArrayRef<support::ulittle32_t>> StreamMap; +}; + +inline bool isValidBlockSize(uint32_t Size) { + switch (Size) { + case 512: + case 1024: + case 2048: + case 4096: + return true; + } + return false; +} + +// Super Block, Fpm0, Fpm1, and Block Map +inline uint32_t getMinimumBlockCount() { return 4; } + +// Super Block, Fpm0, and Fpm1 are reserved. The Block Map, although required +// need not be at block 3. +inline uint32_t getFirstUnreservedBlock() { return 3; } + +inline uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize) { + return alignTo(NumBytes, BlockSize) / BlockSize; +} + +inline uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize) { + return BlockNumber * BlockSize; +} + +Error validateSuperBlock(const SuperBlock &SB); +} // namespace msf +} // namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_MSFCOMMON_H diff --git a/llvm/include/llvm/DebugInfo/MSF/MsfError.h b/llvm/include/llvm/DebugInfo/MSF/MsfError.h new file mode 100644 index 00000000000..e66aeca3cd4 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/MsfError.h @@ -0,0 +1,47 @@ +//===- MSFError.h - Error extensions for MSF Files --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_MSFERROR_H +#define LLVM_DEBUGINFO_MSF_MSFERROR_H + +#include "llvm/Support/Error.h" + +#include <string> + +namespace llvm { +namespace msf { +enum class msf_error_code { + unspecified = 1, + insufficient_buffer, + not_writable, + no_stream, + invalid_format, + block_in_use +}; + +/// Base class for errors originating when parsing raw PDB files +class MSFError : public ErrorInfo<MSFError> { +public: + static char ID; + MSFError(msf_error_code C); + MSFError(const std::string &Context); + MSFError(msf_error_code C, const std::string &Context); + + void log(raw_ostream &OS) const override; + const std::string &getErrorMessage() const; + std::error_code convertToErrorCode() const override; + +private: + std::string ErrMsg; + msf_error_code Code; +}; +} // namespace msf +} // namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_MSFERROR_H diff --git a/llvm/include/llvm/DebugInfo/MSF/MsfStreamLayout.h b/llvm/include/llvm/DebugInfo/MSF/MsfStreamLayout.h new file mode 100644 index 00000000000..1b987aa04cf --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/MsfStreamLayout.h @@ -0,0 +1,35 @@ +//===- MSFStreamLayout.h - Describes the layout of a stream -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_MSFSTREAMLAYOUT_H +#define LLVM_DEBUGINFO_MSF_MSFSTREAMLAYOUT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" + +#include <cstdint> + +namespace llvm { +namespace msf { + +/// \brief Describes the layout of a stream in an MSF layout. A "stream" here +/// is defined as any logical unit of data which may be arranged inside the MSF +/// file as a sequence of (possibly discontiguous) blocks. When we want to read +/// from a particular MSF Stream, we fill out a stream layout structure and the +/// reader uses it to determine which blocks in the underlying MSF file contain +/// the data, so that it can be pieced together in the right order. +class MSFStreamLayout { +public: + uint32_t Length; + ArrayRef<support::ulittle32_t> Blocks; +}; +} // namespace msf +} // namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_MSFSTREAMLAYOUT_H diff --git a/llvm/include/llvm/DebugInfo/MSF/StreamArray.h b/llvm/include/llvm/DebugInfo/MSF/StreamArray.h new file mode 100644 index 00000000000..88f41ddfb52 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/StreamArray.h @@ -0,0 +1,277 @@ +//===- StreamArray.h - Array backed by an arbitrary stream ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_STREAMARRAY_H +#define LLVM_DEBUGINFO_MSF_STREAMARRAY_H + +#include "llvm/DebugInfo/MSF/StreamRef.h" +#include "llvm/Support/Error.h" + +#include <functional> +#include <type_traits> + +namespace llvm { +namespace msf { + +/// VarStreamArrayExtractor is intended to be specialized to provide customized +/// extraction logic. On input it receives a StreamRef pointing to the +/// beginning of the next record, but where the length of the record is not yet +/// known. Upon completion, it should return an appropriate Error instance if +/// a record could not be extracted, or if one could be extracted it should +/// return success and set Len to the number of bytes this record occupied in +/// the underlying stream, and it should fill out the fields of the value type +/// Item appropriately to represent the current record. +/// +/// You can specialize this template for your own custom value types to avoid +/// having to specify a second template argument to VarStreamArray (documented +/// below). +template <typename T> struct VarStreamArrayExtractor { + // Method intentionally deleted. You must provide an explicit specialization + // with the following method implemented. + Error operator()(ReadableStreamRef Stream, uint32_t &Len, + T &Item) const = delete; +}; + +/// VarStreamArray represents an array of variable length records backed by a +/// stream. This could be a contiguous sequence of bytes in memory, it could +/// be a file on disk, or it could be a PDB stream where bytes are stored as +/// discontiguous blocks in a file. Usually it is desirable to treat arrays +/// as contiguous blocks of memory, but doing so with large PDB files, for +/// example, could mean allocating huge amounts of memory just to allow +/// re-ordering of stream data to be contiguous before iterating over it. By +/// abstracting this out, we need not duplicate this memory, and we can +/// iterate over arrays in arbitrarily formatted streams. Elements are parsed +/// lazily on iteration, so there is no upfront cost associated with building +/// a VarStreamArray, no matter how large it may be. +/// +/// You create a VarStreamArray by specifying a ValueType and an Extractor type. +/// If you do not specify an Extractor type, it expects you to specialize +/// VarStreamArrayExtractor<T> for your ValueType. +/// +/// By default an Extractor is default constructed in the class, but in some +/// cases you might find it useful for an Extractor to maintain state across +/// extractions. In this case you can provide your own Extractor through a +/// secondary constructor. The following examples show various ways of +/// creating a VarStreamArray. +/// +/// // Will use VarStreamArrayExtractor<MyType> as the extractor. +/// VarStreamArray<MyType> MyTypeArray; +/// +/// // Will use a default-constructed MyExtractor as the extractor. +/// VarStreamArray<MyType, MyExtractor> MyTypeArray2; +/// +/// // Will use the specific instance of MyExtractor provided. +/// // MyExtractor need not be default-constructible in this case. +/// MyExtractor E(SomeContext); +/// VarStreamArray<MyType, MyExtractor> MyTypeArray3(E); +/// +template <typename ValueType, typename Extractor> class VarStreamArrayIterator; + +template <typename ValueType, + typename Extractor = VarStreamArrayExtractor<ValueType>> +class VarStreamArray { + friend class VarStreamArrayIterator<ValueType, Extractor>; + +public: + typedef VarStreamArrayIterator<ValueType, Extractor> Iterator; + + VarStreamArray() {} + explicit VarStreamArray(const Extractor &E) : E(E) {} + + explicit VarStreamArray(ReadableStreamRef Stream) : Stream(Stream) {} + VarStreamArray(ReadableStreamRef Stream, const Extractor &E) + : Stream(Stream), E(E) {} + + VarStreamArray(const VarStreamArray<ValueType, Extractor> &Other) + : Stream(Other.Stream), E(Other.E) {} + + Iterator begin(bool *HadError = nullptr) const { + return Iterator(*this, E, HadError); + } + + Iterator end() const { return Iterator(E); } + + const Extractor &getExtractor() const { return E; } + + ReadableStreamRef getUnderlyingStream() const { return Stream; } + +private: + ReadableStreamRef Stream; + Extractor E; +}; + +template <typename ValueType, typename Extractor> class VarStreamArrayIterator { + typedef VarStreamArrayIterator<ValueType, Extractor> IterType; + typedef VarStreamArray<ValueType, Extractor> ArrayType; + +public: + VarStreamArrayIterator(const ArrayType &Array, const Extractor &E, + bool *HadError = nullptr) + : IterRef(Array.Stream), Array(&Array), HadError(HadError), Extract(E) { + if (IterRef.getLength() == 0) + moveToEnd(); + else { + auto EC = Extract(IterRef, ThisLen, ThisValue); + if (EC) { + consumeError(std::move(EC)); + markError(); + } + } + } + VarStreamArrayIterator() {} + explicit VarStreamArrayIterator(const Extractor &E) : Extract(E) {} + ~VarStreamArrayIterator() {} + + bool operator==(const IterType &R) const { + if (Array && R.Array) { + // Both have a valid array, make sure they're same. + assert(Array == R.Array); + return IterRef == R.IterRef; + } + + // Both iterators are at the end. + if (!Array && !R.Array) + return true; + + // One is not at the end and one is. + return false; + } + + bool operator!=(const IterType &R) { return !(*this == R); } + + const ValueType &operator*() const { + assert(Array && !HasError); + return ThisValue; + } + + IterType &operator++() { + // We are done with the current record, discard it so that we are + // positioned at the next record. + IterRef = IterRef.drop_front(ThisLen); + if (IterRef.getLength() == 0) { + // There is nothing after the current record, we must make this an end + // iterator. + moveToEnd(); + } else { + // There is some data after the current record. + auto EC = Extract(IterRef, ThisLen, ThisValue); + if (EC) { + consumeError(std::move(EC)); + markError(); + } else if (ThisLen == 0) { + // An empty record? Make this an end iterator. + moveToEnd(); + } + } + return *this; + } + + IterType operator++(int) { + IterType Original = *this; + ++*this; + return Original; + } + +private: + void moveToEnd() { + Array = nullptr; + ThisLen = 0; + } + void markError() { + moveToEnd(); + HasError = true; + if (HadError != nullptr) + *HadError = true; + } + + ValueType ThisValue; + ReadableStreamRef IterRef; + const ArrayType *Array{nullptr}; + uint32_t ThisLen{0}; + bool HasError{false}; + bool *HadError{nullptr}; + Extractor Extract; +}; + +template <typename T> class FixedStreamArrayIterator; + +template <typename T> class FixedStreamArray { + friend class FixedStreamArrayIterator<T>; + +public: + FixedStreamArray() : Stream() {} + FixedStreamArray(ReadableStreamRef Stream) : Stream(Stream) { + assert(Stream.getLength() % sizeof(T) == 0); + } + + const T &operator[](uint32_t Index) const { + assert(Index < size()); + uint32_t Off = Index * sizeof(T); + ArrayRef<uint8_t> Data; + if (auto EC = Stream.readBytes(Off, sizeof(T), Data)) { + assert(false && "Unexpected failure reading from stream"); + // This should never happen since we asserted that the stream length was + // an exact multiple of the element size. + consumeError(std::move(EC)); + } + return *reinterpret_cast<const T *>(Data.data()); + } + + uint32_t size() const { return Stream.getLength() / sizeof(T); } + + FixedStreamArrayIterator<T> begin() const { + return FixedStreamArrayIterator<T>(*this, 0); + } + FixedStreamArrayIterator<T> end() const { + return FixedStreamArrayIterator<T>(*this, size()); + } + + ReadableStreamRef getUnderlyingStream() const { return Stream; } + +private: + ReadableStreamRef Stream; +}; + +template <typename T> class FixedStreamArrayIterator { +public: + FixedStreamArrayIterator(const FixedStreamArray<T> &Array, uint32_t Index) + : Array(Array), Index(Index) {} + + bool operator==(const FixedStreamArrayIterator<T> &R) { + assert(&Array == &R.Array); + return Index == R.Index; + } + + bool operator!=(const FixedStreamArrayIterator<T> &R) { + return !(*this == R); + } + + const T &operator*() const { return Array[Index]; } + + FixedStreamArrayIterator<T> &operator++() { + assert(Index < Array.size()); + ++Index; + return *this; + } + + FixedStreamArrayIterator<T> operator++(int) { + FixedStreamArrayIterator<T> Original = *this; + ++*this; + return Original; + } + +private: + const FixedStreamArray<T> &Array; + uint32_t Index; +}; + +} // namespace msf +} // namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_STREAMARRAY_H diff --git a/llvm/include/llvm/DebugInfo/MSF/StreamInterface.h b/llvm/include/llvm/DebugInfo/MSF/StreamInterface.h new file mode 100644 index 00000000000..78347e46c22 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/StreamInterface.h @@ -0,0 +1,53 @@ +//===- StreamInterface.h - Base interface for a stream of data --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_STREAMINTERFACE_H +#define LLVM_DEBUGINFO_MSF_STREAMINTERFACE_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Error.h" +#include <cstdint> + +namespace llvm { +namespace msf { + +class ReadableStream { +public: + virtual ~ReadableStream() {} + + // Given an offset into the stream and a number of bytes, attempt to read + // the bytes and set the output ArrayRef to point to a reference into the + // stream, without copying any data. + virtual Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const = 0; + + // Given an offset into the stream, read as much as possible without copying + // any data. + virtual Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const = 0; + + virtual uint32_t getLength() const = 0; +}; + +class WritableStream : public ReadableStream { +public: + virtual ~WritableStream() {} + + // Attempt to write the given bytes into the stream at the desired offset. + // This will always necessitate a copy. Cannot shrink or grow the stream, + // only writes into existing allocated space. + virtual Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) const = 0; + + virtual Error commit() const = 0; +}; + +} // end namespace msf +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_STREAMINTERFACE_H diff --git a/llvm/include/llvm/DebugInfo/MSF/StreamReader.h b/llvm/include/llvm/DebugInfo/MSF/StreamReader.h new file mode 100644 index 00000000000..ac43aaa8447 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/StreamReader.h @@ -0,0 +1,110 @@ +//===- StreamReader.h - Reads bytes and objects from a stream ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_STREAMREADER_H +#define LLVM_DEBUGINFO_MSF_STREAMREADER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/MSF/MSFError.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamInterface.h" +#include "llvm/DebugInfo/MSF/StreamRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +#include <string> + +namespace llvm { +namespace msf { + +class StreamReader { +public: + StreamReader(ReadableStreamRef Stream); + + Error readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer); + Error readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size); + Error readInteger(uint16_t &Dest); + Error readInteger(uint32_t &Dest); + Error readZeroString(StringRef &Dest); + Error readFixedString(StringRef &Dest, uint32_t Length); + Error readStreamRef(ReadableStreamRef &Ref); + Error readStreamRef(ReadableStreamRef &Ref, uint32_t Length); + + template <typename T> Error readEnum(T &Dest) { + typename std::underlying_type<T>::type N; + if (auto EC = readInteger(N)) + return EC; + Dest = static_cast<T>(N); + return Error::success(); + } + + template <typename T> Error readObject(const T *&Dest) { + ArrayRef<uint8_t> Buffer; + if (auto EC = readBytes(Buffer, sizeof(T))) + return EC; + Dest = reinterpret_cast<const T *>(Buffer.data()); + return Error::success(); + } + + template <typename T> + Error readArray(ArrayRef<T> &Array, uint32_t NumElements) { + ArrayRef<uint8_t> Bytes; + if (NumElements == 0) { + Array = ArrayRef<T>(); + return Error::success(); + } + + if (NumElements > UINT32_MAX / sizeof(T)) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + + if (auto EC = readBytes(Bytes, NumElements * sizeof(T))) + return EC; + Array = ArrayRef<T>(reinterpret_cast<const T *>(Bytes.data()), NumElements); + return Error::success(); + } + + template <typename T, typename U> + Error readArray(VarStreamArray<T, U> &Array, uint32_t Size) { + ReadableStreamRef S; + if (auto EC = readStreamRef(S, Size)) + return EC; + Array = VarStreamArray<T, U>(S, Array.getExtractor()); + return Error::success(); + } + + template <typename T> + Error readArray(FixedStreamArray<T> &Array, uint32_t NumItems) { + if (NumItems == 0) { + Array = FixedStreamArray<T>(); + return Error::success(); + } + uint32_t Length = NumItems * sizeof(T); + if (Length / sizeof(T) != NumItems) + return make_error<MSFError>(msf_error_code::invalid_format); + if (Offset + Length > Stream.getLength()) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + ReadableStreamRef View = Stream.slice(Offset, Length); + Array = FixedStreamArray<T>(View); + Offset += Length; + return Error::success(); + } + + void setOffset(uint32_t Off) { Offset = Off; } + uint32_t getOffset() const { return Offset; } + uint32_t getLength() const { return Stream.getLength(); } + uint32_t bytesRemaining() const { return getLength() - getOffset(); } + +private: + ReadableStreamRef Stream; + uint32_t Offset; +}; +} // namespace msf +} // namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_STREAMREADER_H diff --git a/llvm/include/llvm/DebugInfo/MSF/StreamRef.h b/llvm/include/llvm/DebugInfo/MSF/StreamRef.h new file mode 100644 index 00000000000..c25fd381ef2 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/StreamRef.h @@ -0,0 +1,130 @@ +//===- StreamRef.h - A copyable reference to a stream -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_STREAMREF_H +#define LLVM_DEBUGINFO_MSF_STREAMREF_H + +#include "llvm/DebugInfo/MSF/MSFError.h" +#include "llvm/DebugInfo/MSF/StreamInterface.h" + +namespace llvm { +namespace msf { +template <class StreamType, class RefType> class StreamRefBase { +public: + StreamRefBase() : Stream(nullptr), ViewOffset(0), Length(0) {} + StreamRefBase(const StreamType &Stream, uint32_t Offset, uint32_t Length) + : Stream(&Stream), ViewOffset(Offset), Length(Length) {} + + uint32_t getLength() const { return Length; } + const StreamType *getStream() const { return Stream; } + + RefType drop_front(uint32_t N) const { + if (!Stream) + return RefType(); + + N = std::min(N, Length); + return RefType(*Stream, ViewOffset + N, Length - N); + } + + RefType keep_front(uint32_t N) const { + if (!Stream) + return RefType(); + N = std::min(N, Length); + return RefType(*Stream, ViewOffset, N); + } + + RefType slice(uint32_t Offset, uint32_t Len) const { + return drop_front(Offset).keep_front(Len); + } + + bool operator==(const RefType &Other) const { + if (Stream != Other.Stream) + return false; + if (ViewOffset != Other.ViewOffset) + return false; + if (Length != Other.Length) + return false; + return true; + } + +protected: + const StreamType *Stream; + uint32_t ViewOffset; + uint32_t Length; +}; + +class ReadableStreamRef + : public StreamRefBase<ReadableStream, ReadableStreamRef> { +public: + ReadableStreamRef() : StreamRefBase() {} + ReadableStreamRef(const ReadableStream &Stream) + : StreamRefBase(Stream, 0, Stream.getLength()) {} + ReadableStreamRef(const ReadableStream &Stream, uint32_t Offset, + uint32_t Length) + : StreamRefBase(Stream, Offset, Length) {} + + // Use StreamRef.slice() instead. + ReadableStreamRef(const ReadableStreamRef &S, uint32_t Offset, + uint32_t Length) = delete; + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef<uint8_t> &Buffer) const { + if (ViewOffset + Offset < Offset) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + if (Size + Offset > Length) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + return Stream->readBytes(ViewOffset + Offset, Size, Buffer); + } + + // Given an offset into the stream, read as much as possible without copying + // any data. + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef<uint8_t> &Buffer) const { + if (Offset >= Length) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + + if (auto EC = Stream->readLongestContiguousChunk(Offset, Buffer)) + return EC; + // This StreamRef might refer to a smaller window over a larger stream. In + // that case we will have read out more bytes than we should return, because + // we should not read past the end of the current view. + uint32_t MaxLength = Length - Offset; + if (Buffer.size() > MaxLength) + Buffer = Buffer.slice(0, MaxLength); + return Error::success(); + } +}; + +class WritableStreamRef + : public StreamRefBase<WritableStream, WritableStreamRef> { +public: + WritableStreamRef() : StreamRefBase() {} + WritableStreamRef(const WritableStream &Stream) + : StreamRefBase(Stream, 0, Stream.getLength()) {} + WritableStreamRef(const WritableStream &Stream, uint32_t Offset, + uint32_t Length) + : StreamRefBase(Stream, Offset, Length) {} + + // Use StreamRef.slice() instead. + WritableStreamRef(const WritableStreamRef &S, uint32_t Offset, + uint32_t Length) = delete; + + Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) const { + if (Data.size() + Offset > Length) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + return Stream->writeBytes(ViewOffset + Offset, Data); + } + + Error commit() const { return Stream->commit(); } +}; + +} // namespace msf +} // namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_STREAMREF_H diff --git a/llvm/include/llvm/DebugInfo/MSF/StreamWriter.h b/llvm/include/llvm/DebugInfo/MSF/StreamWriter.h new file mode 100644 index 00000000000..88043c737bd --- /dev/null +++ b/llvm/include/llvm/DebugInfo/MSF/StreamWriter.h @@ -0,0 +1,85 @@ +//===- StreamWriter.h - Writes bytes and objects to a stream ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_MSF_STREAMWRITER_H +#define LLVM_DEBUGINFO_MSF_STREAMWRITER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/MSF/MSFError.h" +#include "llvm/DebugInfo/MSF/StreamArray.h" +#include "llvm/DebugInfo/MSF/StreamInterface.h" +#include "llvm/DebugInfo/MSF/StreamRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +#include <string> + +namespace llvm { +namespace msf { + +class StreamWriter { +public: + StreamWriter(WritableStreamRef Stream); + + Error writeBytes(ArrayRef<uint8_t> Buffer); + Error writeInteger(uint16_t Dest); + Error writeInteger(uint32_t Dest); + Error writeZeroString(StringRef Str); + Error writeFixedString(StringRef Str); + Error writeStreamRef(ReadableStreamRef Ref); + Error writeStreamRef(ReadableStreamRef Ref, uint32_t Size); + + template <typename T> Error writeEnum(T Num) { + return writeInteger( + static_cast<typename std::underlying_type<T>::type>(Num)); + } + + template <typename T> Error writeObject(const T &Obj) { + static_assert(!std::is_pointer<T>::value, + "writeObject should not be used with pointers, to write " + "the pointed-to value dereference the pointer before calling " + "writeObject"); + return writeBytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&Obj), sizeof(T))); + } + + template <typename T> Error writeArray(ArrayRef<T> Array) { + if (Array.size() == 0) + return Error::success(); + + if (Array.size() > UINT32_MAX / sizeof(T)) + return make_error<MSFError>(msf_error_code::insufficient_buffer); + + return writeBytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Array.data()), + Array.size() * sizeof(T))); + } + + template <typename T, typename U> + Error writeArray(VarStreamArray<T, U> Array) { + return writeStreamRef(Array.getUnderlyingStream()); + } + + template <typename T> Error writeArray(FixedStreamArray<T> Array) { + return writeStreamRef(Array.getUnderlyingStream()); + } + + void setOffset(uint32_t Off) { Offset = Off; } + uint32_t getOffset() const { return Offset; } + uint32_t getLength() const { return Stream.getLength(); } + uint32_t bytesRemaining() const { return getLength() - getOffset(); } + +private: + WritableStreamRef Stream; + uint32_t Offset; +}; +} // namespace msf +} // namespace llvm + +#endif // LLVM_DEBUGINFO_MSF_STREAMWRITER_H |