#serialization #deserialize #binary-encoding #persistent-storage

savefile-derive

Savefile crate 的自定义 derive 宏 - 简单、方便、快速、版本化的二进制序列化和反序列化库

65 个版本

0.17.7 2024 年 8 月 3 日
0.17.6 2024 年 7 月 2 日
0.17.4 2024 年 5 月 12 日
0.16.5 2024 年 2 月 5 日
0.2.7 2018 年 3 月 28 日

#2306编码

Download history 740/week @ 2024-05-01 503/week @ 2024-05-08 123/week @ 2024-05-15 90/week @ 2024-05-22 152/week @ 2024-05-29 125/week @ 2024-06-05 103/week @ 2024-06-12 121/week @ 2024-06-19 224/week @ 2024-06-26 211/week @ 2024-07-03 63/week @ 2024-07-10 67/week @ 2024-07-17 216/week @ 2024-07-24 273/week @ 2024-07-31 112/week @ 2024-08-07 78/week @ 2024-08-14

688 每月下载量
9 个 crate 中使用 (7 直接)

MIT/Apache

200KB
3.5K SLoC

build

在 0.17 版本中遇到问题? - 请参阅此文档下方的升级指南!

Savefile 简介

Savefile 是一个用于轻松序列化 Rust 结构体和枚举的 crate。它使用高效的二进制格式。它可以序列化到实现了 Write 特质的任何东西,然后从实现了 Read 特质的任何东西反序列化。这意味着 savefile 可以用来轻松地将内存数据结构保存到磁盘上进行持久化存储。

文档:https://docs.rs/savefile/latest/savefile/

功能

  • 易于使用 大多数 std 数据类型都受支持,derive 宏可以用于大多数用户类型。
  • 可靠 - Savefile 拥有广泛的测试套件。
  • 向后兼容 - Savefile 支持模式版本化,具有内置的验证和详细的模式不匹配错误信息。
  • 快速 - Savefile 可以通过安全地将数据类型视为原始字节快速序列化和反序列化许多数据类型。
  • 安全 - Savefile 可以使用而无需用户编写任何不安全代码。

Savefile-Abi

Savefile-Abi 是一个相关的 crate,它允许发布向前和向后兼容的 Rust 编写的共享库(Linux 上的 .so,Windows 上的 .dll),并在 Rust 程序中使用它们作为二进制插件。

文档:https://docs.rs/savefile-abi/latest/

使用方法

Cargo.toml

savefile = "0.17"
savefile-derive = "0.17"

main.rs

extern crate savefile;
use savefile::prelude::*;

#[macro_use]
extern crate savefile_derive;


#[derive(Savefile)]
struct Player {
    name : String,
    strength : u32,
    inventory : Vec<String>,
}

fn save_player(player:&Player) {
    save_file("save.bin", 0, player).unwrap();
}

fn load_player() -> Player {
    load_file("save.bin", 0).unwrap()
}

fn main() {
    let player = Player { name: "Steve".to_string(), strength: 42,
        inventory: vec!(
            "wallet".to_string(),
            "car keys".to_string(),
            "glasses".to_string())};

    save_player(&player);

    let reloaded_player = load_player();

    assert_eq!(reloaded_player.name,"Steve".to_string());
}

有关更多信息(包括模式版本化),请参阅文档:https://docs.rs/savefile/latest/savefile/ .

更新日志

0.17.7

BTreeSet 的支持。

0.17.6

支持 std::time::Duration 和 std::time::SystemTime 的反射。

0.17.5

支持 std::time::Duration 和 std::time::SystemTime 。

0.17.4

改进了错误信息。

0.17.3

改进了某些编译失败的错误信息。

0.17.2

修复了在新 rustc 版本上的失败测试。

0.17.1

只是对文档进行了一些小的改进。

0.17.0

这是一个重大变化!随着 0.17 版本的发布,Savefile 获得了另一个主要功能:支持动态加载插件。也就是说,一种允许 Rust 代码分成不同的共享库(Linux 上的 .so,Windows 上的 .dll)并允许跨库边界调用机制。

