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;
33   BinaryByteStream(ArrayRef<uint8_t> Data, llvm::support::endianness Endian)
34       : Endian(Endian), Data(Data) {}
35   BinaryByteStream(StringRef Data, llvm::support::endianness Endian)
36       : Endian(Endian), Data(Data.bytes_begin(), Data.bytes_end()) {}
37 
38   llvm::support::endianness getEndian() const override { return Endian; }
39 
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 
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 
56   uint64_t getLength() override { return Data.size(); }
57 
58   ArrayRef<uint8_t> data() const { return Data; }
59 
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::support::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:
76   MemoryBufferByteStream(std::unique_ptr<MemoryBuffer> Buffer,
77                          llvm::support::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;
91   MutableBinaryByteStream(MutableArrayRef<uint8_t> Data,
92                           llvm::support::endianness Endian)
93       : Data(Data), ImmutableStream(Data, Endian) {}
94 
95   llvm::support::endianness getEndian() const override {
96     return ImmutableStream.getEndian();
97   }
98 
99   Error readBytes(uint64_t Offset, uint64_t Size,
100                   ArrayRef<uint8_t> &Buffer) override {
101     return ImmutableStream.readBytes(Offset, Size, Buffer);
102   }
103 
104   Error readLongestContiguousChunk(uint64_t Offset,
105                                    ArrayRef<uint8_t> &Buffer) override {
106     return ImmutableStream.readLongestContiguousChunk(Offset, Buffer);
107   }
108 
109   uint64_t getLength() override { return ImmutableStream.getLength(); }
110 
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 
123   Error commit() override { return Error::success(); }
124 
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::support::endianness Endian = llvm::support::little;
137 
138 public:
139   AppendingBinaryByteStream() = default;
140   AppendingBinaryByteStream(llvm::support::endianness Endian)
141       : Endian(Endian) {}
142 
143   void clear() { Data.clear(); }
144 
145   llvm::support::endianness getEndian() const override { return Endian; }
146 
147   Error readBytes(uint64_t Offset, uint64_t Size,
148                   ArrayRef<uint8_t> &Buffer) override {
149     if (auto EC = checkOffsetForWrite(Offset, Buffer.size()))
150       return EC;
151 
152     Buffer = makeArrayRef(Data).slice(Offset, Size);
153     return Error::success();
154   }
155 
156   void insert(uint64_t Offset, ArrayRef<uint8_t> Bytes) {
157     Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end());
158   }
159 
160   Error readLongestContiguousChunk(uint64_t Offset,
161                                    ArrayRef<uint8_t> &Buffer) override {
162     if (auto EC = checkOffsetForWrite(Offset, 1))
163       return EC;
164 
165     Buffer = makeArrayRef(Data).slice(Offset);
166     return Error::success();
167   }
168 
169   uint64_t getLength() override { return Data.size(); }
170 
171   Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Buffer) override {
172     if (Buffer.empty())
173       return Error::success();
174 
175     // This is well-defined for any case except where offset is strictly
176     // greater than the current length.  If offset is equal to the current
177     // length, we can still grow.  If offset is beyond the current length, we
178     // would have to decide how to deal with the intermediate uninitialized
179     // bytes.  So we punt on that case for simplicity and just say it's an
180     // error.
181     if (Offset > getLength())
182       return make_error<BinaryStreamError>(stream_error_code::invalid_offset);
183 
184     uint64_t RequiredSize = Offset + Buffer.size();
185     if (RequiredSize > Data.size())
186       Data.resize(RequiredSize);
187 
188     ::memcpy(Data.data() + Offset, Buffer.data(), Buffer.size());
189     return Error::success();
190   }
191 
192   Error commit() override { return Error::success(); }
193 
194   /// Return the properties of this stream.
195   BinaryStreamFlags getFlags() const override { return BSF_Write | BSF_Append; }
196 
197   MutableArrayRef<uint8_t> data() { return Data; }
198 };
199 
200 /// An implementation of WritableBinaryStream backed by an llvm
201 /// FileOutputBuffer.
202 class FileBufferByteStream : public WritableBinaryStream {
203 private:
204   class StreamImpl : public MutableBinaryByteStream {
205   public:
206     StreamImpl(std::unique_ptr<FileOutputBuffer> Buffer,
207                llvm::support::endianness Endian)
208         : MutableBinaryByteStream(
209               MutableArrayRef<uint8_t>(Buffer->getBufferStart(),
210                                        Buffer->getBufferEnd()),
211               Endian),
212           FileBuffer(std::move(Buffer)) {}
213 
214     Error commit() override {
215       if (FileBuffer->commit())
216         return make_error<BinaryStreamError>(
217             stream_error_code::filesystem_error);
218       return Error::success();
219     }
220 
221     /// Returns a pointer to the start of the buffer.
222     uint8_t *getBufferStart() const { return FileBuffer->getBufferStart(); }
223 
224     /// Returns a pointer to the end of the buffer.
225     uint8_t *getBufferEnd() const { return FileBuffer->getBufferEnd(); }
226 
227   private:
228     std::unique_ptr<FileOutputBuffer> FileBuffer;
229   };
230 
231 public:
232   FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer,
233                        llvm::support::endianness Endian)
234       : Impl(std::move(Buffer), Endian) {}
235 
236   llvm::support::endianness getEndian() const override {
237     return Impl.getEndian();
238   }
239 
240   Error readBytes(uint64_t Offset, uint64_t Size,
241                   ArrayRef<uint8_t> &Buffer) override {
242     return Impl.readBytes(Offset, Size, Buffer);
243   }
244 
245   Error readLongestContiguousChunk(uint64_t Offset,
246                                    ArrayRef<uint8_t> &Buffer) override {
247     return Impl.readLongestContiguousChunk(Offset, Buffer);
248   }
249 
250   uint64_t getLength() override { return Impl.getLength(); }
251 
252   Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) override {
253     return Impl.writeBytes(Offset, Data);
254   }
255 
256   Error commit() override { return Impl.commit(); }
257 
258   /// Returns a pointer to the start of the buffer.
259   uint8_t *getBufferStart() const { return Impl.getBufferStart(); }
260 
261   /// Returns a pointer to the end of the buffer.
262   uint8_t *getBufferEnd() const { return Impl.getBufferEnd(); }
263 
264 private:
265   StreamImpl Impl;
266 };
267 
268 } // end namespace llvm
269 
270 #endif // LLVM_SUPPORT_BINARYBYTESTREAM_H
271