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
24namespace 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.
30class BinaryByteStream : public BinaryStream {
31public:
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
65protected:
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.
74class MemoryBufferByteStream : public BinaryByteStream {
75public:
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.
88class MutableBinaryByteStream : public WritableBinaryStream {
89public:
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
127private:
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.
134class AppendingBinaryByteStream : public WritableBinaryStream {
135 std::vector<uint8_t> Data;
136 llvm::endianness Endian = llvm::endianness::little;
137
138public:
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.
201class FileBufferByteStream : public WritableBinaryStream {
202private:
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
230public:
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
261private:
262 StreamImpl Impl;
263};
264
265} // end namespace llvm
266
267#endif // LLVM_SUPPORT_BINARYBYTESTREAM_H
268

source code of llvm/include/llvm/Support/BinaryByteStream.h