#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 #include #include #include #include #include "debug_utils-inl.h" // This is related to the blob that is used in snapshots and single executable // applications 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& vec); std::ostream& operator<<(std::ostream& output, const std::vector& vec); std::ostream& operator<<(std::ostream& output, const std::vector& vec); std::ostream& operator<<(std::ostream& output, const PropInfo& info); std::ostream& operator<<(std::ostream& output, const std::vector& vec); std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i); std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i); template void BlobSerializerDeserializer::Debug(const char* format, Args&&... args) const { if (is_debug) { FPrintF(stderr, format, std::forward(args)...); } } template std::string BlobSerializerDeserializer::ToStr(const T& arg) const { std::stringstream ss; ss << arg; return ss.str(); } template 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) { \ return #TypeName; \ } else // NOLINT(readability/braces) TYPE_LIST(V) #undef V if constexpr (std::is_arithmetic_v) { return (std::is_unsigned_v ? "uint" : std::is_integral_v ? "int" : "float") + std::to_string(sizeof(T) * 8) + "_t"; } return ""; } // Helper for reading numeric types. template template T BlobDeserializer::ReadArithmetic() { static_assert(std::is_arithmetic_v, "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 template std::vector BlobDeserializer::ReadVector() { if (is_debug) { std::string name = GetName(); Debug("\nReadVector<%s>()(%d-byte)\n", name.c_str(), sizeof(T)); } size_t count = static_cast(ReadArithmetic()); if (count == 0) { return std::vector(); } if (is_debug) { Debug("Reading %d vector elements...\n", count); } std::vector result; if constexpr (std::is_arithmetic_v) { result = ReadArithmeticVector(count); } else { result = ReadNonArithmeticVector(count); } if (is_debug) { std::string str = std::is_arithmetic_v ? "" : ToStr(result); std::string name = GetName(); Debug("ReadVector<%s>() read %s\n", name.c_str(), str.c_str()); } return result; } template std::string BlobDeserializer::ReadString() { std::string_view view = ReadStringView(StringLogMode::kAddressAndContent); return std::string(view); } template std::string_view BlobDeserializer::ReadStringView(StringLogMode mode) { size_t length = ReadArithmetic(); Debug("ReadStringView(), length=%zu: ", length); if (length == 0) { Debug("ReadStringView() read an empty view\n"); return std::string_view(); } std::string_view result(sink.data() + read_total, length); Debug("%p, read %zu bytes", result.data(), result.size()); if (mode == StringLogMode::kAddressAndContent) { Debug(", content:%s%s", length > 32 ? "\n" : " ", result); } Debug("\n"); read_total += length; return result; } // Helper for reading an array of numeric types. template template void BlobDeserializer::ReadArithmetic(T* out, size_t count) { static_assert(std::is_arithmetic_v, "Not an arithmetic type"); DCHECK_GT(count, 0); // Should not read contents for vectors of size 0. if (is_debug) { std::string name = GetName(); 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 template std::vector BlobDeserializer::ReadArithmeticVector(size_t count) { static_assert(std::is_arithmetic_v, "Not an arithmetic type"); DCHECK_GT(count, 0); // Should not read contents for vectors of size 0. std::vector result(count); ReadArithmetic(result.data(), count); return result; } // Helper for reading non-numeric vectors. template template std::vector BlobDeserializer::ReadNonArithmeticVector(size_t count) { static_assert(!std::is_arithmetic_v, "Arithmetic type"); DCHECK_GT(count, 0); // Should not read contents for vectors of size 0. std::vector result; result.reserve(count); bool original_is_debug = is_debug; is_debug = original_is_debug && !std::is_same_v; for (size_t i = 0; i < count; ++i) { if (is_debug) { Debug("\n[%d] ", i); } result.push_back(ReadElement()); } is_debug = original_is_debug; return result; } template template T BlobDeserializer::ReadElement() { if constexpr (std::is_arithmetic_v) { return ReadArithmetic(); } else if constexpr (std::is_same_v) { return ReadString(); } else { return impl()->template Read(); } } // Helper for writing numeric types. template template size_t BlobSerializer::WriteArithmetic(const T& data) { static_assert(std::is_arithmetic_v, "Not an arithmetic type"); return WriteArithmetic(&data, 1); } // Layout of vectors: // [ 4/8 bytes ] count // [ ... ] contents (count * size of individual elements) template template size_t BlobSerializer::WriteVector(const std::vector& data) { if (is_debug) { std::string str = std::is_arithmetic_v ? "" : ToStr(data); std::string name = GetName(); Debug("\nAt 0x%x: WriteVector<%s>() (%d-byte), count=%d: %s\n", sink.size(), name.c_str(), sizeof(T), data.size(), str.c_str()); } size_t written_total = WriteArithmetic(data.size()); if (data.empty()) { return written_total; } if constexpr (std::is_arithmetic_v) { written_total += WriteArithmeticVector(data); } else { written_total += WriteNonArithmeticVector(data); } if (is_debug) { std::string name = GetName(); 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 size_t BlobSerializer::WriteStringView(std::string_view data, StringLogMode mode) { Debug("At 0x%x: WriteStringView(), length=%zu: %p\n", sink.size(), data.size(), data.data()); size_t written_total = WriteArithmetic(data.size()); size_t length = data.size(); if (length == 0) { Debug("WriteStringView() wrote an empty view\n"); return written_total; } sink.insert(sink.end(), data.data(), data.data() + length); written_total += length; Debug("WriteStringView() wrote %zu bytes\n", written_total); if (mode == StringLogMode::kAddressAndContent) { Debug("%s", data); } return written_total; } template size_t BlobSerializer::WriteString(const std::string& data) { return WriteStringView(data, StringLogMode::kAddressAndContent); } static size_t kPreviewCount = 16; // Helper for writing an array of numeric types. template template size_t BlobSerializer::WriteArithmetic(const T* data, size_t count) { static_assert(std::is_arithmetic_v, "Arithmetic type"); DCHECK_GT(count, 0); // Should not write contents for vectors of size 0. if (is_debug) { size_t preview_count = count < kPreviewCount ? count : kPreviewCount; std::string str = "{ "; for (size_t i = 0; i < preview_count; ++i) { str += (std::to_string(data[i]) + ","); } if (count > preview_count) { str += "..."; } str += "}"; std::string name = GetName(); Debug("At 0x%x: Write<%s>() (%zu-byte), count=%zu: %s", sink.size(), name.c_str(), sizeof(T), count, str.c_str()); } size_t size = sizeof(T) * count; const char* pos = reinterpret_cast(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 template size_t BlobSerializer::WriteArithmeticVector( const std::vector& data) { static_assert(std::is_arithmetic_v, "Arithmetic type"); return WriteArithmetic(data.data(), data.size()); } // Helper for writing non-numeric vectors. template template size_t BlobSerializer::WriteNonArithmeticVector( const std::vector& data) { static_assert(!std::is_arithmetic_v, "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; for (size_t i = 0; i < data.size(); ++i) { if (is_debug) { Debug("\n[%d] ", i); } written_total += WriteElement(data[i]); } is_debug = original_is_debug; return written_total; } template template size_t BlobSerializer::WriteElement(const T& data) { if constexpr (std::is_arithmetic_v) { return WriteArithmetic(data); } else if constexpr (std::is_same_v) { return WriteString(data); } else { return impl()->template Write(data); } } } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_BLOB_SERIALIZER_DESERIALIZER_INL_H_