使用此功能需要使用 crate 'savefile-abi'。正常的 savefile 使用可以像往常一样继续。

模式数据的格式已更改,但仍然向后兼容。也就是说,0.17版本的savefile仍然可以读取0.16及更早版本保存的数据。然而,0.16无法读取0.17保存的数据。

0.17.0版本中的另一件事:我们开始使用'release-plz'来管理发布。希望这会使发布更加专业,拥有正确的git标签、git发布等。

此外,0.17现在可以有时显著优化枚举序列的读取。这对那些具有#[repr(u8)]或类似标记的枚举有效,前提是这个类型不包含填充。

另一个升级是Savefile现在支持具有超过256个变体的枚举!

从0.16升级到0.17的升级指南

不幸的是,0.17是一个相当大的版本。升级将需要一些更改。

这里有一个简短的指南

1:模式已被扩展。

1.1:Schema::Vector现在接受第二个参数。只需将其设置为'VecOrStringLayout::Unknown'或VecOrStringLayout::default()

1.2:Schema::Struct的字段现在接受一个'offset'参数。始终将其设置为None。一些参数已变为私有,因此现在您需要使用'new'-函数来创建Schema::Struct的实例。这样做的原因是需要保证正确性,因为一些新的字段必须给出正确的值,以避免不正确的行为。有一个不安全函数用于初始化这些,因此它们并不是完全隐藏的。

1.3:SchemaEnum Variant的'identifier'字段已被重命名为'discriminant'(因为这是rust项目所称呼的)

1.4:SchemaEnum类型获得了'discriminant_size'字段。这是编码判别符所需的字节数。对于永远不会超过256个字段的枚举,将其设置为1。对于更大的枚举,将其设置为2。如果您需要枚举具有超过65536个字段,将其设置为4。请注意,SchemaEnum类型现在也有私有字段,并且也需要使用'new'来构造。

1.5:WithSchema::schema函数现在接受一个上下文对象。对于大多数数据类型,您可以直接传递此对象。只有智能指针、容器、Box等需要使用此对象,以防止递归。请参阅WithSchemaContext的文档。

'ReprC'-trait已被重命名为'Packed'。在其他方面完全相同。升级只需进行简单的搜索和替换。

现在,几个savefile trait实现已获得了'static-bounds'。例如,Box、Vec等现在需要T:'static。之前没有这样的限制,但由于引用不能反序列化,因此通常不可能反序列化包含引用的任何内容。

结果发现,序列化具有生命周期的对象是有用例的:像Cow这样的东西很有用。由于Deserialize trait的定义(返回值不可能有其他生命周期),实际上所有反序列化生成的对象都必须具有'static'生命周期。

使用生命周期序列化事物仍然是可能的,唯一需要'static'的地方是像Box、Vec等容器的内容。这是因为新的递归支持需要能够创建TypeIds,而这仅适用于具有'static'生命周期的对象。

0.16.5

对savefile-derive进行了一些小的更改,以避免在Rust RFC 3373发布时触发警告。有关更多信息,请参阅错误编号#36 https://github.com/avl/savefile/issues/36

0.16.4

支持boxed slices。即,Savefile现在可以序列化类型为Box<[T]>的数据。

0.16.3

对测试套件进行了一些修复,这是为了使github actions CI工作。

0.16.2

修复了多个问题。最引人注目的是,0.16.1只能使用nightly rust编译器构建。

它还提供了一个可选功能,将 'savefile-derive' 集成到 'savefile' 中。

只需激活 'derive' 功能,您就可以使用 savefile 而不必显式依赖 'savefile-derive'。只需这样做:

use savefile::prelude::*;

#[derive(Savefile)]
struct MyStruct { 
    //...    
}

然后您就可以正常使用了!

0.16.1

修复了一个小问题,其中 #[savefile_introspect_ignore]#[derive(SavefileIntrospectOnly)] 的组合不被接受。

