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, 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
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, 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
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, Buffer.size()))
149      return EC;
150
151    Buffer = ArrayRef(Data).slice(Offset, Size);
152    return Error::success();
153  }
154
155  void insert(uint64_t Offset, ArrayRef<uint8_t> Bytes) {
156    Data.insert(Data.begin() + Offset, Bytes.begin(), Bytes.end());
157  }
158
159  Error readLongestContiguousChunk(uint64_t Offset,
160                                   ArrayRef<uint8_t> &Buffer) override {
161    if (auto EC = checkOffsetForWrite(Offset, 1))
162      return EC;
163
164    Buffer = ArrayRef(Data).slice(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>(stream_error_code::invalid_offset);
182
183    uint64_t RequiredSize = Offset + Buffer.size();
184    if (RequiredSize > Data.size())
185      Data.resize(RequiredSize);
186
187    ::memcpy(Data.data() + Offset, Buffer.data(), 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            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, 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