1 //===- BinaryByteStream.h ---------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //===----------------------------------------------------------------------===//
8 // A BinaryStream which stores data in a single continguous memory buffer.
9 //===----------------------------------------------------------------------===//
10 
11 #ifndef LLVM_SUPPORT_BINARYBYTESTREAM_H
12 #define LLVM_SUPPORT_BINARYBYTESTREAM_H
13 
14 #include "llvm/ADT/ArrayRef.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/BinaryStream.h"
17 #include "llvm/Support/BinaryStreamError.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Support/FileOutputBuffer.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #include <algorithm>
22 #include <cstdint>
23 #include <cstring>
24 #include <memory>
25 
26 namespace llvm {
27 
28 /// An implementation of BinaryStream which holds its entire data set
29 /// in a single contiguous buffer.  BinaryByteStream guarantees that no read
30 /// operation will ever incur a copy.  Note that BinaryByteStream does not
31 /// own the underlying buffer.
32 class BinaryByteStream : public BinaryStream {
33 public:
34   BinaryByteStream() = default;
BinaryByteStream(ArrayRef<uint8_t> Data,llvm::support::endianness Endian)35   BinaryByteStream(ArrayRef<uint8_t> Data, llvm::support::endianness Endian)
36       : Endian(Endian), Data(Data) {}
BinaryByteStream(StringRef Data,llvm::support::endianness Endian)37   BinaryByteStream(StringRef Data, llvm::support::endianness Endian)
38       : Endian(Endian), Data(Data.bytes_begin(), Data.bytes_end()) {}
39 
getEndian()40   llvm::support::endianness getEndian() const override { return Endian; }
41 
readBytes(uint32_t Offset,uint32_t Size,ArrayRef<uint8_t> & Buffer)42   Error readBytes(uint32_t Offset, uint32_t Size,
43                   ArrayRef<uint8_t> &Buffer) override {
44     if (auto EC = checkOffsetForRead(Offset, Size))
45       return EC;
46     Buffer = Data.slice(Offset, Size);
47     return Error::success();
48   }
49 
readLongestContiguousChunk(uint32_t Offset,ArrayRef<uint8_t> & Buffer)50   Error readLongestContiguousChunk(uint32_t Offset,
51                                    ArrayRef<uint8_t> &Buffer) override {
52     if (auto EC = checkOffsetForRead(Offset, 1))
53       return EC;
54     Buffer = Data.slice(Offset);
55     return Error::success();
56   }
57 
getLength()58   uint32_t getLength() override { return Data.size(); }
59 
data()60   ArrayRef<uint8_t> data() const { return Data; }
61 
str()62   StringRef str() const {
63     const char *CharData = reinterpret_cast<const char *>(Data.data());
64     return StringRef(CharData, Data.size());
65   }
66 
67 protected:
68   llvm::support::endianness Endian;
69   ArrayRef<uint8_t> Data;
70 };
71 
72 /// An implementation of BinaryStream whose data is backed by an llvm
73 /// MemoryBuffer object.  MemoryBufferByteStream owns the MemoryBuffer in
74 /// question.  As with BinaryByteStream, reading from a MemoryBufferByteStream
75 /// will never cause a copy.
76 class MemoryBufferByteStream : public BinaryByteStream {
77 public:
MemoryBufferByteStream(std::unique_ptr<MemoryBuffer> Buffer,llvm::support::endianness Endian)78   MemoryBufferByteStream(std::unique_ptr<MemoryBuffer> Buffer,
79                          llvm::support::endianness Endian)
80       : BinaryByteStream(Buffer->getBuffer(), Endian),
81         MemBuffer(std::move(Buffer)) {}
82 
83   std::unique_ptr<MemoryBuffer> MemBuffer;
84 };
85 
86 /// An implementation of BinaryStream which holds its entire data set
87 /// in a single contiguous buffer.  As with BinaryByteStream, the mutable
88 /// version also guarantees that no read operation will ever incur a copy,
89 /// and similarly it does not own the underlying buffer.
90 class MutableBinaryByteStream : public WritableBinaryStream {
91 public:
92   MutableBinaryByteStream() = default;
MutableBinaryByteStream(MutableArrayRef<uint8_t> Data,llvm::support::endianness Endian)93   MutableBinaryByteStream(MutableArrayRef<uint8_t> Data,
94                           llvm::support::endianness Endian)
95       : Data(Data), ImmutableStream(Data, Endian) {}
96 
getEndian()97   llvm::support::endianness getEndian() const override {
98     return ImmutableStream.getEndian();
99   }
100 
readBytes(uint32_t Offset,uint32_t Size,ArrayRef<uint8_t> & Buffer)101   Error readBytes(uint32_t Offset, uint32_t Size,
102                   ArrayRef<uint8_t> &Buffer) override {
103     return ImmutableStream.readBytes(Offset, Size, Buffer);
104   }
105 
readLongestContiguousChunk(uint32_t Offset,ArrayRef<uint8_t> & Buffer)106   Error readLongestContiguousChunk(uint32_t Offset,
107                                    ArrayRef<uint8_t> &Buffer) override {
108     return ImmutableStream.readLongestContiguousChunk(Offset, Buffer);
109   }
110 
getLength()111   uint32_t getLength() override { return ImmutableStream.getLength(); }
112 
writeBytes(uint32_t Offset,ArrayRef<uint8_t> Buffer)113   Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Buffer) override {
114     if (Buffer.empty())
115       return Error::success();
116 
117     if (auto EC = checkOffsetForWrite(Offset, Buffer.size()))
118       return EC;
119 
120     uint8_t *DataPtr = const_cast<uint8_t *>(Data.data());
121     ::memcpy(DataPtr + Offset, Buffer.data(), Buffer.size());
122     return Error::success();
123   }
124 
commit()125   Error commit() override { return Error::success(); }
126 
data()127   MutableArrayRef<uint8_t> data() const { return Data; }
128 
129 private:
130   MutableArrayRef<uint8_t> Data;
131   BinaryByteStream ImmutableStream;
132 };
133 
134 /// An implementation of WritableBinaryStream which can write at its end
135 /// causing the underlying data to grow.  This class owns the underlying data.
136 class AppendingBinaryByteStream : public WritableBinaryStream {
137   std::vector<uint8_t> Data;
138   llvm::support::endianness Endian = llvm::support::little;
139 
140 public:
141   AppendingBinaryByteStream() = default;
AppendingBinaryByteStream(llvm::support::endianness Endian)142   AppendingBinaryByteStream(llvm::support::endianness Endian)
143       : Endian(Endian) {}
144 
clear()145   void clear() { Data.clear(); }
146 
getEndian()147   llvm::support::endianness getEndian() const override { return Endian; }
148 
readBytes(uint32_t Offset,uint32_t Size,ArrayRef<uint8_t> & Buffer)149   Error readBytes(uint32_t Offset, uint32_t Size,
150                   ArrayRef<uint8_t> &Buffer) override {
151     if (auto EC = checkOffsetForWrite(Offset, Buffer.size()))
152       return EC;
153 
154     Buffer = makeArrayRef(Data).slice(Offset, Size);
155     return Error::success();
156   }
157 
insert(uint32_t Offset,ArrayRef<uint8_t> Bytes)158   void insert(uint32_t Offset, ArrayRef<uint8_t> Bytes) {
159     Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end());
160   }
161 
readLongestContiguousChunk(uint32_t Offset,ArrayRef<uint8_t> & Buffer)162   Error readLongestContiguousChunk(uint32_t Offset,
163                                    ArrayRef<uint8_t> &Buffer) override {
164     if (auto EC = checkOffsetForWrite(Offset, 1))
165       return EC;
166 
167     Buffer = makeArrayRef(Data).slice(Offset);
168     return Error::success();
169   }
170 
getLength()171   uint32_t getLength() override { return Data.size(); }
172 
writeBytes(uint32_t Offset,ArrayRef<uint8_t> Buffer)173   Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Buffer) override {
174     if (Buffer.empty())
175       return Error::success();
176 
177     // This is well-defined for any case except where offset is strictly
178     // greater than the current length.  If offset is equal to the current
179     // length, we can still grow.  If offset is beyond the current length, we
180     // would have to decide how to deal with the intermediate uninitialized
181     // bytes.  So we punt on that case for simplicity and just say it's an
182     // error.
183     if (Offset > getLength())
184       return make_error<BinaryStreamError>(stream_error_code::invalid_offset);
185 
186     uint32_t RequiredSize = Offset + Buffer.size();
187     if (RequiredSize > Data.size())
188       Data.resize(RequiredSize);
189 
190     ::memcpy(Data.data() + Offset, Buffer.data(), Buffer.size());
191     return Error::success();
192   }
193 
commit()194   Error commit() override { return Error::success(); }
195 
196   /// Return the properties of this stream.
getFlags()197   virtual BinaryStreamFlags getFlags() const override {
198     return BSF_Write | BSF_Append;
199   }
200 
data()201   MutableArrayRef<uint8_t> data() { return Data; }
202 };
203 
204 /// An implementation of WritableBinaryStream backed by an llvm
205 /// FileOutputBuffer.
206 class FileBufferByteStream : public WritableBinaryStream {
207 private:
208   class StreamImpl : public MutableBinaryByteStream {
209   public:
StreamImpl(std::unique_ptr<FileOutputBuffer> Buffer,llvm::support::endianness Endian)210     StreamImpl(std::unique_ptr<FileOutputBuffer> Buffer,
211                llvm::support::endianness Endian)
212         : MutableBinaryByteStream(
213               MutableArrayRef<uint8_t>(Buffer->getBufferStart(),
214                                        Buffer->getBufferEnd()),
215               Endian),
216           FileBuffer(std::move(Buffer)) {}
217 
commit()218     Error commit() override {
219       if (FileBuffer->commit())
220         return make_error<BinaryStreamError>(
221             stream_error_code::filesystem_error);
222       return Error::success();
223     }
224 
225     /// Returns a pointer to the start of the buffer.
getBufferStart()226     uint8_t *getBufferStart() const { return FileBuffer->getBufferStart(); }
227 
228     /// Returns a pointer to the end of the buffer.
getBufferEnd()229     uint8_t *getBufferEnd() const { return FileBuffer->getBufferEnd(); }
230 
231   private:
232     std::unique_ptr<FileOutputBuffer> FileBuffer;
233   };
234 
235 public:
FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer,llvm::support::endianness Endian)236   FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer,
237                        llvm::support::endianness Endian)
238       : Impl(std::move(Buffer), Endian) {}
239 
getEndian()240   llvm::support::endianness getEndian() const override {
241     return Impl.getEndian();
242   }
243 
readBytes(uint32_t Offset,uint32_t Size,ArrayRef<uint8_t> & Buffer)244   Error readBytes(uint32_t Offset, uint32_t Size,
245                   ArrayRef<uint8_t> &Buffer) override {
246     return Impl.readBytes(Offset, Size, Buffer);
247   }
248 
readLongestContiguousChunk(uint32_t Offset,ArrayRef<uint8_t> & Buffer)249   Error readLongestContiguousChunk(uint32_t Offset,
250                                    ArrayRef<uint8_t> &Buffer) override {
251     return Impl.readLongestContiguousChunk(Offset, Buffer);
252   }
253 
getLength()254   uint32_t getLength() override { return Impl.getLength(); }
255 
writeBytes(uint32_t Offset,ArrayRef<uint8_t> Data)256   Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) override {
257     return Impl.writeBytes(Offset, Data);
258   }
259 
commit()260   Error commit() override { return Impl.commit(); }
261 
262   /// Returns a pointer to the start of the buffer.
getBufferStart()263   uint8_t *getBufferStart() const { return Impl.getBufferStart(); }
264 
265   /// Returns a pointer to the end of the buffer.
getBufferEnd()266   uint8_t *getBufferEnd() const { return Impl.getBufferEnd(); }
267 
268 private:
269   StreamImpl Impl;
270 };
271 
272 } // end namespace llvm
273 
274 #endif // LLVM_SUPPORT_BYTESTREAM_H
275