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::support::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, llvm::support::unaligned>( 63 Buffer, Value, Stream.getEndian()); 64 return writeBytes(Buffer); 65 } 66 67 /// Similar to writeInteger 68 template <typename T> Error writeEnum(T Num) { 69 static_assert(std::is_enum<T>::value, 70 "Cannot call writeEnum with non-Enum type"); 71 72 using U = std::underlying_type_t<T>; 73 return writeInteger<U>(static_cast<U>(Num)); 74 } 75 76 /// Write the unsigned integer Value to the underlying stream using ULEB128 77 /// encoding. 78 /// 79 /// \returns a success error code if the data was successfully written, 80 /// otherwise returns an appropriate error code. 81 Error writeULEB128(uint64_t Value); 82 83 /// Write the unsigned integer Value to the underlying stream using ULEB128 84 /// encoding. 85 /// 86 /// \returns a success error code if the data was successfully written, 87 /// otherwise returns an appropriate error code. 88 Error writeSLEB128(int64_t Value); 89 90 /// Write the string \p Str to the underlying stream followed by a null 91 /// terminator. On success, updates the offset so that subsequent writes 92 /// occur at the next unwritten position. \p Str need not be null terminated 93 /// on input. 94 /// 95 /// \returns a success error code if the data was successfully written, 96 /// otherwise returns an appropriate error code. 97 Error writeCString(StringRef Str); 98 99 /// Write the string \p Str to the underlying stream without a null 100 /// terminator. On success, updates the offset so that subsequent writes 101 /// occur at the next unwritten position. 102 /// 103 /// \returns a success error code if the data was successfully written, 104 /// otherwise returns an appropriate error code. 105 Error writeFixedString(StringRef Str); 106 107 /// Efficiently reads all data from \p Ref, and writes it to this stream. 108 /// This operation will not invoke any copies of the source data, regardless 109 /// of the source stream's implementation. 110 /// 111 /// \returns a success error code if the data was successfully written, 112 /// otherwise returns an appropriate error code. 113 Error writeStreamRef(BinaryStreamRef Ref); 114 115 /// Efficiently reads \p Size bytes from \p Ref, and writes it to this stream. 116 /// This operation will not invoke any copies of the source data, regardless 117 /// of the source stream's implementation. 118 /// 119 /// \returns a success error code if the data was successfully written, 120 /// otherwise returns an appropriate error code. 121 Error writeStreamRef(BinaryStreamRef Ref, uint64_t Size); 122 123 /// Writes the object \p Obj to the underlying stream, as if by using memcpy. 124 /// It is up to the caller to ensure that type of \p Obj can be safely copied 125 /// in this fashion, as no checks are made to ensure that this is safe. 126 /// 127 /// \returns a success error code if the data was successfully written, 128 /// otherwise returns an appropriate error code. 129 template <typename T> Error writeObject(const T &Obj) { 130 static_assert(!std::is_pointer<T>::value, 131 "writeObject should not be used with pointers, to write " 132 "the pointed-to value dereference the pointer before calling " 133 "writeObject"); 134 return writeBytes( 135 ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&Obj), sizeof(T))); 136 } 137 138 /// Writes an array of objects of type T to the underlying stream, as if by 139 /// using memcpy. It is up to the caller to ensure that type of \p Obj can 140 /// be safely copied in this fashion, as no checks are made to ensure that 141 /// this is safe. 142 /// 143 /// \returns a success error code if the data was successfully written, 144 /// otherwise returns an appropriate error code. 145 template <typename T> Error writeArray(ArrayRef<T> Array) { 146 if (Array.empty()) 147 return Error::success(); 148 if (Array.size() > UINT32_MAX / sizeof(T)) 149 return make_error<BinaryStreamError>( 150 stream_error_code::invalid_array_size); 151 152 return writeBytes( 153 ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Array.data()), 154 Array.size() * sizeof(T))); 155 } 156 157 /// Writes all data from the array \p Array to the underlying stream. 158 /// 159 /// \returns a success error code if the data was successfully written, 160 /// otherwise returns an appropriate error code. 161 template <typename T, typename U> 162 Error writeArray(VarStreamArray<T, U> Array) { 163 return writeStreamRef(Array.getUnderlyingStream()); 164 } 165 166 /// Writes all elements from the array \p Array to the underlying stream. 167 /// 168 /// \returns a success error code if the data was successfully written, 169 /// otherwise returns an appropriate error code. 170 template <typename T> Error writeArray(FixedStreamArray<T> Array) { 171 return writeStreamRef(Array.getUnderlyingStream()); 172 } 173 174 /// Splits the Writer into two Writers at a given offset. 175 std::pair<BinaryStreamWriter, BinaryStreamWriter> split(uint64_t Off) const; 176 177 void setOffset(uint64_t Off) { Offset = Off; } 178 uint64_t getOffset() const { return Offset; } 179 uint64_t getLength() const { return Stream.getLength(); } 180 uint64_t bytesRemaining() const { return getLength() - getOffset(); } 181 Error padToAlignment(uint32_t Align); 182 183 protected: 184 WritableBinaryStreamRef Stream; 185 uint64_t Offset = 0; 186 }; 187 188 } // end namespace llvm 189 190 #endif // LLVM_SUPPORT_BINARYSTREAMWRITER_H 191