1 //===- BinaryStreamWriter.h - Writes objects to a BinaryStream ---*- 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_BINARYSTREAMWRITER_H 11 #define LLVM_SUPPORT_BINARYSTREAMWRITER_H 12 13 #include "llvm/ADT/ArrayRef.h" 14 #include "llvm/ADT/STLExtras.h" 15 #include "llvm/ADT/StringRef.h" 16 #include "llvm/Support/BinaryStreamArray.h" 17 #include "llvm/Support/BinaryStreamError.h" 18 #include "llvm/Support/BinaryStreamRef.h" 19 #include "llvm/Support/Endian.h" 20 #include "llvm/Support/Error.h" 21 #include <cstdint> 22 #include <type_traits> 23 #include <utility> 24 25 namespace llvm { 26 27 /// Provides write only access to a subclass of `WritableBinaryStream`. 28 /// Provides bounds checking and helpers for writing certain common data types 29 /// such as null-terminated strings, integers in various flavors of endianness, 30 /// etc. Can be subclassed to provide reading and writing of custom datatypes, 31 /// although no methods are overridable. 32 class BinaryStreamWriter { 33 public: 34 BinaryStreamWriter() = default; 35 explicit BinaryStreamWriter(WritableBinaryStreamRef Ref); 36 explicit BinaryStreamWriter(WritableBinaryStream &Stream); 37 explicit BinaryStreamWriter(MutableArrayRef<uint8_t> Data, 38 llvm::support::endianness Endian); 39 BinaryStreamWriter(const BinaryStreamWriter & Other)40 BinaryStreamWriter(const BinaryStreamWriter &Other) 41 : Stream(Other.Stream), Offset(Other.Offset) {} 42 43 BinaryStreamWriter &operator=(const BinaryStreamWriter &Other) { 44 Stream = Other.Stream; 45 Offset = Other.Offset; 46 return *this; 47 } 48 ~BinaryStreamWriter()49 virtual ~BinaryStreamWriter() {} 50 51 /// Write the bytes specified in \p Buffer to the underlying stream. 52 /// On success, updates the offset so that subsequent writes will occur 53 /// at the next unwritten position. 54 /// 55 /// \returns a success error code if the data was successfully written, 56 /// otherwise returns an appropriate error code. 57 Error writeBytes(ArrayRef<uint8_t> Buffer); 58 59 /// Write the integer \p Value to the underlying stream in the 60 /// specified endianness. On success, updates the offset so that 61 /// subsequent writes occur at the next unwritten position. 62 /// 63 /// \returns a success error code if the data was successfully written, 64 /// otherwise returns an appropriate error code. writeInteger(T Value)65 template <typename T> Error writeInteger(T Value) { 66 static_assert(std::is_integral<T>::value, 67 "Cannot call writeInteger with non-integral value!"); 68 uint8_t Buffer[sizeof(T)]; 69 llvm::support::endian::write<T, llvm::support::unaligned>( 70 Buffer, Value, Stream.getEndian()); 71 return writeBytes(Buffer); 72 } 73 74 /// Similar to writeInteger writeEnum(T Num)75 template <typename T> Error writeEnum(T Num) { 76 static_assert(std::is_enum<T>::value, 77 "Cannot call writeEnum with non-Enum type"); 78 79 using U = typename std::underlying_type<T>::type; 80 return writeInteger<U>(static_cast<U>(Num)); 81 } 82 83 /// Write the string \p Str to the underlying stream followed by a null 84 /// terminator. On success, updates the offset so that subsequent writes 85 /// occur at the next unwritten position. \p Str need not be null terminated 86 /// on input. 87 /// 88 /// \returns a success error code if the data was successfully written, 89 /// otherwise returns an appropriate error code. 90 Error writeCString(StringRef Str); 91 92 /// Write the string \p Str to the underlying stream without a null 93 /// terminator. On success, updates the offset so that subsequent writes 94 /// occur at the next unwritten position. 95 /// 96 /// \returns a success error code if the data was successfully written, 97 /// otherwise returns an appropriate error code. 98 Error writeFixedString(StringRef Str); 99 100 /// Efficiently reads all data from \p Ref, and writes it to this stream. 101 /// This operation will not invoke any copies of the source data, regardless 102 /// of the source stream's implementation. 103 /// 104 /// \returns a success error code if the data was successfully written, 105 /// otherwise returns an appropriate error code. 106 Error writeStreamRef(BinaryStreamRef Ref); 107 108 /// Efficiently reads \p Size bytes from \p Ref, and writes it to this stream. 109 /// This operation will not invoke any copies of the source data, regardless 110 /// of the source stream's implementation. 111 /// 112 /// \returns a success error code if the data was successfully written, 113 /// otherwise returns an appropriate error code. 114 Error writeStreamRef(BinaryStreamRef Ref, uint32_t Size); 115 116 /// Writes the object \p Obj to the underlying stream, as if by using memcpy. 117 /// It is up to the caller to ensure that type of \p Obj can be safely copied 118 /// in this fashion, as no checks are made to ensure that this is safe. 119 /// 120 /// \returns a success error code if the data was successfully written, 121 /// otherwise returns an appropriate error code. writeObject(const T & Obj)122 template <typename T> Error writeObject(const T &Obj) { 123 static_assert(!std::is_pointer<T>::value, 124 "writeObject should not be used with pointers, to write " 125 "the pointed-to value dereference the pointer before calling " 126 "writeObject"); 127 return writeBytes( 128 ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&Obj), sizeof(T))); 129 } 130 131 /// Writes an array of objects of type T to the underlying stream, as if by 132 /// using memcpy. It is up to the caller to ensure that type of \p Obj can 133 /// be safely copied in this fashion, as no checks are made to ensure that 134 /// this is safe. 135 /// 136 /// \returns a success error code if the data was successfully written, 137 /// otherwise returns an appropriate error code. writeArray(ArrayRef<T> Array)138 template <typename T> Error writeArray(ArrayRef<T> Array) { 139 if (Array.empty()) 140 return Error::success(); 141 if (Array.size() > UINT32_MAX / sizeof(T)) 142 return make_error<BinaryStreamError>( 143 stream_error_code::invalid_array_size); 144 145 return writeBytes( 146 ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Array.data()), 147 Array.size() * sizeof(T))); 148 } 149 150 /// Writes all data from the array \p Array to the underlying stream. 151 /// 152 /// \returns a success error code if the data was successfully written, 153 /// otherwise returns an appropriate error code. 154 template <typename T, typename U> writeArray(VarStreamArray<T,U> Array)155 Error writeArray(VarStreamArray<T, U> Array) { 156 return writeStreamRef(Array.getUnderlyingStream()); 157 } 158 159 /// Writes all elements from the array \p Array to the underlying stream. 160 /// 161 /// \returns a success error code if the data was successfully written, 162 /// otherwise returns an appropriate error code. writeArray(FixedStreamArray<T> Array)163 template <typename T> Error writeArray(FixedStreamArray<T> Array) { 164 return writeStreamRef(Array.getUnderlyingStream()); 165 } 166 167 /// Splits the Writer into two Writers at a given offset. 168 std::pair<BinaryStreamWriter, BinaryStreamWriter> split(uint32_t Off) const; 169 setOffset(uint32_t Off)170 void setOffset(uint32_t Off) { Offset = Off; } getOffset()171 uint32_t getOffset() const { return Offset; } getLength()172 uint32_t getLength() const { return Stream.getLength(); } bytesRemaining()173 uint32_t bytesRemaining() const { return getLength() - getOffset(); } 174 Error padToAlignment(uint32_t Align); 175 176 protected: 177 WritableBinaryStreamRef Stream; 178 uint32_t Offset = 0; 179 }; 180 181 } // end namespace llvm 182 183 #endif // LLVM_SUPPORT_BINARYSTREAMWRITER_H 184