1//===- BinaryStreamRef.h - A copyable reference to a stream -----*- 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//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_SUPPORT_BINARYSTREAMREF_H
10#define LLVM_SUPPORT_BINARYSTREAMREF_H
11
12#include "llvm/ADT/ArrayRef.h"
13#include "llvm/Support/BinaryStream.h"
14#include "llvm/Support/BinaryStreamError.h"
15#include "llvm/Support/Error.h"
16#include <cstdint>
17#include <memory>
18#include <optional>
19
20namespace llvm {
21
22/// Common stuff for mutable and immutable StreamRefs.
23template <class RefType, class StreamType> class BinaryStreamRefBase {
24protected:
25  BinaryStreamRefBase() = default;
26  explicit BinaryStreamRefBase(StreamType &BorrowedImpl)
27      : BorrowedImpl(&BorrowedImpl), ViewOffset(0) {
28    if (!(BorrowedImpl.getFlags() & BSF_Append))
29      Length = BorrowedImpl.getLength();
30  }
31
32  BinaryStreamRefBase(std::shared_ptr<StreamType> SharedImpl, uint64_t Offset,
33                      std::optional<uint64_t> Length)
34      : SharedImpl(SharedImpl), BorrowedImpl(SharedImpl.get()),
35        ViewOffset(Offset), Length(Length) {}
36  BinaryStreamRefBase(StreamType &BorrowedImpl, uint64_t Offset,
37                      std::optional<uint64_t> Length)
38      : BorrowedImpl(&BorrowedImpl), ViewOffset(Offset), Length(Length) {}
39  BinaryStreamRefBase(const BinaryStreamRefBase &Other) = default;
40  BinaryStreamRefBase &operator=(const BinaryStreamRefBase &Other) = default;
41
42  BinaryStreamRefBase &operator=(BinaryStreamRefBase &&Other) = default;
43  BinaryStreamRefBase(BinaryStreamRefBase &&Other) = default;
44
45public:
46  llvm::support::endianness getEndian() const {
47    return BorrowedImpl->getEndian();
48  }
49
50  uint64_t getLength() const {
51    if (Length)
52      return *Length;
53
54    return BorrowedImpl ? (BorrowedImpl->getLength() - ViewOffset) : 0;
55  }
56
57  /// Return a new BinaryStreamRef with the first \p N elements removed.  If
58  /// this BinaryStreamRef is length-tracking, then the resulting one will be
59  /// too.
60  RefType drop_front(uint64_t N) const {
61    if (!BorrowedImpl)
62      return RefType();
63
64    N = std::min(N, getLength());
65    RefType Result(static_cast<const RefType &>(*this));
66    if (N == 0)
67      return Result;
68
69    Result.ViewOffset += N;
70    if (Result.Length)
71      *Result.Length -= N;
72    return Result;
73  }
74
75  /// Return a new BinaryStreamRef with the last \p N elements removed.  If
76  /// this BinaryStreamRef is length-tracking and \p N is greater than 0, then
77  /// this BinaryStreamRef will no longer length-track.
78  RefType drop_back(uint64_t N) const {
79    if (!BorrowedImpl)
80      return RefType();
81
82    RefType Result(static_cast<const RefType &>(*this));
83    N = std::min(N, getLength());
84
85    if (N == 0)
86      return Result;
87
88    // Since we're dropping non-zero bytes from the end, stop length-tracking
89    // by setting the length of the resulting StreamRef to an explicit value.
90    if (!Result.Length)
91      Result.Length = getLength();
92
93    *Result.Length -= N;
94    return Result;
95  }
96
97  /// Return a new BinaryStreamRef with only the first \p N elements remaining.
98  RefType keep_front(uint64_t N) const {
99    assert(N <= getLength());
100    return drop_back(getLength() - N);
101  }
102
103  /// Return a new BinaryStreamRef with only the last \p N elements remaining.
104  RefType keep_back(uint64_t N) const {
105    assert(N <= getLength());
106    return drop_front(getLength() - N);
107  }
108
109  /// Return a new BinaryStreamRef with the first and last \p N elements
110  /// removed.
111  RefType drop_symmetric(uint64_t N) const {
112    return drop_front(N).drop_back(N);
113  }
114
115  /// Return a new BinaryStreamRef with the first \p Offset elements removed,
116  /// and retaining exactly \p Len elements.
117  RefType slice(uint64_t Offset, uint64_t Len) const {
118    return drop_front(Offset).keep_front(Len);
119  }
120
121  bool valid() const { return BorrowedImpl != nullptr; }
122
123  friend bool operator==(const RefType &LHS, const RefType &RHS) {
124    if (LHS.BorrowedImpl != RHS.BorrowedImpl)
125      return false;
126    if (LHS.ViewOffset != RHS.ViewOffset)
127      return false;
128    if (LHS.Length != RHS.Length)
129      return false;
130    return true;
131  }
132
133protected:
134  Error checkOffsetForRead(uint64_t Offset, uint64_t DataSize) const {
135    if (Offset > getLength())
136      return make_error<BinaryStreamError>(stream_error_code::invalid_offset);
137    if (getLength() < DataSize + Offset)
138      return make_error<BinaryStreamError>(stream_error_code::stream_too_short);
139    return Error::success();
140  }
141
142  std::shared_ptr<StreamType> SharedImpl;
143  StreamType *BorrowedImpl = nullptr;
144  uint64_t ViewOffset = 0;
145  std::optional<uint64_t> Length;
146};
147
148/// BinaryStreamRef is to BinaryStream what ArrayRef is to an Array.  It
149/// provides copy-semantics and read only access to a "window" of the underlying
150/// BinaryStream. Note that BinaryStreamRef is *not* a BinaryStream.  That is to
151/// say, it does not inherit and override the methods of BinaryStream.  In
152/// general, you should not pass around pointers or references to BinaryStreams
153/// and use inheritance to achieve polymorphism.  Instead, you should pass
154/// around BinaryStreamRefs by value and achieve polymorphism that way.
155class BinaryStreamRef
156    : public BinaryStreamRefBase<BinaryStreamRef, BinaryStream> {
157  friend BinaryStreamRefBase<BinaryStreamRef, BinaryStream>;
158  friend class WritableBinaryStreamRef;
159  BinaryStreamRef(std::shared_ptr<BinaryStream> Impl, uint64_t ViewOffset,
160                  std::optional<uint64_t> Length)
161      : BinaryStreamRefBase(Impl, ViewOffset, Length) {}
162
163public:
164  BinaryStreamRef() = default;
165  BinaryStreamRef(BinaryStream &Stream);
166  BinaryStreamRef(BinaryStream &Stream, uint64_t Offset,
167                  std::optional<uint64_t> Length);
168  explicit BinaryStreamRef(ArrayRef<uint8_t> Data,
169                           llvm::support::endianness Endian);
170  explicit BinaryStreamRef(StringRef Data, llvm::support::endianness Endian);
171
172  BinaryStreamRef(const BinaryStreamRef &Other) = default;
173  BinaryStreamRef &operator=(const BinaryStreamRef &Other) = default;
174  BinaryStreamRef(BinaryStreamRef &&Other) = default;
175  BinaryStreamRef &operator=(BinaryStreamRef &&Other) = default;
176
177  // Use BinaryStreamRef.slice() instead.
178  BinaryStreamRef(BinaryStreamRef &S, uint64_t Offset,
179                  uint64_t Length) = delete;
180
181  /// Given an Offset into this StreamRef and a Size, return a reference to a
182  /// buffer owned by the stream.
183  ///
184  /// \returns a success error code if the entire range of data is within the
185  /// bounds of this BinaryStreamRef's view and the implementation could read
186  /// the data, and an appropriate error code otherwise.
187  Error readBytes(uint64_t Offset, uint64_t Size,
188                  ArrayRef<uint8_t> &Buffer) const;
189
190  /// Given an Offset into this BinaryStreamRef, return a reference to the
191  /// largest buffer the stream could support without necessitating a copy.
192  ///
193  /// \returns a success error code if implementation could read the data,
194  /// and an appropriate error code otherwise.
195  Error readLongestContiguousChunk(uint64_t Offset,
196                                   ArrayRef<uint8_t> &Buffer) const;
197};
198
199struct BinarySubstreamRef {
200  uint64_t Offset = 0;        // Offset in the parent stream
201  BinaryStreamRef StreamData; // Stream Data
202
203  BinarySubstreamRef slice(uint64_t Off, uint64_t Size) const {
204    BinaryStreamRef SubSub = StreamData.slice(Off, Size);
205    return {Off + Offset, SubSub};
206  }
207  BinarySubstreamRef drop_front(uint64_t N) const {
208    return slice(N, size() - N);
209  }
210  BinarySubstreamRef keep_front(uint64_t N) const { return slice(0, N); }
211
212  std::pair<BinarySubstreamRef, BinarySubstreamRef> split(uint64_t Off) const {
213    return std::make_pair(keep_front(Off), drop_front(Off));
214  }
215
216  uint64_t size() const { return StreamData.getLength(); }
217  bool empty() const { return size() == 0; }
218};
219
220class WritableBinaryStreamRef
221    : public BinaryStreamRefBase<WritableBinaryStreamRef,
222                                 WritableBinaryStream> {
223  friend BinaryStreamRefBase<WritableBinaryStreamRef, WritableBinaryStream>;
224  WritableBinaryStreamRef(std::shared_ptr<WritableBinaryStream> Impl,
225                          uint64_t ViewOffset, std::optional<uint64_t> Length)
226      : BinaryStreamRefBase(Impl, ViewOffset, Length) {}
227
228  Error checkOffsetForWrite(uint64_t Offset, uint64_t DataSize) const {
229    if (!(BorrowedImpl->getFlags() & BSF_Append))
230      return checkOffsetForRead(Offset, DataSize);
231
232    if (Offset > getLength())
233      return make_error<BinaryStreamError>(stream_error_code::invalid_offset);
234    return Error::success();
235  }
236
237public:
238  WritableBinaryStreamRef() = default;
239  WritableBinaryStreamRef(WritableBinaryStream &Stream);
240  WritableBinaryStreamRef(WritableBinaryStream &Stream, uint64_t Offset,
241                          std::optional<uint64_t> Length);
242  explicit WritableBinaryStreamRef(MutableArrayRef<uint8_t> Data,
243                                   llvm::support::endianness Endian);
244  WritableBinaryStreamRef(const WritableBinaryStreamRef &Other) = default;
245  WritableBinaryStreamRef &
246  operator=(const WritableBinaryStreamRef &Other) = default;
247
248  WritableBinaryStreamRef(WritableBinaryStreamRef &&Other) = default;
249  WritableBinaryStreamRef &operator=(WritableBinaryStreamRef &&Other) = default;
250
251  // Use WritableBinaryStreamRef.slice() instead.
252  WritableBinaryStreamRef(WritableBinaryStreamRef &S, uint64_t Offset,
253                          uint64_t Length) = delete;
254
255  /// Given an Offset into this WritableBinaryStreamRef and some input data,
256  /// writes the data to the underlying stream.
257  ///
258  /// \returns a success error code if the data could fit within the underlying
259  /// stream at the specified location and the implementation could write the
260  /// data, and an appropriate error code otherwise.
261  Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> Data) const;
262
263  /// Conver this WritableBinaryStreamRef to a read-only BinaryStreamRef.
264  operator BinaryStreamRef() const;
265
266  /// For buffered streams, commits changes to the backing store.
267  Error commit();
268};
269
270} // end namespace llvm
271
272#endif // LLVM_SUPPORT_BINARYSTREAMREF_H
273