summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarshan Sen <raisinten@gmail.com>2023-05-09 13:15:20 +0530
committerDarshan Sen <raisinten@gmail.com>2023-05-11 18:19:24 +0530
commit78fe139b33fb974efdbcbb032c0ca985ea63ef68 (patch)
treea507648f1ddbb69a0d59e8f2ecc6198d7ff299a3
parent24615bd409d148c9ef2d03d6706d0807d5df4557 (diff)
downloadnode-new-78fe139b33fb974efdbcbb032c0ca985ea63ef68.tar.gz
src: move BlobSerializerDeserializer to a separate header file
This should make it possible to reuse the BlobSerializer and the BlobDeserializer classes in SEAs to generate and parse the injected blob. This change also resolves this TODO: https://github.com/nodejs/node/blob/4f69aae6a04a460f267005dcf6551959064b3238/src/node_snapshotable.cc#L187 Refs: https://github.com/nodejs/node/pull/47458 Signed-off-by: Darshan Sen <raisinten@gmail.com> PR-URL: https://github.com/nodejs/node/pull/47933 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
-rw-r--r--node.gyp2
-rw-r--r--src/blob_serializer_deserializer-inl.h359
-rw-r--r--src/blob_serializer_deserializer.h128
-rw-r--r--src/node_snapshotable.cc333
4 files changed, 490 insertions, 332 deletions
diff --git a/node.gyp b/node.gyp
index eb7a43e4a0..f9621fc1e1 100644
--- a/node.gyp
+++ b/node.gyp
@@ -174,6 +174,8 @@
'src/base_object_types.h',
'src/base64.h',
'src/base64-inl.h',
+ 'src/blob_serializer_deserializer.h',
+ 'src/blob_serializer_deserializer-inl.h',
'src/callback_queue.h',
'src/callback_queue-inl.h',
'src/cleanup_queue.h',
diff --git a/src/blob_serializer_deserializer-inl.h b/src/blob_serializer_deserializer-inl.h
new file mode 100644
index 0000000000..9383adee0b
--- /dev/null
+++ b/src/blob_serializer_deserializer-inl.h
@@ -0,0 +1,359 @@
+#ifndef SRC_BLOB_SERIALIZER_DESERIALIZER_INL_H_
+#define SRC_BLOB_SERIALIZER_DESERIALIZER_INL_H_
+
+#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#include "blob_serializer_deserializer.h"
+
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "debug_utils-inl.h"
+
+// This is related to the blob that is used in snapshots and has nothing to do
+// with `node_blob.h`.
+
+namespace node {
+
+struct EnvSerializeInfo;
+struct PropInfo;
+struct RealmSerializeInfo;
+
+namespace builtins {
+struct CodeCacheInfo;
+} // namespace builtins
+
+// These operator<< overload declarations are needed because
+// BlobSerializerDeserializer::ToStr() uses these.
+
+std::ostream& operator<<(std::ostream& output,
+ const builtins::CodeCacheInfo& info);
+
+std::ostream& operator<<(std::ostream& output,
+ const std::vector<builtins::CodeCacheInfo>& vec);
+
+std::ostream& operator<<(std::ostream& output, const std::vector<uint8_t>& vec);
+
+std::ostream& operator<<(std::ostream& output,
+ const std::vector<PropInfo>& vec);
+
+std::ostream& operator<<(std::ostream& output, const PropInfo& info);
+
+std::ostream& operator<<(std::ostream& output,
+ const std::vector<std::string>& vec);
+
+std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i);
+
+std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i);
+
+template <typename... Args>
+void BlobSerializerDeserializer::Debug(const char* format,
+ Args&&... args) const {
+ if (is_debug) {
+ FPrintF(stderr, format, std::forward<Args>(args)...);
+ }
+}
+
+template <typename T>
+std::string BlobSerializerDeserializer::ToStr(const T& arg) const {
+ std::stringstream ss;
+ ss << arg;
+ return ss.str();
+}
+
+template <typename T>
+std::string BlobSerializerDeserializer::GetName() const {
+#define TYPE_LIST(V) \
+ V(builtins::CodeCacheInfo) \
+ V(PropInfo) \
+ V(std::string)
+
+#define V(TypeName) \
+ if constexpr (std::is_same_v<T, TypeName>) { \
+ return #TypeName; \
+ } else // NOLINT(readability/braces)
+ TYPE_LIST(V)
+#undef V
+
+ if constexpr (std::is_arithmetic_v<T>) {
+ return (std::is_unsigned_v<T> ? "uint"
+ : std::is_integral_v<T> ? "int"
+ : "float") +
+ std::to_string(sizeof(T) * 8) + "_t";
+ }
+ return "";
+}
+
+// Helper for reading numeric types.
+template <typename Impl>
+template <typename T>
+T BlobDeserializer<Impl>::ReadArithmetic() {
+ static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
+ T result;
+ ReadArithmetic(&result, 1);
+ return result;
+}
+
+// Layout of vectors:
+// [ 4/8 bytes ] count
+// [ ... ] contents (count * size of individual elements)
+template <typename Impl>
+template <typename T>
+std::vector<T> BlobDeserializer<Impl>::ReadVector() {
+ if (is_debug) {
+ std::string name = GetName<T>();
+ Debug("\nReadVector<%s>()(%d-byte)\n", name.c_str(), sizeof(T));
+ }
+ size_t count = static_cast<size_t>(ReadArithmetic<size_t>());
+ if (count == 0) {
+ return std::vector<T>();
+ }
+ if (is_debug) {
+ Debug("Reading %d vector elements...\n", count);
+ }
+ std::vector<T> result;
+ if constexpr (std::is_arithmetic_v<T>) {
+ result = ReadArithmeticVector<T>(count);
+ } else {
+ result = ReadNonArithmeticVector<T>(count);
+ }
+ if (is_debug) {
+ std::string str = std::is_arithmetic_v<T> ? "" : ToStr(result);
+ std::string name = GetName<T>();
+ Debug("ReadVector<%s>() read %s\n", name.c_str(), str.c_str());
+ }
+ return result;
+}
+
+template <typename Impl>
+std::string BlobDeserializer<Impl>::ReadString() {
+ size_t length = ReadArithmetic<size_t>();
+
+ if (is_debug) {
+ Debug("ReadString(), length=%d: ", length);
+ }
+
+ CHECK_GT(length, 0); // There should be no empty strings.
+ MallocedBuffer<char> buf(length + 1);
+ memcpy(buf.data, sink.data() + read_total, length + 1);
+ std::string result(buf.data, length); // This creates a copy of buf.data.
+
+ if (is_debug) {
+ Debug("\"%s\", read %zu bytes\n", result.c_str(), length + 1);
+ }
+
+ read_total += length + 1;
+ return result;
+}
+
+// Helper for reading an array of numeric types.
+template <typename Impl>
+template <typename T>
+void BlobDeserializer<Impl>::ReadArithmetic(T* out, size_t count) {
+ static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
+ DCHECK_GT(count, 0); // Should not read contents for vectors of size 0.
+ if (is_debug) {
+ std::string name = GetName<T>();
+ Debug("Read<%s>()(%d-byte), count=%d: ", name.c_str(), sizeof(T), count);
+ }
+
+ size_t size = sizeof(T) * count;
+ memcpy(out, sink.data() + read_total, size);
+
+ if (is_debug) {
+ std::string str =
+ "{ " + std::to_string(out[0]) + (count > 1 ? ", ... }" : " }");
+ Debug("%s, read %zu bytes\n", str.c_str(), size);
+ }
+ read_total += size;
+}
+
+// Helper for reading numeric vectors.
+template <typename Impl>
+template <typename Number>
+std::vector<Number> BlobDeserializer<Impl>::ReadArithmeticVector(size_t count) {
+ static_assert(std::is_arithmetic_v<Number>, "Not an arithmetic type");
+ DCHECK_GT(count, 0); // Should not read contents for vectors of size 0.
+ std::vector<Number> result(count);
+ ReadArithmetic(result.data(), count);
+ return result;
+}
+
+// Helper for reading non-numeric vectors.
+template <typename Impl>
+template <typename T>
+std::vector<T> BlobDeserializer<Impl>::ReadNonArithmeticVector(size_t count) {
+ static_assert(!std::is_arithmetic_v<T>, "Arithmetic type");
+ DCHECK_GT(count, 0); // Should not read contents for vectors of size 0.
+ std::vector<T> result;
+ result.reserve(count);
+ bool original_is_debug = is_debug;
+ is_debug = original_is_debug && !std::is_same_v<T, std::string>;
+ for (size_t i = 0; i < count; ++i) {
+ if (is_debug) {
+ Debug("\n[%d] ", i);
+ }
+ result.push_back(ReadElement<T>());
+ }
+ is_debug = original_is_debug;
+
+ return result;
+}
+
+template <typename Impl>
+template <typename T>
+T BlobDeserializer<Impl>::ReadElement() {
+ if constexpr (std::is_arithmetic_v<T>) {
+ return ReadArithmetic<T>();
+ } else if constexpr (std::is_same_v<T, std::string>) {
+ return ReadString();
+ } else {
+ return impl()->template Read<T>();
+ }
+}
+
+// Helper for writing numeric types.
+template <typename Impl>
+template <typename T>
+size_t BlobSerializer<Impl>::WriteArithmetic(const T& data) {
+ static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
+ return WriteArithmetic(&data, 1);
+}
+
+// Layout of vectors:
+// [ 4/8 bytes ] count
+// [ ... ] contents (count * size of individual elements)
+template <typename Impl>
+template <typename T>
+size_t BlobSerializer<Impl>::WriteVector(const std::vector<T>& data) {
+ if (is_debug) {
+ std::string str = std::is_arithmetic_v<T> ? "" : ToStr(data);
+ std::string name = GetName<T>();
+ Debug("\nWriteVector<%s>() (%d-byte), count=%d: %s\n",
+ name.c_str(),
+ sizeof(T),
+ data.size(),
+ str.c_str());
+ }
+
+ size_t written_total = WriteArithmetic<size_t>(data.size());
+ if (data.size() == 0) {
+ return written_total;
+ }
+
+ if constexpr (std::is_arithmetic_v<T>) {
+ written_total += WriteArithmeticVector<T>(data);
+ } else {
+ written_total += WriteNonArithmeticVector<T>(data);
+ }
+
+ if (is_debug) {
+ std::string name = GetName<T>();
+ Debug("WriteVector<%s>() wrote %d bytes\n", name.c_str(), written_total);
+ }
+
+ return written_total;
+}
+
+// The layout of a written string:
+// [ 4/8 bytes ] length
+// [ |length| bytes ] contents
+template <typename Impl>
+size_t BlobSerializer<Impl>::WriteString(const std::string& data) {
+ CHECK_GT(data.size(), 0); // No empty strings should be written.
+ size_t written_total = WriteArithmetic<size_t>(data.size());
+ if (is_debug) {
+ std::string str = ToStr(data);
+ Debug("WriteString(), length=%zu: \"%s\"\n", data.size(), data.c_str());
+ }
+
+ // Write the null-terminated string.
+ size_t length = data.size() + 1;
+ sink.insert(sink.end(), data.c_str(), data.c_str() + length);
+ written_total += length;
+
+ if (is_debug) {
+ Debug("WriteString() wrote %zu bytes\n", written_total);
+ }
+
+ return written_total;
+}
+
+// Helper for writing an array of numeric types.
+template <typename Impl>
+template <typename T>
+size_t BlobSerializer<Impl>::WriteArithmetic(const T* data, size_t count) {
+ static_assert(std::is_arithmetic_v<T>, "Arithmetic type");
+ DCHECK_GT(count, 0); // Should not write contents for vectors of size 0.
+ if (is_debug) {
+ std::string str =
+ "{ " + std::to_string(data[0]) + (count > 1 ? ", ... }" : " }");
+ std::string name = GetName<T>();
+ Debug("Write<%s>() (%zu-byte), count=%zu: %s",
+ name.c_str(),
+ sizeof(T),
+ count,
+ str.c_str());
+ }
+
+ size_t size = sizeof(T) * count;
+ const char* pos = reinterpret_cast<const char*>(data);
+ sink.insert(sink.end(), pos, pos + size);
+
+ if (is_debug) {
+ Debug(", wrote %zu bytes\n", size);
+ }
+ return size;
+}
+
+// Helper for writing numeric vectors.
+template <typename Impl>
+template <typename Number>
+size_t BlobSerializer<Impl>::WriteArithmeticVector(
+ const std::vector<Number>& data) {
+ static_assert(std::is_arithmetic_v<Number>, "Arithmetic type");
+ return WriteArithmetic(data.data(), data.size());
+}
+
+// Helper for writing non-numeric vectors.
+template <typename Impl>
+template <typename T>
+size_t BlobSerializer<Impl>::WriteNonArithmeticVector(
+ const std::vector<T>& data) {
+ static_assert(!std::is_arithmetic_v<T>, "Arithmetic type");
+ DCHECK_GT(data.size(),
+ 0); // Should not write contents for vectors of size 0.
+ size_t written_total = 0;
+ bool original_is_debug = is_debug;
+ is_debug = original_is_debug && !std::is_same_v<T, std::string>;
+ for (size_t i = 0; i < data.size(); ++i) {
+ if (is_debug) {
+ Debug("\n[%d] ", i);
+ }
+ written_total += WriteElement<T>(data[i]);
+ }
+ is_debug = original_is_debug;
+
+ return written_total;
+}
+
+template <typename Impl>
+template <typename T>
+size_t BlobSerializer<Impl>::WriteElement(const T& data) {
+ if constexpr (std::is_arithmetic_v<T>) {
+ return WriteArithmetic<T>(data);
+ } else if constexpr (std::is_same_v<T, std::string>) {
+ return WriteString(data);
+ } else {
+ return impl()->template Write<T>(data);
+ }
+}
+
+} // namespace node
+
+#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#endif // SRC_BLOB_SERIALIZER_DESERIALIZER_INL_H_
diff --git a/src/blob_serializer_deserializer.h b/src/blob_serializer_deserializer.h
new file mode 100644
index 0000000000..3715c5e7c5
--- /dev/null
+++ b/src/blob_serializer_deserializer.h
@@ -0,0 +1,128 @@
+#ifndef SRC_BLOB_SERIALIZER_DESERIALIZER_H_
+#define SRC_BLOB_SERIALIZER_DESERIALIZER_H_
+
+#include <string>
+#include <vector>
+
+#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+// This is related to the blob that is used in snapshots and has nothing to do
+// with `node_blob.h`.
+
+namespace node {
+
+class BlobSerializerDeserializer {
+ public:
+ explicit BlobSerializerDeserializer(bool is_debug_v) : is_debug(is_debug_v) {}
+
+ template <typename... Args>
+ void Debug(const char* format, Args&&... args) const;
+
+ template <typename T>
+ std::string ToStr(const T& arg) const;
+
+ template <typename T>
+ std::string GetName() const;
+
+ bool is_debug = false;
+};
+
+// Child classes are expected to implement T Read<T>() where
+// !std::is_arithmetic_v<T> && !std::is_same_v<T, std::string>
+template <typename Impl>
+class BlobDeserializer : public BlobSerializerDeserializer {
+ public:
+ explicit BlobDeserializer(bool is_debug_v, std::string_view s)
+ : BlobSerializerDeserializer(is_debug_v), sink(s) {}
+ ~BlobDeserializer() {}
+
+ size_t read_total = 0;
+ std::string_view sink;
+
+ Impl* impl() { return static_cast<Impl*>(this); }
+ const Impl* impl() const { return static_cast<const Impl*>(this); }
+
+ // Helper for reading numeric types.
+ template <typename T>
+ T ReadArithmetic();
+
+ // Layout of vectors:
+ // [ 4/8 bytes ] count
+ // [ ... ] contents (count * size of individual elements)
+ template <typename T>
+ std::vector<T> ReadVector();
+
+ std::string ReadString();
+
+ // Helper for reading an array of numeric types.
+ template <typename T>
+ void ReadArithmetic(T* out, size_t count);
+
+ // Helper for reading numeric vectors.
+ template <typename Number>
+ std::vector<Number> ReadArithmeticVector(size_t count);
+
+ private:
+ // Helper for reading non-numeric vectors.
+ template <typename T>
+ std::vector<T> ReadNonArithmeticVector(size_t count);
+
+ template <typename T>
+ T ReadElement();
+};
+
+// Child classes are expected to implement size_t Write<T>(const T&) where
+// !std::is_arithmetic_v<T> && !std::is_same_v<T, std::string>
+template <typename Impl>
+class BlobSerializer : public BlobSerializerDeserializer {
+ public:
+ explicit BlobSerializer(bool is_debug_v)
+ : BlobSerializerDeserializer(is_debug_v) {
+ // Currently the snapshot blob built with an empty script is around 4MB.
+ // So use that as the default sink size.
+ sink.reserve(4 * 1024 * 1024);
+ }
+ ~BlobSerializer() {}
+
+ Impl* impl() { return static_cast<Impl*>(this); }
+ const Impl* impl() const { return static_cast<const Impl*>(this); }
+
+ std::vector<char> sink;
+
+ // Helper for writing numeric types.
+ template <typename T>
+ size_t WriteArithmetic(const T& data);
+
+ // Layout of vectors:
+ // [ 4/8 bytes ] count
+ // [ ... ] contents (count * size of individual elements)
+ template <typename T>
+ size_t WriteVector(const std::vector<T>& data);
+
+ // The layout of a written string:
+ // [ 4/8 bytes ] length
+ // [ |length| bytes ] contents
+ size_t WriteString(const std::string& data);
+
+ // Helper for writing an array of numeric types.
+ template <typename T>
+ size_t WriteArithmetic(const T* data, size_t count);
+
+ // Helper for writing numeric vectors.
+ template <typename Number>
+ size_t WriteArithmeticVector(const std::vector<Number>& data);
+
+ private:
+ // Helper for writing non-numeric vectors.
+ template <typename T>
+ size_t WriteNonArithmeticVector(const std::vector<T>& data);
+
+ template <typename T>
+ size_t WriteElement(const T& data);
+};
+
+} // namespace node
+
+#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#endif // SRC_BLOB_SERIALIZER_DESERIALIZER_H_
diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc
index 5c283bcdbe..b656332f8c 100644
--- a/src/node_snapshotable.cc
+++ b/src/node_snapshotable.cc
@@ -4,6 +4,7 @@
#include <sstream>
#include <vector>
#include "base_object-inl.h"
+#include "blob_serializer_deserializer-inl.h"
#include "debug_utils-inl.h"
#include "encoding_binding.h"
#include "env-inl.h"
@@ -140,338 +141,6 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
return output;
}
-class BlobSerializerDeserializer {
- public:
- explicit BlobSerializerDeserializer(bool is_debug_v) : is_debug(is_debug_v) {}
-
- template <typename... Args>
- void Debug(const char* format, Args&&... args) const {
- if (is_debug) {
- FPrintF(stderr, format, std::forward<Args>(args)...);
- }
- }
-
- template <typename T>
- std::string ToStr(const T& arg) const {
- std::stringstream ss;
- ss << arg;
- return ss.str();
- }
-
- template <typename T>
- std::string GetName() const {
-#define TYPE_LIST(V) \
- V(builtins::CodeCacheInfo) \
- V(PropInfo) \
- V(std::string)
-
-#define V(TypeName) \
- if constexpr (std::is_same_v<T, TypeName>) { \
- return #TypeName; \
- } else // NOLINT(readability/braces)
- TYPE_LIST(V)
-#undef V
-
- if constexpr (std::is_arithmetic_v<T>) {
- return (std::is_unsigned_v<T> ? "uint"
- : std::is_integral_v<T> ? "int"
- : "float") +
- std::to_string(sizeof(T) * 8) + "_t";
- }
- return "";
- }
-
- bool is_debug = false;
-};
-
-// TODO(joyeecheung): move it to the separate header file.
-// Child classes are expected to implement T Read<T>() where
-// !std::is_arithmetic_v<T> && !std::is_same_v<T, std::string>
-template <typename Impl>
-class BlobDeserializer : public BlobSerializerDeserializer {
- public:
- explicit BlobDeserializer(bool is_debug_v, std::string_view s)
- : BlobSerializerDeserializer(is_debug_v), sink(s) {}
- ~BlobDeserializer() {}
-
- size_t read_total = 0;
- std::string_view sink;
-
- Impl* impl() { return static_cast<Impl*>(this); }
- const Impl* impl() const { return static_cast<const Impl*>(this); }
-
- // Helper for reading numeric types.
- template <typename T>
- T ReadArithmetic() {
- static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
- T result;
- ReadArithmetic(&result, 1);
- return result;
- }
-
- // Layout of vectors:
- // [ 4/8 bytes ] count
- // [ ... ] contents (count * size of individual elements)
- template <typename T>
- std::vector<T> ReadVector() {
- if (is_debug) {
- std::string name = GetName<T>();
- Debug("\nReadVector<%s>()(%d-byte)\n", name.c_str(), sizeof(T));
- }
- size_t count = static_cast<size_t>(ReadArithmetic<size_t>());
- if (count == 0) {
- return std::vector<T>();
- }
- if (is_debug) {
- Debug("Reading %d vector elements...\n", count);
- }
- std::vector<T> result;
- if constexpr (std::is_arithmetic_v<T>) {
- result = ReadArithmeticVector<T>(count);
- } else {
- result = ReadNonArithmeticVector<T>(count);
- }
- if (is_debug) {
- std::string str = std::is_arithmetic_v<T> ? "" : ToStr(result);
- std::string name = GetName<T>();
- Debug("ReadVector<%s>() read %s\n", name.c_str(), str.c_str());
- }
- return result;
- }
-
- std::string ReadString() {
- size_t length = ReadArithmetic<size_t>();
-
- if (is_debug) {
- Debug("ReadString(), length=%d: ", length);
- }
-
- CHECK_GT(length, 0); // There should be no empty strings.
- MallocedBuffer<char> buf(length + 1);
- memcpy(buf.data, sink.data() + read_total, length + 1);
- std::string result(buf.data, length); // This creates a copy of buf.data.
-
- if (is_debug) {
- Debug("\"%s\", read %zu bytes\n", result.c_str(), length + 1);
- }
-
- read_total += length + 1;
- return result;
- }
-
- // Helper for reading an array of numeric types.
- template <typename T>
- void ReadArithmetic(T* out, size_t count) {
- static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
- DCHECK_GT(count, 0); // Should not read contents for vectors of size 0.
- if (is_debug) {
- std::string name = GetName<T>();
- Debug("Read<%s>()(%d-byte), count=%d: ", name.c_str(), sizeof(T), count);
- }
-
- size_t size = sizeof(T) * count;
- memcpy(out, sink.data() + read_total, size);
-
- if (is_debug) {
- std::string str =
- "{ " + std::to_string(out[0]) + (count > 1 ? ", ... }" : " }");
- Debug("%s, read %zu bytes\n", str.c_str(), size);
- }
- read_total += size;
- }
-
- // Helper for reading numeric vectors.
- template <typename Number>
- std::vector<Number> ReadArithmeticVector(size_t count) {
- static_assert(std::is_arithmetic_v<Number>, "Not an arithmetic type");
- DCHECK_GT(count, 0); // Should not read contents for vectors of size 0.
- std::vector<Number> result(count);
- ReadArithmetic(result.data(), count);
- return result;
- }
-
- private:
- // Helper for reading non-numeric vectors.
- template <typename T>
- std::vector<T> ReadNonArithmeticVector(size_t count) {
- static_assert(!std::is_arithmetic_v<T>, "Arithmetic type");
- DCHECK_GT(count, 0); // Should not read contents for vectors of size 0.
- std::vector<T> result;
- result.reserve(count);
- bool original_is_debug = is_debug;
- is_debug = original_is_debug && !std::is_same_v<T, std::string>;
- for (size_t i = 0; i < count; ++i) {
- if (is_debug) {
- Debug("\n[%d] ", i);
- }
- result.push_back(ReadElement<T>());
- }
- is_debug = original_is_debug;
-
- return result;
- }
-
- template <typename T>
- T ReadElement() {
- if constexpr (std::is_arithmetic_v<T>) {
- return ReadArithmetic<T>();
- } else if constexpr (std::is_same_v<T, std::string>) {
- return ReadString();
- } else {
- return impl()->template Read<T>();
- }
- }
-};
-
-// TODO(joyeecheung): move it to the separate header file.
-// Child classes are expected to implement size_t Write<T>(const T&) where
-// !std::is_arithmetic_v<T> && !std::is_same_v<T, std::string>
-template <typename Impl>
-class BlobSerializer : public BlobSerializerDeserializer {
- public:
- explicit BlobSerializer(bool is_debug_v)
- : BlobSerializerDeserializer(is_debug_v) {
- // Currently the snapshot blob built with an empty script is around 4MB.
- // So use that as the default sink size.
- sink.reserve(4 * 1024 * 1024);
- }
- ~BlobSerializer() {}
-
- Impl* impl() { return static_cast<Impl*>(this); }
- const Impl* impl() const { return static_cast<const Impl*>(this); }
-
- std::vector<char> sink;
-
- // Helper for writing numeric types.
- template <typename T>
- size_t WriteArithmetic(const T& data) {
- static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
- return WriteArithmetic(&data, 1);
- }
-
- // Layout of vectors:
- // [ 4/8 bytes ] count
- // [ ... ] contents (count * size of individual elements)
- template <typename T>
- size_t WriteVector(const std::vector<T>& data) {
- if (is_debug) {
- std::string str = std::is_arithmetic_v<T> ? "" : ToStr(data);
- std::string name = GetName<T>();
- Debug("\nWriteVector<%s>() (%d-byte), count=%d: %s\n",
- name.c_str(),
- sizeof(T),
- data.size(),
- str.c_str());
- }
-
- size_t written_total = WriteArithmetic<size_t>(data.size());
- if (data.size() == 0) {
- return written_total;
- }
-
- if constexpr (std::is_arithmetic_v<T>) {
- written_total += WriteArithmeticVector<T>(data);
- } else {
- written_total += WriteNonArithmeticVector<T>(data);
- }
-
- if (is_debug) {
- std::string name = GetName<T>();
- Debug("WriteVector<%s>() wrote %d bytes\n", name.c_str(), written_total);
- }
-
- return written_total;
- }
-
- // The layout of a written string:
- // [ 4/8 bytes ] length
- // [ |length| bytes ] contents
- size_t WriteString(const std::string& data) {
- CHECK_GT(data.size(), 0); // No empty strings should be written.
- size_t written_total = WriteArithmetic<size_t>(data.size());
- if (is_debug) {
- std::string str = ToStr(data);
- Debug("WriteString(), length=%zu: \"%s\"\n", data.size(), data.c_str());
- }
-
- // Write the null-terminated string.
- size_t length = data.size() + 1;
- sink.insert(sink.end(), data.c_str(), data.c_str() + length);
- written_total += length;
-
- if (is_debug) {
- Debug("WriteString() wrote %zu bytes\n", written_total);
- }
-
- return written_total;
- }
-
- // Helper for writing an array of numeric types.
- template <typename T>
- size_t WriteArithmetic(const T* data, size_t count) {
- static_assert(std::is_arithmetic_v<T>, "Arithmetic type");
- DCHECK_GT(count, 0); // Should not write contents for vectors of size 0.
- if (is_debug) {
- std::string str =
- "{ " + std::to_string(data[0]) + (count > 1 ? ", ... }" : " }");
- std::string name = GetName<T>();
- Debug("Write<%s>() (%zu-byte), count=%zu: %s",
- name.c_str(),
- sizeof(T),
- count,
- str.c_str());
- }
-
- size_t size = sizeof(T) * count;
- const char* pos = reinterpret_cast<const char*>(data);
- sink.insert(sink.end(), pos, pos + size);
-
- if (is_debug) {
- Debug(", wrote %zu bytes\n", size);
- }
- return size;
- }
-
- // Helper for writing numeric vectors.
- template <typename Number>
- size_t WriteArithmeticVector(const std::vector<Number>& data) {
- static_assert(std::is_arithmetic_v<Number>, "Arithmetic type");
- return WriteArithmetic(data.data(), data.size());
- }
-
- private:
- // Helper for writing non-numeric vectors.
- template <typename T>
- size_t WriteNonArithmeticVector(const std::vector<T>& data) {
- static_assert(!std::is_arithmetic_v<T>, "Arithmetic type");
- DCHECK_GT(data.size(),
- 0); // Should not write contents for vectors of size 0.
- size_t written_total = 0;
- bool original_is_debug = is_debug;
- is_debug = original_is_debug && !std::is_same_v<T, std::string>;
- for (size_t i = 0; i < data.size(); ++i) {
- if (is_debug) {
- Debug("\n[%d] ", i);
- }
- written_total += WriteElement<T>(data[i]);
- }
- is_debug = original_is_debug;
-
- return written_total;
- }
-
- template <typename T>
- size_t WriteElement(const T& data) {
- if constexpr (std::is_arithmetic_v<T>) {
- return WriteArithmetic<T>(data);
- } else if constexpr (std::is_same_v<T, std::string>) {
- return WriteString(data);
- } else {
- return impl()->template Write<T>(data);
- }
- }
-};
-
class SnapshotDeserializer : public BlobDeserializer<SnapshotDeserializer> {
public:
explicit SnapshotDeserializer(std::string_view v)