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
19namespace node {
20
21struct EnvSerializeInfo;
22struct PropInfo;
23struct RealmSerializeInfo;
24
25namespace builtins {
26struct CodeCacheInfo;
27}  // namespace builtins
28
29// These operator<< overload declarations are needed because
30// BlobSerializerDeserializer::ToStr() uses these.
31
32std::ostream& operator<<(std::ostream& output,
33                         const builtins::CodeCacheInfo& info);
34
35std::ostream& operator<<(std::ostream& output,
36                         const std::vector<builtins::CodeCacheInfo>& vec);
37
38std::ostream& operator<<(std::ostream& output, const std::vector<uint8_t>& vec);
39
40std::ostream& operator<<(std::ostream& output,
41                         const std::vector<PropInfo>& vec);
42
43std::ostream& operator<<(std::ostream& output, const PropInfo& info);
44
45std::ostream& operator<<(std::ostream& output,
46                         const std::vector<std::string>& vec);
47
48std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i);
49
50std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i);
51
52template <typename... Args>
53void BlobSerializerDeserializer::Debug(const char* format,
54                                       Args&&... args) const {
55  if (is_debug) {
56    FPrintF(stderr, format, std::forward<Args>(args)...);
57  }
58}
59
60template <typename T>
61std::string BlobSerializerDeserializer::ToStr(const T& arg) const {
62  std::stringstream ss;
63  ss << arg;
64  return ss.str();
65}
66
67template <typename T>
68std::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.
91template <typename Impl>
92template <typename T>
93T 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)
103template <typename Impl>
104template <typename T>
105std::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
131template <typename Impl>
132std::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.
153template <typename Impl>
154template <typename T>
155void 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.
175template <typename Impl>
176template <typename Number>
177std::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.
186template <typename Impl>
187template <typename T>
188std::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
206template <typename Impl>
207template <typename T>
208T 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.
219template <typename Impl>
220template <typename T>
221size_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)
229template <typename Impl>
230template <typename T>
231size_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
264template <typename Impl>
265size_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.
286template <typename Impl>
287template <typename T>
288size_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.
313template <typename Impl>
314template <typename Number>
315size_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.
322template <typename Impl>
323template <typename T>
324size_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
343template <typename Impl>
344template <typename T>
345size_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