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