Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 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 = ArrayRef(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 = ArrayRef(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 |