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