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::endianness Endian) |
34 | : Endian(Endian), Data(Data) {} |
35 | BinaryByteStream(StringRef Data, llvm::endianness Endian) |
36 | : Endian(Endian), Data(Data.bytes_begin(), Data.bytes_end()) {} |
37 | |
38 | llvm::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, DataSize: Size)) |
43 | return EC; |
44 | Buffer = Data.slice(N: Offset, M: 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, DataSize: 1)) |
51 | return EC; |
52 | Buffer = Data.slice(N: 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::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::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::endianness Endian) |
93 | : Data(Data), ImmutableStream(Data, Endian) {} |
94 | |
95 | llvm::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, DataSize: Buffer.size())) |
116 | return EC; |
117 | |
118 | uint8_t *DataPtr = const_cast<uint8_t *>(Data.data()); |
119 | ::memcpy(dest: DataPtr + Offset, src: Buffer.data(), n: 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::endianness Endian = llvm::endianness::little; |
137 | |
138 | public: |
139 | AppendingBinaryByteStream() = default; |
140 | AppendingBinaryByteStream(llvm::endianness Endian) : Endian(Endian) {} |
141 | |
142 | void clear() { Data.clear(); } |
143 | |
144 | llvm::endianness getEndian() const override { return Endian; } |
145 | |
146 | Error readBytes(uint64_t Offset, uint64_t Size, |
147 | ArrayRef<uint8_t> &Buffer) override { |
148 | if (auto EC = checkOffsetForWrite(Offset, DataSize: Buffer.size())) |
149 | return EC; |
150 | |
151 | Buffer = ArrayRef(Data).slice(N: Offset, M: Size); |
152 | return Error::success(); |
153 | } |
154 | |
155 | void insert(uint64_t Offset, ArrayRef<uint8_t> Bytes) { |
156 | Data.insert(position: Data.begin() + Offset, first: Bytes.begin(), last: Bytes.end()); |
157 | } |
158 | |
159 | Error readLongestContiguousChunk(uint64_t Offset, |
160 | ArrayRef<uint8_t> &Buffer) override { |
161 | if (auto EC = checkOffsetForWrite(Offset, DataSize: 1)) |
162 | return EC; |
163 | |
164 | Buffer = ArrayRef(Data).slice(N: Offset); |
165 | return Error::success(); |
166 | } |
167 | |
168 | uint64_t getLength() override { return Data.size(); } |
169 | |
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>(Args: stream_error_code::invalid_offset); |
182 | |
183 | uint64_t RequiredSize = Offset + Buffer.size(); |
184 | if (RequiredSize > Data.size()) |
185 | Data.resize(new_size: RequiredSize); |
186 | |
187 | ::memcpy(dest: Data.data() + Offset, src: Buffer.data(), n: Buffer.size()); |
188 | return Error::success(); |
189 | } |
190 | |
191 | Error commit() override { return Error::success(); } |
192 | |
193 | /// Return the properties of this stream. |
194 | BinaryStreamFlags getFlags() const override { return BSF_Write | BSF_Append; } |
195 | |
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: |
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 | |
213 | Error commit() override { |
214 | if (FileBuffer->commit()) |
215 | return make_error<BinaryStreamError>( |
216 | Args: stream_error_code::filesystem_error); |
217 | return Error::success(); |
218 | } |
219 | |
220 | /// Returns a pointer to the start of the buffer. |
221 | uint8_t *getBufferStart() const { return FileBuffer->getBufferStart(); } |
222 | |
223 | /// Returns a pointer to the end of the buffer. |
224 | uint8_t *getBufferEnd() const { return FileBuffer->getBufferEnd(); } |
225 | |
226 | private: |
227 | std::unique_ptr<FileOutputBuffer> FileBuffer; |
228 | }; |
229 | |
230 | public: |
231 | FileBufferByteStream(std::unique_ptr<FileOutputBuffer> Buffer, |
232 | llvm::endianness Endian) |
233 | : Impl(std::move(Buffer), Endian) {} |
234 | |
235 | llvm::endianness getEndian() const override { return Impl.getEndian(); } |
236 | |
237 | Error readBytes(uint64_t Offset, uint64_t Size, |
238 | ArrayRef<uint8_t> &Buffer) override { |
239 | return Impl.readBytes(Offset, Size, Buffer); |
240 | } |
241 | |
242 | Error readLongestContiguousChunk(uint64_t Offset, |
243 | ArrayRef<uint8_t> &Buffer) override { |
244 | return Impl.readLongestContiguousChunk(Offset, Buffer); |
245 | } |
246 | |
247 | uint64_t getLength() override { return Impl.getLength(); } |
248 | |
249 | Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) override { |
250 | return Impl.writeBytes(Offset, Buffer: Data); |
251 | } |
252 | |
253 | Error commit() override { return Impl.commit(); } |
254 | |
255 | /// Returns a pointer to the start of the buffer. |
256 | uint8_t *getBufferStart() const { return Impl.getBufferStart(); } |
257 | |
258 | /// Returns a pointer to the end of the buffer. |
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 | |