0.16

主要性能提升,轻微的 API 调整。

序列化和反序列化类型现在使用它们所接受的 Write-实现类型进行参数化。之前使用的是 &mut dyn Write 类型,但结果证明这比具有泛型 writer 类型的性能差得多。

这仅影响手动实现的 Serialize 和 Deserialize 特性,使用 derive-macro 的影响不受影响。要修复任何 Serialize 和 Deserialize 的手动实现,您只需将 Serializer 改为 Serializer<impl Write>Deserializer 改为 Deserializer<impl Write> 即可。

此外,依赖项也已更新。

smallvec 1.0 -> 1.11
indexmap 1.6 -> 1.9
byteorder 1.2 -> 1.4

如果您正在序列化 smallvec 和/或 indexmap,则需要升级到 1.11 和 1.9 版本。预期功能不会发生变化。

这个版本也对 derive 宏进行了重大更改。现在它试图检测是否可以一次写入多个字段或整个结构体。通过检查类型的布局,它可以检测到多个字段是否在内存中相邻放置,然后一次写入它们的字节。

这个版本是长期以来对 savefile 的最大改动。测试套件已经运行,但仍然可能存在错误。

0.15

自动激活对可以安全支持的类型的 #[savefile_unsafe_and_fast]-优化。

Savefile 在稳定 rust 上的性能一直不如人意。在 0.14 版本中,这种限制主要被解除。

然而,要获得速度提升仍然需要不安全的代码。

在 0.15 版本中,不再需要不安全的代码来获得速度提升。实际上,在许多情况下,根本不需要做任何特别的事情。

Savefile 现在包含代码来自动检查结构体是否有打包表示形式,如果有,它将自动像以前的手动 opt-in 一样表现。区别在于派生的序列化器现在会自动确定类型是否实际上满足了所需的条件。

注意!在某些情况下,可能需要使用 #[repr(C)] 来获得加速。

0.14.3 对 0.14 版本的错误修复

0.14 版本中包含一些错误。实际上,无法序列化包含许多标准类型的集合。这在 0.14.3 中得到了修复。

0.14 对 Packed(之前称为 'ReprC')系统的重大更改

Savefile 的一个优点是支持非常快速地序列化和反序列化简单复制数据类型的向量。

之前的方法是这样的

#[derive(Savefile,ReprC)]
#[repr(C)]
pub struct Example {
    pub x: u32,
    pub y: u32,
}

新的方法是

#[derive(Savefile)]
#[savefile_require_fast]
#[repr(C)]
pub struct Example {
    pub x: u32,
    pub y: u32,
}

旧方案实现了类型的一个标记特质 'Packed'。然后,Savefile 依赖于特殊化,能够通过简单地复制大量字节区域来更快地序列化这些类型的向量和数组。

当时,专业化仅在夜间版本中可用似乎只是暂时的麻烦。今天,2023年,专业化似乎可能永远都不会在稳定的Rust中实现。

因此,我们现在通过放弃专业化,将速度提升带到稳定版本中!

我们现在正在为所有类型实现 'Packed'(之前被称为 'ReprC'),然后只为不支持优化的类型返回 false。

这有一些缺点。之前,ReprC('Packed'的早期名称)是一个不安全的特征。它不是强制的,但如果你知道你在做什么,你可以在责任下获得额外的性能。

但现在,Packed 将由所有类型实现。我们不希望使用不安全的代码才能使用 Savefile 成为必要。然而,对于 Savefile 的用户来说,除了 Packed 特征总是被派生,还有另一种方式来启用不安全但高效的优化之外,并没有太大区别。

我不知道如何将 'unsafe' 关键字强制应用于派生宏,所以我们使用一个故意引人注目的非符合规范的名字 #[savefile_unsafe_and_fast] 来表示危险。

