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 | Buffer: 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 | Args: stream_error_code::invalid_array_size); |
150 | |
151 | return writeBytes( |
152 | Buffer: 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 | |