当前位置: 首页 > news >正文

alibaba yalantingLibs struct_pack代码梳理

这里写目录标题

  • struct_pack 接口
    • 序列化
      • 序列化对象到新字节容器
      • 序列化对象到容器尾部
      • 将序列化结果保存到指针指向的内存中
      • 多参数序列化
      • 将序列化结果保存到输出流
      • 自定义类型序列化
      • 序列化到自定义的输出流
    • 反序列化
      • 基本反序列化
      • 从指针指向的内存中反序列化
      • 反序列化到已有对象
      • 多参数反序列化
      • 输入流反序列化
      • 部分反序列化
      • 从自定义的输入流中反序列化
  • struct_pack 代码分析
    • 从示例开始 最简单的序列化
    • 函数调用
      • struct_pack::serialize(person1);
        • 模板类:
        • 修饰:
        • 参数:
        • 函数主体:
      • serialize_to(Buffer &buffer, const Args &...args)
        • 模板类:
        • 修饰:
        • 参数:
        • 函数主体:
      • detail::get_serialize_runtime_info(const Args &...args)
        • 模板类
        • 修饰
        • 参数
        • 函数主体
      • get_args_type <Args...>
        • 函数主体
      • get_types<U>
        • 函数主体
      • check_if_add_type_literal< conf>(args...)
        • 函数主体
      • calculate_payload_size
        • 函数主体
      • calculate_one_size
        • 函数主体
      • get_type_id
        • 函数主体
      • serialize_to(Writer &writer, const serialize_buffer_size &info,const Args &...args)
        • 模板类
        • 修饰
        • 参数
        • 函数主体
      • class packer
        • 成员变量
        • public方法
          • void serialize(const T &t, const Args &...args)
            • 函数主体
          • void STRUCT_PACK_INLINE serialize_metainfo()
            • 函数主体
    • 最简单的反序列化
      • auto deserialize(const View &v)
        • 模板类
        • 函数主体
      • template <typename T, typename... Args, detail::deserialize_view View>[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to( T &t, const View &v, Args &...args)
        • 函数主体
      • class unpacker
        • public方法
          • template <typename T, typename... Args> STRUCT_PACK_MAY_INLINE struct_pack::errc deserialize(T &t, Args &...args)
            • 代码
          • template <size_t size_type, uint64_t version, bool NotSkip = true> constexpr struct_pack::errc STRUCT_PACK_INLINE deserialize_many(auto &&first_item, auto &&...items)
            • 函数主体
          • template <size_t size_type, uint64_t version, bool NotSkip> constexpr struct_pack::errc inline deserialize_one(auto &item)
        • 模板类
        • 修饰
        • 参数
        • 函数主体

struct_pack 接口

支持序列化所有的STL容器、自定义容器和optional
对于自定义的类,目前只能序列化std::is_aggregate_v是true的类,也就是聚合.
不支持指针等的序列化,好评是支持代表所有权的unique_ptr序列化,合理
说实话,功能目前还是很简略,肯定是不如boost或者protobuf的,但是代码里面还是有很多trick的,适合填充c++模板入门到初学阶段的代码阅读量.但是想写出这样的代码,估计得c++模板良到熟练了.
注:看template代码真的折寿

序列化

struct person {int64_t id;std::string name;int age;double salary;
};

序列化对象到新字节容器

std::vector<char> result = struct_pack::serialize(person1);

序列化对象到容器尾部

std::string result="The next line is struct_pack serialize result.\n";
auto result = struct_pack::serialize_to(result,person1);

将序列化结果保存到指针指向的内存中

auto sz=struct_pack::get_needed_siarray(person1);
std::unique_ptr array=std::make_unique<char[]>(sz);
auto result = struct_pack::serialize_to(array.get(),sz,person1);

多参数序列化

序列化为一个tuple 然后搭配元组展开

auto result=struct_pack::serialize(person1.id, person1.name, person1.age, person1.salary);
//serialize as std::tuple<int64_t, std::string, int, double>

将序列化结果保存到输出流

std::ofstream writer("struct_pack_demo.data",std::ofstream::out | std::ofstream::binary);
struct_pack::serialize_to(writer, person1);

自定义类型序列化

// We should not inherit from stl container, this case just for testing.
template <typename Key, typename Value>
struct my_map : public std::map<Key, Value> {};my_map<int, std::string> map1;
map1.emplace(1, "tom");
map1.emplace(2, "jerry");absl::flat_hash_map<int, std::string> map2 ={{1, "huey"}, {2, "dewey"}, {3, "louie"},};auto buffer1 = serialize(map1);
auto buffer2 = serialize(map2);

序列化到自定义的输出流

该流需要满足以下约束条件:

template <typename T>
concept writer_t = requires(T t) {t.write((const char *)nullptr, std::size_t{}); //向流输出一段数据。返回值应能隐式转换为bool值,出错时应返回false。
};

例如:

//一个简单的输出流,对fwrite函数进行封装。
struct fwrite_stream {FILE* file;bool write(const char* data, std::size_t sz) {return fwrite(data, sz, 1, file) == 1;}fwrite_stream(const char* file_name) : file(fopen(file_name, "wb")) {}~fwrite_stream() { fclose(file); }
};
// ...
fwrite_stream writer("struct_pack_demo.data");
struct_pack::serialize_to(writer, person);

反序列化

基本反序列化

auto person2 = deserialize<person>(buffer);
assert(person2); // person2.has_value() == true
assert(person2.value()==person1);

从指针指向的内存中反序列化

auto person2 = deserialize<person>(buffer.data(),buffer.size());
assert(person2); //person2.has_value() == true
assert(person2.value()==person1);

反序列化到已有对象

person person2;
std::errc ec = deserialize_to(person2, buffer);
assert(ec==std::errc{}); // person2.has_value() == true
assert(person2==person1);

多参数反序列化

auto person2 = deserialize<int64_t,std::string,int,double>(buffer);
assert(person2); // person2.has_value() == true
auto &&[id,name,age,salary]=person2.value();
assert(person1.id==id);
assert(person1.name==name);
assert(person1.age==age);
assert(person1.salary==salary);

输入流反序列化

std::ifstream ifs("struct_pack_demo.data",std::ofstream::in | std::ofstream::binary);
auto person2 = struct_pack::deserialize<person>(ifs);
assert(person2 == person1);

部分反序列化

// 只反序列化person的第2个字段
auto name = get_field<person, 1>(buffer.data(), buffer.size());
assert(name); // name.has_value() == true
assert(name.value() == "hello struct pack");

从自定义的输入流中反序列化

该流需要满足以下约束条件:

template <typename T>
concept reader_t = requires(T t) {t.read((char *)nullptr, std::size_t{}); //从流中读取一段数据。返回值应能隐式转换为bool值,出错时应返回false。t.ignore(std::size_t{}); //从流中跳过一段数据。返回值应能隐式转换为bool值,出错时应返回false。t.tellg(); //返回一个无符号整数,代表当前的绝对读取位置
};

此外,如果该流还额外支持read_view函数,则支持对string_view的零拷贝优化。

template <typename T>
concept view_reader_t = reader_t<T> && requires(T t) {{ t.read_view(std::size_t{}) } -> std::convertible_to<const char *>;//从流中读取一段视图(零拷贝读取),返回值为该视图指向的起始位置,出错时应返回空指针。
};

示例:

//一个简单的输入流,对fread函数进行封装。
struct fread_stream {FILE* file;bool read(char* data, std::size_t sz) {return fread(data, sz, 1, file) == 1;}bool ignore(std::size_t sz) { return fseek(file, sz, SEEK_CUR) == 0; }std::size_t tellg() {//if you worry about ftell performance, just use an variable to record it.return ftell(file);}fread_stream(const char* file_name) : file(fopen(file_name, "rb")) {}~fread_stream() { fclose(file); }
};//...fread_stream ifs("struct_pack_demo.data");
auto person2 = struct_pack::deserialize<person>(ifs);
assert(person2 == person);

struct_pack 代码分析

从示例开始 最简单的序列化

从一个最基本的序列化代码开始

struct person {int64_t id;std::string name;int age;double salary;
};person person1{.id = 1, .name = "hello struct pack", .age = 20, .salary = 1024.42};
std::vector<char> result = struct_pack::serialize(person1);

函数调用

struct_pack::serialize(person1);

模板类:

  1. buffer 默认是std::vector<char>
  2. serialize_config conf 配置 结构体封装的enum class 有三种类型{ automatic, disable, enable };默认是automatic
  3. typename... Args变长模板参数:读取传入的参数

修饰:

  1. [[nodiscard]] 返回值不可丢弃
  2. #define STRUCT_PACK_INLINE __attribute__((always_inline)) inline 总是内联

参数:

  1. 需要序列化的参数的常引用const Args &...args

函数主体:

template <detail::struct_pack_buffer Buffer = std::vector<char>,serialize_config conf = serialize_config{}, typename... Args>
[[nodiscard]] STRUCT_PACK_INLINE Buffer serialize(const Args &...args) {static_assert(sizeof...(args) > 0);//变长参数需要>0Buffer buffer;//返回的bufferserialize_to<conf>(buffer, args...);//调用更广的处理函数,即在buffer后添加 return buffer;
}

serialize_to(Buffer &buffer, const Args &…args)

模板类:

  1. serialize_config conf 配置 结构体封装的enum class 有三种类型{ automatic, disable, enable };默认是automatic
  2. detail::struct_pack_buffer Buffer 符合约束条件的buffer 有两个concept trivially_copyable_container:一个是对容器的约束:
    连续容器continuous_container
//判断是否为连续容器template <typename Type>concept continuous_container =string<Type> || (container<Type> && requires(Type container) {std::span{container};});

是否可以序列化is_trivial_serializable

  template <typename T, bool ignore_compatible_field = false>struct is_trivial_serializable {private:static constexpr bool solve() {if constexpr (is_compatible_v<T> || trivial_view<T>) {return ignore_compatible_field;}else if constexpr (std::is_enum_v<T> || std::is_fundamental_v<T>) {return true;}else if constexpr (array<T>) {return is_trivial_serializable<typename T::value_type,ignore_compatible_field>::value;}else if constexpr (c_array<T>) {return is_trivial_serializable<typename std::remove_all_extents<T>::type,ignore_compatible_field>::value;}else if constexpr (!pair<T> && tuple<T> && !is_trivial_tuple<T>) {return false;}else if constexpr (container<T> || optional<T> || variant<T> ||unique_ptr<T> || expected<T> || container_adapter<T> ||varint_t<T>) {return false;}else if constexpr (pair<T>) {return is_trivial_serializable<typename T::first_type,ignore_compatible_field>::value &&is_trivial_serializable<typename T::second_type,ignore_compatible_field>::value;}else if constexpr (is_trivial_tuple<T>) {return []<std::size_t... I>(std::index_sequence<I...>)CONSTEXPR_INLINE_LAMBDA {return (is_trivial_serializable<std::tuple_element_t<I, T>,ignore_compatible_field>::value &&...);}(std::make_index_sequence<std::tuple_size_v<T>>{});}else if constexpr (std::is_class_v<T>) {using T_ = decltype(get_types<T>());return []<std::size_t... I>(std::index_sequence<I...>)CONSTEXPR_INLINE_LAMBDA {return (is_trivial_serializable<std::tuple_element_t<I, T_>,ignore_compatible_field>::value &&...);}(std::make_index_sequence<std::tuple_size_v<T_>>{});}elsereturn false;}public:static inline constexpr bool value = is_trivial_serializable::solve();};

一个是对容器内容物的约束,第二个从concept,容器的内容是否是需要的byte类型 只接受三种 char,unsigned char, std::byte.

template <typename T>
concept struct_pack_byte = std::is_same_v<char, T>|| std::is_same_v<unsigned char, T>|| std::is_same_v<std::byte, T>;
  1. typename... Args变长模板参数:读取传入的参数

修饰:

  1. [[nodiscard]] 返回值不可丢弃
  2. #define STRUCT_PACK_INLINE __attribute__((always_inline)) inline 总是内联

参数:

  1. buffer需要附加到尾部的buffer
  2. 需要序列化的参数的常引用const Args &...args

函数主体:

template <serialize_config conf = serialize_config{},detail::struct_pack_buffer Buffer, typename... Args>
void STRUCT_PACK_INLINE serialize_to(Buffer &buffer, const Args &...args) {
//输入需要附加的容器和需要序列化的内容static_assert(sizeof...(args) > 0);//合法性判断auto data_offset = buffer.size();//得到offset,即从哪个index开始填auto info = detail::get_serialize_runtime_info<conf>(args...);//得到用conf这个配置和需要序列化的args的一些数据,比如下面要用的info.size()是序列化后占用char的大小auto total = data_offset + info.size();//得到总得bufferSizebuffer.resize(total);//重置buffer大小auto writer =struct_pack::detail::memory_writer{(char *)buffer.data() + data_offset};//得到写类 里面有一个成员变量char* buffer和方法write(char* src,size_t len) 把从src长度为len的数据memcpy到buffer处.且buffer会移动到写之后的位置.struct_pack::detail::serialize_to<conf>(writer, info, args...);//把序列化结果写入
}

detail::get_serialize_runtime_info(const Args &…args)

得到运行时的数据

模板类

  1. serialize_config conf 配置,见前文描述
  2. typename... Args需要序列化的代码.

修饰

  1. [[nodiscard]] 返回值不可丢弃
  2. #define STRUCT_PACK_INLINE __attribute__((always_inline)) inline 总是内联
  3. constexpr

参数

  1. 返回serialize_buffer_size主要是存长度(对象序列化后的总长度)和metaInfo_
struct serialize_buffer_size {private:std::size_t len_;unsigned char metainfo_;public:constexpr serialize_buffer_size() : len_(sizeof(uint32_t)), metainfo_(0) {}constexpr std::size_t size() const { return len_; }constexpr unsigned char metainfo() const { return metainfo_; }constexpr operator std::size_t() const { return len_; }template <serialize_config conf, typename... Args>friend STRUCT_PACK_INLINE constexpr serialize_buffer_sizestruct_pack::detail::get_serialize_runtime_info(const Args &...args);
};
  1. 需要序列化的对象们const Args &...args.

函数主体

template <serialize_config conf, typename... Args>
[[nodiscard]] STRUCT_PACK_INLINE constexpr serialize_buffer_size
get_serialize_runtime_info(const Args &...args) {using Type = get_args_type<Args...>;//得到需要序列化的对象们的类型constexpr bool has_compatible = serialize_static_config<Type>::has_compatible;//看看类型是否是兼容类型constexpr bool has_type_literal = check_if_add_type_literal<conf, Type>();//看看是否要添加对于类型的描述serialize_buffer_size ret;//返回值auto sz_info = calculate_payload_size(args...);//计算序列化负载的长度//sz_info.max_size存的是最大的单个元素的大小if (sz_info.max_size < (int64_t{1} << 8)) [[likely]] {ret.len_ += sz_info.total + sz_info.size_cnt;ret.metainfo_ = 0b00000;constexpr bool has_compile_time_determined_meta_info =has_compatible || has_type_literal;if constexpr (has_compile_time_determined_meta_info) {ret.len_ += sizeof(unsigned char);}}else {if (sz_info.max_size < (int64_t{1} << 16)) {ret.len_ += sz_info.total + sz_info.size_cnt * 2;ret.metainfo_ = 0b01000;}else if (sz_info.max_size < (int64_t{1} << 32)) {ret.len_ += sz_info.total + sz_info.size_cnt * 4;ret.metainfo_ = 0b10000;}else {ret.len_ += sz_info.total + sz_info.size_cnt * 8;ret.metainfo_ = 0b11000;}// size_type >= 1 , has metainforet.len_ += sizeof(unsigned char);}if constexpr (has_type_literal) {constexpr auto type_literal = struct_pack::get_type_literal<Args...>();// struct_pack::get_type_literal<Args...>().size() crash in clang13. Bug?ret.len_ += type_literal.size() + 1;ret.metainfo_ |= 0b100;}if constexpr (has_compatible) {  // calculate bytes count of serialize// lengthif (ret.len_ + 2 < (int64_t{1} << 16)) [[likely]] {ret.len_ += 2;ret.metainfo_ |= 0b01;}else if (ret.len_ + 4 < (int64_t{1} << 32)) {ret.len_ += 4;ret.metainfo_ |= 0b10;}else {ret.len_ += 8;ret.metainfo_ |= 0b11;}}return ret;
}

get_args_type <Args…>

函数主体

变长参数长度为1时,返回用Args组成的tuple的第0个的类型,即该arg的类型,否则返回用Args组成的tuple的类型

template <typename... Args>
using get_args_type =typename std::conditional<sizeof...(Args) == 1,std::tuple_element_t<0, std::tuple<Args...>>,std::tuple<Args...>>::type;

get_types

得到U的struct_pack需要的类型

函数主体

template <typename U>
constexpr auto get_types() {using T = std::remove_cvref_t<U>;//去类型修饰if constexpr (std::is_fundamental_v<T> || std::is_enum_v<T> || varint_t<T> ||std::is_same_v<std::string, T> || container<T> || optional<T> ||unique_ptr<T> || variant<T> || expected<T> || array<T> ||c_array<T> || std::is_same_v<std::monostate, T>) {return declval<std::tuple<T>>();//如果是基础类型或别的,封装到tuple里,返回一个该类型的右值引用(有引用折叠)}else if constexpr (tuple<T>) {return declval<T>();//本来就是元组,返回一个该类型的右值引用(有引用折叠)}else if constexpr (is_trivial_tuple<T>) {//似乎不会进入这个了,is_trivial_tuple永远为falsereturn declval<T>();}else if constexpr (pair<T>) {//pair也拆进tuple里return declval<std::tuple<typename T::first_type, typename T::second_type>>();}else if constexpr (std::is_aggregate_v<T>) {//是否是聚合类型// clang-format offreturn visit_members(declval<T>(), []<typename... Args>(Args &&...) constexpr {return declval<std::tuple<std::remove_cvref_t<Args>...>>();//如果是,每一个都去修饰});// clang-format on}else {static_assert(!sizeof(T), "the type is not supported!");//否则,说明类型不受支持}
}

check_if_add_type_literal< conf>(args…)

函数主体

根据类型判断要不要加类型描述

template <serialize_config conf, typename T>
constexpr bool check_if_add_type_literal() {if constexpr (conf.add_type_info == type_info_config::automatic) {if constexpr (enable_type_info<T> == type_info_config::automatic) {return serialize_static_config<T>::has_type_literal;}else {return enable_type_info<T> == type_info_config::enable;}}else {return conf.add_type_info == type_info_config::enable;}
}

calculate_payload_size

计算序列化后的大小,dfs计算

函数主体

template <typename T, typename... Args>
constexpr size_info STRUCT_PACK_INLINE
calculate_payload_size(const T &item, const Args &...items) {if constexpr (sizeof...(items))//输入多个参数时,看看第一个和后面的,假如后面的有,则递归求解return calculate_one_size(item) + calculate_payload_size(items...);elsereturn calculate_one_size(item);//只剩一个的时候直接求
}

calculate_one_size

计算一个item的大小

函数主体

template <typename T>
constexpr size_info inline calculate_one_size(const T &item) {constexpr auto id = get_type_id<std::remove_cvref_t<T>>();//得到类型的类型id,几个简单的例子,通用的class和struct都是struct_pack::detail::type_id::trivial_class_tstatic_assert(id != detail::type_id::type_end_flag);//没有对应的id则不合法using type = std::remove_cvref_t<decltype(item)>;//得到item的typestatic_assert(!std::is_pointer_v<type>);//目前不能处理指针size_info ret{.total = 0, .size_cnt = 0, .max_size = 0};//初始化返回值//按类型分类计算if constexpr (id == type_id::monostate_t) {}else if constexpr (std::is_fundamental_v<type> || std::is_enum_v<type>) {ret.total = sizeof(type);//基础类型直接算}else if constexpr (detail::varint_t<type>) {ret.total = detail::calculate_varint_size(item);//varint用函数算}else if constexpr (id == type_id::array_t) {//array看看能不能加,不能则一个一个算if constexpr (is_trivial_serializable<type>::value) {ret.total = sizeof(type);}else {for (auto &i : item) {ret += calculate_one_size(i);//进入类型,逐个算里面的类型}}}else if constexpr (container<type>) {//如果是容器,则按容器的方式计算ret.size_cnt += 1;ret.max_size = (std::max)(ret.max_size, item.size());if constexpr (trivially_copyable_container<type>) {//简单容器直接乘using value_type = typename type::value_type;ret.total = item.size() * sizeof(value_type);}else {for (auto &&i : item) {ret += calculate_one_size(i);//一个一个算}}}else if constexpr (container_adapter<type>) {//如果是容器适配器类型,不支持...确实,主要是,序列化好办,反序列化怎么办static_assert(!sizeof(type), "the container adapter type is not supported");}else if constexpr (!pair<type> && tuple<type> && !is_trivial_tuple<type>) {//如果里面还是个元组,则递归调用算元组的那个函数算std::apply(//std::apply对tuple之类的做元组展开,这里是把item展开填到auto &&...items的位置,已知item是个元组,items里面是元组的元素.[&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {ret += calculate_payload_size(items...);},item);}else if constexpr (optional<type> || unique_ptr<type>) {//如果是optional或者unique_ptr  你看,拥有所有权的时候,就可以做序列化了ret.total = sizeof(char);if (item) {ret += calculate_one_size(*item);}}else if constexpr (variant<type>) {ret.total = sizeof(uint8_t);ret += std::visit(//std::visit用于遍历variant中的所有对象 visit还可以配合lambda的overload{lambda1,lambda2}来进行最优选择[](const auto &e) {return calculate_one_size(e);},item);}else if constexpr (expected<type>) {//用于expectedret.total = sizeof(bool);if (item.has_value()) {if constexpr (!std::is_same_v<typename type::value_type, void>)ret += calculate_one_size(item.value());}else {ret += calculate_one_size(item.error());}}else if constexpr (std::is_class_v<type>) {if constexpr (!pair<type> && !is_trivial_tuple<type>)static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>);if constexpr (is_trivial_serializable<type>::value) {ret.total = sizeof(type);}else if constexpr (is_trivial_serializable<type, true>::value) {visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {ret += calculate_payload_size(items...);ret.total += align::total_padding_size<type>;});}else {//对于item里的每一个元素调用后面这个lambda 由于这个visit_member,现在struct_pack的class里最多有64个成员变量visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {ret += calculate_payload_size(items...);});}}else {static_assert(!sizeof(type), "the type is not supported yet");}return ret;
}template <trivial_view T>
constexpr size_info inline calculate_one_size(const T &item) {return calculate_one_size(item.get());
}

get_type_id

得到类型的id 建立类型和类型id的映射关系 这里似乎可以优化为统一返回值的形式(如果模板元支持的话…).

函数主体

template <typename T>
consteval type_id get_type_id() {static_assert(CHAR_BIT == 8);// compatible member, which should be ignored in MD5 calculated.if constexpr (optional<T> && is_compatible<T>) {return type_id::compatible_t;}else if constexpr (std::is_enum_v<T>) {return get_integral_type<std::underlying_type_t<T>>();}else if constexpr (std::is_integral_v<T>) {return get_integral_type<T>();}else if constexpr (std::is_floating_point_v<T>) {return get_floating_point_type<T>();}else if constexpr (detail::varint_t<T>) {return get_varint_type<T>();}else if constexpr (std::is_same_v<T, std::monostate> ||std::is_same_v<T, void>) {return type_id::monostate_t;}else if constexpr (string<T>) {return type_id::string_t;}else if constexpr (array<T> || c_array<T> || static_span<T>) {return type_id::array_t;}else if constexpr (map_container<T>) {return type_id::map_container_t;}else if constexpr (set_container<T>) {return type_id::set_container_t;}else if constexpr (container<T>) {return type_id::container_t;}else if constexpr (optional<T> || unique_ptr<T>) {return type_id::optional_t;}else if constexpr (variant<T>) {static_assert(std::variant_size_v<T> > 1 ||(std::variant_size_v<T> == 1 &&!std::is_same_v<std::variant_alternative_t<0, T>, std::monostate>),"The variant should contain's at least one type!");static_assert(std::variant_size_v<T> < 256, "The variant is too complex!");return type_id::variant_t;}else if constexpr (expected<T>) {return type_id::expected_t;}else if constexpr (is_trivial_tuple<T> || pair<T>) {return type_id::trivial_class_t;}else if constexpr (tuple<T>) {return type_id::non_trivial_class_t;}else if constexpr (std::is_class_v<T>) {static_assert(std::is_aggregate_v<std::remove_cvref_t<T>>);return type_id::trivial_class_t;}else {static_assert(!sizeof(T), "not supported type");}
}

serialize_to(Writer &writer, const serialize_buffer_size &info,const Args &…args)

write指明往哪个char buffer里写,info是要写的长度信息,args是要序列化的对象们

模板类

修饰

参数

函数主体

template <serialize_config conf = serialize_config{},struct_pack::writer_t Writer, typename... Args>
STRUCT_PACK_MAY_INLINE void serialize_to(Writer &writer,const serialize_buffer_size &info,const Args &...args) {static_assert(sizeof...(args) > 0);//合法性检查detail::packer<Writer, detail::get_args_type<Args...>> o(writer, info);//实际负责序列化switch ((info.metainfo() & 0b11000) >> 3) {case 0:o.template serialize<conf, 1>(args...);//调用序列化函数break;
#ifdef STRUCT_PACK_OPTIMIZEcase 1:o.template serialize<conf, 2>(args...);break;case 2:o.template serialize<conf, 4>(args...);break;case 3:o.template serialize<conf, 8>(args...);break;
#elsecase 1:case 2:case 3:o.template serialize<conf, 2>(args...);break;
#endifdefault:detail::unreachable();break;};
}

class packer

实际负责序列化的类

成员变量

都是构造时传入的

  1. writer &writer_; 往哪写
  2. const serialize_buffer_size &info;长度数据

public方法

void serialize(const T &t, const Args &…args)
函数主体
  template <serialize_config conf, std::size_t size_type, typename T,typename... Args>STRUCT_PACK_INLINE void serialize(const T &t, const Args &...args) {serialize_metainfo<conf, size_type == 1, T, Args...>();serialize_many<size_type, UINT64_MAX>(t, args...);using Type = get_args_type<T, Args...>;if constexpr (serialize_static_config<Type>::has_compatible) {constexpr std::size_t sz = compatible_version_number<Type>.size();[&]<std::size_t... I>(std::index_sequence<I...>) {(serialize_many<size_type, compatible_version_number<Type>[I]>(t,args...),...);}(std::make_index_sequence<sz>{});}}
void STRUCT_PACK_INLINE serialize_metainfo()
函数主体
  template <serialize_config conf, bool is_default_size_type, typename T,typename... Args>constexpr void STRUCT_PACK_INLINE serialize_metainfo() {constexpr auto hash_head = calculate_hash_head<conf, T, Args...>() |(is_default_size_type ? 0 : 1);//hash得到一个头writer_.write((char *)&hash_head, sizeof(uint32_t));//写hash的结果if constexpr (hash_head % 2) {  // has more metainfo auto metainfo = info.metainfo();writer_.write((char *)&metainfo, sizeof(char));//写 meta infoif constexpr (serialize_static_config<serialize_type>::has_compatible) {constexpr std::size_t sz[] = {0, 2, 4, 8};auto len_size = sz[metainfo & 0b11];auto len = info.size();writer_.write((char *)&len, len_size);}if constexpr (check_if_add_type_literal<conf, serialize_type>()) {constexpr auto type_literal =struct_pack::get_type_literal<T, Args...>();writer_.write(type_literal.data(), type_literal.size() + 1);}}}

最简单的反序列化

反序列化的内容result被抽象为view,需要满足有data() 和size()两个方法的容器

//expect<对象,error> 
auto person2 = struct_pack::deserialize<person>(result);

auto deserialize(const View &v)

把符合一定条件的类抽象成view类,view类满足一般的连续容器条件.

模板类

  1. T要反序列化成的类型
  2. typename... Args参数
  3. detail::deserialize_view View满足一定条件的view

函数主体

template <typename T, typename... Args, detail::deserialize_view View>//只有一个元素时,Args为空这里可以传入多个类
[[nodiscard]] STRUCT_PACK_INLINE auto deserialize(const View &v) {expected<detail::get_args_type<T, Args...>, struct_pack::errc> ret;//返回值,类或错误的expect,如果传入的是多个类,参见detail::get_args_type<T, Args...>,将得到元组,传入单个参数得到这个类的类型if (auto errc = deserialize_to(ret.value(), v); errc != struct_pack::errc{})//用更基础的函数deserialize_to[[unlikely]] {ret = unexpected<struct_pack::errc>{errc};//非空则有异常}return ret;
}

template <typename T, typename… Args, detail::deserialize_view View>[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to( T &t, const View &v, Args &…args)

函数主体

template <typename T, typename... Args, detail::deserialize_view View>
[[nodiscard]] STRUCT_PACK_INLINE struct_pack::errc deserialize_to(T &t, const View &v, Args &...args) {//T&t写对象的位置 View &v反序列化的容器detail::memory_reader reader{(const char *)v.data(),(const char *)v.data() + v.size()};//构造读取器 限定读写内存边界detail::unpacker in(reader);//用读取器初始化反序列化类return in.deserialize(t, args...);//反序列化
}

class unpacker

用于反序列化的类

public方法

template <typename T, typename… Args> STRUCT_PACK_MAY_INLINE struct_pack::errc deserialize(T &t, Args &…args)
代码
  template <typename T, typename... Args>STRUCT_PACK_MAY_INLINE struct_pack::errc deserialize(T &t, Args &...args) {using Type = get_args_type<T, Args...>;//单个的得到类型,多个的得到tupleconstexpr bool has_compatible =check_if_compatible_element_exist<decltype(get_types<Type>())>();//是否满足兼容性if constexpr (has_compatible) {data_len_ = reader_.tellg();}auto &&[err_code, buffer_len] = deserialize_metainfo<Type>();if (err_code != struct_pack::errc{}) [[unlikely]] {return err_code;}if constexpr (has_compatible) {data_len_ += buffer_len;}switch (size_type_) {case 0:err_code = deserialize_many<1, UINT64_MAX>(t, args...);break;
#ifdef STRUCT_PACK_OPTIMIZEcase 1:err_code = deserialize_many<2, UINT64_MAX>(t, args...);break;case 2:err_code = deserialize_many<4, UINT64_MAX>(t, args...);break;case 3:err_code = deserialize_many<8, UINT64_MAX>(t, args...);break;
#elsecase 1:case 2:case 3:err_code = deserialize_many<2, UINT64_MAX>(t, args...);break;
#endifdefault:unreachable();}if constexpr (has_compatible) {if (err_code != errc::ok) [[unlikely]] {return err_code;}constexpr std::size_t sz = compatible_version_number<Type>.size();err_code = deserialize_compatibles<T, Args...>(t, args..., std::make_index_sequence<sz>{});}return err_code;}
template <size_t size_type, uint64_t version, bool NotSkip = true> constexpr struct_pack::errc STRUCT_PACK_INLINE deserialize_many(auto &&first_item, auto &&…items)
函数主体
  template <size_t size_type, uint64_t version, bool NotSkip = true>constexpr struct_pack::errc STRUCT_PACK_INLINEdeserialize_many(auto &&first_item, auto &&...items) {auto code = deserialize_one<size_type, version, NotSkip>(first_item);if (code != struct_pack::errc{}) [[unlikely]] {return code;}return deserialize_many<size_type, version, NotSkip>(items...);}
template <size_t size_type, uint64_t version, bool NotSkip> constexpr struct_pack::errc inline deserialize_one(auto &item)

模板类

修饰

参数

函数主体

  template <size_t size_type, uint64_t version, bool NotSkip>constexpr struct_pack::errc inline deserialize_one(auto &item) {struct_pack::errc code{};//错误码using type = std::remove_cvref_t<decltype(item)>;//拿去修饰的类型static_assert(!std::is_pointer_v<type>);//不能是指针constexpr auto id = get_type_id<type>();//由类型得到类型的idif constexpr (version == UINT64_MAX) {//判断版本,目前似乎是写死的if constexpr (id == type_id::compatible_t) {//逐个判断类型// do nothing}else if constexpr (std::is_same_v<type, std::monostate>) {// do nothing}else if constexpr (std::is_fundamental_v<type> || std::is_enum_v<type>) {//基础类型,读一个大小直接塞到item里if constexpr (NotSkip) {if (!reader_.read((char *)&item, sizeof(type))) [[unlikely]] {return struct_pack::errc::no_buffer_space;}}else {return reader_.ignore(sizeof(type)) ? errc{} : errc::no_buffer_space;}}else if constexpr (detail::varint_t<type>) {code = detail::deserialize_varint<NotSkip>(reader_, item);}else if constexpr (id == type_id::array_t) {if constexpr (is_trivial_serializable<type>::value) {if constexpr (NotSkip) {if (!reader_.read((char *)&item, sizeof(type))) [[unlikely]] {return struct_pack::errc::no_buffer_space;}}else {return reader_.ignore(sizeof(type)) ? errc{}: errc::no_buffer_space;}}else {for (auto &i : item) {code = deserialize_one<size_type, version, NotSkip>(i);if (code != struct_pack::errc{}) [[unlikely]] {return code;}}}}else if constexpr (container<type>) {size_t size = 0;
#ifdef STRUCT_PACK_OPTIMIZEif (!reader_.read((char *)&size, size_type)) [[unlikely]] {return struct_pack::errc::no_buffer_space;}
#elseif constexpr (size_type == 1) {if (!reader_.read((char *)&size, size_type)) [[unlikely]] {return struct_pack::errc::no_buffer_space;}}else {switch (size_type_) {case 1:if (!reader_.read((char *)&size, 2)) [[unlikely]] {return struct_pack::errc::no_buffer_space;}break;case 2:if (!reader_.read((char *)&size, 4)) [[unlikely]] {return struct_pack::errc::no_buffer_space;}break;case 3:if (!reader_.read((char *)&size, 8)) [[unlikely]] {return struct_pack::errc::no_buffer_space;}break;default:unreachable();}}
#endifif (size == 0) [[unlikely]] {return {};}if constexpr (map_container<type> || set_container<type>) {// value is the element of map/set container.// if the type is set, then value is set::value_type;// if the type is map, then value is pair<key_type,mapped_type>decltype([]<typename T>(const T &) {if constexpr (map_container<T>) {return std::pair<typename T::key_type, typename T::mapped_type>{};}else {return typename T::value_type{};}}(item)) value;if constexpr (is_trivial_serializable<decltype(value)>::value &&!NotSkip) {return reader_.ignore(size * sizeof(value)) ? errc{}: errc::no_buffer_space;}else {for (size_t i = 0; i < size; ++i) {code = deserialize_one<size_type, version, NotSkip>(value);if (code != struct_pack::errc{}) [[unlikely]] {return code;}if constexpr (NotSkip) {item.emplace(std::move(value));// TODO: mapped_type can deserialize without be moved}}}}else {using value_type = typename type::value_type;if constexpr (trivially_copyable_container<type>) {size_t mem_sz = size * sizeof(value_type);if constexpr (NotSkip) {if constexpr (string_view<type> || dynamic_span<type>) {static_assert(view_reader_t<Reader>,"The Reader isn't a view_reader, can't deserialize ""a string_view/span");const char *view = reader_.read_view(mem_sz);if (view == nullptr) [[unlikely]] {return struct_pack::errc::no_buffer_space;}item = {(value_type *)(view), size};}else {if (mem_sz >= PTRDIFF_MAX) [[unlikely]]unreachable();else {item.resize(size);if (!reader_.read((char *)item.data(), mem_sz)) [[unlikely]] {return struct_pack::errc::no_buffer_space;}}}}else {return reader_.ignore(mem_sz) ? errc{} : errc::no_buffer_space;}}else {if constexpr (NotSkip) {if constexpr (dynamic_span<type>) {static_assert(!dynamic_span<type>,"It's illegal to deserialize a span<T> which T ""is a non-trival-serializable type.");}else {item.resize(size);for (auto &i : item) {code = deserialize_one<size_type, version, NotSkip>(i);if (code != struct_pack::errc{}) [[unlikely]] {return code;}}}}else {value_type useless;for (size_t i = 0; i < size; ++i) {code = deserialize_one<size_type, version, NotSkip>(useless);if (code != struct_pack::errc{}) [[unlikely]] {return code;}}}}}}else if constexpr (container_adapter<type>) {static_assert(!sizeof(type),"the container adapter type is not supported");}else if constexpr (!pair<type> && tuple<type> &&!is_trivial_tuple<type>) {std::apply([&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {code = deserialize_many<size_type, version, NotSkip>(items...);},item);}else if constexpr (optional<type> || expected<type>) {bool has_value;if (!reader_.read((char *)&has_value, sizeof(bool))) [[unlikely]] {return struct_pack::errc::no_buffer_space;}if (!has_value) [[unlikely]] {if constexpr (expected<type>) {item = typename type::unexpected_type{typename type::error_type{}};deserialize_one<size_type, version, NotSkip>(item.error());}else {return {};}}else {if constexpr (expected<type>) {if constexpr (!std::is_same_v<typename type::value_type, void>)deserialize_one<size_type, version, NotSkip>(item.value());}else {item = type{std::in_place_t{}};deserialize_one<size_type, version, NotSkip>(*item);}}}else if constexpr (variant<type>) {uint8_t index;if (!reader_.read((char *)&index, sizeof(index))) [[unlikely]] {return struct_pack::errc::no_buffer_space;}if (index >= std::variant_size_v<type>) [[unlikely]] {return struct_pack::errc::invalid_buffer;}else {template_switch<variant_construct_helper,std::integral_constant<std::size_t, size_type>,std::integral_constant<std::uint64_t, version>,std::integral_constant<bool, NotSkip>>(index, *this,item);}}else if constexpr (std::is_class_v<type>) {//如果是类if constexpr (!pair<type> && !is_trivial_tuple<type>)//如果是元组static_assert(std::is_aggregate_v<std::remove_cvref_t<type>>);if constexpr (is_trivial_serializable<type>::value) {//简单线性if constexpr (NotSkip) {if (!reader_.read((char *)&item, sizeof(type))) [[unlikely]] {return struct_pack::errc::no_buffer_space;}}else {return reader_.ignore(sizeof(type)) ? errc{}: errc::no_buffer_space;}}else if constexpr (is_trivial_serializable<type, true>::value) {visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {int i = 1;[[maybe_unused]] bool op =(([&]() {if (code = deserialize_one<size_type, version, NotSkip>(items);code == errc::ok) [[likely]] {code = ignore_padding(align::padding_size<type>[i]);++i;}}(),code == errc::ok) &&...);});}else {//逐个访问元素 假定每个子元素还是多个的进行访问,单个的最终会被deserialize_one接收.visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {code = deserialize_many<size_type, version, NotSkip>(items...);});}}else {static_assert(!sizeof(type), "the type is not supported yet");}}else if constexpr (exist_compatible_member<type, version>) {if constexpr (id == type_id::compatible_t) {if constexpr (version == type::version_number) {auto pos = reader_.tellg();if ((std::size_t)pos >= data_len_) {if (std::is_unsigned_v<decltype(pos)> || pos >= 0) {size_type_ = UCHAR_MAX;  // Just notice that this is not a real// error, this is a flag for exit.}return struct_pack::errc::no_buffer_space;}bool has_value;if (!reader_.read((char *)&has_value, sizeof(bool))) [[unlikely]] {return struct_pack::errc::no_buffer_space;}if (!has_value) {return code;}else {item = type{std::in_place_t{}};deserialize_one<size_type, UINT64_MAX, NotSkip>(*item);}}}else if constexpr (id == type_id::array_t) {for (auto &i : item) {code = deserialize_one<size_type, version, NotSkip>(i);if (code != struct_pack::errc{}) [[unlikely]] {return code;}}}else if constexpr (container<type>) {if constexpr (id == type_id::set_container_t) {// TODO: support it.static_assert(!sizeof(type),"we don't support compatible field in set now.");}else if constexpr (id == type_id::map_container_t) {static_assert(!exist_compatible_member<typename type::key_type>,"we don't support compatible field in map's key_type now.");if constexpr (NotSkip) {for (auto &e : item) {code = deserialize_one<size_type, version, NotSkip>(e.second);if (code != struct_pack::errc{}) [[unlikely]] {return code;}}}else {// TODO: support it.static_assert(!sizeof(type),"we don't support skip compatible field in container now.");}// how to deserialize it quickly?}else {if constexpr (NotSkip) {for (auto &i : item) {code = deserialize_one<size_type, version, NotSkip>(i);if (code != struct_pack::errc{}) [[unlikely]] {return code;}}}else {// TODO: support it.static_assert(!sizeof(type),"we don't support skip compatible field in container now.");}}}else if constexpr (!pair<type> && tuple<type> &&!is_trivial_tuple<type>) {std::apply([&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {code = deserialize_many<size_type, version, NotSkip>(items...);},item);}else if constexpr (optional<type> || expected<type>) {bool has_value = item.has_value();if (!has_value) {if constexpr (expected<type>) {deserialize_one<size_type, version, NotSkip>(item.error());}}else {if constexpr (expected<type>) {if constexpr (!std::is_same_v<typename type::value_type, void>)deserialize_one<size_type, version, NotSkip>(item.value());}else {deserialize_one<size_type, version, NotSkip>(item.value());}}}else if constexpr (variant<type>) {std::visit([this](auto &item) {deserialize_one<size_type, version, NotSkip>(item);},item);}else if constexpr (std::is_class_v<type>) {visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {code = deserialize_many<size_type, version, NotSkip>(items...);});}}return code;}

相关文章:

alibaba yalantingLibs struct_pack代码梳理

这里写目录标题 struct_pack 接口序列化序列化对象到新字节容器序列化对象到容器尾部将序列化结果保存到指针指向的内存中多参数序列化将序列化结果保存到输出流自定义类型序列化序列化到自定义的输出流 反序列化基本反序列化从指针指向的内存中反序列化反序列化到已有对象多参…...

JavaWeb( 二 ) URL

1.4.URL统一资源定位符 URL代表Uniform Resource Locator 统一资源定位符&#xff0c;也叫 URL地址 。是用于标识和定位Web上资源的地址&#xff0c;通常用于在Web浏览器中访问网站和文件。 URL由若干部分组成&#xff0c;scheme:// host : port / path 例如&#xff1a; htt…...

Python斐波那契数列

斐波那契数列是一个经典的数学问题&#xff0c;在 Python 中可以使用多种方法来实现&#xff0c;下面是几个常见的实现方式&#xff1a; 1. 使用递归 python def fibonacci_recursive(n): if n < 1: return n else: return fibonacci_recursive(n…...

华为OD机试 - 模拟商场优惠打折(Python)

题目描述 模拟商场优惠打折,有三种优惠券可以用,满减券、打折券和无门槛券。 满减券:满100减10,满200减20,满300减30,满400减40,以此类推不限制使用; 打折券:固定折扣92折,且打折之后向下取整,每次购物只能用1次; 无门槛券:一张券减5元,没有使用限制。 每个人…...

【JAVA程序设计】(C00132)基于SSM的固定资产管理系统

基于SSM的固定资产管理系统 项目简介项目获取开发环境项目技术运行截图 项目简介 本系统为基于SSM的固定资产管理系统&#xff0c;本系统分为二种用户&#xff1a;超级管理员和普通管理员&#xff1b; 超级管理员功能&#xff1a; 首页查看、设备管理、平台账户管理、设备台账…...

简单的无理函数的不定积分

前置知识&#xff1a; 直接积分法有理函数的不定积分 简单的无理函数的不定积分 对无理函数积分的基本方法就是通过换元将其化为有理函数的积分。下面讲讲几类无理函数积分的求法。 注&#xff1a; R ( u , v ) R(u,v) R(u,v)是由 u , v u,v u,v与常数经过有限次四则运算得…...

《国际联网安全保护管理办法》

1.基本信息 &#xff08;1997年12月11日国务院批准 1997年12月16日公安部令第33号发布 根据2011年1月8日《国务院关于废止和修改部分行政法规的决定》修订&#xff09; 2.办法内容 第一章 总 则 第一条为了加强对计算机信息网络国际联网的安全保护&#xff0c;维护公共…...

Redis常用命令

目录 一. 字符串string常用操作命令 二. 哈希hash常用操作命令 三. 列表list常用操作命令 四. 集合set常用操作命令 五. 有序集合sorted set常用操作命令 六. 通用命令 一. 字符串string常用操作命令 SET key value 设置指定key的值GET key 获取指定key的值 SETEX key…...

功能齐全的 DIY ESP32 智能手表设计之原理图讲解二

相关设计资料下载ESP32 智能手表带心率、指南针设计资料(包含Arduino源码+原理图+Gerber+3D文件).zip 目录 构建 ESP32 智能手表所需的组件 光照度传感器电路讲解...

烦恼的高考志愿

烦恼的高考志愿 题目背景 计算机竞赛小组的神牛 V 神终于结束了高考&#xff0c;然而作为班长的他还不能闲下来&#xff0c;班主任老 t 给了他一个艰巨的任务&#xff1a;帮同学找出最合理的大学填报方案。可是 v 神太忙了&#xff0c;身后还有一群小姑娘等着和他约会&#x…...

【地铁上的设计模式】--结构型模式:适配器模式

前面几篇文章我们学习了创建型模式&#xff0c;从本篇文章开始&#xff0c;我们将学习结构型模式。 什么是结构型模式 结构型模式是一种设计模式&#xff0c;它描述了如何将类或对象结合在一起形成更大的结构&#xff0c;以提供新的功能或实现更复杂的行为。结构型模式包括以…...

重大剧透:你不用ChatGPT,它砸你饭碗

早晨看到路透社报道&#xff0c;盖茨说&#xff0c;与其争论技术的未来&#xff0c;不如专注于如何更好地利用人工智能。 这可能是他对马斯克他们呼吁暂停AI研发6个月的一种回应吧。 有种古语说&#xff1a;天下大势&#xff0c;浩浩汤汤&#xff0c;顺之者昌&#xff0c;逆之者…...

状态机模式

状态模式 状态模式定义:使用场景角色定义1. State一抽象状态角色2. ConcreteState一-具体状态角色3. Context--环境角色 需求背景1. 订单状态抽象类2. 定义订单具体状态类并集成基类&#xff08;抽象类&#xff09;2.1 订单创建状态2.2 订单已支付状态2.3 订单已发货状态2.4 订…...

瑞吉外卖:后台系统登录功能

文章目录 需求分析代码开发创建实体类导入返回结果类Rcontroller、service与mapperlogin.html 需求分析 点击登录按钮后&#xff0c;浏览器以POST方式向employee/login提交username和password&#xff0c;服务器经过处理后向浏览器返回某种格式的数据&#xff0c;其中包含&…...

Linux拓展:链接库

一.说明 本篇博客介绍Linux操作系统下的链接库相关知识&#xff0c;由于相关概念已在Windows下链接库一文中介绍&#xff0c;本篇博客直接上操作。 二.静态链接库的创建和使用 1.提前看 这里主要介绍的是C语言的链接库技术&#xff0c;而在Linux下实现C语言程序&#xff0c…...

基于.Net开发的、支持多平台、多语言餐厅点餐系统

今天给大家推荐一套支持多平台、多语言版本的订单系统&#xff0c;适合餐厅、酒店等场景。 项目简介 这是基于.Net Framework开发的&#xff0c;支持手机、平板、PC等平台、多语言版本开源的点餐系统&#xff0c;非常适合餐厅、便利店、超市、酒店等&#xff0c;该系统基础功…...

Windows系统SSL/TLS安全协议介绍

支持安全加密的https底层使用的就是SSL/TLS,在发起https请求之前需要先建立TCP连接,之后再进行SSL/TLS协议协商,协商通过后才能发起https请求。本文将详细介绍SSL/TLS协议相关的内容。 之前在项目中就出现过客户端SSL/TLS版本过低,导致向服务器发起连接时被服务器拒绝的问题…...

ovs-vsctl 命令详解

ovs-vsctl 命令详解 网桥Bridge 创建 Bridge ovs-vsctl add-br br0 删除 Bridge ovs-vsctl del-br br0 列出 Bridge ovs-vsctl list-br 显示详情 ovs-vsctl show 端口 Port 添加端口 ovs-vsctl add-port br0 p1 其中br0 为上面添加的bridge p1可以是物理端口或者vN…...

具备“记忆”功能的VBA目录选择器

大家使用任意一款浏览器&#xff08;例如&#xff1a;Chrome、Edge&#xff09;下载文件时&#xff0c;如果【另存为】对话框选择C:\Download&#xff0c;那么下次再次使用【另存为】功能&#xff0c;对话框默认显示C:\Download&#xff0c;而不是根目录。 在VBA开发中调用目录…...

electron入门 | 手把手带electron项目初始化

Electron是一个基于Chromium和 Node.js&#xff0c;可以使用 HTML、CSS和JavaScript构建跨平台应用的技术框架&#xff0c;兼容 Mac、Windows 和 Linux。 目录 1.了解electron 2.开发环境 3.初始化 采坑插曲&#xff1a; 1.了解electron Electron 可以让你使用纯 JavaScrip…...

​力扣解法汇总2423. 删除字符使频率相同

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 给你一个下标从 0 开始的字符串 word &#xff0c;字符串只包含小写英文字母。你…...

【超算/先进计算学习】日报8

目录 今日已完成任务列表遇到的问题及解决方案任务完成详细笔记阶段一阶段二阶段三阶段四 对自己的表现是否满意简述下次计划其他反馈 今日已完成任务列表 超算/高性能计算总结 遇到的问题及解决方案 无 任务完成详细笔记 阶段一 在学习的第一阶段&#xff0c;我们首先对需要…...

《LearnUE——基础指南:上篇—2》——GamePlay架构之Level和World

目录 听说世界是由多个Level组成的 1.2.1 引言 1.2.2 建造大陆&#xff08;ULevel&#xff09; 1.2.3构建世界&#xff08;World&#xff09; 1.2.4总结 听说世界是由多个Level组成的 1.2.1 引言 上小节谈到Actor和Component的关系&#xff0c;UE利用Actor的概念组成了世…...

IDEA部署tomcat项目

文章目录 只是部署一下看到这里即可war和war exploded的区别warwar exploded update的动作update resourcesupdate classes and resourcesredeployrestart server 解决了拿到了一个tomcat项目后如何将它部署到IDEA里面的问题。 file->open 选中pom.xml并open as project …...

IAM角色

Identity-based policy&#xff0c;它关联到特定的User/Role/Group上&#xff0c;指定这些主体能对哪些资源进行怎样的操作 Resource-based policy&#xff0c;它关联到具体的AWS资源上&#xff0c;指定哪些主体可以对这个资源做怎样的操作 aws受信任关系视为aws服务可以实现&a…...

【VAR | 时间序列】以美国 GDP 和通货膨胀数据为例的VAR模型简单实战(含Python源代码)

以美国 GDP 和通货膨胀数据为例&#xff1a; 1. 数据集 下载数据我们需要从 FRED 数据库下载美国 GDP 和通货膨胀数据&#xff0c;并将它们存储在 CSV 文件中。可以在 FRED 网站&#xff08;https://fred.stlouisfed.org/&#xff09;搜索并下载需要的数据。在这里&#xff0…...

常用的设计模式之二(行为型模式)

文章目录 观察者模式模板模式 观察者模式 观察者模式是一种行为型设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象&#xff0c;当主题对象发生变化时&#xff0c;它的所有观察者都会收到通知并进行相应的处理。 观察者…...

MYSQL基本操作(增删改查)

数据库的列类型 int&#xff1a;整型 用于定义整数类型的数据 float&#xff1a;单精度浮点4字节32位 准确表示到小数点后六位 double&#xff1a;双精度浮点8字节64位 char&#xff1a;固定长度的字符类 用于定义字符类型数据&…...

双周赛103(模拟、网格图BFS、树状数组)

文章目录 双周赛103[6406. K 个元素的最大和](https://leetcode.cn/problems/maximum-sum-with-exactly-k-elements/)模拟 [6405. 找到两个数组的前缀公共数组](https://leetcode.cn/problems/find-the-prefix-common-array-of-two-arrays/)模拟 [6403. 网格图中鱼的最大数目](…...

【数据结构】二叉树(详细)

二叉树 1.树1.1定义1.2基本术语1.3树形结构和线性结构1.4树的存储结构1.4.1双亲表示法1.4.2孩子兄弟表示法 2.二叉树2.1定义2.2特殊二叉树2.3性质2.4存储结构2.4.1顺序存储2.4.2链式存储结构 3.二叉树的基本操作3.1前序遍历&#xff08;先序遍历&#xff09;3.2中序遍历3.3后序…...

南京十大软件公司排名/淘宝关键词优化技巧教程

前言&#xff1a;医院的视频监控系统如何设计呢&#xff1f;需要考虑哪些方面呢&#xff1f;如何规划呢&#xff1f;今天分享一下医院项目视频监控系统建设思路&#xff0c;仅供参考。正文&#xff1a;一、系统规划系统要求建立一个集数字视频图像采集、传输、处理、控制、指挥…...

建设厅网站平顶山/沈阳cms建站模板

❝作者介绍&#xff1a;何凯&#xff0c;百度商业平台研发部-资深研发工程师&#xff0c;主要负责商业平台创意中心前端技术研发与架构&#xff0c;专注于打造创意中心前端SDK技术架构&#xff0c;赋能业务系统创意管理、推荐、制作和加工等创意核心能力&#xff0c;持续快速迭…...

深圳做微信网站设计/河南平价的seo整站优化定制

大家好&#xff0c;最近不少小伙伴在学 Python&#xff0c;想找个好玩的练手项目。那今天分享一个简单、好玩&#xff0c;适合新手的 Python 小项目。文章较长&#xff0c;建议收藏、喜欢关注、点赞&#xff0c;文末提供技术交流群。 以下是具体项目&#xff1a; 本文将以哔哩…...

wordpress可视编辑器/网页制作教程书籍

今天学校放假了&#xff0c;去广州市区玩&#xff0c;见识见识大城市的魅力。 今天就不发代码了&#xff0c;给大家看一下广州市区的样子&#xff0c;好美啊&#xff01;...

做网站一定要买/搜狗搜索网页版

异常 原因 没有以管理员身份打开DOS窗口。 解决...

中山市企业网站建设/龙南黄页全部电话

1. Karatsuba乘法 Karatsuba 乘法是一种快速乘法。此算法在1960年由 Anatolii Alexeevitch Karatsuba 提出&#xff0c;并于1962年得以发表。此算法主要用于两个大数相乘。普通乘法的复杂度是 n&#xff0c;而 Karatsuba 算法的复杂度仅为 3nlog2(3) 2. 算法原理 可以注意到…...