1 //===- BinaryStreamReader.h - Reads objects from a binary stream *- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef LLVM_SUPPORT_BINARYSTREAMREADER_H 11 #define LLVM_SUPPORT_BINARYSTREAMREADER_H 12 13 #include "llvm/ADT/ArrayRef.h" 14 #include "llvm/ADT/STLExtras.h" 15 #include "llvm/Support/BinaryStreamArray.h" 16 #include "llvm/Support/BinaryStreamRef.h" 17 #include "llvm/Support/Endian.h" 18 #include "llvm/Support/Error.h" 19 #include "llvm/Support/MathExtras.h" 20 #include "llvm/Support/type_traits.h" 21 22 #include <string> 23 #include <type_traits> 24 25 namespace llvm { 26 27 /// \brief 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 explicit BinaryStreamReader(BinaryStreamRef Stream); 35 virtual ~BinaryStreamReader() {} 36 37 /// Read as much as possible from the underlying string at the current offset 38 /// without invoking a copy, and set \p Buffer to the resulting data slice. 39 /// Updates the stream's offset to point after the newly read data. 40 /// 41 /// \returns a success error code if the data was successfully read, otherwise 42 /// returns an appropriate error code. 43 Error readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer); 44 45 /// Read \p Size bytes from the underlying stream at the current offset and 46 /// and set \p Buffer to the resulting data slice. Whether a copy occurs 47 /// depends on the implementation of the underlying stream. Updates the 48 /// stream's offset to point after the newly read data. 49 /// 50 /// \returns a success error code if the data was successfully read, otherwise 51 /// returns an appropriate error code. 52 Error readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size); 53 54 /// Read an integer of the specified endianness into \p Dest and update the 55 /// stream's offset. The data is always copied from the stream's underlying 56 /// buffer into \p Dest. Updates the stream's offset to point after the newly 57 /// read data. 58 /// 59 /// \returns a success error code if the data was successfully read, otherwise 60 /// returns an appropriate error code. 61 template <typename T> Error readInteger(T &Dest) { 62 static_assert(std::is_integral<T>::value, 63 "Cannot call readInteger with non-integral value!"); 64 65 ArrayRef<uint8_t> Bytes; 66 if (auto EC = readBytes(Bytes, sizeof(T))) 67 return EC; 68 69 Dest = llvm::support::endian::read<T, llvm::support::unaligned>( 70 Bytes.data(), Stream.getEndian()); 71 return Error::success(); 72 } 73 74 /// Similar to readInteger. 75 template <typename T> Error readEnum(T &Dest) { 76 static_assert(std::is_enum<T>::value, 77 "Cannot call readEnum with non-enum value!"); 78 typename std::underlying_type<T>::type N; 79 if (auto EC = readInteger(N)) 80 return EC; 81 Dest = static_cast<T>(N); 82 return Error::success(); 83 } 84 85 /// Read a null terminated string from \p Dest. Whether a copy occurs depends 86 /// on the implementation of the underlying stream. Updates the stream's 87 /// offset to point after the newly read data. 88 /// 89 /// \returns a success error code if the data was successfully read, otherwise 90 /// returns an appropriate error code. 91 Error readCString(StringRef &Dest); 92 93 /// Read a \p Length byte string into \p Dest. Whether a copy occurs depends 94 /// on the implementation of the underlying stream. Updates the stream's 95 /// offset to point after the newly read data. 96 /// 97 /// \returns a success error code if the data was successfully read, otherwise 98 /// returns an appropriate error code. 99 Error readFixedString(StringRef &Dest, uint32_t Length); 100 101 /// Read the entire remainder of the underlying stream into \p Ref. This is 102 /// equivalent to calling getUnderlyingStream().slice(Offset). Updates the 103 /// stream's offset to point to the end of the stream. Never causes a copy. 104 /// 105 /// \returns a success error code if the data was successfully read, otherwise 106 /// returns an appropriate error code. 107 Error readStreamRef(BinaryStreamRef &Ref); 108 109 /// Read \p Length bytes from the underlying stream into \p Ref. This is 110 /// equivalent to calling getUnderlyingStream().slice(Offset, Length). 111 /// Updates the stream's offset to point after the newly read object. Never 112 /// causes a copy. 113 /// 114 /// \returns a success error code if the data was successfully read, otherwise 115 /// returns an appropriate error code. 116 Error readStreamRef(BinaryStreamRef &Ref, uint32_t Length); 117 118 /// Get a pointer to an object of type T from the underlying stream, as if by 119 /// memcpy, and store the result into \p Dest. It is up to the caller to 120 /// ensure that objects of type T can be safely treated in this manner. 121 /// Updates the stream's offset to point after the newly read object. Whether 122 /// a copy occurs depends upon the implementation of the underlying 123 /// stream. 124 /// 125 /// \returns a success error code if the data was successfully read, otherwise 126 /// returns an appropriate error code. 127 template <typename T> Error readObject(const T *&Dest) { 128 ArrayRef<uint8_t> Buffer; 129 if (auto EC = readBytes(Buffer, sizeof(T))) 130 return EC; 131 Dest = reinterpret_cast<const T *>(Buffer.data()); 132 return Error::success(); 133 } 134 135 /// Get a reference to a \p NumElements element array of objects of type T 136 /// from the underlying stream as if by memcpy, and store the resulting array 137 /// slice into \p array. It is up to the caller to ensure that objects of 138 /// type T can be safely treated in this manner. Updates the stream's offset 139 /// to point after the newly read object. Whether a copy occurs depends upon 140 /// the implementation of the underlying stream. 141 /// 142 /// \returns a success error code if the data was successfully read, otherwise 143 /// returns an appropriate error code. 144 template <typename T> 145 Error readArray(ArrayRef<T> &Array, uint32_t NumElements) { 146 ArrayRef<uint8_t> Bytes; 147 if (NumElements == 0) { 148 Array = ArrayRef<T>(); 149 return Error::success(); 150 } 151 152 if (NumElements > UINT32_MAX / sizeof(T)) 153 return make_error<BinaryStreamError>( 154 stream_error_code::invalid_array_size); 155 156 if (auto EC = readBytes(Bytes, NumElements * sizeof(T))) 157 return EC; 158 159 assert(alignmentAdjustment(Bytes.data(), alignof(T)) == 0 && 160 "Reading at invalid alignment!"); 161 162 Array = ArrayRef<T>(reinterpret_cast<const T *>(Bytes.data()), NumElements); 163 return Error::success(); 164 } 165 166 /// Read a VarStreamArray of size \p Size bytes and store the result into 167 /// \p Array. Updates the stream's offset to point after the newly read 168 /// array. Never causes a copy (although iterating the elements of the 169 /// VarStreamArray may, depending upon the implementation of the underlying 170 /// stream). 171 /// 172 /// \returns a success error code if the data was successfully read, otherwise 173 /// returns an appropriate error code. 174 template <typename T, typename U> 175 Error readArray(VarStreamArray<T, U> &Array, uint32_t Size) { 176 BinaryStreamRef S; 177 if (auto EC = readStreamRef(S, Size)) 178 return EC; 179 Array = VarStreamArray<T, U>(S, Array.getExtractor()); 180 return Error::success(); 181 } 182 183 /// Read a FixedStreamArray of \p NumItems elements and store the result into 184 /// \p Array. Updates the stream's offset to point after the newly read 185 /// array. Never causes a copy (although iterating the elements of the 186 /// FixedStreamArray may, depending upon the implementation of the underlying 187 /// stream). 188 /// 189 /// \returns a success error code if the data was successfully read, otherwise 190 /// returns an appropriate error code. 191 template <typename T> 192 Error readArray(FixedStreamArray<T> &Array, uint32_t NumItems) { 193 if (NumItems == 0) { 194 Array = FixedStreamArray<T>(); 195 return Error::success(); 196 } 197 198 if (NumItems > UINT32_MAX / sizeof(T)) 199 return make_error<BinaryStreamError>( 200 stream_error_code::invalid_array_size); 201 202 BinaryStreamRef View; 203 if (auto EC = readStreamRef(View, NumItems * sizeof(T))) 204 return EC; 205 206 Array = FixedStreamArray<T>(View); 207 return Error::success(); 208 } 209 210 bool empty() const { return bytesRemaining() == 0; } 211 void setOffset(uint32_t Off) { Offset = Off; } 212 uint32_t getOffset() const { return Offset; } 213 uint32_t getLength() const { return Stream.getLength(); } 214 uint32_t bytesRemaining() const { return getLength() - getOffset(); } 215 216 /// Advance the stream's offset by \p Amount bytes. 217 /// 218 /// \returns a success error code if at least \p Amount bytes remain in the 219 /// stream, otherwise returns an appropriate error code. 220 Error skip(uint32_t Amount); 221 222 /// Examine the next byte of the underlying stream without advancing the 223 /// stream's offset. If the stream is empty the behavior is undefined. 224 /// 225 /// \returns the next byte in the stream. 226 uint8_t peek() const; 227 228 private: 229 BinaryStreamRef Stream; 230 uint32_t Offset; 231 }; 232 } // namespace llvm 233 234 #endif // LLVM_SUPPORT_BINARYSTREAMREADER_H 235