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