更新:随着 0.15 版本的发布,savefile_unsafe_and_fast 大多数情况下不再需要。取而代之的是,有一个新的属性 'savefile_require_fast'。使用它,如果由于对齐或填充不当而无法进行快速操作,则编译器将产生错误。没有 'savefile_require_fast' 属性,savefile 即使对齐不良也能工作,但速度较慢。

0.13 支持不带有 Savefile 类型约束的泛型结构体

以前,如果你尝试实现以下结构体

#[derive(Savefile)]
pub struct ExampleGeneric<T> {
    pub x: T
}

你会得到编译器错误,因为 T 不能使用 Savefile 进行序列化。然而,作为一个用户,可能 ExampleGeneric 结构体并不总是需要可序列化。真正需要的是,每当尝试序列化特定实例时,该实例的类型必须是可序列化的。

其他派生宏,如 'Debug' 宏,不是这样工作的。只有当所有类型参数本身实现了 Debug 时,它们才为这样的结构体实现 Debug,以避免编译器错误。

从 0.13 版本开始,savefile 以相同的方式工作。

将来,有可能使类型要求更加智能,只需要求在序列化过程中实际使用的类型支持 savefile 特征。

版本 0.14 和 0.14.1 的 Readme 中包含了一些小错误。这在 0.14.2 中已修复,否则一切相同。

0.12 支持 char

结果证明,最基本的类型 'char' 实际上并不支持 savefile。

这个版本修复了这一疏忽。

0.11.1 支持单元结构体

Savefile-derive 获得了单元结构体的支持。也就是说,以下现在可以编译

#[derive(Savefile)]
struct MyUnitStruct;

0.11 改善 bit-vec 序列化性能,升级 arrayvec,支持更多数据类型

Savefile 缺乏对 u128 和 i128 类型的支持。这已修复。

此外:对 bit-vec 的序列化和反序列化进行了一些轻微的改进。磁盘格式已更改,但反序列化器知道如何读取旧版本。Bit-vec 在大端机器上不再受支持。如果这是一个重大限制,请与我们联系。这种变化的原因是可以使用更有效的格式。在大端机器上需要转换,这将非常昂贵且难以测试,除非能够访问大端机器。

接受大端支持的 PR。

此外,增加了对 bit-set crate 和 rustc-hash 的支持。

Arrayvec 升级到版本 0.7。他们稍微更改了他们的 API。请参阅 arrayvec 文档。

本版本将 'syn' 和 'quote' 依赖项更新到版本 1.0。

Savefile 现在支持具有自定义哈希器的 HashSet 和 HashMap。只有当哈希器实现 Default 时才支持反序列化(无法向反序列化的哈希表提供有状态的哈希器)。

0.10.1 使依赖项更加可配置,并升级一些依赖项

以下可配置功能已创建:

  • "压缩" - 启用压缩和解压缩功能(使用库 'bzip2')
  • "加密" - 启用加密支持(使用库 'ring')

以下依赖项已设置为可配置:"bit-vec", "arrayvec", "smallvec", "indexmap", "parking_lot"

此外,以下依赖项已升级:

  • parking_lot,从 0.11 升级到 0.12
  • rand,从 0.7 升级到 0.8(仅由 '加密' 功能使用)
  • bzip2,从 0.3.2 升级到 0.4(仅由 '压缩' 功能使用)

0.9.1 减少默认依赖项,并作一些其他改进

更直观的 load_file 方法

load_file 方法的路径参数已被更改为接受实现 AsRef 的任何内容。之前,需要 &str,这意味着 Path 和 PathBuf 类型不被接受。

迁移说明:如果您之前显式指定了 load_file 或类似函数的类型参数,现在需要添加一个 ",_"。因此


    let object = load_file::<MyType>("save.bin",0);

必须变为


    let object = load_file::<MyType,_>("save.bin",0);

然而,一个始终有效且继续有效的方法是

    let object : MyType = load_file("save.bin",0);

希望这不会造成太多问题。原因是,要求打开文件函数使用 '&str' 从来不是好的设计,因为原则上可能存在文件名实际上不是有效的 utf8 的文件。使用旧设计,这些文件无法通过 Savefile 打开。

