1 #ifndef SRC_BLOB_SERIALIZER_DESERIALIZER_INL_H_
2 #define SRC_BLOB_SERIALIZER_DESERIALIZER_INL_H_
3
4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5
6 #include "blob_serializer_deserializer.h"
7
8 #include <ostream>
9 #include <sstream>
10 #include <string>
11 #include <type_traits>
12 #include <utility>
13
14 #include "debug_utils-inl.h"
15
16 // This is related to the blob that is used in snapshots and has nothing to do
17 // with `node_blob.h`.
18
19 namespace node {
20
21 struct EnvSerializeInfo;
22 struct PropInfo;
23 struct RealmSerializeInfo;
24
25 namespace builtins {
26 struct CodeCacheInfo;
27 } // namespace builtins
28
29 // These operator<< overload declarations are needed because
30 // BlobSerializerDeserializer::ToStr() uses these.
31
32 std::ostream& operator<<(std::ostream& output,
33 const builtins::CodeCacheInfo& info);
34
35 std::ostream& operator<<(std::ostream& output,
36 const std::vector<builtins::CodeCacheInfo>& vec);
37
38 std::ostream& operator<<(std::ostream& output, const std::vector<uint8_t>& vec);
39
40 std::ostream& operator<<(std::ostream& output,
41 const std::vector<PropInfo>& vec);
42
43 std::ostream& operator<<(std::ostream& output, const PropInfo& info);
44
45 std::ostream& operator<<(std::ostream& output,
46 const std::vector<std::string>& vec);
47
48 std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i);
49
50 std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i);
51
52 template <typename... Args>
Debug(const char * format,Args &&...args)53 void BlobSerializerDeserializer::Debug(const char* format,
54 Args&&... args) const {
55 if (is_debug) {
56 FPrintF(stderr, format, std::forward<Args>(args)...);
57 }
58 }
59
60 template <typename T>
ToStr(const T & arg)61 std::string BlobSerializerDeserializer::ToStr(const T& arg) const {
62 std::stringstream ss;
63 ss << arg;
64 return ss.str();
65 }
66
67 template <typename T>
GetName()68 std::string BlobSerializerDeserializer::GetName() const {
69 #define TYPE_LIST(V) \
70 V(builtins::CodeCacheInfo) \
71 V(PropInfo) \
72 V(std::string)
73
74 #define V(TypeName) \
75 if constexpr (std::is_same_v<T, TypeName>) { \
76 return #TypeName; \
77 } else // NOLINT(readability/braces)
78 TYPE_LIST(V)
79 #undef V
80
81 if constexpr (std::is_arithmetic_v<T>) {
82 return (std::is_unsigned_v<T> ? "uint"
83 : std::is_integral_v<T> ? "int"
84 : "float") +
85 std::to_string(sizeof(T) * 8) + "_t";
86 }
87 return "";
88 }
89
90 // Helper for reading numeric types.
91 template <typename Impl>
92 template <typename T>
ReadArithmetic()93 T BlobDeserializer<Impl>::ReadArithmetic() {
94 static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
95 T result;
96 ReadArithmetic(&result, 1);
97 return result;
98 }
99
100 // Layout of vectors:
101 // [ 4/8 bytes ] count
102 // [ ... ] contents (count * size of individual elements)
103 template <typename Impl>
104 template <typename T>
ReadVector()105 std::vector<T> BlobDeserializer<Impl>::ReadVector() {
106 if (is_debug) {
107 std::string name = GetName<T>();
108 Debug("\nReadVector<%s>()(%d-byte)\n", name.c_str(), sizeof(T));
109 }
110 size_t count = static_cast<size_t>(ReadArithmetic<size_t>());
111 if (count == 0) {
112 return std::vector<T>();
113 }
114 if (is_debug) {
115 Debug("Reading %d vector elements...\n", count);
116 }
117 std::vector<T> result;
118 if constexpr (std::is_arithmetic_v<T>) {
119 result = ReadArithmeticVector<T>(count);
120 } else {
121 result = ReadNonArithmeticVector<T>(count);
122 }
123 if (is_debug) {
124 std::string str = std::is_arithmetic_v<T> ? "" : ToStr(result);
125 std::string name = GetName<T>();
126 Debug("ReadVector<%s>() read %s\n", name.c_str(), str.c_str());
127 }
128 return result;
129 }
130
131 template <typename Impl>
ReadString()132 std::string BlobDeserializer<Impl>::ReadString() {
133 size_t length = ReadArithmetic<size_t>();
134
135 if (is_debug) {
136 Debug("ReadString(), length=%d: ", length);
137 }
138
139 CHECK_GT(length, 0); // There should be no empty strings.
140 MallocedBuffer<char> buf(length + 1);
141 memcpy(buf.data, sink.data() + read_total, length + 1);
142 std::string result(buf.data, length); // This creates a copy of buf.data.
143
144 if (is_debug) {
145 Debug("\"%s\", read %zu bytes\n", result.c_str(), length + 1);
146 }
147
148 read_total += length + 1;
149 return result;
150 }
151
152 // Helper for reading an array of numeric types.
153 template <typename Impl>
154 template <typename T>
ReadArithmetic(T * out,size_t count)155 void BlobDeserializer<Impl>::ReadArithmetic(T* out, size_t count) {
156 static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
157 DCHECK_GT(count, 0); // Should not read contents for vectors of size 0.
158 if (is_debug) {
159 std::string name = GetName<T>();
160 Debug("Read<%s>()(%d-byte), count=%d: ", name.c_str(), sizeof(T), count);
161 }
162
163 size_t size = sizeof(T) * count;
164 memcpy(out, sink.data() + read_total, size);
165
166 if (is_debug) {
167 std::string str =
168 "{ " + std::to_string(out[0]) + (count > 1 ? ", ... }" : " }");
169 Debug("%s, read %zu bytes\n", str.c_str(), size);
170 }
171 read_total += size;
172 }
173
174 // Helper for reading numeric vectors.
175 template <typename Impl>
176 template <typename Number>
ReadArithmeticVector(size_t count)177 std::vector<Number> BlobDeserializer<Impl>::ReadArithmeticVector(size_t count) {
178 static_assert(std::is_arithmetic_v<Number>, "Not an arithmetic type");
179 DCHECK_GT(count, 0); // Should not read contents for vectors of size 0.
180 std::vector<Number> result(count);
181 ReadArithmetic(result.data(), count);
182 return result;
183 }
184
185 // Helper for reading non-numeric vectors.
186 template <typename Impl>
187 template <typename T>
ReadNonArithmeticVector(size_t count)188 std::vector<T> BlobDeserializer<Impl>::ReadNonArithmeticVector(size_t count) {
189 static_assert(!std::is_arithmetic_v<T>, "Arithmetic type");
190 DCHECK_GT(count, 0); // Should not read contents for vectors of size 0.
191 std::vector<T> result;
192 result.reserve(count);
193 bool original_is_debug = is_debug;
194 is_debug = original_is_debug && !std::is_same_v<T, std::string>;
195 for (size_t i = 0; i < count; ++i) {
196 if (is_debug) {
197 Debug("\n[%d] ", i);
198 }
199 result.push_back(ReadElement<T>());
200 }
201 is_debug = original_is_debug;
202
203 return result;
204 }
205
206 template <typename Impl>
207 template <typename T>
ReadElement()208 T BlobDeserializer<Impl>::ReadElement() {
209 if constexpr (std::is_arithmetic_v<T>) {
210 return ReadArithmetic<T>();
211 } else if constexpr (std::is_same_v<T, std::string>) {
212 return ReadString();
213 } else {
214 return impl()->template Read<T>();
215 }
216 }
217
218 // Helper for writing numeric types.
219 template <typename Impl>
220 template <typename T>
WriteArithmetic(const T & data)221 size_t BlobSerializer<Impl>::WriteArithmetic(const T& data) {
222 static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
223 return WriteArithmetic(&data, 1);
224 }
225
226 // Layout of vectors:
227 // [ 4/8 bytes ] count
228 // [ ... ] contents (count * size of individual elements)
229 template <typename Impl>
230 template <typename T>
WriteVector(const std::vector<T> & data)231 size_t BlobSerializer<Impl>::WriteVector(const std::vector<T>& data) {
232 if (is_debug) {
233 std::string str = std::is_arithmetic_v<T> ? "" : ToStr(data);
234 std::string name = GetName<T>();
235 Debug("\nWriteVector<%s>() (%d-byte), count=%d: %s\n",
236 name.c_str(),
237 sizeof(T),
238 data.size(),
239 str.c_str());
240 }
241
242 size_t written_total = WriteArithmetic<size_t>(data.size());
243 if (data.size() == 0) {
244 return written_total;
245 }
246
247 if constexpr (std::is_arithmetic_v<T>) {
248 written_total += WriteArithmeticVector<T>(data);
249 } else {
250 written_total += WriteNonArithmeticVector<T>(data);
251 }
252
253 if (is_debug) {
254 std::string name = GetName<T>();
255 Debug("WriteVector<%s>() wrote %d bytes\n", name.c_str(), written_total);
256 }
257
258 return written_total;
259 }
260
261 // The layout of a written string:
262 // [ 4/8 bytes ] length
263 // [ |length| bytes ] contents
264 template <typename Impl>
WriteString(const std::string & data)265 size_t BlobSerializer<Impl>::WriteString(const std::string& data) {
266 CHECK_GT(data.size(), 0); // No empty strings should be written.
267 size_t written_total = WriteArithmetic<size_t>(data.size());
268 if (is_debug) {
269 std::string str = ToStr(data);
270 Debug("WriteString(), length=%zu: \"%s\"\n", data.size(), data.c_str());
271 }
272
273 // Write the null-terminated string.
274 size_t length = data.size() + 1;
275 sink.insert(sink.end(), data.c_str(), data.c_str() + length);
276 written_total += length;
277
278 if (is_debug) {
279 Debug("WriteString() wrote %zu bytes\n", written_total);
280 }
281
282 return written_total;
283 }
284
285 // Helper for writing an array of numeric types.
286 template <typename Impl>
287 template <typename T>
WriteArithmetic(const T * data,size_t count)288 size_t BlobSerializer<Impl>::WriteArithmetic(const T* data, size_t count) {
289 static_assert(std::is_arithmetic_v<T>, "Arithmetic type");
290 DCHECK_GT(count, 0); // Should not write contents for vectors of size 0.
291 if (is_debug) {
292 std::string str =
293 "{ " + std::to_string(data[0]) + (count > 1 ? ", ... }" : " }");
294 std::string name = GetName<T>();
295 Debug("Write<%s>() (%zu-byte), count=%zu: %s",
296 name.c_str(),
297 sizeof(T),
298 count,
299 str.c_str());
300 }
301
302 size_t size = sizeof(T) * count;
303 const char* pos = reinterpret_cast<const char*>(data);
304 sink.insert(sink.end(), pos, pos + size);
305
306 if (is_debug) {
307 Debug(", wrote %zu bytes\n", size);
308 }
309 return size;
310 }
311
312 // Helper for writing numeric vectors.
313 template <typename Impl>
314 template <typename Number>
WriteArithmeticVector(const std::vector<Number> & data)315 size_t BlobSerializer<Impl>::WriteArithmeticVector(
316 const std::vector<Number>& data) {
317 static_assert(std::is_arithmetic_v<Number>, "Arithmetic type");
318 return WriteArithmetic(data.data(), data.size());
319 }
320
321 // Helper for writing non-numeric vectors.
322 template <typename Impl>
323 template <typename T>
WriteNonArithmeticVector(const std::vector<T> & data)324 size_t BlobSerializer<Impl>::WriteNonArithmeticVector(
325 const std::vector<T>& data) {
326 static_assert(!std::is_arithmetic_v<T>, "Arithmetic type");
327 DCHECK_GT(data.size(),
328 0); // Should not write contents for vectors of size 0.
329 size_t written_total = 0;
330 bool original_is_debug = is_debug;
331 is_debug = original_is_debug && !std::is_same_v<T, std::string>;
332 for (size_t i = 0; i < data.size(); ++i) {
333 if (is_debug) {
334 Debug("\n[%d] ", i);
335 }
336 written_total += WriteElement<T>(data[i]);
337 }
338 is_debug = original_is_debug;
339
340 return written_total;
341 }
342
343 template <typename Impl>
344 template <typename T>
WriteElement(const T & data)345 size_t BlobSerializer<Impl>::WriteElement(const T& data) {
346 if constexpr (std::is_arithmetic_v<T>) {
347 return WriteArithmetic<T>(data);
348 } else if constexpr (std::is_same_v<T, std::string>) {
349 return WriteString(data);
350 } else {
351 return impl()->template Write<T>(data);
352 }
353 }
354
355 } // namespace node
356
357 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
358
359 #endif // SRC_BLOB_SERIALIZER_DESERIALIZER_INL_H_
360