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