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