Home | History | Annotate | Download | only in Support
      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