1 //===- BinaryStreamReader.h - Reads objects from a binary 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_BINARYSTREAMREADER_H 10 #define LLVM_SUPPORT_BINARYSTREAMREADER_H 11 12 #include "llvm/ADT/ArrayRef.h" 13 #include "llvm/ADT/StringRef.h" 14 #include "llvm/Support/Alignment.h" 15 #include "llvm/Support/BinaryStreamArray.h" 16 #include "llvm/Support/BinaryStreamRef.h" 17 #include "llvm/Support/ConvertUTF.h" 18 #include "llvm/Support/Endian.h" 19 #include "llvm/Support/Error.h" 20 #include <type_traits> 21 22 namespace llvm { 23 24 /// Provides read only access to a subclass of `BinaryStream`. Provides 25 /// bounds checking and helpers for writing certain common data types such as 26 /// null-terminated strings, integers in various flavors of endianness, etc. 27 /// Can be subclassed to provide reading of custom datatypes, although no 28 /// are overridable. 29 class BinaryStreamReader { 30 public: 31 BinaryStreamReader() = default; 32 explicit BinaryStreamReader(BinaryStreamRef Ref); 33 explicit BinaryStreamReader(BinaryStream &Stream); 34 explicit BinaryStreamReader(ArrayRef<uint8_t> Data, 35 llvm::support::endianness Endian); 36 explicit BinaryStreamReader(StringRef Data, llvm::support::endianness Endian); 37 38 BinaryStreamReader(const BinaryStreamReader &Other) = default; 39 40 BinaryStreamReader &operator=(const BinaryStreamReader &Other) = default; 41 42 virtual ~BinaryStreamReader() = default; 43 44 /// Read as much as possible from the underlying string at the current offset 45 /// without invoking a copy, and set \p Buffer to the resulting data slice. 46 /// Updates the stream's offset to point after the newly read data. 47 /// 48 /// \returns a success error code if the data was successfully read, otherwise 49 /// returns an appropriate error code. 50 Error readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer); 51 52 /// Read \p Size bytes from the underlying stream at the current offset and 53 /// and set \p Buffer to the resulting data slice. Whether a copy occurs 54 /// depends on the implementation of the underlying stream. Updates the 55 /// stream's offset to point after the newly read data. 56 /// 57 /// \returns a success error code if the data was successfully read, otherwise 58 /// returns an appropriate error code. 59 Error readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size); 60 61 /// Read an integer of the specified endianness into \p Dest and update the 62 /// stream's offset. The data is always copied from the stream's underlying 63 /// buffer into \p Dest. Updates the stream's offset to point after the newly 64 /// read data. 65 /// 66 /// \returns a success error code if the data was successfully read, otherwise 67 /// returns an appropriate error code. readInteger(T & Dest)68 template <typename T> Error readInteger(T &Dest) { 69 static_assert(std::is_integral_v<T>, 70 "Cannot call readInteger with non-integral value!"); 71 72 ArrayRef<uint8_t> Bytes; 73 if (auto EC = readBytes(Bytes, sizeof(T))) 74 return EC; 75 76 Dest = llvm::support::endian::read<T, llvm::support::unaligned>( 77 Bytes.data(), Stream.getEndian()); 78 return Error::success(); 79 } 80 81 /// Similar to readInteger. readEnum(T & Dest)82 template <typename T> Error readEnum(T &Dest) { 83 static_assert(std::is_enum<T>::value, 84 "Cannot call readEnum with non-enum value!"); 85 std::underlying_type_t<T> N; 86 if (auto EC = readInteger(N)) 87 return EC; 88 Dest = static_cast<T>(N); 89 return Error::success(); 90 } 91 92 /// Read an unsigned LEB128 encoded value. 93 /// 94 /// \returns a success error code if the data was successfully read, otherwise 95 /// returns an appropriate error code. 96 Error readULEB128(uint64_t &Dest); 97 98 /// Read a signed LEB128 encoded value. 99 /// 100 /// \returns a success error code if the data was successfully read, otherwise 101 /// returns an appropriate error code. 102 Error readSLEB128(int64_t &Dest); 103 104 /// Read a null terminated string from \p Dest. Whether a copy occurs depends 105 /// on the implementation of the underlying stream. Updates the stream's 106 /// offset to point after the newly read data. 107 /// 108 /// \returns a success error code if the data was successfully read, otherwise 109 /// returns an appropriate error code. 110 Error readCString(StringRef &Dest); 111 112 /// Similar to readCString, however read a null-terminated UTF16 string 113 /// instead. 114 /// 115 /// \returns a success error code if the data was successfully read, otherwise 116 /// returns an appropriate error code. 117 Error readWideString(ArrayRef<UTF16> &Dest); 118 119 /// Read a \p Length byte string into \p Dest. Whether a copy occurs depends 120 /// on the implementation of the underlying stream. Updates the stream's 121 /// offset to point after the newly read data. 122 /// 123 /// \returns a success error code if the data was successfully read, otherwise 124 /// returns an appropriate error code. 125 Error readFixedString(StringRef &Dest, uint32_t Length); 126 127 /// Read the entire remainder of the underlying stream into \p Ref. This is 128 /// equivalent to calling getUnderlyingStream().slice(Offset). Updates the 129 /// stream's offset to point to the end of the stream. Never causes a copy. 130 /// 131 /// \returns a success error code if the data was successfully read, otherwise 132 /// returns an appropriate error code. 133 Error readStreamRef(BinaryStreamRef &Ref); 134 135 /// Read \p Length bytes from the underlying stream into \p Ref. This is 136 /// equivalent to calling getUnderlyingStream().slice(Offset, Length). 137 /// Updates the stream's offset to point after the newly read object. Never 138 /// causes a copy. 139 /// 140 /// \returns a success error code if the data was successfully read, otherwise 141 /// returns an appropriate error code. 142 Error readStreamRef(BinaryStreamRef &Ref, uint32_t Length); 143 144 /// Read \p Length bytes from the underlying stream into \p Ref. This is 145 /// equivalent to calling getUnderlyingStream().slice(Offset, Length). 146 /// Updates the stream's offset to point after the newly read object. Never 147 /// causes a copy. 148 /// 149 /// \returns a success error code if the data was successfully read, otherwise 150 /// returns an appropriate error code. 151 Error readSubstream(BinarySubstreamRef &Ref, uint32_t Length); 152 153 /// Get a pointer to an object of type T from the underlying stream, as if by 154 /// memcpy, and store the result into \p Dest. It is up to the caller to 155 /// ensure that objects of type T can be safely treated in this manner. 156 /// Updates the stream's offset to point after the newly read object. Whether 157 /// a copy occurs depends upon the implementation of the underlying 158 /// stream. 159 /// 160 /// \returns a success error code if the data was successfully read, otherwise 161 /// returns an appropriate error code. readObject(const T * & Dest)162 template <typename T> Error readObject(const T *&Dest) { 163 ArrayRef<uint8_t> Buffer; 164 if (auto EC = readBytes(Buffer, sizeof(T))) 165 return EC; 166 Dest = reinterpret_cast<const T *>(Buffer.data()); 167 return Error::success(); 168 } 169 170 /// Get a reference to a \p NumElements element array of objects of type T 171 /// from the underlying stream as if by memcpy, and store the resulting array 172 /// slice into \p array. It is up to the caller to ensure that objects of 173 /// type T can be safely treated in this manner. Updates the stream's offset 174 /// to point after the newly read object. Whether a copy occurs depends upon 175 /// the implementation of the underlying stream. 176 /// 177 /// \returns a success error code if the data was successfully read, otherwise 178 /// returns an appropriate error code. 179 template <typename T> readArray(ArrayRef<T> & Array,uint32_t NumElements)180 Error readArray(ArrayRef<T> &Array, uint32_t NumElements) { 181 ArrayRef<uint8_t> Bytes; 182 if (NumElements == 0) { 183 Array = ArrayRef<T>(); 184 return Error::success(); 185 } 186 187 if (NumElements > UINT32_MAX / sizeof(T)) 188 return make_error<BinaryStreamError>( 189 stream_error_code::invalid_array_size); 190 191 if (auto EC = readBytes(Bytes, NumElements * sizeof(T))) 192 return EC; 193 194 assert(isAddrAligned(Align::Of<T>(), Bytes.data()) && 195 "Reading at invalid alignment!"); 196 197 Array = ArrayRef<T>(reinterpret_cast<const T *>(Bytes.data()), NumElements); 198 return Error::success(); 199 } 200 201 /// Read a VarStreamArray of size \p Size bytes and store the result into 202 /// \p Array. Updates the stream's offset to point after the newly read 203 /// array. Never causes a copy (although iterating the elements of the 204 /// VarStreamArray may, depending upon the implementation of the underlying 205 /// stream). 206 /// 207 /// \returns a success error code if the data was successfully read, otherwise 208 /// returns an appropriate error code. 209 template <typename T, typename U> 210 Error readArray(VarStreamArray<T, U> &Array, uint32_t Size, 211 uint32_t Skew = 0) { 212 BinaryStreamRef S; 213 if (auto EC = readStreamRef(S, Size)) 214 return EC; 215 Array.setUnderlyingStream(S, Skew); 216 return Error::success(); 217 } 218 219 /// Read a FixedStreamArray of \p NumItems elements and store the result into 220 /// \p Array. Updates the stream's offset to point after the newly read 221 /// array. Never causes a copy (although iterating the elements of the 222 /// FixedStreamArray may, depending upon the implementation of the underlying 223 /// stream). 224 /// 225 /// \returns a success error code if the data was successfully read, otherwise 226 /// returns an appropriate error code. 227 template <typename T> readArray(FixedStreamArray<T> & Array,uint32_t NumItems)228 Error readArray(FixedStreamArray<T> &Array, uint32_t NumItems) { 229 if (NumItems == 0) { 230 Array = FixedStreamArray<T>(); 231 return Error::success(); 232 } 233 234 if (NumItems > UINT32_MAX / sizeof(T)) 235 return make_error<BinaryStreamError>( 236 stream_error_code::invalid_array_size); 237 238 BinaryStreamRef View; 239 if (auto EC = readStreamRef(View, NumItems * sizeof(T))) 240 return EC; 241 242 Array = FixedStreamArray<T>(View); 243 return Error::success(); 244 } 245 empty()246 bool empty() const { return bytesRemaining() == 0; } setOffset(uint64_t Off)247 void setOffset(uint64_t Off) { Offset = Off; } getOffset()248 uint64_t getOffset() const { return Offset; } getLength()249 uint64_t getLength() const { return Stream.getLength(); } bytesRemaining()250 uint64_t bytesRemaining() const { return getLength() - getOffset(); } 251 252 /// Advance the stream's offset by \p Amount bytes. 253 /// 254 /// \returns a success error code if at least \p Amount bytes remain in the 255 /// stream, otherwise returns an appropriate error code. 256 Error skip(uint64_t Amount); 257 258 /// Examine the next byte of the underlying stream without advancing the 259 /// stream's offset. If the stream is empty the behavior is undefined. 260 /// 261 /// \returns the next byte in the stream. 262 uint8_t peek() const; 263 264 Error padToAlignment(uint32_t Align); 265 266 std::pair<BinaryStreamReader, BinaryStreamReader> 267 split(uint64_t Offset) const; 268 269 private: 270 BinaryStreamRef Stream; 271 uint64_t Offset = 0; 272 }; 273 } // namespace llvm 274 275 #endif // LLVM_SUPPORT_BINARYSTREAMREADER_H 276