将 bzip2 和 ring 依赖项设置为可选

将 bzip2 和 ring 依赖项放在功能标志后面。这使得希望不使用压缩或加密的用户可以轻松地选择不使用这些依赖项。

这些功能默认不激活,因此如果您想使用它们,请确保在 Cargo.toml 中按如下方式启用它们

savefile = { version = "0.14", features = ["ring","bzip2"] }

可以说,savefile 从来不应该包含这种支持,因为这是可以通过其他 crate 容易添加的功能。尽管如此,内置它确实提供了一些便利,希望使其可配置能够提供两者之最佳。

添加 SavefileNoIntrospect-derive

现在可以放弃自动推导 Introspect-trait,但仍然自动推导序列化特质。之前可以做到相反,自动推导 introspect 特质但不推导序列化特质。现在这个差距被填补了。

要推导所有特质:#[derive(Savefile)]

仅推导 Introspect:#[derive(SavefileIntrospectOnly)]

除了 Introspect 外,推导所有特质:#[derive(SavefileNoIntrospect)]

0.8.3 修复 savefile_introspect_ignore 属性的 bug

如果字段不是数据类型的最后一个字段,则在字段上指定 savefile_introspect_ignore 触发了一个 bug。该 bug 导致在 introspect 期间字段索引不匹配,这将导致字段在检查时不可见。

0.8.2 更新依赖项

  • parking_lot 从 0.10 -> 0.11
  • smallvec 1.4 -> 1.6(由 parking_lot 升级实现)
  • 使 Removed 实现 Clone、Copy、PartialEq 等等。Removed 字段不应限制结构体可以实现哪些特质。
  • 同时使 Removed 实现 Send 和 Sync。它是一个零尺寸类型,在多线程中没有问题。
  • 添加对 std::borrow::Cow 的支持。感谢 GitHub 用户 PonasKovas 提供此补丁!

0.8.1 停止依赖于 'failure' crate

这也意味着 SavefileError 现在终于实现了 Error 特质。

0.8.0 支持 min_const_generics

Savefile 现在支持任意大小的数组序列化和反序列化,即使在稳定版 Rust 中。

请注意,0.8.0 仅与 Rust 1.51 及更高版本兼容。如果您需要支持旧版本的 Rust,应继续使用 0.7.x。

0.7.5 Arc 的去重

之前,无法对 Arc 进行序列化。现在已添加支持,包括对 str 对象的去重。请注意,去重实际上不在序列化格式中,只是在去重后的内存结果中。去重并不知道保存前的内存图是什么样的,它只是确保在加载后相同的字符串由相同的内存支持。

如果代码依赖于 &str 对象的地址,可能会导致问题。然而,在实际中这种情况相当罕见。如果您觉得这可能是一个问题,请提交一个错误报告!

0.7.4 添加对 PathBuf 的反射

PathBuf 没有实现 Introspect,这导致尝试在包含 PathBuf 的任何地方使用 derive(Savefile) 会导致失败,因为 derive 宏要求所有组件都实现所有 Savefile 特性。

0.7.3 支持与 Serde 共存

Savefile-derive 在一个同时使用 Serde 的 crate 中使用时之前不能正确工作。

这个问题已经修复。

此外,还支持序列化和反序列化 PathBuf。

0.8.0

(尚未发布)

  • 支持任意大小数组,即使在稳定版 Rust 中(得益于 Rust 中现在支持的 min_const_generics)。

0.8.x 版本支持的最小 Rust 版本是 1.51。

0.7.2 支持稳定编译器

Savefile 现在可以使用稳定编译器,而不仅仅是 nightly 版本。

在稳定版上运行时,以下功能将停止工作

注意!以下信息已过时,从 0.16 版本开始不再有效。

  • 整个 'ReprC' 子系统。这意味着字节数组(或其他小型拷贝类型)的序列化速度不如预期。减慢速度可能达到几个数量级。

  • 对任意大小数组的序列化。在稳定版中,仅支持大小为 0-4 的数组。

  • 对具有字符串键的哈希表的反射进行了特殊化。这意味着哈希表的反射效果不是很好。

