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
25namespace 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.
31class BinaryByteStream : public BinaryStream {
32public:
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
66protected:
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.
75class MemoryBufferByteStream : public BinaryByteStream {
76public:
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.
89class MutableBinaryByteStream : public WritableBinaryStream {
90public:
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
128private:
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.
135class AppendingBinaryByteStream : public WritableBinaryStream {
136 std::vector<uint8_t> Data;
137 llvm::support::endianness Endian = llvm::support::little;
138
139public:
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.
205class FileBufferByteStream : public WritableBinaryStream {
206private:
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
234public:
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
267private:
268 StreamImpl Impl;
269};
270
271} // end namespace llvm
272
273#endif // LLVM_SUPPORT_BINARYBYTESTREAM_H
274