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