当使用 nightly 编译器时,nightly-only 特性会自动激活。

此外,还支持 Arc<[T]>。

0.7.1 更好地支持 ArrayVec

arrayvec crate 已作为依赖项存在很长时间,但 ArrayVec 类型本身并未得到支持。我们支持 ArrayString,但直到 0.7.1 版本才支持 ArrayVec。

0.7.0 更新一些过时的依赖项

对 bitvec、arrayvec 和 parking_lot 的依赖项版本太旧。它们已更新到

bit-vec = 0.6

arrayvec = 0.5

parking_lot = 0.10

0.6.1 修复对 SavefileError 的错误引用

如果您遇到编译错误,表明 SavefileError 未声明,则需要这个小修复。

0.6.0 对加密例程的临界修复

这是一个破坏性更改。希望这是最后一个!现在有一个更详细的文件头,这可能会使未来避免对基础二进制框架进行破坏性更改成为可能。

在加密文件加密和解密的方式中存在两个错误。可能会发生数据损坏。

此外,savefile 现在支持 bzip2 压缩。像加密支持一样,这只是为了方便。它可能最好在树外。但我喜欢“电池包含”的想法。

0.5.0 反射

Savefile 现在包含一个反射特性。更多内容请参阅文档。

0.4.0 破坏性更改

我刚刚意识到,'ignore' 作为一个自定义属性的名字非常糟糕,因为现在这已经是一个 Rust 语言的内建属性。

我已经将所有 savefile-attributes 前缀为字符串 "savefile_"。

这会破坏现有代码,以一种相当沉默的方式。修复方法是简单地更新所有 savefile-attributes 的使用,包括前缀 'savefile_'。文档中的示例是正确的。

0.3.0 破坏性变更

此外,版本 0.3.0 与 0.2.* 版本不兼容。这是因为现在支持泛型长度的数组(使用 Rust 夜间构建版本的 const_generics 特性)。之前只支持短数组,并且通过(黑客式地)将数组序列化为元组。现在真正支持数组了,尽管非常大的数组可能会耗尽栈空间,因为反序列化框架将数组视为值。这意味着使用 0.2.* 版本序列化的任何数组都无法使用 0.3.* 版本进行反序列化。

如果这是阻碍项目的问题,请与我联系。不过,我的感觉是,除了作者之外,没有其他人使用这个软件。

文档

保存文件文档可在以下位置找到: https://docs.rs/savefile/latest/savefile/

功能和目标

savefile 具有的功能

  • 快速的二进制序列化和反序列化
  • 支持旧版本的保存格式
  • 完全自动实现,使用 "custom derive"。您无需确定如何保存数据。

savefile 不具有的功能

  • 不支持递归数据结构

savefile 不具有,也不会具有的功能

  • 不支持外部协议/数据格式。永远不会支持 json、yaml、xml 或任何其他后端。savefile 仅使用 savefile 格式。
  • 不支持序列化图。如果数据在 RAM 中具有树结构且没有循环,savefile 可以序列化您的数据。
  • 不支持序列化装箱特性("对象")。您可以通过手动实现 Serialize 和 Deserialize 特性并在反序列化器中手动选择具体类型(可能)来实现这一点。

故障排除

常见错误

"the trait bound MyStuff: WithSchema is not satisfied"

这通常意味着您忘记推导 Savefile 特性。添加 #[derive(Savefile)]

the trait ReprC is not implemented

这个问题很简单。 ReprC 已重命名为 Packed。只需将其更改为 Packed,事情就应该会正常工作。

许可证

Savefile 根据 Apache 许可证 2.0 版本或 MIT 许可证进行许可

由您选择。

MIT 许可证文本

Copyright 2018 Anders Musikka

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

依赖项

~1.5MB
~36K SLoC