#serialization #binary-encoding #deserialize #introspection #binary-data #binary-format

savefile

简单、方便、快速、支持版本控制,二进制序列化和反序列化库

59个版本

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日

#116 in 编码

Download history 736/week @ 2024-05-01 505/week @ 2024-05-08 130/week @ 2024-05-15 97/week @ 2024-05-22 145/week @ 2024-05-29 128/week @ 2024-06-05 101/week @ 2024-06-12 137/week @ 2024-06-19 226/week @ 2024-06-26 214/week @ 2024-07-03 61/week @ 2024-07-10 57/week @ 2024-07-17 215/week @ 2024-07-24 276/week @ 2024-07-31 113/week @ 2024-08-07 75/week @ 2024-08-14

687 每月下载量
9 crates 中使用

MIT/Apache

325KB
6K 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 编写的共享库,这些库可以用作 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的'もちろんだ'字段已被重命名为'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落地时触发警告。有关更多信息,请参阅bug #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-derive'的情况下使用savefile。只需这样做:

use savefile::prelude::*;

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

然后您就可以使用了!

0.16.1

修复了在#[savefile_introspect_ignore]#[derive(SavefileIntrospectOnly)]结合使用时不被接受的小问题。

0.16

主要性能改进,轻微的API调整。

序列化和反序列化类型现在针对它们所采用的Write实现的类型进行了参数化。之前使用的是&mut dyn Write类型,但结果表明,这比具有泛型写入器类型的性能差得多。

这仅影响Serialize和Deserialize特质的手动实现,不会影响derive宏的使用。要修复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版开始,这种限制基本上被取消了。

然而,要获得速度提升仍然需要使用unsafe代码。

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

Savefile现在包含代码,可以自动检查结构体是否有打包表示形式,如果有,它将自动表现得与之前我们手动选择的opt-in完全一样。区别在于派生序列化器现在会自动确定类型是否实际上满足了所需的要求。

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

0.14.3修复了0.14版本的错误

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

0.14对打包(之前称为'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 支持 unit 结构体

Savefile-derive增加了对unit结构体的支持。也就是说,以下现在可以编译

#[derive(Savefile)]
struct MyUnitStruct;

0.11 改善位向量序列化性能,升级arrayvec,支持更多数据类型

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

此外:对位向量序列化和反序列化进行了一些轻微的改进。磁盘上的格式已更改,但反序列化器知道如何读取旧版本。位向量不再支持在大端机器上。如果您认为这是一个很大的限制,请与我们联系。这种变化的理由是可以使用更有效的格式。在大端机器上需要转换,这将非常昂贵,而且没有大端机器难以测试。

将接受大端支持的PR。

此外,增加了对位集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 从来不应该包含这项支持,因为这是一些可以通过其他 crates 容易添加的东西。然而,内置它确实提供了一些便利,希望使其可配置能提供两者之间的最佳选择。

添加 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 共存

如果在一个同时使用 Serde 的 crate 中使用 Savefile-derive,它之前可能不会正确工作。

这个问题已经得到修复。

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

0.8.0

(尚未发布)

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

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

0.7.2 支持稳定编译器

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

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

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

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

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

  • 对具有字符串键的哈希表的特定化。这意味着哈希表的内省不如预期。

当使用夜间编译器时,夜间仅有的功能会自动激活。

此外,支持 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开头的属性前缀为字符串"savefile_"。

这将以一种相对沉默的方式破坏现有代码。修复方法很简单,只需将所有savefile属性的用法更新为包含前缀"savefile_"。文档中的示例是正确的。

0.3.0重大变更

此外,版本0.3.0破坏了与0.2.*版本的二进制兼容性。这是因为现在支持泛型长度的数组(使用Rust nightly的const_generics特性)。之前支持短数组,并且通过巧妙的方式将其序列化为元组。现在数组得到了真正的支持,尽管非常大的数组可能会导致栈耗尽,因为反序列化框架将数组视为值。这意味着使用0.2.*序列化的任何数组都不能使用0.3.*反序列化。

如果这是绊脚石,请与我联系。尽管如此,我的感觉是,除了作者之外,没有其他用户使用这个软件。

文档

savefile文档可在以下地址找到:https://docs.rs/savefile/latest/savefile/

功能和目标

savefile拥有的功能

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

savefile没有的功能

  • 不支持递归数据结构

savefile没有的功能,将来也不会有

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

故障排除

常见错误

"the trait bound MyStuff: WithSchema is not satisfied"

这很可能意味着您忘记派生Savefile特质。添加#[derive(Savefile)]

the trait ReprC is not implemented

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

许可证

Savefile根据您的选择受以下许可证之一许可

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.6–10MB
~114K SLoC