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