forked from LeenkxTeam/Kmake
386 lines
11 KiB
C++
386 lines
11 KiB
C++
#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 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<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() {
|
|
std::string_view view = ReadStringView(StringLogMode::kAddressAndContent);
|
|
return std::string(view);
|
|
}
|
|
|
|
template <typename Impl>
|
|
std::string_view BlobDeserializer<Impl>::ReadStringView(StringLogMode mode) {
|
|
size_t length = ReadArithmetic<size_t>();
|
|
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 <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("\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<size_t>(data.size());
|
|
if (data.empty()) {
|
|
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>::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<size_t>(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 <typename Impl>
|
|
size_t BlobSerializer<Impl>::WriteString(const std::string& data) {
|
|
return WriteStringView(data, StringLogMode::kAddressAndContent);
|
|
}
|
|
|
|
static size_t kPreviewCount = 16;
|
|
|
|
// 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) {
|
|
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<T>();
|
|
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<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_
|