1317017Sdim//===- BinaryStreamRef.h - A copyable reference to a stream -----*- C++ -*-===//
2317017Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6317017Sdim//
7317017Sdim//===----------------------------------------------------------------------===//
8317017Sdim
9317017Sdim#ifndef LLVM_SUPPORT_BINARYSTREAMREF_H
10317017Sdim#define LLVM_SUPPORT_BINARYSTREAMREF_H
11317017Sdim
12317017Sdim#include "llvm/ADT/ArrayRef.h"
13327952Sdim#include "llvm/ADT/Optional.h"
14317017Sdim#include "llvm/Support/BinaryStream.h"
15317017Sdim#include "llvm/Support/BinaryStreamError.h"
16317017Sdim#include "llvm/Support/Error.h"
17317017Sdim#include <algorithm>
18317017Sdim#include <cstdint>
19318681Sdim#include <memory>
20317017Sdim
21317017Sdimnamespace llvm {
22317017Sdim
23317017Sdim/// Common stuff for mutable and immutable StreamRefs.
24318681Sdimtemplate <class RefType, class StreamType> class BinaryStreamRefBase {
25318681Sdimprotected:
26318681Sdim  BinaryStreamRefBase() = default;
27327952Sdim  explicit BinaryStreamRefBase(StreamType &BorrowedImpl)
28327952Sdim      : BorrowedImpl(&BorrowedImpl), ViewOffset(0) {
29327952Sdim    if (!(BorrowedImpl.getFlags() & BSF_Append))
30327952Sdim      Length = BorrowedImpl.getLength();
31327952Sdim  }
32327952Sdim
33318681Sdim  BinaryStreamRefBase(std::shared_ptr<StreamType> SharedImpl, uint32_t Offset,
34327952Sdim                      Optional<uint32_t> Length)
35318681Sdim      : SharedImpl(SharedImpl), BorrowedImpl(SharedImpl.get()),
36318681Sdim        ViewOffset(Offset), Length(Length) {}
37318681Sdim  BinaryStreamRefBase(StreamType &BorrowedImpl, uint32_t Offset,
38327952Sdim                      Optional<uint32_t> Length)
39318681Sdim      : BorrowedImpl(&BorrowedImpl), ViewOffset(Offset), Length(Length) {}
40327952Sdim  BinaryStreamRefBase(const BinaryStreamRefBase &Other) = default;
41327952Sdim  BinaryStreamRefBase &operator=(const BinaryStreamRefBase &Other) = default;
42318681Sdim
43327952Sdim  BinaryStreamRefBase &operator=(BinaryStreamRefBase &&Other) = default;
44327952Sdim  BinaryStreamRefBase(BinaryStreamRefBase &&Other) = default;
45327952Sdim
46317017Sdimpublic:
47318681Sdim  llvm::support::endianness getEndian() const {
48318681Sdim    return BorrowedImpl->getEndian();
49318681Sdim  }
50317017Sdim
51327952Sdim  uint32_t getLength() const {
52327952Sdim    if (Length.hasValue())
53327952Sdim      return *Length;
54317017Sdim
55327952Sdim    return BorrowedImpl ? (BorrowedImpl->getLength() - ViewOffset) : 0;
56327952Sdim  }
57327952Sdim
58327952Sdim  /// Return a new BinaryStreamRef with the first \p N elements removed.  If
59327952Sdim  /// this BinaryStreamRef is length-tracking, then the resulting one will be
60327952Sdim  /// too.
61317017Sdim  RefType drop_front(uint32_t N) const {
62318681Sdim    if (!BorrowedImpl)
63317017Sdim      return RefType();
64317017Sdim
65327952Sdim    N = std::min(N, getLength());
66318681Sdim    RefType Result(static_cast<const RefType &>(*this));
67327952Sdim    if (N == 0)
68327952Sdim      return Result;
69327952Sdim
70318681Sdim    Result.ViewOffset += N;
71327952Sdim    if (Result.Length.hasValue())
72327952Sdim      *Result.Length -= N;
73318681Sdim    return Result;
74317017Sdim  }
75317017Sdim
76327952Sdim  /// Return a new BinaryStreamRef with the last \p N elements removed.  If
77327952Sdim  /// this BinaryStreamRef is length-tracking and \p N is greater than 0, then
78327952Sdim  /// this BinaryStreamRef will no longer length-track.
79318681Sdim  RefType drop_back(uint32_t N) const {
80318681Sdim    if (!BorrowedImpl)
81317017Sdim      return RefType();
82318681Sdim
83318681Sdim    RefType Result(static_cast<const RefType &>(*this));
84327952Sdim    N = std::min(N, getLength());
85327952Sdim
86327952Sdim    if (N == 0)
87327952Sdim      return Result;
88327952Sdim
89327952Sdim    // Since we're dropping non-zero bytes from the end, stop length-tracking
90327952Sdim    // by setting the length of the resulting StreamRef to an explicit value.
91327952Sdim    if (!Result.Length.hasValue())
92327952Sdim      Result.Length = getLength();
93327952Sdim
94327952Sdim    *Result.Length -= N;
95318681Sdim    return Result;
96317017Sdim  }
97317017Sdim
98318681Sdim  /// Return a new BinaryStreamRef with only the first \p N elements remaining.
99318681Sdim  RefType keep_front(uint32_t N) const {
100318681Sdim    assert(N <= getLength());
101318681Sdim    return drop_back(getLength() - N);
102318681Sdim  }
103318681Sdim
104318681Sdim  /// Return a new BinaryStreamRef with only the last \p N elements remaining.
105318681Sdim  RefType keep_back(uint32_t N) const {
106318681Sdim    assert(N <= getLength());
107318681Sdim    return drop_front(getLength() - N);
108318681Sdim  }
109318681Sdim
110318681Sdim  /// Return a new BinaryStreamRef with the first and last \p N elements
111318681Sdim  /// removed.
112318681Sdim  RefType drop_symmetric(uint32_t N) const {
113318681Sdim    return drop_front(N).drop_back(N);
114318681Sdim  }
115318681Sdim
116317017Sdim  /// Return a new BinaryStreamRef with the first \p Offset elements removed,
117317017Sdim  /// and retaining exactly \p Len elements.
118317017Sdim  RefType slice(uint32_t Offset, uint32_t Len) const {
119317017Sdim    return drop_front(Offset).keep_front(Len);
120317017Sdim  }
121317017Sdim
122318681Sdim  bool valid() const { return BorrowedImpl != nullptr; }
123318681Sdim
124317017Sdim  bool operator==(const RefType &Other) const {
125318681Sdim    if (BorrowedImpl != Other.BorrowedImpl)
126317017Sdim      return false;
127317017Sdim    if (ViewOffset != Other.ViewOffset)
128317017Sdim      return false;
129317017Sdim    if (Length != Other.Length)
130317017Sdim      return false;
131317017Sdim    return true;
132317017Sdim  }
133317017Sdim
134317017Sdimprotected:
135327952Sdim  Error checkOffsetForRead(uint32_t Offset, uint32_t DataSize) const {
136317017Sdim    if (Offset > getLength())
137317017Sdim      return make_error<BinaryStreamError>(stream_error_code::invalid_offset);
138317017Sdim    if (getLength() < DataSize + Offset)
139317017Sdim      return make_error<BinaryStreamError>(stream_error_code::stream_too_short);
140317017Sdim    return Error::success();
141317017Sdim  }
142317017Sdim
143318681Sdim  std::shared_ptr<StreamType> SharedImpl;
144318681Sdim  StreamType *BorrowedImpl = nullptr;
145318681Sdim  uint32_t ViewOffset = 0;
146327952Sdim  Optional<uint32_t> Length;
147317017Sdim};
148317017Sdim
149341825Sdim/// BinaryStreamRef is to BinaryStream what ArrayRef is to an Array.  It
150317017Sdim/// provides copy-semantics and read only access to a "window" of the underlying
151317017Sdim/// BinaryStream. Note that BinaryStreamRef is *not* a BinaryStream.  That is to
152317017Sdim/// say, it does not inherit and override the methods of BinaryStream.  In
153317017Sdim/// general, you should not pass around pointers or references to BinaryStreams
154317017Sdim/// and use inheritance to achieve polymorphism.  Instead, you should pass
155317017Sdim/// around BinaryStreamRefs by value and achieve polymorphism that way.
156317017Sdimclass BinaryStreamRef
157318681Sdim    : public BinaryStreamRefBase<BinaryStreamRef, BinaryStream> {
158318681Sdim  friend BinaryStreamRefBase<BinaryStreamRef, BinaryStream>;
159318681Sdim  friend class WritableBinaryStreamRef;
160318681Sdim  BinaryStreamRef(std::shared_ptr<BinaryStream> Impl, uint32_t ViewOffset,
161327952Sdim                  Optional<uint32_t> Length)
162318681Sdim      : BinaryStreamRefBase(Impl, ViewOffset, Length) {}
163318681Sdim
164317017Sdimpublic:
165317017Sdim  BinaryStreamRef() = default;
166318681Sdim  BinaryStreamRef(BinaryStream &Stream);
167327952Sdim  BinaryStreamRef(BinaryStream &Stream, uint32_t Offset,
168327952Sdim                  Optional<uint32_t> Length);
169318681Sdim  explicit BinaryStreamRef(ArrayRef<uint8_t> Data,
170318681Sdim                           llvm::support::endianness Endian);
171318681Sdim  explicit BinaryStreamRef(StringRef Data, llvm::support::endianness Endian);
172317017Sdim
173327952Sdim  BinaryStreamRef(const BinaryStreamRef &Other) = default;
174327952Sdim  BinaryStreamRef &operator=(const BinaryStreamRef &Other) = default;
175327952Sdim  BinaryStreamRef(BinaryStreamRef &&Other) = default;
176327952Sdim  BinaryStreamRef &operator=(BinaryStreamRef &&Other) = default;
177318681Sdim
178317017Sdim  // Use BinaryStreamRef.slice() instead.
179317017Sdim  BinaryStreamRef(BinaryStreamRef &S, uint32_t Offset,
180317017Sdim                  uint32_t Length) = delete;
181317017Sdim
182317017Sdim  /// Given an Offset into this StreamRef and a Size, return a reference to a
183317017Sdim  /// buffer owned by the stream.
184317017Sdim  ///
185317017Sdim  /// \returns a success error code if the entire range of data is within the
186317017Sdim  /// bounds of this BinaryStreamRef's view and the implementation could read
187317017Sdim  /// the data, and an appropriate error code otherwise.
188317017Sdim  Error readBytes(uint32_t Offset, uint32_t Size,
189318681Sdim                  ArrayRef<uint8_t> &Buffer) const;
190317017Sdim
191317017Sdim  /// Given an Offset into this BinaryStreamRef, return a reference to the
192317017Sdim  /// largest buffer the stream could support without necessitating a copy.
193317017Sdim  ///
194317017Sdim  /// \returns a success error code if implementation could read the data,
195317017Sdim  /// and an appropriate error code otherwise.
196317017Sdim  Error readLongestContiguousChunk(uint32_t Offset,
197318681Sdim                                   ArrayRef<uint8_t> &Buffer) const;
198317017Sdim};
199317017Sdim
200320397Sdimstruct BinarySubstreamRef {
201360784Sdim  uint32_t Offset = 0;        // Offset in the parent stream
202320397Sdim  BinaryStreamRef StreamData; // Stream Data
203320397Sdim
204320397Sdim  BinarySubstreamRef slice(uint32_t Off, uint32_t Size) const {
205320397Sdim    BinaryStreamRef SubSub = StreamData.slice(Off, Size);
206320397Sdim    return {Off + Offset, SubSub};
207320397Sdim  }
208320397Sdim  BinarySubstreamRef drop_front(uint32_t N) const {
209320397Sdim    return slice(N, size() - N);
210320397Sdim  }
211320397Sdim  BinarySubstreamRef keep_front(uint32_t N) const { return slice(0, N); }
212320397Sdim
213320397Sdim  std::pair<BinarySubstreamRef, BinarySubstreamRef>
214360784Sdim  split(uint32_t Off) const {
215360784Sdim    return std::make_pair(keep_front(Off), drop_front(Off));
216320397Sdim  }
217320397Sdim
218320397Sdim  uint32_t size() const { return StreamData.getLength(); }
219320397Sdim  bool empty() const { return size() == 0; }
220320397Sdim};
221320397Sdim
222317017Sdimclass WritableBinaryStreamRef
223318681Sdim    : public BinaryStreamRefBase<WritableBinaryStreamRef,
224318681Sdim                                 WritableBinaryStream> {
225318681Sdim  friend BinaryStreamRefBase<WritableBinaryStreamRef, WritableBinaryStream>;
226318681Sdim  WritableBinaryStreamRef(std::shared_ptr<WritableBinaryStream> Impl,
227327952Sdim                          uint32_t ViewOffset, Optional<uint32_t> Length)
228318681Sdim      : BinaryStreamRefBase(Impl, ViewOffset, Length) {}
229318681Sdim
230327952Sdim  Error checkOffsetForWrite(uint32_t Offset, uint32_t DataSize) const {
231327952Sdim    if (!(BorrowedImpl->getFlags() & BSF_Append))
232327952Sdim      return checkOffsetForRead(Offset, DataSize);
233327952Sdim
234327952Sdim    if (Offset > getLength())
235327952Sdim      return make_error<BinaryStreamError>(stream_error_code::invalid_offset);
236327952Sdim    return Error::success();
237327952Sdim  }
238327952Sdim
239317017Sdimpublic:
240317017Sdim  WritableBinaryStreamRef() = default;
241318681Sdim  WritableBinaryStreamRef(WritableBinaryStream &Stream);
242317017Sdim  WritableBinaryStreamRef(WritableBinaryStream &Stream, uint32_t Offset,
243327952Sdim                          Optional<uint32_t> Length);
244318681Sdim  explicit WritableBinaryStreamRef(MutableArrayRef<uint8_t> Data,
245318681Sdim                                   llvm::support::endianness Endian);
246327952Sdim  WritableBinaryStreamRef(const WritableBinaryStreamRef &Other) = default;
247327952Sdim  WritableBinaryStreamRef &
248327952Sdim  operator=(const WritableBinaryStreamRef &Other) = default;
249317017Sdim
250327952Sdim  WritableBinaryStreamRef(WritableBinaryStreamRef &&Other) = default;
251327952Sdim  WritableBinaryStreamRef &operator=(WritableBinaryStreamRef &&Other) = default;
252327952Sdim
253317017Sdim  // Use WritableBinaryStreamRef.slice() instead.
254317017Sdim  WritableBinaryStreamRef(WritableBinaryStreamRef &S, uint32_t Offset,
255317017Sdim                          uint32_t Length) = delete;
256317017Sdim
257317017Sdim  /// Given an Offset into this WritableBinaryStreamRef and some input data,
258317017Sdim  /// writes the data to the underlying stream.
259317017Sdim  ///
260317017Sdim  /// \returns a success error code if the data could fit within the underlying
261317017Sdim  /// stream at the specified location and the implementation could write the
262317017Sdim  /// data, and an appropriate error code otherwise.
263318681Sdim  Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Data) const;
264317017Sdim
265318681Sdim  /// Conver this WritableBinaryStreamRef to a read-only BinaryStreamRef.
266318681Sdim  operator BinaryStreamRef() const;
267317017Sdim
268341825Sdim  /// For buffered streams, commits changes to the backing store.
269318681Sdim  Error commit();
270317017Sdim};
271317017Sdim
272317017Sdim} // end namespace llvm
273317017Sdim
274317017Sdim#endif // LLVM_SUPPORT_BINARYSTREAMREF_H
275