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