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