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;
34   BinaryByteStream(ArrayRef<uint8_t> Data, llvm::support::endianness Endian)
35       : Endian(Endian), Data(Data) {}
36   BinaryByteStream(StringRef Data, llvm::support::endianness Endian)
37       : Endian(Endian), Data(Data.bytes_begin(), Data.bytes_end()) {}
38 
39   llvm::support::endianness getEndian() const override { return Endian; }
40 
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 
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 
57   uint32_t getLength() override { return Data.size(); }
58 
59   ArrayRef<uint8_t> data() const { return Data; }
60 
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:
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;
92   MutableBinaryByteStream(MutableArrayRef<uint8_t> Data,
93                           llvm::support::endianness Endian)
94       : Data(Data), ImmutableStream(Data, Endian) {}
95 
96   llvm::support::endianness getEndian() const override {
97     return ImmutableStream.getEndian();
98   }
99 
100   Error readBytes(uint32_t Offset, uint32_t Size,
101                   ArrayRef<uint8_t> &Buffer) override {
102     return ImmutableStream.readBytes(Offset, Size, Buffer);
103   }
104 
105   Error readLongestContiguousChunk(uint32_t Offset,
106                                    ArrayRef<uint8_t> &Buffer) override {
107     return ImmutableStream.readLongestContiguousChunk(Offset, Buffer);
108   }
109 
110   uint32_t getLength() override { return ImmutableStream.getLength(); }
111 
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 
124   Error commit() override { return Error::success(); }
125 
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;
141   AppendingBinaryByteStream(llvm::support::endianness Endian)
142       : Endian(Endian) {}
143 
144   void clear() { Data.clear(); }
145 
146   llvm::support::endianness getEndian() const override { return Endian; }
147 
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 
157   void insert(uint32_t Offset, ArrayRef<uint8_t> Bytes) {
158     Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end());
159   }
160 
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 
170   uint32_t getLength() override { return Data.size(); }
171 
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 
193   Error commit() override { return Error::success(); }
194 
195   /// Return the properties of this stream.
196   virtual BinaryStreamFlags getFlags() const override {
197     return BSF_Write | BSF_Append;
198   }
199 
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:
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 
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.
225     uint8_t *getBufferStart() const { return FileBuffer->getBufferStart(); }
226 
227     /// Returns a pointer to the end of the buffer.
228     uint8_t *getBufferEnd() const { return FileBuffer->getBufferEnd(); }
229 
230   private:
231     std::unique_ptr<FileOutputBuffer> FileBuffer;
232   };
233 
234 public:
235   FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer,
236                        llvm::support::endianness Endian)
237       : Impl(std::move(Buffer), Endian) {}
238 
239   llvm::support::endianness getEndian() const override {
240     return Impl.getEndian();
241   }
242 
243   Error readBytes(uint32_t Offset, uint32_t Size,
244                   ArrayRef<uint8_t> &Buffer) override {
245     return Impl.readBytes(Offset, Size, Buffer);
246   }
247 
248   Error readLongestContiguousChunk(uint32_t Offset,
249                                    ArrayRef<uint8_t> &Buffer) override {
250     return Impl.readLongestContiguousChunk(Offset, Buffer);
251   }
252 
253   uint32_t getLength() override { return Impl.getLength(); }
254 
255   Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) override {
256     return Impl.writeBytes(Offset, Data);
257   }
258 
259   Error commit() override { return Impl.commit(); }
260 
261   /// Returns a pointer to the start of the buffer.
262   uint8_t *getBufferStart() const { return Impl.getBufferStart(); }
263 
264   /// Returns a pointer to the end of the buffer.
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