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