From 81421402872fd4043099ae76449be147d31dd734 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Tue, 16 May 2023 16:00:54 +0900 Subject: [PATCH] Import rkyv 0.7.42 --- .cargo_vcs_info.json | 6 + Cargo.toml | 131 +++ Cargo.toml.orig | 54 ++ LICENSE | 7 + README.md | 132 +++ build.rs | 39 + crates-io.md | 110 +++ src/bitvec.rs | 30 + src/boxed.rs | 297 +++++++ src/collections/btree_map/mod.rs | 738 +++++++++++++++++ src/collections/btree_map/validation.rs | 638 +++++++++++++++ src/collections/btree_set.rs | 118 +++ src/collections/hash_index/mod.rs | 254 ++++++ src/collections/hash_index/validation.rs | 120 +++ src/collections/hash_map/mod.rs | 522 ++++++++++++ src/collections/hash_map/validation.rs | 151 ++++ src/collections/hash_set.rs | 123 +++ src/collections/index_map/mod.rs | 414 ++++++++++ src/collections/index_map/validation.rs | 197 +++++ src/collections/index_set.rs | 174 ++++ src/collections/mod.rs | 19 + src/collections/util/mod.rs | 53 ++ src/collections/util/validation.rs | 58 ++ src/copy.rs | 185 +++++ src/de/deserializers/alloc.rs | 103 +++ src/de/deserializers/mod.rs | 8 + src/de/mod.rs | 67 ++ src/ffi.rs | 204 +++++ src/impls/alloc/boxed.rs | 60 ++ src/impls/alloc/collections/btree_map.rs | 75 ++ src/impls/alloc/collections/btree_set.rs | 66 ++ src/impls/alloc/collections/mod.rs | 2 + src/impls/alloc/mod.rs | 6 + src/impls/alloc/niche.rs | 33 + src/impls/alloc/rc.rs | 244 ++++++ src/impls/alloc/string.rs | 65 ++ src/impls/alloc/vec.rs | 72 ++ src/impls/arrayvec.rs | 68 ++ src/impls/bitvec.rs | 183 +++++ src/impls/core/mod.rs | 403 ++++++++++ src/impls/core/ops.rs | 249 ++++++ src/impls/core/option.rs | 65 ++ src/impls/core/primitive.rs | 399 +++++++++ src/impls/core/result.rs | 73 ++ src/impls/core/time.rs | 33 + src/impls/hashbrown/hash_map.rs | 129 +++ src/impls/hashbrown/hash_set.rs | 127 +++ src/impls/hashbrown/mod.rs | 2 + src/impls/indexmap/index_map.rs | 119 +++ src/impls/indexmap/index_set.rs | 108 +++ src/impls/indexmap/mod.rs | 2 + src/impls/mod.rs | 29 + src/impls/rend.rs | 268 ++++++ src/impls/smallvec.rs | 67 ++ src/impls/std/collections/hash_map.rs | 75 ++ src/impls/std/collections/hash_set.rs | 74 ++ src/impls/std/collections/mod.rs | 2 + src/impls/std/ffi.rs | 121 +++ src/impls/std/mod.rs | 4 + src/impls/std/net.rs | 698 ++++++++++++++++ src/impls/std/time.rs | 16 + src/impls/tinyvec.rs | 174 ++++ src/impls/uuid.rs | 60 ++ src/lib.rs | 753 +++++++++++++++++ src/macros.rs | 160 ++++ src/net.rs | 171 ++++ src/niche/mod.rs | 5 + src/niche/option_box.rs | 213 +++++ src/niche/option_nonzero.rs | 195 +++++ src/ops.rs | 238 ++++++ src/option.rs | 252 ++++++ src/rc/mod.rs | 270 +++++++ src/rc/validation.rs | 179 +++++ src/rel_ptr/mod.rs | 508 ++++++++++++ src/rel_ptr/validation.rs | 62 ++ src/result.rs | 208 +++++ src/ser/mod.rs | 188 +++++ src/ser/serializers/alloc.rs | 407 ++++++++++ src/ser/serializers/core.rs | 422 ++++++++++ src/ser/serializers/mod.rs | 234 ++++++ src/ser/serializers/std.rs | 61 ++ src/string/mod.rs | 271 +++++++ src/string/repr.rs | 201 +++++ src/time.rs | 152 ++++ src/util/aligned_vec.rs | 984 +++++++++++++++++++++++ src/util/mod.rs | 336 ++++++++ src/util/scratch_vec.rs | 495 ++++++++++++ src/validation/mod.rs | 371 +++++++++ src/validation/owned.rs | 56 ++ src/validation/validators/archive.rs | 434 ++++++++++ src/validation/validators/mod.rs | 238 ++++++ src/validation/validators/shared.rs | 106 +++ src/validation/validators/util.rs | 74 ++ src/vec/mod.rs | 358 +++++++++ src/vec/raw.rs | 225 ++++++ src/with/alloc.rs | 604 ++++++++++++++ src/with/atomic.rs | 184 +++++ src/with/core.rs | 434 ++++++++++ src/with/mod.rs | 714 ++++++++++++++++ src/with/std.rs | 328 ++++++++ 100 files changed, 20214 insertions(+) create mode 100644 .cargo_vcs_info.json create mode 100644 Cargo.toml create mode 100644 Cargo.toml.orig create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build.rs create mode 100644 crates-io.md create mode 100644 src/bitvec.rs create mode 100644 src/boxed.rs create mode 100644 src/collections/btree_map/mod.rs create mode 100644 src/collections/btree_map/validation.rs create mode 100644 src/collections/btree_set.rs create mode 100644 src/collections/hash_index/mod.rs create mode 100644 src/collections/hash_index/validation.rs create mode 100644 src/collections/hash_map/mod.rs create mode 100644 src/collections/hash_map/validation.rs create mode 100644 src/collections/hash_set.rs create mode 100644 src/collections/index_map/mod.rs create mode 100644 src/collections/index_map/validation.rs create mode 100644 src/collections/index_set.rs create mode 100644 src/collections/mod.rs create mode 100644 src/collections/util/mod.rs create mode 100644 src/collections/util/validation.rs create mode 100644 src/copy.rs create mode 100644 src/de/deserializers/alloc.rs create mode 100644 src/de/deserializers/mod.rs create mode 100644 src/de/mod.rs create mode 100644 src/ffi.rs create mode 100644 src/impls/alloc/boxed.rs create mode 100644 src/impls/alloc/collections/btree_map.rs create mode 100644 src/impls/alloc/collections/btree_set.rs create mode 100644 src/impls/alloc/collections/mod.rs create mode 100644 src/impls/alloc/mod.rs create mode 100644 src/impls/alloc/niche.rs create mode 100644 src/impls/alloc/rc.rs create mode 100644 src/impls/alloc/string.rs create mode 100644 src/impls/alloc/vec.rs create mode 100644 src/impls/arrayvec.rs create mode 100644 src/impls/bitvec.rs create mode 100644 src/impls/core/mod.rs create mode 100644 src/impls/core/ops.rs create mode 100644 src/impls/core/option.rs create mode 100644 src/impls/core/primitive.rs create mode 100644 src/impls/core/result.rs create mode 100644 src/impls/core/time.rs create mode 100644 src/impls/hashbrown/hash_map.rs create mode 100644 src/impls/hashbrown/hash_set.rs create mode 100644 src/impls/hashbrown/mod.rs create mode 100644 src/impls/indexmap/index_map.rs create mode 100644 src/impls/indexmap/index_set.rs create mode 100644 src/impls/indexmap/mod.rs create mode 100644 src/impls/mod.rs create mode 100644 src/impls/rend.rs create mode 100644 src/impls/smallvec.rs create mode 100644 src/impls/std/collections/hash_map.rs create mode 100644 src/impls/std/collections/hash_set.rs create mode 100644 src/impls/std/collections/mod.rs create mode 100644 src/impls/std/ffi.rs create mode 100644 src/impls/std/mod.rs create mode 100644 src/impls/std/net.rs create mode 100644 src/impls/std/time.rs create mode 100644 src/impls/tinyvec.rs create mode 100644 src/impls/uuid.rs create mode 100644 src/lib.rs create mode 100644 src/macros.rs create mode 100644 src/net.rs create mode 100644 src/niche/mod.rs create mode 100644 src/niche/option_box.rs create mode 100644 src/niche/option_nonzero.rs create mode 100644 src/ops.rs create mode 100644 src/option.rs create mode 100644 src/rc/mod.rs create mode 100644 src/rc/validation.rs create mode 100644 src/rel_ptr/mod.rs create mode 100644 src/rel_ptr/validation.rs create mode 100644 src/result.rs create mode 100644 src/ser/mod.rs create mode 100644 src/ser/serializers/alloc.rs create mode 100644 src/ser/serializers/core.rs create mode 100644 src/ser/serializers/mod.rs create mode 100644 src/ser/serializers/std.rs create mode 100644 src/string/mod.rs create mode 100644 src/string/repr.rs create mode 100644 src/time.rs create mode 100644 src/util/aligned_vec.rs create mode 100644 src/util/mod.rs create mode 100644 src/util/scratch_vec.rs create mode 100644 src/validation/mod.rs create mode 100644 src/validation/owned.rs create mode 100644 src/validation/validators/archive.rs create mode 100644 src/validation/validators/mod.rs create mode 100644 src/validation/validators/shared.rs create mode 100644 src/validation/validators/util.rs create mode 100644 src/vec/mod.rs create mode 100644 src/vec/raw.rs create mode 100644 src/with/alloc.rs create mode 100644 src/with/atomic.rs create mode 100644 src/with/core.rs create mode 100644 src/with/mod.rs create mode 100644 src/with/std.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..9a6cc7e --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "4645bf686ffc142c45c3be652247d13e54288f70" + }, + "path_in_vcs": "rkyv" +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3c8042d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,131 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "rkyv" +version = "0.7.42" +authors = ["David Koloski "] +description = "Zero-copy deserialization framework for Rust" +readme = "crates-io.md" +keywords = [ + "archive", + "rkyv", + "serialization", + "zero-copy", + "no_std", +] +categories = [ + "encoding", + "no-std", +] +license = "MIT" +repository = "https://github.com/rkyv/rkyv" +resolver = "1" + +[package.metadata.docs.rs] +features = ["validation"] + +[dependencies.arrayvec] +version = "0.7" +optional = true +default-features = false + +[dependencies.bitvec] +version = "1.0" +optional = true +default-features = false + +[dependencies.bytecheck] +version = "=0.6.11" +optional = true +default-features = false + +[dependencies.hashbrown] +version = "0.12" +optional = true + +[dependencies.indexmap] +version = "1.7" +optional = true +default-features = false + +[dependencies.ptr_meta] +version = "~0.1.3" +default-features = false + +[dependencies.rend] +version = "0.4" +optional = true +default-features = false + +[dependencies.rkyv_derive] +version = "0.7.41" + +[dependencies.seahash] +version = "4.0" + +[dependencies.smallvec] +version = "1.7" +optional = true +default-features = false + +[dependencies.tinyvec] +version = "1.5" +optional = true +default-features = false + +[dependencies.uuid] +version = "1.3" +optional = true +default-features = false + +[features] +alloc = [ + "hashbrown", + "bitvec?/alloc", + "tinyvec?/alloc", +] +arbitrary_enum_discriminant = ["rkyv_derive/arbitrary_enum_discriminant"] +archive_be = [ + "rend", + "rkyv_derive/archive_be", +] +archive_le = [ + "rend", + "rkyv_derive/archive_le", +] +copy = ["rkyv_derive/copy"] +copy_unsafe = [] +default = [ + "size_32", + "std", +] +size_16 = [] +size_32 = [] +size_64 = [] +std = [ + "alloc", + "bytecheck?/std", + "ptr_meta/std", + "rend?/std", + "uuid?/std", +] +strict = ["rkyv_derive/strict"] +uuid = [ + "dep:uuid", + "bytecheck?/uuid", +] +validation = [ + "alloc", + "bytecheck", + "rend/validation", +] diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..26e7e65 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,54 @@ +[package] +name = "rkyv" +description = "Zero-copy deserialization framework for Rust" +keywords = ["archive", "rkyv", "serialization", "zero-copy", "no_std"] +categories = ["encoding", "no-std"] +readme = "crates-io.md" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bytecheck = { workspace = true, optional = true } +hashbrown = { version = "0.12", optional = true } +ptr_meta = { workspace = true, default-features = false } +rend = { version = "0.4", optional = true, default-features = false } +rkyv_derive = { version = "0.7.41", path = "../rkyv_derive" } +seahash = "4.0" + +# Support for various common crates. These are primarily to get users off the ground and build some +# momentum. + +# These are NOT PLANNED to remain in rkyv for the final release. Much like serde, these +# implementations should be moved into their respective crates over time. Before adding support for +# another crate, please consider getting rkyv support in the crate instead. + +bitvec = { version = "1.0", optional = true, default-features = false } +indexmap = { version = "1.7", optional = true, default-features = false } +smallvec = { version = "1.7", optional = true, default-features = false } +arrayvec = { version = "0.7", optional = true, default-features = false } +tinyvec = { version = "1.5", optional = true, default-features = false } +uuid = { version = "1.3", optional = true, default-features = false } + +[features] +default = ["size_32", "std"] +alloc = ["hashbrown", "bitvec?/alloc", "tinyvec?/alloc"] +arbitrary_enum_discriminant = ["rkyv_derive/arbitrary_enum_discriminant"] +archive_be = ["rend", "rkyv_derive/archive_be"] +archive_le = ["rend", "rkyv_derive/archive_le"] +copy = ["rkyv_derive/copy"] +copy_unsafe = [] +size_16 = [] +size_32 = [] +size_64 = [] +std = ["alloc", "bytecheck?/std", "ptr_meta/std", "rend?/std", "uuid?/std"] +strict = ["rkyv_derive/strict"] +uuid = ["dep:uuid", "bytecheck?/uuid"] +validation = ["alloc", "bytecheck", "rend/validation"] + +[package.metadata.docs.rs] +features = ["validation"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f451631 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2021 David Koloski + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee25c28 --- /dev/null +++ b/README.md @@ -0,0 +1,132 @@ +

+ +

+

+ rkyv (archive) is a zero-copy deserialization framework for Rust +

+

+ + Discord + + + + + + + + + + + + + +

+ +# Resources + +## Learning Materials + +- The [rkyv book](https://rkyv.github.io/rkyv) covers the motivation, architecture, and major + features of rkyv +- The [rkyv discord](https://discord.gg/65F6MdnbQh) is a great place to get help with specific issues and meet + other people using rkyv + +## Documentation + +- [rkyv](https://docs.rs/rkyv), the core library +- [rkyv_dyn](https://docs.rs/rkyv_dyn), which adds trait object support to rkyv +- [rkyv_typename](https://docs.rs/rkyv_typename), a type naming library + +## Benchmarks + +- The [rust serialization benchmark](https://github.com/djkoloski/rust_serialization_benchmark) is a + shootout style benchmark comparing many rust serialization solutions. It includes special + benchmarks for zero-copy serialization solutions like rkyv. + +## Sister Crates + +- [bytecheck](https://github.com/rkyv/bytecheck), which rkyv uses for validation +- [ptr_meta](https://github.com/rkyv/ptr_meta), which rkyv uses for pointer manipulation +- [rend](https://github.com/rkyv/rend), which rkyv uses for endian-agnostic features + +# Example + +```rust +use rkyv::{Archive, Deserialize, Serialize}; + +#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] +#[archive( + // This will generate a PartialEq impl between our unarchived and archived + // types: + compare(PartialEq), + // bytecheck can be used to validate your data if you want. To use the safe + // API, you have to derive CheckBytes for the archived type: + check_bytes, +)] +// Derives can be passed through to the generated type: +#[archive_attr(derive(Debug))] +struct Test { + int: u8, + string: String, + option: Option>, +} + +let value = Test { + int: 42, + string: "hello world".to_string(), + option: Some(vec![1, 2, 3, 4]), +}; + +// Serializing is as easy as a single function call +let bytes = rkyv::to_bytes::<_, 256>(&value).unwrap(); + +// Or you can customize your serialization for better performance +// and compatibility with #![no_std] environments +use rkyv::ser::{Serializer, serializers::AllocSerializer}; + +let mut serializer = AllocSerializer::<0>::default(); +serializer.serialize_value(&value).unwrap(); +let bytes = serializer.into_serializer().into_inner(); + +// You can use the safe API for fast zero-copy deserialization +let archived = rkyv::check_archived_root::(&bytes[..]).unwrap(); +assert_eq!(archived, &value); + +// Or you can use the unsafe API for maximum performance +let archived = unsafe { rkyv::archived_root::(&bytes[..]) }; +assert_eq!(archived, &value); + +// And you can always deserialize back to the original type +let deserialized: Test = archived.deserialize(&mut rkyv::Infallible).unwrap(); +assert_eq!(deserialized, value); +``` + +_Note: the safe API requires the `validation` feature:_ + +```toml +rkyv = { version = "0.7", features = ["validation"] } +``` + +_Read more about [available features](https://docs.rs/rkyv/latest/rkyv/#features)._ + +# Thanks + +Thanks to all the sponsors that keep development sustainable. Special thanks to the following sponsors for going above and beyond supporting rkyv: + +## Platinum Sponsors + +

+ + Dusk Network + +

+ +> Dusk Network is the first privacy blockchain for financial applications. Our mission is to enable any size enterprise to collaborate at scale, meet compliance requirements, and ensure that transaction data remains confidential. + +## Bronze Sponsors + +

+ + Traverse Research + +

diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6de5755 --- /dev/null +++ b/build.rs @@ -0,0 +1,39 @@ +use std::env; + +fn main() { + let mut has_atomic32 = true; + let mut has_atomic64 = true; + + let target = env::var("TARGET").unwrap(); + + // Full target triples that have specific limitations: + match target.as_str() { + "arm-linux-androideabi" + | "asmjs-unknown-emscripten" + | "wasm32-unknown-emscripten" + | "wasm32-unknown-unknown" => has_atomic64 = false, + _ => {} + } + + // Architecture-specific limitations: + let arch = target.split('-').next().unwrap_or(&target); + match arch { + // NOTE: Not all ARMv7 variants are listed here, as certain variants do actually provide + // 64-bit atomics. (`armv7`, `armv7a`, and `armv7s`, specifically) + "armv5te" | "mips" | "mipsel" | "powerpc" | "riscv32imac" | "thumbv7em" | "thumbv7m" + | "thumbv8m.base" | "thumbv8m.main" | "armebv7r" | "armv7r" => has_atomic64 = false, + "avr" | "riscv32i" | "riscv32im" | "riscv32imc" | "thumbv6m" => { + has_atomic32 = false; + has_atomic64 = false; + } + _ => {} + } + + if has_atomic64 { + println!("cargo:rustc-cfg=has_atomics_64"); + } + + if has_atomic32 { + println!("cargo:rustc-cfg=has_atomics"); + } +} diff --git a/crates-io.md b/crates-io.md new file mode 100644 index 0000000..2f39901 --- /dev/null +++ b/crates-io.md @@ -0,0 +1,110 @@ +

+ +

+

+ rkyv (archive) is a zero-copy deserialization framework for Rust +

+

+ + Discord + + + + + + + + + + + + + +

+ +# Resources + +## Learning Materials + +- The [rkyv book](https://rkyv.github.io/rkyv) covers the motivation, architecture, and major + features of rkyv +- The [rkyv discord](https://discord.gg/65F6MdnbQh) is a great place to get help with specific issues and meet + other people using rkyv + +## Documentation + +- [rkyv](https://docs.rs/rkyv), the core library +- [rkyv_dyn](https://docs.rs/rkyv_dyn), which adds trait object support to rkyv +- [rkyv_typename](https://docs.rs/rkyv_typename), a type naming library + +## Benchmarks + +- The [rust serialization benchmark](https://github.com/djkoloski/rust_serialization_benchmark) is a + shootout style benchmark comparing many rust serialization solutions. It includes special + benchmarks for zero-copy serialization solutions like rkyv. + +## Sister Crates + +- [bytecheck](https://github.com/rkyv/bytecheck), which rkyv uses for validation +- [ptr_meta](https://github.com/rkyv/ptr_meta), which rkyv uses for pointer manipulation +- [rend](https://github.com/rkyv/rend), which rkyv uses for endian-agnostic features + +# Example + +```rust +use rkyv::{Archive, Deserialize, Serialize}; + +#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] +#[archive( + // This will generate a PartialEq impl between our unarchived and archived + // types: + compare(PartialEq), + // bytecheck can be used to validate your data if you want. To use the safe + // API, you have to derive CheckBytes for the archived type: + check_bytes, +)] +// Derives can be passed through to the generated type: +#[archive_attr(derive(Debug))] +struct Test { + int: u8, + string: String, + option: Option>, +} + +let value = Test { + int: 42, + string: "hello world".to_string(), + option: Some(vec![1, 2, 3, 4]), +}; + +// Serializing is as easy as a single function call +let bytes = rkyv::to_bytes::<_, 256>(&value).unwrap(); + +// Or you can customize your serialization for better performance +// and compatibility with #![no_std] environments +use rkyv::ser::{Serializer, serializers::AllocSerializer}; + +let mut serializer = AllocSerializer::<0>::default(); +serializer.serialize_value(&value).unwrap(); +let bytes = serializer.into_serializer().into_inner(); + +// You can use the safe API for fast zero-copy deserialization +let archived = rkyv::check_archived_root::(&bytes[..]).unwrap(); +assert_eq!(archived, &value); + +// Or you can use the unsafe API for maximum performance +let archived = unsafe { rkyv::archived_root::(&bytes[..]) }; +assert_eq!(archived, &value); + +// And you can always deserialize back to the original type +let deserialized: Test = archived.deserialize(&mut rkyv::Infallible).unwrap(); +assert_eq!(deserialized, value); +``` + +_Note: the safe API requires the `validation` feature:_ + +```toml +rkyv = { version = "0.7", features = ["validation"] } +``` + +_Read more about [available features](https://docs.rs/rkyv/latest/rkyv/#features)._ diff --git a/src/bitvec.rs b/src/bitvec.rs new file mode 100644 index 0000000..fcc6542 --- /dev/null +++ b/src/bitvec.rs @@ -0,0 +1,30 @@ +//! Archived bitwise containers. + +use crate::{vec::ArchivedVec, Archived}; +use bitvec::{ + order::{BitOrder, Lsb0}, + slice::BitSlice, + store::BitStore, + view::BitView, +}; +use core::{marker::PhantomData, ops::Deref}; + +/// An archived `BitVec`. +// We also have to store the bit length in the archived `BitVec`. +// This is because when calling `as_raw_slice` we will get unwanted bits if the `BitVec` bit length is not a multiple of the bit size of T. +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[cfg_attr(feature = "strict", repr(C))] +#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ArchivedBitVec, O = Lsb0> { + pub(crate) inner: ArchivedVec, + pub(crate) bit_len: Archived, + pub(crate) _or: PhantomData, +} + +impl Deref for ArchivedBitVec { + type Target = BitSlice; + + fn deref(&self) -> &Self::Target { + &self.inner.view_bits::()[..self.bit_len as usize] + } +} diff --git a/src/boxed.rs b/src/boxed.rs new file mode 100644 index 0000000..1dc995e --- /dev/null +++ b/src/boxed.rs @@ -0,0 +1,297 @@ +//! An archived version of `Box`. + +use crate::{ + ser::Serializer, ArchivePointee, ArchiveUnsized, Fallible, MetadataResolver, RelPtr, Serialize, + SerializeUnsized, +}; +use core::{borrow::Borrow, cmp, fmt, hash, ops::Deref, pin::Pin}; + +/// An archived [`Box`]. +/// +/// This is a thin wrapper around a [`RelPtr`] to the archived type. +#[repr(transparent)] +pub struct ArchivedBox(RelPtr); + +impl ArchivedBox { + /// Returns a reference to the value of this archived box. + #[inline] + pub fn get(&self) -> &T { + unsafe { &*self.0.as_ptr() } + } + + /// Returns a pinned mutable reference to the value of this archived box + #[inline] + pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { + unsafe { self.map_unchecked_mut(|s| &mut *s.0.as_mut_ptr()) } + } + + /// Resolves an archived box from the given value and parameters. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing `value` + #[inline] + pub unsafe fn resolve_from_ref + ?Sized>( + value: &U, + pos: usize, + resolver: BoxResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.0); + value.resolve_unsized(pos + fp, resolver.pos, resolver.metadata_resolver, fo); + } + + /// Serializes an archived box from the given value and serializer. + #[inline] + pub fn serialize_from_ref( + value: &U, + serializer: &mut S, + ) -> Result, S::Error> + where + U: SerializeUnsized + ?Sized, + S: Fallible + ?Sized, + { + Ok(BoxResolver { + pos: value.serialize_unsized(serializer)?, + metadata_resolver: value.serialize_metadata(serializer)?, + }) + } + + /// Resolves an archived box from a [`BoxResolver`] which contains + /// the raw [`::ArchivedMetadata`] directly. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be obtained by following the safety documentation of + /// [`BoxResolver::from_raw_parts`]. + /// + /// [`::ArchivedMetadata`]: ArchivePointee::ArchivedMetadata + pub unsafe fn resolve_from_raw_parts( + pos: usize, + resolver: BoxResolver<::ArchivedMetadata>, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.0); + RelPtr::resolve_emplace_from_raw_parts( + pos + fp, + resolver.pos, + resolver.metadata_resolver, + fo, + ); + } + + #[doc(hidden)] + #[inline] + pub fn is_null(&self) -> bool { + self.0.is_null() + } +} + +impl ArchivedBox<[T]> { + /// Serializes an archived `Box` from a given slice by directly copying bytes. + /// + /// # Safety + /// + /// The type being serialized must be copy-safe. Copy-safe types must be trivially copyable + /// (have the same archived and unarchived representations) and contain no padding bytes. In + /// situations where copying uninitialized bytes the output is acceptable, this function may be + /// used with types that contain padding bytes. + #[inline] + pub unsafe fn serialize_copy_from_slice( + slice: &[U], + serializer: &mut S, + ) -> Result>, S::Error> + where + U: Serialize, + S: Serializer + ?Sized, + { + use ::core::{mem::size_of, slice::from_raw_parts}; + + let pos = serializer.align_for::()?; + + let bytes = from_raw_parts(slice.as_ptr().cast::(), size_of::() * slice.len()); + serializer.write(bytes)?; + + Ok(BoxResolver { + pos, + metadata_resolver: (), + }) + } +} + +impl ArchivedBox +where + T::ArchivedMetadata: Default, +{ + #[doc(hidden)] + #[inline] + pub unsafe fn emplace_null(pos: usize, out: *mut Self) { + let (fp, fo) = out_field!(out.0); + RelPtr::emplace_null(pos + fp, fo); + } +} + +impl AsRef for ArchivedBox { + #[inline] + fn as_ref(&self) -> &T { + self.get() + } +} + +impl Borrow for ArchivedBox { + #[inline] + fn borrow(&self) -> &T { + self.get() + } +} + +impl fmt::Debug for ArchivedBox +where + T::ArchivedMetadata: fmt::Debug, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ArchivedBox").field(&self.0).finish() + } +} + +impl Deref for ArchivedBox { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl fmt::Display for ArchivedBox { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl Eq for ArchivedBox {} + +impl hash::Hash for ArchivedBox { + #[inline] + fn hash(&self, state: &mut H) { + self.get().hash(state); + } +} + +impl Ord for ArchivedBox { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.as_ref().cmp(other.as_ref()) + } +} + +impl + ?Sized, U: ArchivePointee + ?Sized> + PartialEq> for ArchivedBox +{ + #[inline] + fn eq(&self, other: &ArchivedBox) -> bool { + self.get().eq(other.get()) + } +} + +impl PartialOrd for ArchivedBox { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.get().partial_cmp(other.get()) + } +} + +impl fmt::Pointer for ArchivedBox { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ptr = self.get() as *const T; + fmt::Pointer::fmt(&ptr, f) + } +} + +/// The resolver for `Box`. +pub struct BoxResolver { + pos: usize, + metadata_resolver: M, +} + +impl BoxResolver { + /// Create a a new [`BoxResolver`] from raw parts. Note that `M` here is ***not*** the same + /// `T` which should be serialized/contained in the resulting [`ArchivedBox`], and is rather + /// a type that can be used to resolve any needed [`ArchivePointee::ArchivedMetadata`] + /// for the serialized pointed-to value. + /// + /// In most cases, you won't need to create a [`BoxResolver`] yourself and can instead obtain it through + /// [`ArchivedBox::serialize_from_ref`] or [`ArchivedBox::serialize_copy_from_slice`]. + /// + /// # Safety + /// + /// Technically no unsafety can happen directly from calling this function, however, passing this as a resolver to + /// [`ArchivedBox`]'s resolving functions absolutely can. In general this should be treated as a semi-private type, as + /// constructing a valid resolver is quite fraught. Please make sure you understand what the implications are before doing it. + /// + /// - `pos`: You must ensure that you serialized and resolved (i.e. [`Serializer::serialize_value`]) + /// a `T` which will be pointed to by the final [`ArchivedBox`] that this resolver will help resolve + /// at the given `pos` within the archive. + /// + /// - `metadata_resolver`: You must also ensure that the given `metadata_resolver` can be used to successfully produce + /// valid [`::ArchivedMetadata`] for that serialized `T`. This means it must either be: + /// - The necessary [`::ArchivedMetadata`] itself, in which case you may use the created + /// `BoxResolver<::ArchivedMetadata>` as a resolver in [`ArchivedBox::resolve_from_raw_parts`] + /// - An [`ArchiveUnsized::MetadataResolver`] obtained from some `value: &U` where `U: ArchiveUnsized`, in which case you + /// must pass that same `value: &U` into [`ArchivedBox::resolve_from_ref`] along with this [`BoxResolver`]. + /// + /// [`::ArchivedMetadata`]: ArchivePointee::ArchivedMetadata + pub unsafe fn from_raw_parts(pos: usize, metadata_resolver: M) -> Self { + Self { + pos, + metadata_resolver, + } + } +} + +#[cfg(feature = "validation")] +const _: () = { + use crate::validation::{ + owned::{CheckOwnedPointerError, OwnedPointerError}, + ArchiveContext, LayoutRaw, + }; + use bytecheck::{CheckBytes, Error}; + use ptr_meta::Pointee; + + impl CheckBytes for ArchivedBox + where + T: ArchivePointee + CheckBytes + LayoutRaw + Pointee + ?Sized, + C: ArchiveContext + ?Sized, + T::ArchivedMetadata: CheckBytes, + C::Error: Error, + { + type Error = CheckOwnedPointerError; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + let rel_ptr = RelPtr::::manual_check_bytes(value.cast(), context) + .map_err(OwnedPointerError::PointerCheckBytesError)?; + let ptr = context + .check_subtree_rel_ptr(rel_ptr) + .map_err(OwnedPointerError::ContextError)?; + + let range = context + .push_prefix_subtree(ptr) + .map_err(OwnedPointerError::ContextError)?; + T::check_bytes(ptr, context).map_err(OwnedPointerError::ValueCheckBytesError)?; + context + .pop_prefix_range(range) + .map_err(OwnedPointerError::ContextError)?; + + Ok(&*value) + } + } +}; diff --git a/src/collections/btree_map/mod.rs b/src/collections/btree_map/mod.rs new file mode 100644 index 0000000..a378d13 --- /dev/null +++ b/src/collections/btree_map/mod.rs @@ -0,0 +1,738 @@ +//! [`Archive`](crate::Archive) implementation for B-tree maps. + +#[cfg(feature = "validation")] +pub mod validation; + +use crate::{Archive, ArchivePointee, Archived, RelPtr}; +use core::{ + borrow::Borrow, + cmp::Ordering, + fmt, + hash::{Hash, Hasher}, + iter::FusedIterator, + marker::PhantomData, + ops::Index, + ptr::NonNull, +}; +use ptr_meta::Pointee; + +#[cfg_attr(feature = "strict", repr(C))] +struct InnerNodeEntry { + ptr: RelPtr, + key: K, +} + +#[cfg_attr(feature = "strict", repr(C))] +struct LeafNodeEntry { + key: K, + value: V, +} + +impl<'a, UK: Archive, UV: Archive> Archive for LeafNodeEntry<&'a UK, &'a UV> { + type Archived = LeafNodeEntry; + type Resolver = (UK::Resolver, UV::Resolver); + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.key); + self.key.resolve(pos + fp, resolver.0, fo); + let (fp, fo) = out_field!(out.value); + self.value.resolve(pos + fp, resolver.1, fo); + } +} + +#[cfg_attr(feature = "strict", repr(C))] +struct NodeHeader { + meta: Archived, + size: Archived, + // For leaf nodes, this points to the next leaf node in order + // For inner nodes, this points to the node in the next layer that's less than the first key in + // this node + ptr: RelPtr, +} + +impl NodeHeader { + #[inline] + fn is_inner(&self) -> bool { + split_meta(from_archived!(self.meta)).0 + } + + #[inline] + fn is_leaf(&self) -> bool { + !split_meta(from_archived!(self.meta)).0 + } + + #[inline] + fn len(&self) -> usize { + split_meta(from_archived!(self.meta)).1 + } +} + +#[inline] +#[cfg(feature = "alloc")] +fn combine_meta(is_inner: bool, len: usize) -> u16 { + if is_inner { + 0x80_00 | len as u16 + } else { + len as u16 + } +} + +#[inline] +fn split_meta(meta: u16) -> (bool, usize) { + (meta & 0x80_00 == 0x80_00, (meta & 0x7F_FF) as usize) +} + +#[cfg_attr(feature = "strict", repr(C))] +struct Node { + header: NodeHeader, + tail: T, +} + +impl Pointee for Node<[T]> { + type Metadata = usize; +} + +impl ArchivePointee for Node<[T]> { + type ArchivedMetadata = Archived; + + #[inline] + fn pointer_metadata(archived: &Self::ArchivedMetadata) -> ::Metadata { + from_archived!(*archived) as usize + } +} + +type InnerNode = Node<[InnerNodeEntry]>; +type LeafNode = Node<[LeafNodeEntry]>; + +struct NodeHeaderData { + meta: u16, + size: usize, + pos: Option, +} + +impl Archive for NodeHeaderData { + type Archived = NodeHeader; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, pos: usize, _: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.meta); + self.meta.resolve(pos + fp, (), fo); + + let (fp, fo) = out_field!(out.size); + self.size.resolve(pos + fp, (), fo); + + let (fp, fo) = out_field!(out.ptr); + RelPtr::emplace(pos + fp, self.pos.unwrap_or(pos + fp), fo); + } +} + +struct InnerNodeEntryData<'a, UK> { + key: &'a UK, +} + +impl<'a, UK: Archive> Archive for InnerNodeEntryData<'a, UK> { + type Archived = InnerNodeEntry; + type Resolver = (usize, UK::Resolver); + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.ptr); + RelPtr::emplace(pos + fp, resolver.0, fo); + let (fp, fo) = out_field!(out.key); + self.key.resolve(pos + fp, resolver.1, fo); + } +} + +enum ClassifiedNode<'a, K, V> { + Inner(&'a InnerNode), + Leaf(&'a LeafNode), +} + +impl NodeHeader { + #[inline] + fn classify(&self) -> ClassifiedNode<'_, K, V> { + if self.is_inner() { + ClassifiedNode::Inner(self.classify_inner()) + } else { + ClassifiedNode::Leaf(self.classify_leaf()) + } + } + + #[inline] + fn classify_inner_ptr(&self) -> *const InnerNode { + ptr_meta::from_raw_parts(self as *const Self as *const (), self.len()) + } + + #[inline] + fn classify_inner(&self) -> &'_ InnerNode { + debug_assert!(self.is_inner()); + unsafe { &*self.classify_inner_ptr() } + } + + #[inline] + fn classify_leaf_ptr(&self) -> *const LeafNode { + ptr_meta::from_raw_parts(self as *const Self as *const (), self.len()) + } + + #[inline] + fn classify_leaf(&self) -> &'_ LeafNode { + debug_assert!(self.is_leaf()); + unsafe { &*self.classify_leaf_ptr() } + } +} + +/// An archived [`BTreeMap`](std::collections::BTreeMap). +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedBTreeMap { + len: Archived, + root: RelPtr, + _phantom: PhantomData<(K, V)>, +} + +/// The resolver for an [`ArchivedBTreeMap`]. +pub struct BTreeMapResolver { + root_pos: usize, +} + +/// The minimum number of entries to place in a leaf node. +/// +/// This value must be greater than 0 +pub const MIN_ENTRIES_PER_LEAF_NODE: usize = 1; + +/// The minimum number of entries to place in an inner node. +/// +/// This value must be greater than 1 +pub const MIN_ENTRIES_PER_INNER_NODE: usize = 2; + +impl ArchivedBTreeMap { + #[inline] + fn root(&self) -> Option> { + if self.is_empty() { + None + } else { + let root = unsafe { &*self.root.as_ptr() }; + Some(root.classify()) + } + } + + #[inline] + fn first(&self) -> NonNull { + if let Some(mut node) = self.root() { + while let ClassifiedNode::Inner(inner) = node { + let next = unsafe { &*inner.header.ptr.as_ptr() }; + node = next.classify(); + } + match node { + ClassifiedNode::Leaf(leaf) => unsafe { + let node = (leaf as *const LeafNode as *mut LeafNode).cast(); + NonNull::new_unchecked(node) + }, + ClassifiedNode::Inner(_) => unsafe { core::hint::unreachable_unchecked() }, + } + } else { + NonNull::dangling() + } + } + + /// Returns `true` if the map contains a value for the specified key. + /// + /// The key may be any borrowed form of the map's key type, but the ordering on the borrowed + /// form _must_ match the ordering on the key type. + #[inline] + pub fn contains_key(&self, key: &Q) -> bool + where + K: Borrow + Ord, + { + self.get_key_value(key).is_some() + } + + /// Returns a reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map’s key type, but the ordering on the borrowed + /// form must match the ordering on the key type. + #[inline] + pub fn get(&self, key: &Q) -> Option<&V> + where + K: Borrow + Ord, + { + self.get_key_value(key).map(|(_, v)| v) + } + + /// Returns the key-value pair corresponding to the supplied key. + /// + /// The supplied key may be any borrowed form of the map’s key type, but the ordering on the + /// borrowed form must match the ordering on the key type. + pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> + where + K: Borrow + Ord, + { + if let Some(mut current) = self.root() { + loop { + match current { + ClassifiedNode::Inner(node) => { + // Binary search for the next node layer + let next = match node + .tail + .binary_search_by(|probe| probe.key.borrow().cmp(k)) + { + Ok(i) => unsafe { &*node.tail[i].ptr.as_ptr() }, + Err(i) => { + if i == 0 { + unsafe { &*node.header.ptr.as_ptr() } + } else { + unsafe { &*node.tail[i - 1].ptr.as_ptr() } + } + } + }; + current = next.classify(); + } + ClassifiedNode::Leaf(node) => { + // Binary search for the value + if let Ok(i) = node + .tail + .binary_search_by(|probe| probe.key.borrow().cmp(k)) + { + let entry = &node.tail[i]; + break Some((&entry.key, &entry.value)); + } else { + break None; + } + } + } + } + } else { + None + } + } + + /// Returns `true` if the map contains no elements. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Gets an iterator over the entries of the map, sorted by key. + #[inline] + pub fn iter(&self) -> Iter<'_, K, V> { + Iter { + inner: RawIter::new(self.first(), 0, self.len()), + } + } + + /// Gets an iterator over the keys of the map, in sorted order. + #[inline] + pub fn keys(&self) -> Keys<'_, K, V> { + Keys { + inner: RawIter::new(self.first(), 0, self.len()), + } + } + + /// Returns the number of items in the archived B-tree map. + #[inline] + pub fn len(&self) -> usize { + from_archived!(self.len) as usize + } + + /// Gets an iterator over the values of the map, in order by key. + #[inline] + pub fn values(&self) -> Values<'_, K, V> { + Values { + inner: RawIter::new(self.first(), 0, self.len()), + } + } + + /// Resolves a B-tree map from its length. + /// + /// # Safety + /// + /// - `len` must be the number of elements that were serialized + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing a B-tree map + #[inline] + pub unsafe fn resolve_from_len( + len: usize, + pos: usize, + resolver: BTreeMapResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.len); + len.resolve(pos + fp, (), fo); + + let (fp, fo) = out_field!(out.root); + RelPtr::emplace(pos + fp, resolver.root_pos, fo); + } +} + +#[cfg(feature = "alloc")] +const _: () = { + use crate::{ser::Serializer, Serialize}; + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + use core::mem; + + impl ArchivedBTreeMap { + /// Serializes an ordered iterator of key-value pairs as a B-tree map. + /// + /// # Safety + /// + /// - Keys returned by the iterator must be unique + /// - Keys must be in reverse sorted order from last to first + pub unsafe fn serialize_from_reverse_iter<'a, UK, UV, S, I>( + mut iter: I, + serializer: &mut S, + ) -> Result + where + UK: 'a + Serialize, + UV: 'a + Serialize, + S: Serializer + ?Sized, + I: ExactSizeIterator, + { + if iter.len() == 0 { + Ok(BTreeMapResolver { root_pos: 0 }) + } else { + // The memory span of a single node should not exceed 4kb to keep everything within + // the distance of a single IO page + const MAX_NODE_SIZE: usize = 4096; + + // The nodes that must go in the next level in reverse order (key, node_pos) + let mut next_level = Vec::new(); + let mut resolvers = Vec::new(); + + while let Some((key, value)) = iter.next() { + // Start a new block + let block_start_pos = serializer.pos(); + + // Serialize the last entry + resolvers.push(( + key, + value, + key.serialize(serializer)?, + value.serialize(serializer)?, + )); + + loop { + // This is an estimate of the block size + // It's not exact because there may be padding to align the node and entries + // slice + let estimated_block_size = serializer.pos() - block_start_pos + + mem::size_of::() + + resolvers.len() * mem::size_of::>(); + + // If we've reached or exceeded the maximum node size and have put enough + // entries in this node, then break + if estimated_block_size >= MAX_NODE_SIZE + && resolvers.len() >= MIN_ENTRIES_PER_LEAF_NODE + { + break; + } + + if let Some((key, value)) = iter.next() { + // Serialize the next entry + resolvers.push(( + key, + value, + key.serialize(serializer)?, + value.serialize(serializer)?, + )); + } else { + break; + } + } + + // Finish the current node + serializer.align(usize::max( + mem::align_of::(), + mem::align_of::>(), + ))?; + let raw_node = NodeHeaderData { + meta: combine_meta(false, resolvers.len()), + size: serializer.pos() - block_start_pos, + // The last element of next_level is the next block we're linked to + pos: next_level.last().map(|&(_, pos)| pos), + }; + + // Add the first key and node position to the next level + next_level.push(( + resolvers.last().unwrap().0, + serializer.resolve_aligned(&raw_node, ())?, + )); + + serializer.align_for::>()?; + for (key, value, key_resolver, value_resolver) in resolvers.drain(..).rev() { + serializer.resolve_aligned( + &LeafNodeEntry { key, value }, + (key_resolver, value_resolver), + )?; + } + } + + // Subsequent levels are populated by serializing node keys from the previous level + // When there's only one node left, that's our root + let mut current_level = Vec::new(); + let mut resolvers = Vec::new(); + while next_level.len() > 1 { + // Our previous next_level becomes our current level, and current_level is + // guaranteed to be empty at this point + mem::swap(&mut current_level, &mut next_level); + + let mut iter = current_level.drain(..); + while iter.len() > 1 { + // Start a new inner block + let block_start_pos = serializer.pos(); + + // When we break, we're guaranteed to have at least one node left + while iter.len() > 1 { + let (key, pos) = iter.next().unwrap(); + + // Serialize the next entry + resolvers.push((key, pos, key.serialize(serializer)?)); + + // Estimate the block size + let estimated_block_size = serializer.pos() - block_start_pos + + mem::size_of::() + + resolvers.len() * mem::size_of::>(); + + // If we've reached or exceeded the maximum node size and have put enough + // keys in this node, then break + if estimated_block_size >= MAX_NODE_SIZE + && resolvers.len() >= MIN_ENTRIES_PER_INNER_NODE + { + break; + } + } + + // Three cases here: + // 1 entry left: use it as the last key + // 2 entries left: serialize the next one and use the last as last to avoid + // putting only one entry in the final block + // 3+ entries left: use next as last, next block will contain at least two + // entries + + if iter.len() == 2 { + let (key, pos) = iter.next().unwrap(); + + // Serialize the next entry + resolvers.push((key, pos, key.serialize(serializer)?)); + } + + // The next item is the first node + let (first_key, first_pos) = iter.next().unwrap(); + + // Finish the current node + serializer.align(usize::max( + mem::align_of::(), + mem::align_of::>(), + ))?; + let node_header = NodeHeaderData { + meta: combine_meta(true, resolvers.len()), + size: serializer.pos() - block_start_pos, + // The pos of the first key is used to make the pointer for inner nodes + pos: Some(first_pos), + }; + + // Add the second key and node position to the next level + next_level.push((first_key, serializer.resolve_aligned(&node_header, ())?)); + + serializer.align_for::>()?; + for (key, pos, resolver) in resolvers.drain(..).rev() { + let inner_node_data = InnerNodeEntryData:: { key }; + serializer.resolve_aligned(&inner_node_data, (pos, resolver))?; + } + } + + debug_assert!(iter.len() == 0); + } + + // The root is only node in the final level + Ok(BTreeMapResolver { + root_pos: next_level[0].1, + }) + } + } + } +}; + +impl fmt::Debug for ArchivedBTreeMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl Index<&'_ Q> for ArchivedBTreeMap +where + K: Borrow + Ord, + Q: Ord + ?Sized, +{ + type Output = V; + + fn index(&self, key: &Q) -> &V { + self.get(key).unwrap() + } +} + +impl<'a, K, V> IntoIterator for &'a ArchivedBTreeMap { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl Eq for ArchivedBTreeMap {} + +impl Hash for ArchivedBTreeMap { + #[inline] + fn hash(&self, state: &mut H) { + for pair in self.iter() { + pair.hash(state); + } + } +} + +impl Ord for ArchivedBTreeMap { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other.iter()) + } +} + +impl PartialEq for ArchivedBTreeMap { + #[inline] + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + false + } else { + self.iter().zip(other.iter()).all(|(a, b)| a.eq(&b)) + } + } +} + +impl PartialOrd for ArchivedBTreeMap { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other.iter()) + } +} + +// RawIter + +struct RawIter<'a, K, V> { + leaf: NonNull, + index: usize, + remaining: usize, + _phantom: PhantomData<(&'a K, &'a V)>, +} + +impl<'a, K, V> RawIter<'a, K, V> { + fn new(leaf: NonNull, index: usize, remaining: usize) -> Self { + Self { + leaf, + index, + remaining, + _phantom: PhantomData, + } + } +} + +impl<'a, K, V> Iterator for RawIter<'a, K, V> { + type Item = (&'a K, &'a V); + + #[inline] + fn next(&mut self) -> Option { + if self.remaining == 0 { + None + } else { + unsafe { + // SAFETY: self.leaf is valid when self.remaining > 0 + // SAFETY: self.leaf always points to a leaf node header + let leaf = self.leaf.as_ref().classify_leaf::(); + if self.index == leaf.tail.len() { + self.index = 0; + // SAFETY: when self.remaining > 0 this is guaranteed to point to a leaf node + self.leaf = NonNull::new_unchecked(leaf.header.ptr.as_ptr() as *mut _); + } + let result = &self.leaf.as_ref().classify_leaf().tail[self.index]; + self.index += 1; + self.remaining -= 1; + Some((&result.key, &result.value)) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.remaining, Some(self.remaining)) + } +} + +impl<'a, K, V> ExactSizeIterator for RawIter<'a, K, V> {} +impl<'a, K, V> FusedIterator for RawIter<'a, K, V> {} + +/// An iterator over the key-value pairs of an archived B-tree map. +pub struct Iter<'a, K, V> { + inner: RawIter<'a, K, V>, +} + +impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + #[inline] + fn next(&mut self) -> Option { + self.inner.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {} +impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} + +/// An iterator over the keys of an archived B-tree map. +pub struct Keys<'a, K, V> { + inner: RawIter<'a, K, V>, +} + +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|(k, _)| k) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> {} +impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} + +/// An iterator over the values of an archived B-tree map. +pub struct Values<'a, K, V> { + inner: RawIter<'a, K, V>, +} + +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|(_, v)| v) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> {} +impl<'a, K, V> FusedIterator for Values<'a, K, V> {} diff --git a/src/collections/btree_map/validation.rs b/src/collections/btree_map/validation.rs new file mode 100644 index 0000000..90199f4 --- /dev/null +++ b/src/collections/btree_map/validation.rs @@ -0,0 +1,638 @@ +//! Validation implementation for BTreeMap. + +use super::{ + ArchivedBTreeMap, ClassifiedNode, InnerNode, InnerNodeEntry, LeafNode, LeafNodeEntry, Node, + NodeHeader, MIN_ENTRIES_PER_INNER_NODE, MIN_ENTRIES_PER_LEAF_NODE, +}; +use crate::{ + rel_ptr::RelPtr, + validation::{ArchiveContext, LayoutRaw}, + Archived, Fallible, +}; +use bytecheck::{CheckBytes, Error}; +use core::{ + alloc::{Layout, LayoutError}, + convert::{Infallible, TryFrom}, + fmt, + hint::unreachable_unchecked, + ptr, +}; +use ptr_meta::Pointee; + +impl CheckBytes for InnerNodeEntry +where + K: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, +{ + type Error = K::Error; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + RelPtr::manual_check_bytes(ptr::addr_of!((*value).ptr), context) + .unwrap_or_else(|_| core::hint::unreachable_unchecked()); + K::check_bytes(ptr::addr_of!((*value).key), context)?; + + Ok(&*value) + } +} + +/// An error that can occur while checking a leaf node entry. +#[derive(Debug)] +pub enum LeafNodeEntryError { + /// An error occurred while checking the entry's key. + KeyCheckError(K), + /// An error occurred while checking the entry's value. + ValueCheckError(V), +} + +impl fmt::Display for LeafNodeEntryError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LeafNodeEntryError::KeyCheckError(e) => write!(f, "key check error: {}", e), + LeafNodeEntryError::ValueCheckError(e) => write!(f, "value check error: {}", e), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for LeafNodeEntryError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::KeyCheckError(e) => Some(e as &dyn Error), + Self::ValueCheckError(e) => Some(e as &dyn Error), + } + } + } +}; + +impl CheckBytes for LeafNodeEntry +where + K: CheckBytes, + V: CheckBytes, + C: Fallible + ?Sized, + C::Error: Error, +{ + type Error = LeafNodeEntryError; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + K::check_bytes(ptr::addr_of!((*value).key), context) + .map_err(LeafNodeEntryError::KeyCheckError)?; + V::check_bytes(ptr::addr_of!((*value).value), context) + .map_err(LeafNodeEntryError::ValueCheckError)?; + Ok(&*value) + } +} + +/// Errors that can occur while checking an archived B-tree. +#[derive(Debug)] +pub enum ArchivedBTreeMapError { + /// An error occurred while checking the bytes of a key + KeyCheckError(K), + /// An error occurred while checking the bytes of a value + ValueCheckError(V), + /// The number of entries in the inner node is less than the minimum number of entries required + TooFewInnerNodeEntries(usize), + /// The number of entries in the leaf node is less than the minimum number of entries + TooFewLeafNodeEntries(usize), + /// An error occurred while checking the entries of an inner node + CheckInnerNodeEntryError { + /// The index of the inner node entry + index: usize, + /// The inner error that occurred + inner: K, + }, + /// An error occurred while checking the entries of a leaf node + CheckLeafNodeEntryError { + /// The index of the leaf node entry + index: usize, + /// The inner error that occurred + inner: LeafNodeEntryError, + }, + /// The size of an inner node was invalid + InvalidNodeSize(usize), + /// The child of an inner node had a first key that did not match the inner node's key + MismatchedInnerChildKey, + /// The leaf level of the B-tree contained an inner node + InnerNodeInLeafLevel, + /// The leaves of the B-tree were not all located at the same depth + InvalidLeafNodeDepth { + /// The depth of the first leaf node in the tree + expected: usize, + /// The depth of the invalid leaf node + actual: usize, + }, + /// A leaf node did not contain entries in sorted order + UnsortedLeafNodeEntries, + /// A leaf node is not linked after a node despite being the next leaf node + UnlinkedLeafNode, + /// A leaf node with lesser keys is linked after a leaf node with greater keys + UnsortedLeafNode, + /// The forward pointer of the last leaf did not have an offset of 0 + LastLeafForwardPointerNotNull, + /// The number of entries the B-tree claims to have does not match the actual number of entries + LengthMismatch { + /// The number of entries the B-tree claims to have + expected: usize, + /// The actual number of entries in the B-tree + actual: usize, + }, + /// The keys for an inner node were incorrect + IncorrectChildKey, + /// An context error occurred + ContextError(C), +} + +impl From for ArchivedBTreeMapError { + fn from(_: Infallible) -> Self { + unsafe { core::hint::unreachable_unchecked() } + } +} + +impl fmt::Display for ArchivedBTreeMapError +where + K: fmt::Display, + V: fmt::Display, + C: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::KeyCheckError(e) => write!(f, "key check error: {}", e), + Self::ValueCheckError(e) => write!(f, "value check error: {}", e), + Self::TooFewInnerNodeEntries(n) => write!( + f, + "too few inner node entries (expected at least {}): {}", + MIN_ENTRIES_PER_INNER_NODE, n + ), + Self::TooFewLeafNodeEntries(n) => write!( + f, + "too few leaf node entries (expected at least {}): {}", + MIN_ENTRIES_PER_LEAF_NODE, n, + ), + Self::CheckInnerNodeEntryError { index, inner } => write!( + f, + "inner node entry check error: index {}, error {}", + index, inner + ), + Self::CheckLeafNodeEntryError { index, inner } => write!( + f, + "leaf node entry check error: index {}, error {}", + index, inner + ), + Self::InvalidNodeSize(n) => write!(f, "invalid node size: {}", n), + Self::MismatchedInnerChildKey => write!(f, "mismatched inner child key"), + Self::InnerNodeInLeafLevel => write!(f, "inner node in leaf level"), + Self::InvalidLeafNodeDepth { expected, actual } => write!( + f, + "expected leaf node depth {} but found leaf node depth {}", + expected, actual, + ), + Self::UnsortedLeafNodeEntries => write!(f, "leaf node contains keys in unsorted order"), + Self::UnlinkedLeafNode => write!(f, "leaf nodes are not linked in the sorted order"), + Self::UnsortedLeafNode => write!(f, "leaf nodes are not linked in sorted order"), + Self::LastLeafForwardPointerNotNull => { + write!(f, "the forward pointer of the last leaf was not null") + } + Self::LengthMismatch { expected, actual } => write!( + f, + "expected {} entries but there were actually {} entries", + expected, actual, + ), + Self::IncorrectChildKey => write!(f, "incorrect child key in inner node"), + Self::ContextError(e) => write!(f, "context error: {}", e), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for ArchivedBTreeMapError + where + K: Error + 'static, + V: Error + 'static, + C: Error + 'static, + { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::KeyCheckError(e) => Some(e as &dyn Error), + Self::ValueCheckError(e) => Some(e as &dyn Error), + Self::TooFewInnerNodeEntries(_) => None, + Self::TooFewLeafNodeEntries(_) => None, + Self::CheckInnerNodeEntryError { inner, .. } => Some(inner as &dyn Error), + Self::CheckLeafNodeEntryError { inner, .. } => Some(inner as &dyn Error), + Self::InvalidNodeSize(_) => None, + Self::MismatchedInnerChildKey => None, + Self::InnerNodeInLeafLevel => None, + Self::InvalidLeafNodeDepth { .. } => None, + Self::UnsortedLeafNodeEntries => None, + Self::UnlinkedLeafNode => None, + Self::UnsortedLeafNode => None, + Self::LastLeafForwardPointerNotNull => None, + Self::LengthMismatch { .. } => None, + Self::IncorrectChildKey => None, + Self::ContextError(e) => Some(e as &dyn Error), + } + } + } +}; + +impl LayoutRaw for Node<[T]> { + fn layout_raw(metadata: ::Metadata) -> Result { + let result = Layout::new::() + .extend(Layout::array::(metadata).unwrap())? + .0; + #[cfg(not(feature = "strict"))] + { + Ok(result) + } + #[cfg(feature = "strict")] + { + Ok(result.pad_to_align()) + } + } +} + +type ABTMError = ArchivedBTreeMapError< + >::Error, + >::Error, + ::Error, +>; + +impl NodeHeader { + #[inline] + unsafe fn manual_check_bytes<'a, K, V, C>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, ABTMError> + where + K: CheckBytes, + V: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, + { + let raw_node = Self::manual_check_header(value, context) + .map_err(ArchivedBTreeMapError::ContextError)?; + + let node_layout = if raw_node.is_inner() { + InnerNode::::layout_raw(ptr_meta::metadata(raw_node.classify_inner_ptr::())) + .map_err(C::wrap_layout_error) + .map_err(ArchivedBTreeMapError::ContextError)? + } else { + LeafNode::::layout_raw(ptr_meta::metadata(raw_node.classify_leaf_ptr::())) + .map_err(C::wrap_layout_error) + .map_err(ArchivedBTreeMapError::ContextError)? + }; + + context + .bounds_check_subtree_ptr_layout((raw_node as *const NodeHeader).cast(), &node_layout) + .map_err(ArchivedBTreeMapError::ContextError)?; + + Self::manual_check_contents::(raw_node, context)?; + + Ok(raw_node) + } + + #[inline] + unsafe fn manual_check_header<'a, C>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, C::Error> + where + C: ArchiveContext + ?Sized, + C::Error: Error, + { + CheckBytes::check_bytes(ptr::addr_of!((*value).meta), context).map_err( + // SAFETY: Infallible cannot exist + |_: Infallible| unreachable_unchecked(), + )?; + CheckBytes::check_bytes(ptr::addr_of!((*value).size), context).map_err( + // SAFETY: Infallible cannot exist + |_: Infallible| unreachable_unchecked(), + )?; + RelPtr::manual_check_bytes(ptr::addr_of!((*value).ptr), context).map_err( + // SAFETY: Infallible cannot exist + |_: Infallible| unreachable_unchecked(), + )?; + + // All the fields have been checked and this is a valid RawNode + Ok(&*value) + } + + #[inline] + unsafe fn manual_check_contents( + raw_node: &Self, + context: &mut C, + ) -> Result<(), ABTMError> + where + K: CheckBytes, + V: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, + { + // Now that the fields have been checked, we can start checking the specific subtype + let root = (raw_node as *const Self).cast(); + let size = from_archived!(raw_node.size) as usize; + let offset = + -isize::try_from(size).map_err(|_| ArchivedBTreeMapError::InvalidNodeSize(size))?; + let start = context + .check_ptr(root, offset, ()) + .map_err(ArchivedBTreeMapError::ContextError)?; + + // Push a new suffix range and check the inner or leaf part + let range = context + .push_suffix_subtree_range(start, root) + .map_err(ArchivedBTreeMapError::ContextError)?; + if raw_node.is_inner() { + InnerNode::manual_check_bytes::(raw_node.classify_inner_ptr::(), context)?; + } else { + CheckBytes::check_bytes(raw_node.classify_leaf_ptr::(), context)?; + } + context + .pop_suffix_range(range) + .map_err(ArchivedBTreeMapError::ContextError)?; + + Ok(()) + } +} + +impl InnerNode { + #[allow(clippy::type_complexity)] + fn verify_integrity<'a, V, C>( + &'a self, + ) -> Result<&K, ArchivedBTreeMapError> + where + K: CheckBytes + PartialEq, + V: CheckBytes + 'a, + C: Fallible + ?Sized, + { + for entry in self.tail.iter() { + let child = unsafe { &*entry.ptr.as_ptr() }.classify::(); + let first_key = match child { + ClassifiedNode::Inner(c) => c.verify_integrity::()?, + ClassifiedNode::Leaf(c) => &c.tail[0].key, + }; + if !entry.key.eq(first_key) { + return Err(ArchivedBTreeMapError::IncorrectChildKey); + } + } + + let least_child = unsafe { &*self.header.ptr.as_ptr() }.classify::(); + let first_key = match least_child { + ClassifiedNode::Inner(c) => c.verify_integrity::()?, + ClassifiedNode::Leaf(c) => &c.tail[0].key, + }; + + Ok(first_key) + } + + #[inline] + unsafe fn manual_check_bytes<'a, V, C>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, ABTMError> + where + K: CheckBytes, + V: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, + { + // meta, size, and ptr have already been checked by the check_bytes for RawNode + let len = ptr_meta::metadata(value); + + // Each inner node actually contains one more entry that the length indicates (the least + // child pointer) + if len + 1 < MIN_ENTRIES_PER_INNER_NODE { + return Err(ArchivedBTreeMapError::TooFewInnerNodeEntries(len + 1)); + } + + // The subtree range has already been set up for us so we can just check our tail + let tail_ptr = ptr::addr_of!((*value).tail) as *const InnerNodeEntry; + for index in (0..len).rev() { + CheckBytes::check_bytes(tail_ptr.add(index), context).map_err(|inner| { + ArchivedBTreeMapError::CheckInnerNodeEntryError { index, inner } + })?; + } + + Ok(&*value) + } +} + +impl CheckBytes for LeafNode +where + K: CheckBytes, + V: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, +{ + type Error = ArchivedBTreeMapError; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + // meta, size, and ptr have already been checked by the check_bytes for RawNode + let len = ptr_meta::metadata(value); + + if len < MIN_ENTRIES_PER_LEAF_NODE { + return Err(ArchivedBTreeMapError::TooFewLeafNodeEntries(len)); + } + + // The subtree range has already been set up for us so we can just check our tail + let tail_ptr = ptr::addr_of!((*value).tail) as *const LeafNodeEntry; + for index in (0..len).rev() { + CheckBytes::check_bytes(tail_ptr.add(index), context) + .map_err(|inner| ArchivedBTreeMapError::CheckLeafNodeEntryError { index, inner })?; + } + + Ok(&*value) + } +} + +#[cfg(feature = "alloc")] +const _: () = { + #[cfg(not(feature = "std"))] + use alloc::collections::VecDeque; + #[cfg(feature = "std")] + use std::collections::VecDeque; + + impl CheckBytes for ArchivedBTreeMap + where + K: CheckBytes + Ord, + V: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, + { + type Error = ArchivedBTreeMapError; + + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + let len = from_archived!(*Archived::::check_bytes( + ptr::addr_of!((*value).len), + context, + )?) as usize; + + if len > 0 { + let root_rel_ptr = + RelPtr::manual_check_bytes(ptr::addr_of!((*value).root), context)?; + + // Walk all the inner nodes, claim their memory, and check their contents + let mut nodes = VecDeque::new(); + let root_ptr = context + .check_subtree_rel_ptr(root_rel_ptr) + .map_err(ArchivedBTreeMapError::ContextError)?; + + // Before checking all the nodes, we have to push an additional prefix subtree with + // the root node + // Otherwise, when the suffix subtree of the root node is popped it will remove any + // trailing suffix space that should be checked by subsequent fields + let root = NodeHeader::manual_check_header(root_ptr, context) + .map_err(ArchivedBTreeMapError::ContextError)?; + + // To push the subtree, we need to know the real size of the root node + // Since the header is checked, we can just classify the pointer and use layout_raw + let root_layout = if root.is_inner() { + InnerNode::::layout_raw(ptr_meta::metadata(root.classify_inner_ptr::())) + .map_err(C::wrap_layout_error) + .map_err(ArchivedBTreeMapError::ContextError)? + } else { + LeafNode::::layout_raw(ptr_meta::metadata( + root.classify_leaf_ptr::(), + )) + .map_err(C::wrap_layout_error) + .map_err(ArchivedBTreeMapError::ContextError)? + }; + + // Because the layout of the subtree is dynamic, we need to bounds check the layout + // declared by the root node. + context + .bounds_check_subtree_ptr_layout(root_ptr.cast(), &root_layout) + .map_err(ArchivedBTreeMapError::ContextError)?; + + // Now we can push the prefix subtree range. + let nodes_range = context + .push_prefix_subtree_range( + root_ptr.cast(), + root_ptr.cast::().add(root_layout.size()), + ) + .map_err(ArchivedBTreeMapError::ContextError)?; + + // Now we're finally ready to check node subtrees + NodeHeader::manual_check_contents::(root, context)?; + + nodes.push_back((root, 0)); + + while let Some(&(node, depth)) = nodes.front() { + // Break when a leaf is found + if !node.is_inner() { + break; + } + nodes.pop_front(); + let inner = node.classify_inner::(); + + let child_ptr = context + .check_subtree_rel_ptr(&inner.header.ptr) + .map_err(ArchivedBTreeMapError::ContextError)?; + let child = NodeHeader::manual_check_bytes::(child_ptr, context)?; + nodes.push_back((child, depth + 1)); + + // The invariant that this node contains keys less than the first key of this node will + // be checked when we iterate through the leaf nodes in order and check ordering + for entry in inner.tail.iter() { + let child_ptr = context + .check_subtree_rel_ptr(&entry.ptr) + .map_err(ArchivedBTreeMapError::ContextError)?; + let child = NodeHeader::manual_check_bytes::(child_ptr, context)?; + nodes.push_back((child, depth + 1)); + } + } + + // We're done checking node subtrees now + context + .pop_prefix_range(nodes_range) + .map_err(ArchivedBTreeMapError::ContextError)?; + + // The remaining nodes must all be leaf nodes + let mut entry_count = 0; + for (node, depth) in nodes.iter() { + if !node.is_leaf() { + return Err(ArchivedBTreeMapError::InnerNodeInLeafLevel); + } + let leaf = node.classify_leaf::(); + + // Leaf nodes must all be the same depth + let expected_depth = nodes.front().unwrap().1; + if *depth != expected_depth { + return Err(ArchivedBTreeMapError::InvalidLeafNodeDepth { + expected: expected_depth, + actual: *depth, + }); + } + + // They must contain entries in sorted order + for (prev, next) in leaf.tail.iter().zip(leaf.tail.iter().skip(1)) { + if next.key < prev.key { + return Err(ArchivedBTreeMapError::UnsortedLeafNodeEntries); + } + } + + // Keep track of the number of entries found + entry_count += leaf.tail.len(); + } + + for (i, (node, _)) in nodes.iter().enumerate() { + let leaf = node.classify_leaf::(); + + // And they must link together in sorted order + if i < nodes.len() - 1 { + let next_ptr = context + .check_rel_ptr(&leaf.header.ptr) + .map_err(ArchivedBTreeMapError::ContextError)?; + let next_node = nodes[i + 1].0.classify_leaf(); + + if next_ptr != (next_node as *const LeafNode).cast() { + return Err(ArchivedBTreeMapError::UnlinkedLeafNode); + } + if next_node.tail[0].key < leaf.tail[leaf.tail.len() - 1].key { + return Err(ArchivedBTreeMapError::UnsortedLeafNode); + } + } else { + // The last node must have a null pointer forward + if !leaf.header.ptr.is_null() { + return Err(ArchivedBTreeMapError::LastLeafForwardPointerNotNull); + } + } + } + + // Make sure that the number of entries matches the length + if entry_count != len { + return Err(ArchivedBTreeMapError::LengthMismatch { + expected: len, + actual: entry_count, + }); + } + + // Make sure that inner nodes are constructed appropriately + if root.is_inner() { + root.classify_inner::().verify_integrity::()?; + } + } + + Ok(&*value) + } + } +}; diff --git a/src/collections/btree_set.rs b/src/collections/btree_set.rs new file mode 100644 index 0000000..e976628 --- /dev/null +++ b/src/collections/btree_set.rs @@ -0,0 +1,118 @@ +//! [`Archive`](crate::Archive) implementation for B-tree sets. + +use crate::collections::btree_map::{ArchivedBTreeMap, BTreeMapResolver, Keys}; +use core::{borrow::Borrow, fmt}; + +/// An archived `BTreeSet`. This is a wrapper around a B-tree map with the same key and a value of +/// `()`. +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct ArchivedBTreeSet(ArchivedBTreeMap); + +impl ArchivedBTreeSet { + /// Returns `true` if the set contains a value for the specified key. + /// + /// The key may be any borrowed form of the set's key type, but the ordering on the borrowed + /// form _must_ match the ordering on the key type. + #[inline] + pub fn contains_key(&self, key: &Q) -> bool + where + K: Borrow + Ord, + { + self.0.contains_key(key) + } + + /// Returns a reference to the value int he set, if any, that is equal to the given value. + /// + /// The value may be any borrowed form of the set's value type, but the ordering on the borrowed + /// form _must_ match the ordering on the value type. + #[inline] + pub fn get(&self, value: &Q) -> Option<&K> + where + K: Borrow + Ord, + { + self.0.get_key_value(value).map(|(key, _)| key) + } + + /// Returns `true` if the set contains no elements. + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Gets an iterator over the keys of the set, in sorted order. + #[inline] + pub fn iter(&self) -> Keys { + self.0.keys() + } + + /// Returns the number of items in the archived B-tree set. + #[inline] + pub fn len(&self) -> usize { + self.0.len() + } + + /// Resolves a B-tree set from its length. + /// + /// # Safety + /// + /// - `len` must be the number of elements that were serialized + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing a B-tree set + #[inline] + pub unsafe fn resolve_from_len( + len: usize, + pos: usize, + resolver: BTreeSetResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.0); + ArchivedBTreeMap::resolve_from_len(len, pos + fp, resolver.0, fo); + } +} + +#[cfg(feature = "alloc")] +const _: () = { + use crate::{ser::Serializer, Serialize}; + + impl ArchivedBTreeSet { + /// Serializes an ordered iterator of key-value pairs as a B-tree map. + /// + /// # Safety + /// + /// - Keys returned by the iterator must be unique + /// - Keys must be in reverse sorted order from last to first + pub unsafe fn serialize_from_reverse_iter<'a, UK, S, I>( + iter: I, + serializer: &mut S, + ) -> Result + where + UK: 'a + Serialize, + S: Serializer + ?Sized, + I: ExactSizeIterator, + { + Ok(BTreeSetResolver( + ArchivedBTreeMap::serialize_from_reverse_iter(iter.map(|x| (x, &())), serializer)?, + )) + } + } +}; + +impl fmt::Debug for ArchivedBTreeSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} + +impl<'a, K> IntoIterator for &'a ArchivedBTreeSet { + type Item = &'a K; + type IntoIter = Keys<'a, K, ()>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +/// The resolver for archived B-tree sets. +pub struct BTreeSetResolver(BTreeMapResolver); diff --git a/src/collections/hash_index/mod.rs b/src/collections/hash_index/mod.rs new file mode 100644 index 0000000..92c3d3a --- /dev/null +++ b/src/collections/hash_index/mod.rs @@ -0,0 +1,254 @@ +//! A helper type that archives index data for hashed collections using +//! [compress, hash and displace](http://cmph.sourceforge.net/papers/esa09.pdf). + +use crate::{Archive, Archived, RelPtr}; +use core::{ + fmt, + hash::{Hash, Hasher}, + slice, +}; + +/// The hash builder for archived hash indexes. +pub use seahash::SeaHasher as HashBuilder; + +#[cfg(feature = "validation")] +pub mod validation; + +/// An archived hash index. +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedHashIndex { + len: Archived, + displace: RelPtr>, +} + +impl ArchivedHashIndex { + /// Gets the number of items in the hash index. + #[inline] + pub const fn len(&self) -> usize { + from_archived!(self.len) as usize + } + + #[inline] + fn make_hasher() -> HashBuilder { + HashBuilder::with_seeds( + 0x08576fb6170b5f5f, + 0x587775eeb84a7e46, + 0xac701115428ee569, + 0x910feb91b92bb1cd, + ) + } + + /// Gets the hasher for this hash index. The hasher for all archived hash indexes is the same + /// for reproducibility. + #[inline] + pub fn hasher(&self) -> HashBuilder { + Self::make_hasher() + } + + #[inline] + fn displace_slice(&self) -> &[Archived] { + unsafe { slice::from_raw_parts(self.displace.as_ptr(), self.len()) } + } + + #[inline] + fn displace(&self, index: usize) -> u32 { + from_archived!(self.displace_slice()[index]) + } + + /// Returns the index where a key may be located in the hash index. + /// + /// The hash index does not have access to the keys used to build it, so the key at the returned + /// index must be checked for equality. + #[inline] + pub fn index(&self, k: &K) -> Option { + if self.is_empty() { + return None; + } + let mut hasher = self.hasher(); + k.hash(&mut hasher); + let displace_index = hasher.finish() % self.len() as u64; + let displace = self.displace(displace_index as usize); + + if displace == u32::MAX { + None + } else if displace & 0x80_00_00_00 == 0 { + Some(displace as usize) + } else { + let mut hasher = self.hasher(); + displace.hash(&mut hasher); + k.hash(&mut hasher); + let index = hasher.finish() % self.len() as u64; + Some(index as usize) + } + } + + /// Returns whether there are no items in the hash index. + #[inline] + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Resolves an archived hash index from a given length and parameters. + /// + /// # Safety + /// + /// - `len` must be the number of elements in the hash index + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of building and serializing a hash index + #[inline] + pub unsafe fn resolve_from_len( + len: usize, + pos: usize, + resolver: HashIndexResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.len); + len.resolve(pos + fp, (), fo); + + let (fp, fo) = out_field!(out.displace); + RelPtr::emplace(pos + fp, resolver.displace_pos, fo); + } +} + +#[cfg(feature = "alloc")] +const _: () = { + use crate::{ + ser::{ScratchSpace, Serializer}, + ScratchVec, + }; + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + use core::{ + cmp::Reverse, + mem::{size_of, MaybeUninit}, + }; + + impl ArchivedHashIndex { + /// Builds and serializes a hash index from an iterator of key-value pairs. + /// + /// # Safety + /// + /// - The keys returned by the iterator must be unique. + /// - `entries` must have a capacity of `iter.len()` entries. + #[allow(clippy::type_complexity)] + pub unsafe fn build_and_serialize<'a, K, V, S, I>( + iter: I, + serializer: &mut S, + entries: &mut ScratchVec>, + ) -> Result + where + K: 'a + Hash, + V: 'a, + S: Serializer + ScratchSpace + ?Sized, + I: ExactSizeIterator, + { + let len = iter.len(); + + let mut bucket_size = ScratchVec::new(serializer, len)?; + for _ in 0..len { + bucket_size.push(0u32); + } + + let mut displaces = ScratchVec::new(serializer, len)?; + + for (key, value) in iter { + let mut hasher = Self::make_hasher(); + key.hash(&mut hasher); + let displace = (hasher.finish() % len as u64) as u32; + displaces.push((displace, (key, value))); + bucket_size[displace as usize] += 1; + } + + displaces + .sort_by_key(|&(displace, _)| (Reverse(bucket_size[displace as usize]), displace)); + + let mut occupied = ScratchVec::new(serializer, len)?; + for _ in 0..len { + occupied.push(false); + } + + let mut displacements = ScratchVec::new(serializer, len)?; + for _ in 0..len { + displacements.push(to_archived!(u32::MAX)); + } + + let mut first_empty = 0; + let mut assignments = Vec::with_capacity(8); + + let mut start = 0; + while start < displaces.len() { + let displace = displaces[start].0; + let bucket_size = bucket_size[displace as usize] as usize; + let end = start + bucket_size; + let bucket = &displaces[start..end]; + start = end; + + if bucket_size > 1 { + 'find_seed: for seed in 0x80_00_00_00u32..=0xFF_FF_FF_FFu32 { + let mut base_hasher = Self::make_hasher(); + seed.hash(&mut base_hasher); + + assignments.clear(); + + for &(_, (key, _)) in bucket.iter() { + let mut hasher = base_hasher; + key.hash(&mut hasher); + let index = (hasher.finish() % len as u64) as u32; + if occupied[index as usize] || assignments.contains(&index) { + continue 'find_seed; + } else { + assignments.push(index); + } + } + + for i in 0..bucket_size { + occupied[assignments[i] as usize] = true; + entries[assignments[i] as usize] + .as_mut_ptr() + .write(bucket[i].1); + } + displacements[displace as usize] = to_archived!(seed); + break; + } + } else { + let offset = occupied[first_empty..] + .iter() + .position(|value| !value) + .unwrap(); + first_empty += offset; + occupied[first_empty] = true; + entries[first_empty].as_mut_ptr().write(bucket[0].1); + displacements[displace as usize] = to_archived!(first_empty as u32); + first_empty += 1; + } + } + + // Write displacements + let displace_pos = serializer.align_for::>()?; + let displacements_slice = slice::from_raw_parts( + displacements.as_ptr().cast::(), + len * size_of::>(), + ); + serializer.write(displacements_slice)?; + + // Free scratch vecs + displacements.free(serializer)?; + occupied.free(serializer)?; + displaces.free(serializer)?; + bucket_size.free(serializer)?; + + Ok(HashIndexResolver { displace_pos }) + } + } +}; + +impl fmt::Debug for ArchivedHashIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.displace_slice()).finish() + } +} + +/// The resolver for an archived hash index. +pub struct HashIndexResolver { + displace_pos: usize, +} diff --git a/src/collections/hash_index/validation.rs b/src/collections/hash_index/validation.rs new file mode 100644 index 0000000..3515882 --- /dev/null +++ b/src/collections/hash_index/validation.rs @@ -0,0 +1,120 @@ +//! Validation implementation for ArchivedHashIndex. + +use crate::{collections::ArchivedHashIndex, validation::ArchiveContext, Archived, RelPtr}; +use bytecheck::{CheckBytes, Error, SliceCheckError}; +use core::{ + alloc::{Layout, LayoutError}, + convert::Infallible, + fmt, ptr, +}; + +/// Errors that can occur while checking an archived hash index. +#[derive(Debug)] +pub enum HashIndexError { + /// An error occurred while checking the layouts of displacements or entries + LayoutError(LayoutError), + /// A displacement value was invalid + InvalidDisplacement { + /// The index of the entry with an invalid displacement + index: usize, + /// The value of the entry at the invalid location + value: u32, + }, + /// A bounds error occurred + ContextError(C), +} + +impl From for HashIndexError { + #[inline] + fn from(e: LayoutError) -> Self { + Self::LayoutError(e) + } +} + +impl From for HashIndexError { + #[inline] + fn from(_: Infallible) -> Self { + unsafe { core::hint::unreachable_unchecked() } + } +} + +impl From> for HashIndexError { + #[inline] + fn from(_: SliceCheckError) -> Self { + unsafe { core::hint::unreachable_unchecked() } + } +} + +impl fmt::Display for HashIndexError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + HashIndexError::LayoutError(e) => write!(f, "layout error: {}", e), + HashIndexError::InvalidDisplacement { index, value } => write!( + f, + "invalid displacement: value {} at index {}", + value, index, + ), + HashIndexError::ContextError(e) => e.fmt(f), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for HashIndexError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + HashIndexError::LayoutError(e) => Some(e as &dyn Error), + HashIndexError::InvalidDisplacement { .. } => None, + HashIndexError::ContextError(e) => Some(e as &dyn Error), + } + } + } +}; + +impl CheckBytes for ArchivedHashIndex +where + C::Error: Error, +{ + type Error = HashIndexError; + + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + let len = from_archived!(*Archived::::check_bytes( + ptr::addr_of!((*value).len), + context, + )?) as usize; + Layout::array::>(len)?; + + let displace_rel_ptr = + RelPtr::manual_check_bytes(ptr::addr_of!((*value).displace), context)?; + let displace_ptr = context + .check_subtree_ptr::<[Archived]>( + displace_rel_ptr.base(), + displace_rel_ptr.offset(), + len, + ) + .map_err(HashIndexError::ContextError)?; + + let range = context + .push_prefix_subtree(displace_ptr) + .map_err(HashIndexError::ContextError)?; + let displace = <[Archived]>::check_bytes(displace_ptr, context)?; + context + .pop_prefix_range(range) + .map_err(HashIndexError::ContextError)?; + + for (i, &d) in displace.iter().enumerate() { + let d = from_archived!(d); + if d as usize >= len && d < 0x80_00_00_00 { + return Err(HashIndexError::InvalidDisplacement { index: i, value: d }); + } + } + + Ok(&*value) + } +} diff --git a/src/collections/hash_map/mod.rs b/src/collections/hash_map/mod.rs new file mode 100644 index 0000000..64791ff --- /dev/null +++ b/src/collections/hash_map/mod.rs @@ -0,0 +1,522 @@ +//! Archived hash map implementation. +//! +//! During archiving, hashmaps are built into minimal perfect hashmaps using +//! [compress, hash and displace](http://cmph.sourceforge.net/papers/esa09.pdf). + +#[cfg(feature = "validation")] +pub mod validation; + +use crate::{ + collections::{ + hash_index::{ArchivedHashIndex, HashIndexResolver}, + util::Entry, + }, + RelPtr, +}; +#[cfg(feature = "alloc")] +use crate::{ + ser::{ScratchSpace, Serializer}, + Serialize, +}; +use core::{ + borrow::Borrow, fmt, hash::Hash, iter::FusedIterator, marker::PhantomData, ops::Index, pin::Pin, +}; + +/// An archived `HashMap`. +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedHashMap { + index: ArchivedHashIndex, + entries: RelPtr>, +} + +impl ArchivedHashMap { + /// Gets the number of items in the hash map. + #[inline] + pub const fn len(&self) -> usize { + self.index.len() + } + + /// Gets the hasher for this hashmap. The hasher for all archived hashmaps is the same for + /// reproducibility. + #[inline] + pub fn hasher(&self) -> seahash::SeaHasher { + self.index.hasher() + } + + #[inline] + unsafe fn entry(&self, index: usize) -> &Entry { + &*self.entries.as_ptr().add(index) + } + + #[inline] + unsafe fn entry_mut(&mut self, index: usize) -> &mut Entry { + &mut *self.entries.as_mut_ptr().add(index) + } + + #[inline] + fn find(&self, k: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq, + { + self.index.index(k).and_then(|i| { + let entry = unsafe { self.entry(i) }; + if entry.key.borrow() == k { + Some(i) + } else { + None + } + }) + } + + /// Finds the key-value entry for a key. + #[inline] + pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> + where + K: Borrow, + Q: Hash + Eq, + { + self.find(k).map(move |index| { + let entry = unsafe { self.entry(index) }; + (&entry.key, &entry.value) + }) + } + + /// Finds the mutable key-value entry for a key. + #[inline] + pub fn get_key_value_pin(self: Pin<&mut Self>, k: &Q) -> Option<(&K, Pin<&mut V>)> + where + K: Borrow, + Q: Hash + Eq, + { + unsafe { + let hash_map = self.get_unchecked_mut(); + hash_map.find(k).map(move |index| { + let entry = hash_map.entry_mut(index); + (&entry.key, Pin::new_unchecked(&mut entry.value)) + }) + } + } + + /// Returns whether a key is present in the hash map. + #[inline] + pub fn contains_key(&self, k: &Q) -> bool + where + K: Borrow, + Q: Hash + Eq, + { + self.find(k).is_some() + } + + /// Gets the value associated with the given key. + #[inline] + pub fn get(&self, k: &Q) -> Option<&V> + where + K: Borrow, + Q: Hash + Eq, + { + self.find(k) + .map(|index| unsafe { &self.entry(index).value }) + } + + /// Gets the mutable value associated with the given key. + #[inline] + pub fn get_pin(self: Pin<&mut Self>, k: &Q) -> Option> + where + K: Borrow, + Q: Hash + Eq, + { + unsafe { + let hash_map = self.get_unchecked_mut(); + hash_map + .find(k) + .map(move |index| Pin::new_unchecked(&mut hash_map.entry_mut(index).value)) + } + } + + /// Returns `true` if the map contains no elements. + #[inline] + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[inline] + fn raw_iter(&self) -> RawIter { + RawIter::new(self.entries.as_ptr().cast(), self.len()) + } + + #[inline] + fn raw_iter_pin(self: Pin<&mut Self>) -> RawIterPin { + unsafe { + let hash_map = self.get_unchecked_mut(); + RawIterPin::new(hash_map.entries.as_mut_ptr().cast(), hash_map.len()) + } + } + + /// Gets an iterator over the key-value entries in the hash map. + #[inline] + pub fn iter(&self) -> Iter { + Iter { + inner: self.raw_iter(), + } + } + + /// Gets an iterator over the mutable key-value entries in the hash map. + #[inline] + pub fn iter_pin(self: Pin<&mut Self>) -> IterPin { + IterPin { + inner: self.raw_iter_pin(), + } + } + + /// Gets an iterator over the keys in the hash map. + #[inline] + pub fn keys(&self) -> Keys { + Keys { + inner: self.raw_iter(), + } + } + + /// Gets an iterator over the values in the hash map. + #[inline] + pub fn values(&self) -> Values { + Values { + inner: self.raw_iter(), + } + } + + /// Gets an iterator over the mutable values in the hash map. + #[inline] + pub fn values_pin(self: Pin<&mut Self>) -> ValuesPin { + ValuesPin { + inner: self.raw_iter_pin(), + } + } + + /// Resolves an archived hash map from a given length and parameters. + /// + /// # Safety + /// + /// - `len` must be the number of elements that were serialized + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing a hash map + #[inline] + pub unsafe fn resolve_from_len( + len: usize, + pos: usize, + resolver: HashMapResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.index); + ArchivedHashIndex::resolve_from_len(len, pos + fp, resolver.index_resolver, fo); + + let (fp, fo) = out_field!(out.entries); + RelPtr::emplace(pos + fp, resolver.entries_pos, fo); + } +} + +#[cfg(feature = "alloc")] +const _: () = { + impl ArchivedHashMap { + /// Serializes an iterator of key-value pairs as a hash map. + /// + /// # Safety + /// + /// The keys returned by the iterator must be unique. + pub unsafe fn serialize_from_iter<'a, KU, VU, S, I>( + iter: I, + serializer: &mut S, + ) -> Result + where + KU: 'a + Serialize + Hash + Eq, + VU: 'a + Serialize, + S: Serializer + ScratchSpace + ?Sized, + I: ExactSizeIterator, + { + use crate::ScratchVec; + + let len = iter.len(); + + let mut entries = ScratchVec::new(serializer, len)?; + entries.set_len(len); + let index_resolver = + ArchivedHashIndex::build_and_serialize(iter, serializer, &mut entries)?; + let mut entries = entries.assume_init(); + + // Serialize entries + let mut resolvers = ScratchVec::new(serializer, len)?; + for (key, value) in entries.iter() { + resolvers.push((key.serialize(serializer)?, value.serialize(serializer)?)); + } + + let entries_pos = serializer.align_for::>()?; + for ((key, value), (key_resolver, value_resolver)) in + entries.drain(..).zip(resolvers.drain(..)) + { + serializer + .resolve_aligned(&Entry { key, value }, (key_resolver, value_resolver))?; + } + + // Free scratch vecs + resolvers.free(serializer)?; + entries.free(serializer)?; + + Ok(HashMapResolver { + index_resolver, + entries_pos, + }) + } + } +}; + +impl fmt::Debug for ArchivedHashMap { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl Eq for ArchivedHashMap {} + +impl, Q: Eq + Hash + ?Sized, V> Index<&'_ Q> for ArchivedHashMap { + type Output = V; + + #[inline] + fn index(&self, key: &Q) -> &V { + self.get(key).unwrap() + } +} + +impl PartialEq for ArchivedHashMap { + #[inline] + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + false + } else { + self.iter() + .all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) + } + } +} + +struct RawIter<'a, K, V> { + current: *const Entry, + remaining: usize, + _phantom: PhantomData<(&'a K, &'a V)>, +} + +impl<'a, K, V> RawIter<'a, K, V> { + #[inline] + fn new(pairs: *const Entry, len: usize) -> Self { + Self { + current: pairs, + remaining: len, + _phantom: PhantomData, + } + } +} + +impl<'a, K, V> Iterator for RawIter<'a, K, V> { + type Item = *const Entry; + + #[inline] + fn next(&mut self) -> Option { + unsafe { + if self.remaining == 0 { + None + } else { + let result = self.current; + self.current = self.current.add(1); + self.remaining -= 1; + Some(result) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.remaining, Some(self.remaining)) + } +} + +impl<'a, K, V> ExactSizeIterator for RawIter<'a, K, V> {} +impl<'a, K, V> FusedIterator for RawIter<'a, K, V> {} + +struct RawIterPin<'a, K, V> { + current: *mut Entry, + remaining: usize, + _phantom: PhantomData<(&'a K, Pin<&'a mut V>)>, +} + +impl<'a, K, V> RawIterPin<'a, K, V> { + #[inline] + fn new(pairs: *mut Entry, len: usize) -> Self { + Self { + current: pairs, + remaining: len, + _phantom: PhantomData, + } + } +} + +impl<'a, K, V> Iterator for RawIterPin<'a, K, V> { + type Item = *mut Entry; + + #[inline] + fn next(&mut self) -> Option { + unsafe { + if self.remaining == 0 { + None + } else { + let result = self.current; + self.current = self.current.add(1); + self.remaining -= 1; + Some(result) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.remaining, Some(self.remaining)) + } +} + +impl ExactSizeIterator for RawIterPin<'_, K, V> {} +impl FusedIterator for RawIterPin<'_, K, V> {} + +/// An iterator over the key-value pairs of a hash map. +#[repr(transparent)] +pub struct Iter<'a, K, V> { + inner: RawIter<'a, K, V>, +} + +impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|x| unsafe { + let pair = &*x; + (&pair.key, &pair.value) + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for Iter<'_, K, V> {} +impl FusedIterator for Iter<'_, K, V> {} + +/// An iterator over the mutable key-value pairs of a hash map. +#[repr(transparent)] +pub struct IterPin<'a, K, V> { + inner: RawIterPin<'a, K, V>, +} + +impl<'a, K, V> Iterator for IterPin<'a, K, V> { + type Item = (&'a K, Pin<&'a mut V>); + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|x| unsafe { + let pair = &mut *x; + (&pair.key, Pin::new_unchecked(&mut pair.value)) + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for IterPin<'_, K, V> {} +impl FusedIterator for IterPin<'_, K, V> {} + +/// An iterator over the keys of a hash map. +#[repr(transparent)] +pub struct Keys<'a, K, V> { + inner: RawIter<'a, K, V>, +} + +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|x| unsafe { + let pair = &*x; + &pair.key + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for Keys<'_, K, V> {} +impl FusedIterator for Keys<'_, K, V> {} + +/// An iterator over the values of a hash map. +#[repr(transparent)] +pub struct Values<'a, K, V> { + inner: RawIter<'a, K, V>, +} + +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|x| unsafe { + let pair = &*x; + &pair.value + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for Values<'_, K, V> {} +impl FusedIterator for Values<'_, K, V> {} + +/// An iterator over the mutable values of a hash map. +#[repr(transparent)] +pub struct ValuesPin<'a, K, V> { + inner: RawIterPin<'a, K, V>, +} + +impl<'a, K, V> Iterator for ValuesPin<'a, K, V> { + type Item = Pin<&'a mut V>; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|x| unsafe { + let pair = &mut *x; + Pin::new_unchecked(&mut pair.value) + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for ValuesPin<'_, K, V> {} +impl FusedIterator for ValuesPin<'_, K, V> {} + +/// The resolver for archived hash maps. +pub struct HashMapResolver { + index_resolver: HashIndexResolver, + entries_pos: usize, +} diff --git a/src/collections/hash_map/validation.rs b/src/collections/hash_map/validation.rs new file mode 100644 index 0000000..9ed1c9b --- /dev/null +++ b/src/collections/hash_map/validation.rs @@ -0,0 +1,151 @@ +//! Validation implementation for ArchiveHashMap. + +use crate::{ + collections::{ + hash_index::validation::HashIndexError, + hash_map::ArchivedHashMap, + util::{validation::ArchivedEntryError, Entry}, + ArchivedHashIndex, + }, + validation::ArchiveContext, + RelPtr, +}; +use bytecheck::{CheckBytes, Error, SliceCheckError}; +use core::{ + alloc::{Layout, LayoutError}, + convert::Infallible, + fmt, + hash::Hash, + ptr, +}; + +/// Errors that can occur while checking an archived hash map. +#[derive(Debug)] +pub enum HashMapError { + /// An error occurred while checking the hash index + HashIndexError(HashIndexError), + /// An error occurred while checking the layouts of displacements or entries + LayoutError(LayoutError), + /// An error occurred while checking the entries + CheckEntryError(SliceCheckError>), + /// A key is not located at the correct position + InvalidKeyPosition { + /// The index of the key when iterating + index: usize, + }, + /// A bounds error occurred + ContextError(C), +} + +impl fmt::Display for HashMapError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + HashMapError::HashIndexError(e) => write!(f, "hash index check error: {}", e), + HashMapError::LayoutError(e) => write!(f, "layout error: {}", e), + HashMapError::CheckEntryError(e) => write!(f, "entry check error: {}", e), + HashMapError::InvalidKeyPosition { index } => { + write!(f, "invalid key position: at index {}", index) + } + HashMapError::ContextError(e) => e.fmt(f), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for HashMapError + where + K: Error + 'static, + V: Error + 'static, + C: Error + 'static, + { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + HashMapError::HashIndexError(e) => Some(e as &dyn Error), + HashMapError::LayoutError(e) => Some(e as &dyn Error), + HashMapError::CheckEntryError(e) => Some(e as &dyn Error), + HashMapError::InvalidKeyPosition { .. } => None, + HashMapError::ContextError(e) => Some(e as &dyn Error), + } + } + } +}; + +impl From for HashMapError { + fn from(_: Infallible) -> Self { + unsafe { core::hint::unreachable_unchecked() } + } +} + +impl From> for HashMapError { + #[inline] + fn from(_: SliceCheckError) -> Self { + unsafe { core::hint::unreachable_unchecked() } + } +} + +impl From> for HashMapError { + #[inline] + fn from(e: HashIndexError) -> Self { + Self::HashIndexError(e) + } +} + +impl From for HashMapError { + #[inline] + fn from(e: LayoutError) -> Self { + Self::LayoutError(e) + } +} + +impl From>> for HashMapError { + #[inline] + fn from(e: SliceCheckError>) -> Self { + Self::CheckEntryError(e) + } +} + +impl CheckBytes for ArchivedHashMap +where + K: CheckBytes + Eq + Hash, + V: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, +{ + type Error = HashMapError; + + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + let index = ArchivedHashIndex::check_bytes(ptr::addr_of!((*value).index), context)?; + Layout::array::>(index.len())?; + + let entries_rel_ptr = RelPtr::manual_check_bytes(ptr::addr_of!((*value).entries), context)?; + let entries_ptr = context + .check_subtree_ptr::<[Entry]>( + entries_rel_ptr.base(), + entries_rel_ptr.offset(), + index.len(), + ) + .map_err(HashMapError::ContextError)?; + + let range = context + .push_prefix_subtree(entries_ptr) + .map_err(HashMapError::ContextError)?; + let entries = <[Entry]>::check_bytes(entries_ptr, context)?; + context + .pop_prefix_range(range) + .map_err(HashMapError::ContextError)?; + + for (i, entry) in entries.iter().enumerate() { + if index.index(&entry.key) != Some(i) { + return Err(HashMapError::InvalidKeyPosition { index: i }); + } + } + + Ok(&*value) + } +} diff --git a/src/collections/hash_set.rs b/src/collections/hash_set.rs new file mode 100644 index 0000000..503fdcc --- /dev/null +++ b/src/collections/hash_set.rs @@ -0,0 +1,123 @@ +//! Archived hash set implementation. +//! +//! During archiving, hashsets are built into minimal perfect hashsets using +//! [compress, hash and displace](http://cmph.sourceforge.net/papers/esa09.pdf). + +use crate::collections::hash_map::{ArchivedHashMap, HashMapResolver, Keys}; +#[cfg(feature = "alloc")] +use crate::{ + ser::{ScratchSpace, Serializer}, + Serialize, +}; +use core::{borrow::Borrow, fmt, hash::Hash}; + +/// An archived `HashSet`. This is a wrapper around a hash map with the same key and a value of +/// `()`. +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[repr(transparent)] +pub struct ArchivedHashSet(ArchivedHashMap); + +impl ArchivedHashSet { + /// Gets the number of items in the hash set. + #[inline] + pub const fn len(&self) -> usize { + self.0.len() + } + + /// Gets the key corresponding to the given key in the hash set. + #[inline] + pub fn get(&self, k: &Q) -> Option<&K> + where + K: Borrow, + Q: Hash + Eq, + { + self.0.get_key_value(k).map(|(k, _)| k) + } + + /// Returns whether the given key is in the hash set. + #[inline] + pub fn contains(&self, k: &Q) -> bool + where + K: Borrow, + Q: Hash + Eq, + { + self.0.contains_key(k) + } + + /// Gets the hasher for the underlying hash map. + #[cfg(feature = "alloc")] + #[inline] + pub fn hasher(&self) -> seahash::SeaHasher { + self.0.hasher() + } + + /// Returns whether there are no items in the hash set. + #[inline] + pub const fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Gets an iterator over the keys of the underlying hash map. + #[inline] + pub fn iter(&self) -> Keys { + self.0.keys() + } + + /// Resolves an archived hash set from the given length and parameters. + /// + /// # Safety + /// + /// - `len` must be the number of elements that were serialized + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing a hash map + #[inline] + pub unsafe fn resolve_from_len( + len: usize, + pos: usize, + resolver: HashSetResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.0); + ArchivedHashMap::resolve_from_len(len, pos + fp, resolver.0, fo); + } + + /// Serializes an iterator of keys as a hash set. + /// + /// # Safety + /// + /// The keys returned by the iterator must be unique. + #[cfg(feature = "alloc")] + #[inline] + pub unsafe fn serialize_from_iter<'a, KU, S, I>( + iter: I, + serializer: &mut S, + ) -> Result + where + KU: 'a + Serialize + Hash + Eq, + S: Serializer + ScratchSpace + ?Sized, + I: ExactSizeIterator, + { + Ok(HashSetResolver(ArchivedHashMap::serialize_from_iter( + iter.map(|x| (x, &())), + serializer, + )?)) + } +} + +impl fmt::Debug for ArchivedHashSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} + +/// The resolver for archived hash sets. +pub struct HashSetResolver(HashMapResolver); + +impl PartialEq for ArchivedHashSet { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for ArchivedHashSet {} diff --git a/src/collections/index_map/mod.rs b/src/collections/index_map/mod.rs new file mode 100644 index 0000000..307f518 --- /dev/null +++ b/src/collections/index_map/mod.rs @@ -0,0 +1,414 @@ +//! Archived index map implementation. +//! +//! During archiving, hashmaps are built into minimal perfect hashmaps using +//! [compress, hash and displace](http://cmph.sourceforge.net/papers/esa09.pdf). + +#[cfg(feature = "validation")] +pub mod validation; + +use crate::{ + collections::{ + hash_index::{ArchivedHashIndex, HashBuilder, HashIndexResolver}, + util::Entry, + }, + out_field, Archived, RelPtr, +}; +use core::{borrow::Borrow, fmt, hash::Hash, iter::FusedIterator, marker::PhantomData}; + +/// An archived `IndexMap`. +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedIndexMap { + index: ArchivedHashIndex, + pivots: RelPtr>, + entries: RelPtr>, +} + +impl ArchivedIndexMap { + #[inline] + unsafe fn pivot(&self, index: usize) -> usize { + from_archived!(*self.pivots.as_ptr().add(index)) as usize + } + + #[inline] + unsafe fn entry(&self, index: usize) -> &Entry { + &*self.entries.as_ptr().add(index) + } + + #[inline] + fn find(&self, k: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq, + { + self.index.index(k).and_then(|pivot_index| { + let index = unsafe { self.pivot(pivot_index) }; + let entry = unsafe { self.entry(index) }; + if entry.key.borrow() == k { + Some(index) + } else { + None + } + }) + } + + /// Returns whether a key is present in the hash map. + #[inline] + pub fn contains_key(&self, k: &Q) -> bool + where + K: Borrow, + Q: Hash + Eq, + { + self.find(k).is_some() + } + + /// Returns the first key-value pair. + #[inline] + pub fn first(&self) -> Option<(&K, &V)> { + if !self.is_empty() { + let entry = unsafe { self.entry(0) }; + Some((&entry.key, &entry.value)) + } else { + None + } + } + + /// Gets the value associated with the given key. + #[inline] + pub fn get(&self, k: &Q) -> Option<&V> + where + K: Borrow, + Q: Hash + Eq, + { + self.find(k) + .map(|index| unsafe { &self.entry(index).value }) + } + + /// Gets the index, key, and value associated with the given key. + #[inline] + pub fn get_full(&self, k: &Q) -> Option<(usize, &K, &V)> + where + K: Borrow, + Q: Hash + Eq, + { + self.find(k).map(|index| { + let entry = unsafe { &self.entry(index) }; + (index, &entry.key, &entry.value) + }) + } + + /// Gets a key-value pair by index. + #[inline] + pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { + if index < self.len() { + let entry = unsafe { &self.entry(index) }; + Some((&entry.key, &entry.value)) + } else { + None + } + } + + /// Gets the index of a key if it exists in the map. + #[inline] + pub fn get_index_of(&self, key: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq, + { + self.find(key) + } + + /// Gets the key-value pair associated with the given key. + #[inline] + pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> + where + K: Borrow, + Q: Hash + Eq, + { + self.find(k).map(|index| { + let entry = unsafe { &self.entry(index) }; + (&entry.key, &entry.value) + }) + } + + /// Gets the hasher for this index map. + #[inline] + pub fn hasher(&self) -> HashBuilder { + self.index.hasher() + } + + /// Returns `true` if the map contains no elements. + #[inline] + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[inline] + fn raw_iter(&self) -> RawIter { + RawIter::new(self.entries.as_ptr().cast(), self.len()) + } + + /// Returns an iterator over the key-value pairs of the map in order + #[inline] + pub fn iter(&self) -> Iter { + Iter { + inner: self.raw_iter(), + } + } + + /// Returns an iterator over the keys of the map in order + #[inline] + pub fn keys(&self) -> Keys { + Keys { + inner: self.raw_iter(), + } + } + + /// Returns the last key-value pair. + #[inline] + pub fn last(&self) -> Option<(&K, &V)> { + if !self.is_empty() { + let entry = unsafe { self.entry(self.len() - 1) }; + Some((&entry.key, &entry.value)) + } else { + None + } + } + + /// Gets the number of items in the index map. + #[inline] + pub const fn len(&self) -> usize { + self.index.len() + } + + /// Returns an iterator over the values of the map in order. + #[inline] + pub fn values(&self) -> Values { + Values { + inner: self.raw_iter(), + } + } + + /// Resolves an archived index map from a given length and parameters. + /// + /// # Safety + /// + /// - `len` must be the number of elements that were serialized + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing a hash map + pub unsafe fn resolve_from_len( + len: usize, + pos: usize, + resolver: IndexMapResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.index); + ArchivedHashIndex::resolve_from_len(len, pos + fp, resolver.index_resolver, fo); + + let (fp, fo) = out_field!(out.pivots); + RelPtr::emplace(pos + fp, resolver.pivots_pos, fo); + + let (fp, fo) = out_field!(out.entries); + RelPtr::emplace(pos + fp, resolver.entries_pos, fo); + } +} + +#[cfg(feature = "alloc")] +const _: () = { + use crate::{ + ser::{ScratchSpace, Serializer}, + Serialize, + }; + + impl ArchivedIndexMap { + /// Serializes an iterator of key-value pairs as an index map. + /// + /// # Safety + /// + /// - The keys returned by the iterator must be unique + /// - The index function must return the index of the given key within the iterator + pub unsafe fn serialize_from_iter_index<'a, UK, UV, I, F, S>( + iter: I, + index: F, + serializer: &mut S, + ) -> Result + where + UK: 'a + Serialize + Hash + Eq, + UV: 'a + Serialize, + I: Clone + ExactSizeIterator, + F: Fn(&UK) -> usize, + S: Serializer + ScratchSpace + ?Sized, + { + use crate::ScratchVec; + + let len = iter.len(); + + let mut entries = ScratchVec::new(serializer, iter.len())?; + entries.set_len(len); + let index_resolver = + ArchivedHashIndex::build_and_serialize(iter.clone(), serializer, &mut entries)?; + let mut entries = entries.assume_init(); + + // Serialize entries + let mut resolvers = ScratchVec::new(serializer, iter.len())?; + for (key, value) in iter.clone() { + resolvers.push((key.serialize(serializer)?, value.serialize(serializer)?)); + } + + let entries_pos = serializer.align_for::>()?; + for ((key, value), (key_resolver, value_resolver)) in iter.zip(resolvers.drain(..)) { + serializer + .resolve_aligned(&Entry { key, value }, (key_resolver, value_resolver))?; + } + + // Serialize pivots + let pivots_pos = serializer.align_for::>()?; + for (key, _) in entries.drain(..) { + serializer.resolve_aligned(&index(key), ())?; + } + + // Free scratch vecs + resolvers.free(serializer)?; + entries.free(serializer)?; + + Ok(IndexMapResolver { + index_resolver, + pivots_pos, + entries_pos, + }) + } + } +}; + +impl fmt::Debug for ArchivedIndexMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl PartialEq for ArchivedIndexMap { + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other.iter()) + } +} + +struct RawIter<'a, K, V> { + current: *const Entry, + remaining: usize, + _phantom: PhantomData<(&'a K, &'a V)>, +} + +impl<'a, K, V> RawIter<'a, K, V> { + #[inline] + fn new(pairs: *const Entry, len: usize) -> Self { + Self { + current: pairs, + remaining: len, + _phantom: PhantomData, + } + } +} + +impl<'a, K, V> Iterator for RawIter<'a, K, V> { + type Item = (&'a K, &'a V); + + #[inline] + fn next(&mut self) -> Option { + unsafe { + if self.remaining == 0 { + None + } else { + let result = self.current; + self.current = self.current.add(1); + self.remaining -= 1; + let entry = &*result; + Some((&entry.key, &entry.value)) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.remaining, Some(self.remaining)) + } +} + +impl<'a, K, V> ExactSizeIterator for RawIter<'a, K, V> {} +impl<'a, K, V> FusedIterator for RawIter<'a, K, V> {} + +/// An iterator over the key-value pairs of an index map. +#[repr(transparent)] +pub struct Iter<'a, K, V> { + inner: RawIter<'a, K, V>, +} + +impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + #[inline] + fn next(&mut self) -> Option { + self.inner.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for Iter<'_, K, V> {} +impl FusedIterator for Iter<'_, K, V> {} + +/// An iterator over the keys of an index map. +#[repr(transparent)] +pub struct Keys<'a, K, V> { + inner: RawIter<'a, K, V>, +} + +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|(k, _)| k) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for Keys<'_, K, V> {} +impl FusedIterator for Keys<'_, K, V> {} + +/// An iterator over the values of an index map. +#[repr(transparent)] +pub struct Values<'a, K, V> { + inner: RawIter<'a, K, V>, +} + +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next().map(|(_, v)| v) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl ExactSizeIterator for Values<'_, K, V> {} +impl FusedIterator for Values<'_, K, V> {} + +// Archive implementations + +/// The resolver for an `IndexMap`. +pub struct IndexMapResolver { + index_resolver: HashIndexResolver, + pivots_pos: usize, + entries_pos: usize, +} diff --git a/src/collections/index_map/validation.rs b/src/collections/index_map/validation.rs new file mode 100644 index 0000000..4ca01ff --- /dev/null +++ b/src/collections/index_map/validation.rs @@ -0,0 +1,197 @@ +//! Validation implementation for ArchivedIndexMap. + +use crate::{ + collections::{ + hash_index::validation::HashIndexError, + index_map::ArchivedIndexMap, + util::{validation::ArchivedEntryError, Entry}, + ArchivedHashIndex, + }, + validation::ArchiveContext, + Archived, RelPtr, +}; +use bytecheck::{CheckBytes, Error, SliceCheckError}; +use core::{ + alloc::{Layout, LayoutError}, + convert::Infallible, + fmt, + hash::Hash, + ptr, +}; + +/// Errors that can occur while checking an archived index map. +#[derive(Debug)] +pub enum IndexMapError { + /// An error occurred while checking the hash index + HashIndexError(HashIndexError), + /// An error occurred while checking the layouts of displacements or entries + LayoutError(LayoutError), + /// A pivot indexes outside of the entries array + PivotOutOfBounds { + /// The index of the pivot when iterating + index: usize, + /// The pivot value that was invalid + pivot: usize, + }, + /// An error occurred while checking the entries + CheckEntryError(SliceCheckError>), + /// A key is not located at the correct position + /// + /// This can either be due to the key being invalid for the hash index, or the pivot for the key + /// not pointing to it. + InvalidKeyPosition { + /// The index of the key when iterating + index: usize, + }, + /// A bounds error occurred + ContextError(C), +} + +impl fmt::Display for IndexMapError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + IndexMapError::HashIndexError(e) => write!(f, "hash index check error: {}", e), + IndexMapError::LayoutError(e) => write!(f, "layout error: {}", e), + IndexMapError::PivotOutOfBounds { index, pivot } => { + write!(f, "pivot out of bounds: {} at index {}", pivot, index) + } + IndexMapError::CheckEntryError(e) => write!(f, "entry check error: {}", e), + IndexMapError::InvalidKeyPosition { index } => { + write!(f, "invalid key position: at index {}", index) + } + IndexMapError::ContextError(e) => e.fmt(f), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for IndexMapError + where + K: Error + 'static, + V: Error + 'static, + C: Error + 'static, + { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + IndexMapError::HashIndexError(e) => Some(e as &dyn Error), + IndexMapError::LayoutError(e) => Some(e as &dyn Error), + IndexMapError::PivotOutOfBounds { .. } => None, + IndexMapError::CheckEntryError(e) => Some(e as &dyn Error), + IndexMapError::InvalidKeyPosition { .. } => None, + IndexMapError::ContextError(e) => Some(e as &dyn Error), + } + } + } +}; + +impl From for IndexMapError { + fn from(_: Infallible) -> Self { + unsafe { core::hint::unreachable_unchecked() } + } +} + +impl From> for IndexMapError { + #[inline] + fn from(_: SliceCheckError) -> Self { + unsafe { core::hint::unreachable_unchecked() } + } +} + +impl From> for IndexMapError { + #[inline] + fn from(e: HashIndexError) -> Self { + Self::HashIndexError(e) + } +} + +impl From for IndexMapError { + #[inline] + fn from(e: LayoutError) -> Self { + Self::LayoutError(e) + } +} + +impl From>> for IndexMapError { + #[inline] + fn from(e: SliceCheckError>) -> Self { + Self::CheckEntryError(e) + } +} + +impl CheckBytes for ArchivedIndexMap +where + K: CheckBytes + Eq + Hash, + V: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, +{ + type Error = IndexMapError; + + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + let index = ArchivedHashIndex::check_bytes(ptr::addr_of!((*value).index), context)?; + + // Entries + Layout::array::>(index.len())?; + let entries_rel_ptr = RelPtr::manual_check_bytes(ptr::addr_of!((*value).entries), context)?; + let entries_ptr = context + .check_subtree_ptr::<[Entry]>( + entries_rel_ptr.base(), + entries_rel_ptr.offset(), + index.len(), + ) + .map_err(IndexMapError::ContextError)?; + + let range = context + .push_prefix_subtree(entries_ptr) + .map_err(IndexMapError::ContextError)?; + let entries = <[Entry]>::check_bytes(entries_ptr, context)?; + context + .pop_prefix_range(range) + .map_err(IndexMapError::ContextError)?; + + // Pivots + Layout::array::>(index.len())?; + let pivots_rel_ptr = RelPtr::manual_check_bytes(ptr::addr_of!((*value).pivots), context)?; + let pivots_ptr = context + .check_subtree_ptr::<[Archived]>( + pivots_rel_ptr.base(), + pivots_rel_ptr.offset(), + index.len(), + ) + .map_err(IndexMapError::ContextError)?; + + let range = context + .push_prefix_subtree(pivots_ptr) + .map_err(IndexMapError::ContextError)?; + let pivots = <[Archived]>::check_bytes(pivots_ptr, context)?; + context + .pop_prefix_range(range) + .map_err(IndexMapError::ContextError)?; + + for (i, pivot) in pivots.iter().enumerate() { + let pivot = from_archived!(*pivot) as usize; + if pivot >= index.len() { + return Err(IndexMapError::PivotOutOfBounds { index: i, pivot }); + } + } + + for (i, entry) in entries.iter().enumerate() { + if let Some(pivot_index) = index.index(&entry.key) { + let pivot = from_archived!(pivots[pivot_index]) as usize; + if pivot != i { + return Err(IndexMapError::InvalidKeyPosition { index: i }); + } + } else { + return Err(IndexMapError::InvalidKeyPosition { index: i }); + } + } + + Ok(&*value) + } +} diff --git a/src/collections/index_set.rs b/src/collections/index_set.rs new file mode 100644 index 0000000..723fbc7 --- /dev/null +++ b/src/collections/index_set.rs @@ -0,0 +1,174 @@ +//! Archived index set implementation. +//! +//! During archiving, index sets are built into minimal perfect index sets using +//! [compress, hash and displace](http://cmph.sourceforge.net/papers/esa09.pdf). + +use crate::{ + collections::{ + hash_index::HashBuilder, + index_map::{ArchivedIndexMap, IndexMapResolver, Keys}, + }, + out_field, +}; +use core::{borrow::Borrow, fmt, hash::Hash}; + +/// An archived `IndexSet`. +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[repr(transparent)] +pub struct ArchivedIndexSet { + inner: ArchivedIndexMap, +} + +impl ArchivedIndexSet { + /// Returns whether a key is present in the hash set. + #[inline] + pub fn contains(&self, k: &Q) -> bool + where + K: Borrow, + Q: Hash + Eq, + { + self.inner.contains_key(k) + } + + /// Returns the first key. + #[inline] + pub fn first(&self) -> Option<&K> { + self.inner.first().map(|(k, _)| k) + } + + /// Returns the value stored in the set, if any. + #[inline] + pub fn get(&self, k: &Q) -> Option<&K> + where + K: Borrow, + Q: Hash + Eq, + { + self.inner.get_full(k).map(|(_, k, _)| k) + } + + /// Returns the item index and value stored in the set, if any. + #[inline] + pub fn get_full(&self, k: &Q) -> Option<(usize, &K)> + where + K: Borrow, + Q: Hash + Eq, + { + self.inner.get_full(k).map(|(i, k, _)| (i, k)) + } + + /// Gets a key by index. + #[inline] + pub fn get_index(&self, index: usize) -> Option<&K> { + self.inner.get_index(index).map(|(k, _)| k) + } + + /// Returns the index of a key if it exists in the set. + #[inline] + pub fn get_index_of(&self, key: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq, + { + self.inner.get_index_of(key) + } + + /// Gets the hasher for this index set. + #[inline] + pub fn hasher(&self) -> HashBuilder { + self.inner.hasher() + } + + /// Returns whether the index set contains no values. + #[inline] + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Returns an iterator over the keys of the index set in order. + #[inline] + pub fn iter(&self) -> Keys { + self.inner.keys() + } + + /// Returns the last key. + #[inline] + pub fn last(&self) -> Option<&K> { + self.inner.last().map(|(k, _)| k) + } + + /// Returns the number of elements in the index set. + #[inline] + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Resolves an archived index map from a given length and parameters. + /// + /// # Safety + /// + /// - `len` must be the number of elements that were serialized + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing a hash map + #[inline] + pub unsafe fn resolve_from_len( + len: usize, + pos: usize, + resolver: IndexSetResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.inner); + ArchivedIndexMap::resolve_from_len(len, pos + fp, resolver.0, fo); + } +} + +#[cfg(feature = "alloc")] +const _: () = { + use crate::{ + ser::{ScratchSpace, Serializer}, + Serialize, + }; + + impl ArchivedIndexSet { + /// Serializes an iterator of keys as an index set. + /// + /// # Safety + /// + /// - The keys returned by the iterator must be unique + /// - The index function must return the index of the given key within the iterator + #[inline] + pub unsafe fn serialize_from_iter_index<'a, UK, I, F, S>( + iter: I, + index: F, + serializer: &mut S, + ) -> Result + where + UK: 'a + Hash + Eq + Serialize, + I: Clone + ExactSizeIterator, + F: Fn(&UK) -> usize, + S: ScratchSpace + Serializer + ?Sized, + { + Ok(IndexSetResolver( + ArchivedIndexMap::serialize_from_iter_index( + iter.map(|k| (k, &())), + index, + serializer, + )?, + )) + } + } +}; + +impl fmt::Debug for ArchivedIndexSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} + +impl PartialEq for ArchivedIndexSet { + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other.iter()) + } +} + +/// The resolver for `IndexSet`. +pub struct IndexSetResolver(IndexMapResolver); diff --git a/src/collections/mod.rs b/src/collections/mod.rs new file mode 100644 index 0000000..c59615d --- /dev/null +++ b/src/collections/mod.rs @@ -0,0 +1,19 @@ +//! Archived versions of standard library containers. + +pub mod btree_map; +pub mod btree_set; +pub mod hash_index; +pub mod hash_map; +pub mod hash_set; +// TODO: move these into a separate crate when indexmap adds rkyv support +pub mod index_map; +pub mod index_set; +pub mod util; + +pub use self::btree_map::ArchivedBTreeMap; +pub use self::hash_index::ArchivedHashIndex; +pub use self::hash_map::ArchivedHashMap; +pub use self::hash_set::ArchivedHashSet; +// TODO: move these into a separate crate when indexmap adds rkyv support +pub use self::index_map::ArchivedIndexMap; +pub use self::index_set::ArchivedIndexSet; diff --git a/src/collections/util/mod.rs b/src/collections/util/mod.rs new file mode 100644 index 0000000..74ebf70 --- /dev/null +++ b/src/collections/util/mod.rs @@ -0,0 +1,53 @@ +//! Utilities for archived collections. + +#[cfg(feature = "validation")] +pub mod validation; + +use crate::{Archive, Fallible, Serialize}; + +/// A simple key-value pair. +/// +/// This is typically used by associative containers that store keys and values together. +#[derive(Debug, Eq)] +#[cfg_attr(feature = "strict", repr(C))] +pub struct Entry { + /// The key of the pair. + pub key: K, + /// The value of the pair. + pub value: V, +} + +impl Archive for Entry<&'_ K, &'_ V> { + type Archived = Entry; + type Resolver = (K::Resolver, V::Resolver); + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.key); + self.key.resolve(pos + fp, resolver.0, fo); + + let (fp, fo) = out_field!(out.value); + self.value.resolve(pos + fp, resolver.1, fo); + } +} + +impl, V: Serialize, S: Fallible + ?Sized> Serialize for Entry<&'_ K, &'_ V> { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + Ok(( + self.key.serialize(serializer)?, + self.value.serialize(serializer)?, + )) + } +} + +impl PartialEq> for Entry +where + K: PartialEq, + V: PartialEq, +{ + #[inline] + fn eq(&self, other: &Entry) -> bool { + self.key.eq(&other.key) && self.value.eq(&other.value) + } +} diff --git a/src/collections/util/validation.rs b/src/collections/util/validation.rs new file mode 100644 index 0000000..41c3469 --- /dev/null +++ b/src/collections/util/validation.rs @@ -0,0 +1,58 @@ +//! Validation implementation for utility types. + +use crate::{collections::util::Entry, validation::ArchiveContext}; +use ::bytecheck::CheckBytes; +use ::core::{fmt, ptr}; + +/// Errors that can occur while checking an archived hash map entry. +#[derive(Debug)] +pub enum ArchivedEntryError { + /// An error occurred while checking the bytes of a key + KeyCheckError(K), + /// An error occurred while checking the bytes of a value + ValueCheckError(V), +} + +impl fmt::Display for ArchivedEntryError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ArchivedEntryError::KeyCheckError(e) => write!(f, "key check error: {}", e), + ArchivedEntryError::ValueCheckError(e) => write!(f, "value check error: {}", e), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for ArchivedEntryError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + ArchivedEntryError::KeyCheckError(e) => Some(e as &dyn Error), + ArchivedEntryError::ValueCheckError(e) => Some(e as &dyn Error), + } + } + } +}; + +impl CheckBytes for Entry +where + K: CheckBytes, + V: CheckBytes, + C: ArchiveContext + ?Sized, +{ + type Error = ArchivedEntryError; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + K::check_bytes(ptr::addr_of!((*value).key), context) + .map_err(ArchivedEntryError::KeyCheckError)?; + V::check_bytes(ptr::addr_of!((*value).value), context) + .map_err(ArchivedEntryError::ValueCheckError)?; + Ok(&*value) + } +} diff --git a/src/copy.rs b/src/copy.rs new file mode 100644 index 0000000..932b7fe --- /dev/null +++ b/src/copy.rs @@ -0,0 +1,185 @@ +//! Optimization primitives for copyable types. + +#[cfg(has_atomics)] +use core::sync::atomic::{ + AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicU16, AtomicU32, AtomicU8, +}; +#[cfg(has_atomics_64)] +use core::sync::atomic::{AtomicI64, AtomicU64}; +use core::{ + marker::{PhantomData, PhantomPinned}, + num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, + }, +}; + +/// A type that is `Copy` and can be archived without additional processing. +/// +/// This trait is similar to `Copy` in that it's automatically implemented for all types composed +/// entirely of other `ArchiveCopy` types. `Copy` is necessary, but not sufficient for `ArchiveCopy` +/// as some `Copy` type representations may vary from platform to platform. +#[rustc_unsafe_specialization_marker] +pub auto trait ArchiveCopy {} + +// (), PhantomData, PhantomPinned, bool, i8, u8, NonZeroI8, and NonZeroU8 are always ArchiveCopy +impl ArchiveCopy for PhantomData {} + +// Multibyte integers are not ArchiveCopy if the target does not match the archive endianness +#[cfg(any( + all(target_endian = "little", feature = "archive_be"), + all(target_endian = "big", feature = "archive_le"), +))] +const _: () = { + impl !ArchiveCopy for i16 {} + impl !ArchiveCopy for i32 {} + impl !ArchiveCopy for i64 {} + impl !ArchiveCopy for i128 {} + impl !ArchiveCopy for u16 {} + impl !ArchiveCopy for u32 {} + impl !ArchiveCopy for u64 {} + impl !ArchiveCopy for u128 {} + impl !ArchiveCopy for f32 {} + impl !ArchiveCopy for f64 {} + impl !ArchiveCopy for char {} + impl !ArchiveCopy for NonZeroI16 {} + impl !ArchiveCopy for NonZeroI32 {} + impl !ArchiveCopy for NonZeroI64 {} + impl !ArchiveCopy for NonZeroI128 {} + impl !ArchiveCopy for NonZeroU16 {} + impl !ArchiveCopy for NonZeroU32 {} + impl !ArchiveCopy for NonZeroU64 {} + impl !ArchiveCopy for NonZeroU128 {} +}; + +// Pointer-sized integers are not ArchiveCopy if the target pointer width does not match the archive +// pointer width +#[cfg(any( + all(target_pointer_width = "16", not(feature = "size_16")), + all(target_pointer_width = "32", not(feature = "size_32")), + all(target_pointer_width = "64", not(feature = "size_64")) +))] +const _: () = { + impl !ArchiveCopy for isize {} + impl !ArchiveCopy for usize {} + impl !ArchiveCopy for NonZeroIsize {} + impl !ArchiveCopy for NonZeroUsize {} +}; + +// Atomics are not ArchiveCopy if the platform supports them +#[cfg(has_atomics)] +const _: () = { + impl !ArchiveCopy for AtomicBool {} + impl !ArchiveCopy for AtomicI8 {} + impl !ArchiveCopy for AtomicI16 {} + impl !ArchiveCopy for AtomicI32 {} + impl !ArchiveCopy for AtomicU8 {} + impl !ArchiveCopy for AtomicU16 {} + impl !ArchiveCopy for AtomicU32 {} +}; +#[cfg(has_atomics_64)] +const _: () = { + impl !ArchiveCopy for AtomicI64 {} + impl !ArchiveCopy for AtomicU64 {} +}; + +// Pointers and references are never ArchiveCopy +impl !ArchiveCopy for *const T {} +impl !ArchiveCopy for *mut T {} +impl !ArchiveCopy for &T {} +impl !ArchiveCopy for &mut T {} + +impl !ArchiveCopy for crate::RawRelPtr {} + +/// Types that are `ArchiveCopy` and have no padding. +/// +/// These types are always safe to `memcpy` around because they will never contain uninitialized +/// padding. +#[rustc_unsafe_specialization_marker] +pub unsafe trait ArchiveCopySafe: ArchiveCopy + Sized {} + +// (), PhantomData, PhantomPinned, bool, i8, u8, NonZeroI8, and NonZeroU8 are always ArchiveCopySafe +unsafe impl ArchiveCopySafe for () {} +unsafe impl ArchiveCopySafe for PhantomData {} +unsafe impl ArchiveCopySafe for PhantomPinned {} +unsafe impl ArchiveCopySafe for bool {} +unsafe impl ArchiveCopySafe for i8 {} +unsafe impl ArchiveCopySafe for u8 {} +unsafe impl ArchiveCopySafe for NonZeroI8 {} +unsafe impl ArchiveCopySafe for NonZeroU8 {} + +// Multibyte integers are ArchiveCopySafe if the target matches the archived endianness +#[cfg(not(any( + all(target_endian = "little", feature = "archive_be"), + all(target_endian = "big", feature = "archive_le"), +)))] +const _: () = { + unsafe impl ArchiveCopySafe for i16 {} + unsafe impl ArchiveCopySafe for i32 {} + unsafe impl ArchiveCopySafe for i64 {} + unsafe impl ArchiveCopySafe for i128 {} + unsafe impl ArchiveCopySafe for u16 {} + unsafe impl ArchiveCopySafe for u32 {} + unsafe impl ArchiveCopySafe for u64 {} + unsafe impl ArchiveCopySafe for u128 {} + unsafe impl ArchiveCopySafe for f32 {} + unsafe impl ArchiveCopySafe for f64 {} + unsafe impl ArchiveCopySafe for char {} + unsafe impl ArchiveCopySafe for NonZeroI16 {} + unsafe impl ArchiveCopySafe for NonZeroI32 {} + unsafe impl ArchiveCopySafe for NonZeroI64 {} + unsafe impl ArchiveCopySafe for NonZeroI128 {} + unsafe impl ArchiveCopySafe for NonZeroU16 {} + unsafe impl ArchiveCopySafe for NonZeroU32 {} + unsafe impl ArchiveCopySafe for NonZeroU64 {} + unsafe impl ArchiveCopySafe for NonZeroU128 {} +}; + +// Pointer-sized integers are ArchiveCopySafe if the target pointer width matches the archive +// pointer width +#[cfg(not(any( + all(target_pointer_width = "16", not(feature = "size_16")), + all(target_pointer_width = "32", not(feature = "size_32")), + all(target_pointer_width = "64", not(feature = "size_64")) +)))] +const _: () = { + unsafe impl ArchiveCopySafe for isize {} + unsafe impl ArchiveCopySafe for usize {} + unsafe impl ArchiveCopySafe for NonZeroIsize {} + unsafe impl ArchiveCopySafe for NonZeroUsize {} +}; + +macro_rules! impl_tuple { + () => {}; + (T, $($ts:ident,)*) => { + unsafe impl ArchiveCopySafe for (T, $($ts,)*) {} + + impl_tuple!($($ts,)*); + }; +} + +impl_tuple!(T, T, T, T, T, T, T, T, T, T, T,); + +unsafe impl ArchiveCopySafe for [T; N] {} + +/// Types that may be copy optimized. +/// +/// By default, only [`ArchiveCopySafe`] types may be copy optimized. By enabling the `copy_unsafe` +/// feature, all types that are [`ArchiveCopy`] may be copy optimized. +#[cfg(not(feature = "copy_unsafe"))] +#[rustc_unsafe_specialization_marker] +pub trait ArchiveCopyOptimize: ArchiveCopySafe {} + +#[cfg(not(feature = "copy_unsafe"))] +impl ArchiveCopyOptimize for T {} + +/// Types that may be copy optimized. +/// +/// By default, only [`ArchiveCopySafe`] types may be copy optimized. By enabling the `copy_unsafe` +/// feature, all types that are [`ArchiveCopy`] may be copy optimized. +#[cfg(feature = "copy_unsafe")] +#[rustc_unsafe_specialization_marker] +pub trait ArchiveCopyOptimize: ArchiveCopy {} + +#[cfg(feature = "copy_unsafe")] +impl ArchiveCopyOptimize for T {} diff --git a/src/de/deserializers/alloc.rs b/src/de/deserializers/alloc.rs new file mode 100644 index 0000000..a5bfc59 --- /dev/null +++ b/src/de/deserializers/alloc.rs @@ -0,0 +1,103 @@ +//! Adapters wrap deserializers and add support for deserializer traits. + +use crate::{ + de::{SharedDeserializeRegistry, SharedPointer}, + Fallible, +}; +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; +use core::fmt; +#[cfg(not(feature = "std"))] +use hashbrown::hash_map; +#[cfg(feature = "std")] +use std::collections::hash_map; + +/// An error that can occur while deserializing shared pointers. +#[derive(Debug)] +pub enum SharedDeserializeMapError { + /// A shared pointer was added multiple times + DuplicateSharedPointer(*const u8), +} + +// SAFETY: SharedDeserializeMapError is safe to send to another thread +// This trait is not automatically implemented because the enum contains a pointer +unsafe impl Send for SharedDeserializeMapError {} + +// SAFETY: SharedDeserializeMapError is safe to share between threads +// This trait is not automatically implemented because the enum contains a pointer +unsafe impl Sync for SharedDeserializeMapError {} + +impl fmt::Display for SharedDeserializeMapError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::DuplicateSharedPointer(p) => write!(f, "duplicate shared pointer: {:p}", p), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for SharedDeserializeMapError {} +}; + +/// An adapter that adds shared deserialization support to a deserializer. +pub struct SharedDeserializeMap { + shared_pointers: hash_map::HashMap<*const u8, Box>, +} + +impl SharedDeserializeMap { + /// Wraps the given deserializer and adds shared memory support. + #[inline] + pub fn new() -> Self { + Self { + shared_pointers: hash_map::HashMap::new(), + } + } +} + +impl fmt::Debug for SharedDeserializeMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map() + .entries( + self.shared_pointers + .iter() + .map(|(s, p)| (s, &**p as *const _)), + ) + .finish() + } +} + +impl Default for SharedDeserializeMap { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Fallible for SharedDeserializeMap { + type Error = SharedDeserializeMapError; +} + +impl SharedDeserializeRegistry for SharedDeserializeMap { + fn get_shared_ptr(&mut self, ptr: *const u8) -> Option<&dyn SharedPointer> { + self.shared_pointers.get(&ptr).map(|p| p.as_ref()) + } + + fn add_shared_ptr( + &mut self, + ptr: *const u8, + shared: Box, + ) -> Result<(), Self::Error> { + match self.shared_pointers.entry(ptr) { + hash_map::Entry::Occupied(_) => { + Err(SharedDeserializeMapError::DuplicateSharedPointer(ptr)) + } + hash_map::Entry::Vacant(e) => { + e.insert(shared); + Ok(()) + } + } + } +} diff --git a/src/de/deserializers/mod.rs b/src/de/deserializers/mod.rs new file mode 100644 index 0000000..5960a23 --- /dev/null +++ b/src/de/deserializers/mod.rs @@ -0,0 +1,8 @@ +//! Deserializers that can be used standalone and provide basic capabilities. + +#[cfg(feature = "alloc")] +mod alloc; + +#[doc(inline)] +#[cfg(feature = "alloc")] +pub use self::alloc::*; diff --git a/src/de/mod.rs b/src/de/mod.rs new file mode 100644 index 0000000..1607de0 --- /dev/null +++ b/src/de/mod.rs @@ -0,0 +1,67 @@ +//! Deserialization traits, deserializers, and adapters. + +pub mod deserializers; + +#[cfg(feature = "alloc")] +use crate::{ArchiveUnsized, DeserializeUnsized, Fallible}; +#[cfg(all(feature = "alloc", not(feature = "std")))] +use ::alloc::boxed::Box; +#[cfg(feature = "alloc")] +use ::core::alloc::Layout; + +/// A deserializable shared pointer type. +#[cfg(feature = "alloc")] +pub trait SharedPointer { + /// Returns the data address for this shared pointer. + fn data_address(&self) -> *const (); +} + +/// A registry that tracks deserialized shared memory. +/// +/// This trait is required to deserialize shared pointers. +#[cfg(feature = "alloc")] +pub trait SharedDeserializeRegistry: Fallible { + /// Gets the data pointer of a previously-deserialized shared pointer. + fn get_shared_ptr(&mut self, ptr: *const u8) -> Option<&dyn SharedPointer>; + + /// Adds the data address of a deserialized shared pointer to the registry. + fn add_shared_ptr( + &mut self, + ptr: *const u8, + shared: Box, + ) -> Result<(), Self::Error>; + + /// Checks whether the given reference has been deserialized and either uses the existing shared + /// pointer to it, or deserializes it and converts it to a shared pointer with `to_shared`. + #[inline] + fn deserialize_shared( + &mut self, + value: &T::Archived, + to_shared: F, + alloc: A, + ) -> Result<*const T, Self::Error> + where + T: ArchiveUnsized + ?Sized, + P: SharedPointer + 'static, + F: FnOnce(*mut T) -> P, + A: FnMut(Layout) -> *mut u8, + T::Archived: DeserializeUnsized, + { + let ptr = value as *const T::Archived as *const u8; + let metadata = T::Archived::deserialize_metadata(value, self)?; + + if let Some(shared_pointer) = self.get_shared_ptr(ptr) { + Ok(ptr_meta::from_raw_parts( + shared_pointer.data_address(), + metadata, + )) + } else { + let deserialized_data = unsafe { value.deserialize_unsized(self, alloc)? }; + let shared_ptr = to_shared(ptr_meta::from_raw_parts_mut(deserialized_data, metadata)); + let data_address = shared_ptr.data_address(); + + self.add_shared_ptr(ptr, Box::new(shared_ptr) as Box)?; + Ok(ptr_meta::from_raw_parts(data_address, metadata)) + } + } +} diff --git a/src/ffi.rs b/src/ffi.rs new file mode 100644 index 0000000..2465e27 --- /dev/null +++ b/src/ffi.rs @@ -0,0 +1,204 @@ +//! Archived versions of FFI types. + +use crate::{ser::Serializer, ArchiveUnsized, MetadataResolver, RelPtr, SerializeUnsized}; +use core::{ + borrow::Borrow, + cmp, fmt, hash, + ops::{Deref, Index, RangeFull}, + pin::Pin, +}; +use std::ffi::CStr; + +/// An archived [`CString`](std::ffi::CString). +/// +/// Uses a [`RelPtr`] to a `CStr` under the hood. +#[repr(transparent)] +pub struct ArchivedCString(RelPtr); + +impl ArchivedCString { + /// Returns the contents of this CString as a slice of bytes. + /// + /// The returned slice does **not** contain the trailing nul terminator, and it is guaranteed to + /// not have any interior nul bytes. If you need the nul terminator, use + /// [`as_bytes_with_nul`][ArchivedCString::as_bytes_with_nul()] instead. + #[inline] + pub fn as_bytes(&self) -> &[u8] { + self.as_c_str().to_bytes() + } + + /// Equivalent to [`as_bytes`][ArchivedCString::as_bytes()] except that the returned slice + /// includes the trailing nul terminator. + #[inline] + pub fn as_bytes_with_nul(&self) -> &[u8] { + self.as_c_str().to_bytes_with_nul() + } + + /// Extracts a `CStr` slice containing the entire string. + #[inline] + pub fn as_c_str(&self) -> &CStr { + unsafe { &*self.0.as_ptr() } + } + + /// Extracts a pinned mutable `CStr` slice containing the entire string. + #[inline] + pub fn pin_mut_c_str(self: Pin<&mut Self>) -> Pin<&mut CStr> { + unsafe { self.map_unchecked_mut(|s| &mut *s.0.as_mut_ptr()) } + } + + /// Resolves an archived C string from the given C string and parameters. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing a C string + #[inline] + pub unsafe fn resolve_from_c_str( + c_str: &CStr, + pos: usize, + resolver: CStringResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.0); + // metadata_resolver is guaranteed to be (), but it's better to be explicit about it + #[allow(clippy::unit_arg)] + c_str.resolve_unsized(pos + fp, resolver.pos, resolver.metadata_resolver, fo); + } + + /// Serializes a C string. + #[inline] + pub fn serialize_from_c_str( + c_str: &CStr, + serializer: &mut S, + ) -> Result { + Ok(CStringResolver { + pos: c_str.serialize_unsized(serializer)?, + metadata_resolver: c_str.serialize_metadata(serializer)?, + }) + } +} + +impl AsRef for ArchivedCString { + fn as_ref(&self) -> &CStr { + self.as_c_str() + } +} + +impl Borrow for ArchivedCString { + #[inline] + fn borrow(&self) -> &CStr { + self.as_c_str() + } +} + +impl fmt::Debug for ArchivedCString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_c_str().fmt(f) + } +} + +impl Deref for ArchivedCString { + type Target = CStr; + + #[inline] + fn deref(&self) -> &Self::Target { + self.as_c_str() + } +} + +impl Eq for ArchivedCString {} + +impl hash::Hash for ArchivedCString { + #[inline] + fn hash(&self, state: &mut H) { + self.as_bytes_with_nul().hash(state); + } +} + +impl Index for ArchivedCString { + type Output = CStr; + + #[inline] + fn index(&self, _: RangeFull) -> &Self::Output { + self.as_c_str() + } +} + +impl Ord for ArchivedCString { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.as_bytes().cmp(other.as_bytes()) + } +} + +impl PartialEq for ArchivedCString { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq<&CStr> for ArchivedCString { + #[inline] + fn eq(&self, other: &&CStr) -> bool { + PartialEq::eq(self.as_c_str(), other) + } +} + +impl PartialEq for &CStr { + #[inline] + fn eq(&self, other: &ArchivedCString) -> bool { + PartialEq::eq(other.as_c_str(), self) + } +} + +impl PartialOrd for ArchivedCString { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.as_bytes().partial_cmp(other.as_bytes()) + } +} + +/// The resolver for `CString`. +pub struct CStringResolver { + pos: usize, + metadata_resolver: MetadataResolver, +} + +#[cfg(feature = "validation")] +const _: () = { + use crate::validation::{ + owned::{CheckOwnedPointerError, OwnedPointerError}, + ArchiveContext, + }; + use bytecheck::{CheckBytes, Error}; + + impl CheckBytes for ArchivedCString + where + C::Error: Error, + { + type Error = CheckOwnedPointerError; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + let rel_ptr = RelPtr::::manual_check_bytes(value.cast(), context) + .map_err(OwnedPointerError::PointerCheckBytesError)?; + let ptr = context + .check_subtree_rel_ptr(rel_ptr) + .map_err(OwnedPointerError::ContextError)?; + + let range = context + .push_prefix_subtree(ptr) + .map_err(OwnedPointerError::ContextError)?; + CStr::check_bytes(ptr, context).map_err(OwnedPointerError::ValueCheckBytesError)?; + context + .pop_prefix_range(range) + .map_err(OwnedPointerError::ContextError)?; + + Ok(&*value) + } + } +}; diff --git a/src/impls/alloc/boxed.rs b/src/impls/alloc/boxed.rs new file mode 100644 index 0000000..a9e0672 --- /dev/null +++ b/src/impls/alloc/boxed.rs @@ -0,0 +1,60 @@ +use crate::{ + boxed::{ArchivedBox, BoxResolver}, + Archive, ArchivePointee, ArchiveUnsized, Deserialize, DeserializeUnsized, Fallible, Serialize, + SerializeUnsized, +}; +#[cfg(not(feature = "std"))] +use ::alloc::{alloc, boxed::Box}; +use ::core::cmp; +#[cfg(feature = "std")] +use ::std::alloc; + +impl Archive for Box { + type Archived = ArchivedBox; + type Resolver = BoxResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedBox::resolve_from_ref(self.as_ref(), pos, resolver, out); + } +} + +impl + ?Sized, S: Fallible + ?Sized> Serialize for Box { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedBox::serialize_from_ref(self.as_ref(), serializer) + } +} + +impl Deserialize, D> for ArchivedBox +where + T: ArchiveUnsized + ?Sized, + T::Archived: DeserializeUnsized, + D: Fallible + ?Sized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + unsafe { + let data_address = self + .get() + .deserialize_unsized(deserializer, |layout| alloc::alloc(layout))?; + let metadata = self.get().deserialize_metadata(deserializer)?; + let ptr = ptr_meta::from_raw_parts_mut(data_address, metadata); + Ok(Box::from_raw(ptr)) + } + } +} + +impl + ?Sized, U: ?Sized> PartialEq> for ArchivedBox { + #[inline] + fn eq(&self, other: &Box) -> bool { + self.get().eq(other.as_ref()) + } +} + +impl + ?Sized, U: ?Sized> PartialOrd> for ArchivedBox { + #[inline] + fn partial_cmp(&self, other: &Box) -> Option { + self.get().partial_cmp(other.as_ref()) + } +} diff --git a/src/impls/alloc/collections/btree_map.rs b/src/impls/alloc/collections/btree_map.rs new file mode 100644 index 0000000..5ad564f --- /dev/null +++ b/src/impls/alloc/collections/btree_map.rs @@ -0,0 +1,75 @@ +use crate::{ + collections::btree_map::{ArchivedBTreeMap, BTreeMapResolver}, + ser::Serializer, + Archive, Deserialize, Fallible, Serialize, +}; +#[cfg(not(feature = "std"))] +use alloc::collections::BTreeMap; +#[cfg(feature = "std")] +use std::collections::BTreeMap; + +impl Archive for BTreeMap +where + K::Archived: Ord, +{ + type Archived = ArchivedBTreeMap; + type Resolver = BTreeMapResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedBTreeMap::resolve_from_len(self.len(), pos, resolver, out); + } +} + +impl + Ord, V: Serialize, S: Serializer + ?Sized> Serialize for BTreeMap +where + K::Archived: Ord, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + unsafe { ArchivedBTreeMap::serialize_from_reverse_iter(self.iter().rev(), serializer) } + } +} + +impl Deserialize, D> + for ArchivedBTreeMap +where + K::Archived: Deserialize + Ord, + V::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = BTreeMap::new(); + for (key, value) in self.iter() { + result.insert( + key.deserialize(deserializer)?, + value.deserialize(deserializer)?, + ); + } + Ok(result) + } +} + +impl, AV: PartialEq> PartialEq> + for ArchivedBTreeMap +{ + #[inline] + fn eq(&self, other: &BTreeMap) -> bool { + if self.len() != other.len() { + false + } else { + self.iter() + .zip(other.iter()) + .all(|(a, b)| a.0.eq(b.0) && a.1.eq(b.1)) + } + } +} + +impl, AV: PartialEq> PartialEq> + for BTreeMap +{ + #[inline] + fn eq(&self, other: &ArchivedBTreeMap) -> bool { + other.eq(self) + } +} diff --git a/src/impls/alloc/collections/btree_set.rs b/src/impls/alloc/collections/btree_set.rs new file mode 100644 index 0000000..7a12f30 --- /dev/null +++ b/src/impls/alloc/collections/btree_set.rs @@ -0,0 +1,66 @@ +use crate::{ + collections::btree_set::{ArchivedBTreeSet, BTreeSetResolver}, + ser::Serializer, + Archive, Deserialize, Fallible, Serialize, +}; +#[cfg(not(feature = "std"))] +use alloc::collections::BTreeSet; +#[cfg(feature = "std")] +use std::collections::BTreeSet; + +impl Archive for BTreeSet +where + K::Archived: Ord, +{ + type Archived = ArchivedBTreeSet; + type Resolver = BTreeSetResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedBTreeSet::::resolve_from_len(self.len(), pos, resolver, out); + } +} + +impl + Ord, S: Serializer + ?Sized> Serialize for BTreeSet +where + K::Archived: Ord, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + unsafe { ArchivedBTreeSet::serialize_from_reverse_iter(self.iter().rev(), serializer) } + } +} + +impl Deserialize, D> for ArchivedBTreeSet +where + K: Archive + Ord, + K::Archived: Deserialize + Ord, + D: Fallible + ?Sized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = BTreeSet::new(); + for k in self.iter() { + result.insert(k.deserialize(deserializer)?); + } + Ok(result) + } +} + +impl> PartialEq> for ArchivedBTreeSet { + #[inline] + fn eq(&self, other: &BTreeSet) -> bool { + if self.len() != other.len() { + false + } else { + self.iter().zip(other.iter()).all(|(a, b)| a.eq(b)) + } + } +} + +impl> PartialEq> for BTreeSet { + #[inline] + fn eq(&self, other: &ArchivedBTreeSet) -> bool { + other.eq(self) + } +} diff --git a/src/impls/alloc/collections/mod.rs b/src/impls/alloc/collections/mod.rs new file mode 100644 index 0000000..06eb094 --- /dev/null +++ b/src/impls/alloc/collections/mod.rs @@ -0,0 +1,2 @@ +mod btree_map; +mod btree_set; diff --git a/src/impls/alloc/mod.rs b/src/impls/alloc/mod.rs new file mode 100644 index 0000000..d475bb3 --- /dev/null +++ b/src/impls/alloc/mod.rs @@ -0,0 +1,6 @@ +mod boxed; +mod collections; +mod niche; +mod rc; +mod string; +mod vec; diff --git a/src/impls/alloc/niche.rs b/src/impls/alloc/niche.rs new file mode 100644 index 0000000..8d29d8a --- /dev/null +++ b/src/impls/alloc/niche.rs @@ -0,0 +1,33 @@ +use crate::{niche::option_box::ArchivedOptionBox, ArchivePointee}; +#[cfg(not(feature = "std"))] +use ::alloc::boxed::Box; + +impl PartialEq>> for ArchivedOptionBox +where + T: ?Sized, + U: ArchivePointee + PartialEq + ?Sized, +{ + #[inline] + fn eq(&self, other: &Option>) -> bool { + if let Some(self_value) = self.as_deref() { + if let Some(other_value) = other.as_deref() { + self_value.eq(other_value) + } else { + false + } + } else { + other.is_none() + } + } +} + +impl PartialEq> for Option> +where + T: ArchivePointee + PartialEq + ?Sized, + U: ?Sized, +{ + #[inline] + fn eq(&self, other: &ArchivedOptionBox) -> bool { + other.eq(self) + } +} diff --git a/src/impls/alloc/rc.rs b/src/impls/alloc/rc.rs new file mode 100644 index 0000000..ffca281 --- /dev/null +++ b/src/impls/alloc/rc.rs @@ -0,0 +1,244 @@ +use crate::{ + de::{SharedDeserializeRegistry, SharedPointer}, + rc::{ArchivedRc, ArchivedRcWeak, RcResolver, RcWeakResolver}, + ser::{Serializer, SharedSerializeRegistry}, + Archive, ArchivePointee, ArchiveUnsized, Deserialize, DeserializeUnsized, Serialize, + SerializeUnsized, +}; +#[cfg(all(not(feature = "std"), has_atomics))] +use ::alloc::sync; +#[cfg(not(feature = "std"))] +use ::alloc::{alloc, boxed::Box, rc}; +use ::core::mem::forget; +#[cfg(all(feature = "std", has_atomics))] +use ::std::sync; +#[cfg(feature = "std")] +use ::std::{alloc, rc}; + +// Rc + +/// The flavor type for `Rc`. +pub struct RcFlavor; + +impl SharedPointer for rc::Rc { + #[inline] + fn data_address(&self) -> *const () { + rc::Rc::as_ptr(self) as *const () + } +} + +impl Archive for rc::Rc { + type Archived = ArchivedRc; + type Resolver = RcResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedRc::resolve_from_ref(self.as_ref(), pos, resolver, out); + } +} + +impl Serialize for rc::Rc +where + T: SerializeUnsized + ?Sized + 'static, + S: Serializer + SharedSerializeRegistry + ?Sized, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedRc::::serialize_from_ref(self.as_ref(), serializer) + } +} + +impl Deserialize, D> for ArchivedRc +where + T: ArchiveUnsized + ?Sized + 'static, + T::Archived: DeserializeUnsized, + D: SharedDeserializeRegistry + ?Sized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let raw_shared_ptr = deserializer.deserialize_shared( + self.get(), + |ptr| rc::Rc::::from(unsafe { Box::from_raw(ptr) }), + |layout| unsafe { alloc::alloc(layout) }, + )?; + let shared_ptr = unsafe { rc::Rc::::from_raw(raw_shared_ptr) }; + forget(shared_ptr.clone()); + Ok(shared_ptr) + } +} + +impl + ?Sized, U: ?Sized> PartialEq> + for ArchivedRc +{ + #[inline] + fn eq(&self, other: &rc::Rc) -> bool { + self.get().eq(other.as_ref()) + } +} + +// rc::Weak + +impl Archive for rc::Weak { + type Archived = ArchivedRcWeak; + type Resolver = RcWeakResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedRcWeak::resolve_from_ref( + self.upgrade().as_ref().map(|v| v.as_ref()), + pos, + resolver, + out, + ); + } +} + +impl Serialize for rc::Weak +where + T: SerializeUnsized + ?Sized + 'static, + S: Serializer + SharedSerializeRegistry + ?Sized, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedRcWeak::::serialize_from_ref( + self.upgrade().as_ref().map(|v| v.as_ref()), + serializer, + ) + } +} + +// Deserialize can only be implemented for sized types because weak pointers don't have from/into +// raw functions. +impl Deserialize, D> for ArchivedRcWeak +where + T: Archive + 'static, + T::Archived: DeserializeUnsized, + D: SharedDeserializeRegistry + ?Sized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(match self { + ArchivedRcWeak::None => rc::Weak::new(), + ArchivedRcWeak::Some(r) => rc::Rc::downgrade(&r.deserialize(deserializer)?), + }) + } +} + +// Arc + +/// The flavor type for `Arc`. +#[cfg(has_atomics)] +pub struct ArcFlavor; + +#[cfg(has_atomics)] +impl SharedPointer for sync::Arc { + #[inline] + fn data_address(&self) -> *const () { + sync::Arc::as_ptr(self) as *const () + } +} + +#[cfg(has_atomics)] +impl Archive for sync::Arc { + type Archived = ArchivedRc; + type Resolver = RcResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedRc::resolve_from_ref(self.as_ref(), pos, resolver, out); + } +} + +#[cfg(has_atomics)] +impl Serialize for sync::Arc +where + T: SerializeUnsized + ?Sized + 'static, + S: Serializer + SharedSerializeRegistry + ?Sized, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedRc::::serialize_from_ref(self.as_ref(), serializer) + } +} + +#[cfg(has_atomics)] +impl + Deserialize, D> for ArchivedRc +where + T::Archived: DeserializeUnsized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let raw_shared_ptr = deserializer.deserialize_shared( + self.get(), + |ptr| sync::Arc::::from(unsafe { Box::from_raw(ptr) }), + |layout| unsafe { alloc::alloc(layout) }, + )?; + let shared_ptr = unsafe { sync::Arc::::from_raw(raw_shared_ptr) }; + forget(shared_ptr.clone()); + Ok(shared_ptr) + } +} + +#[cfg(has_atomics)] +impl PartialEq> for ArchivedRc +where + T: ArchivePointee + PartialEq + ?Sized, + U: ?Sized, +{ + #[inline] + fn eq(&self, other: &sync::Arc) -> bool { + self.get().eq(other.as_ref()) + } +} + +// sync::Weak + +#[cfg(has_atomics)] +impl Archive for sync::Weak { + type Archived = ArchivedRcWeak; + type Resolver = RcWeakResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedRcWeak::resolve_from_ref( + self.upgrade().as_ref().map(|v| v.as_ref()), + pos, + resolver, + out, + ); + } +} + +#[cfg(has_atomics)] +impl Serialize for sync::Weak +where + T: SerializeUnsized + ?Sized + 'static, + S: Serializer + SharedSerializeRegistry + ?Sized, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedRcWeak::::serialize_from_ref( + self.upgrade().as_ref().map(|v| v.as_ref()), + serializer, + ) + } +} + +// Deserialize can only be implemented for sized types because weak pointers don't have from/into +// raw functions. +#[cfg(has_atomics)] +impl Deserialize, D> for ArchivedRcWeak +where + T: Archive + 'static, + T::Archived: DeserializeUnsized, + D: SharedDeserializeRegistry + ?Sized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(match self { + ArchivedRcWeak::None => sync::Weak::new(), + ArchivedRcWeak::Some(r) => sync::Arc::downgrade(&r.deserialize(deserializer)?), + }) + } +} diff --git a/src/impls/alloc/string.rs b/src/impls/alloc/string.rs new file mode 100644 index 0000000..080ad0e --- /dev/null +++ b/src/impls/alloc/string.rs @@ -0,0 +1,65 @@ +use crate::{ + string::{ArchivedString, StringResolver}, + Archive, Deserialize, DeserializeUnsized, Fallible, Serialize, SerializeUnsized, +}; +use ::core::cmp::Ordering; +#[cfg(not(feature = "std"))] +use ::alloc::string::{String, ToString}; + +impl Archive for String { + type Archived = ArchivedString; + type Resolver = StringResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedString::resolve_from_str(self.as_str(), pos, resolver, out); + } +} + +impl Serialize for String +where + str: SerializeUnsized, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedString::serialize_from_str(self.as_str(), serializer) + } +} + +impl Deserialize for ArchivedString +where + str: DeserializeUnsized, +{ + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(self.as_str().to_string()) + } +} + +impl PartialEq for ArchivedString { + #[inline] + fn eq(&self, other: &String) -> bool { + PartialEq::eq(self.as_str(), other.as_str()) + } +} + +impl PartialEq for String { + #[inline] + fn eq(&self, other: &ArchivedString) -> bool { + PartialEq::eq(other.as_str(), self.as_str()) + } +} + +impl PartialOrd for String { + #[inline] + fn partial_cmp(&self, other: &ArchivedString) -> Option { + self.as_str().partial_cmp(other.as_str()) + } +} + +impl PartialOrd for ArchivedString { + #[inline] + fn partial_cmp(&self, other: &String) -> Option { + self.as_str().partial_cmp(other.as_str()) + } +} diff --git a/src/impls/alloc/vec.rs b/src/impls/alloc/vec.rs new file mode 100644 index 0000000..a523cc2 --- /dev/null +++ b/src/impls/alloc/vec.rs @@ -0,0 +1,72 @@ +use crate::{ + ser::{ScratchSpace, Serializer}, + vec::{ArchivedVec, VecResolver}, + Archive, Deserialize, DeserializeUnsized, Fallible, Serialize, +}; +#[cfg(not(feature = "std"))] +use ::alloc::{alloc, boxed::Box, vec::Vec}; +use ::core::cmp; +#[cfg(feature = "std")] +use ::std::alloc; + +impl, U> PartialEq> for ArchivedVec { + #[inline] + fn eq(&self, other: &Vec) -> bool { + self.as_slice().eq(other.as_slice()) + } +} + +impl, U> PartialEq> for Vec { + #[inline] + fn eq(&self, other: &ArchivedVec) -> bool { + self.as_slice().eq(other.as_slice()) + } +} + +impl PartialOrd> for ArchivedVec { + #[inline] + fn partial_cmp(&self, other: &Vec) -> Option { + self.as_slice().partial_cmp(other.as_slice()) + } +} + +impl PartialOrd> for Vec { + #[inline] + fn partial_cmp(&self, other: &ArchivedVec) -> Option { + self.as_slice().partial_cmp(other.as_slice()) + } +} + +impl Archive for Vec { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedVec::resolve_from_slice(self.as_slice(), pos, resolver, out); + } +} + +impl, S: ScratchSpace + Serializer + ?Sized> Serialize for Vec { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedVec::::serialize_from_slice(self.as_slice(), serializer) + } +} + +impl Deserialize, D> for ArchivedVec +where + [T::Archived]: DeserializeUnsized<[T], D>, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + unsafe { + let data_address = self + .as_slice() + .deserialize_unsized(deserializer, |layout| alloc::alloc(layout))?; + let metadata = self.as_slice().deserialize_metadata(deserializer)?; + let ptr = ptr_meta::from_raw_parts_mut(data_address, metadata); + Ok(Box::<[T]>::from_raw(ptr).into()) + } + } +} diff --git a/src/impls/arrayvec.rs b/src/impls/arrayvec.rs new file mode 100644 index 0000000..49a13fd --- /dev/null +++ b/src/impls/arrayvec.rs @@ -0,0 +1,68 @@ +use crate::{ + ser::{ScratchSpace, Serializer}, + vec::{ArchivedVec, VecResolver}, + Archive, Archived, Deserialize, Fallible, Serialize, +}; +use arrayvec::ArrayVec; + +impl Archive for ArrayVec +where + T: Archive, +{ + type Archived = ArchivedVec>; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedVec::resolve_from_slice(self.as_slice(), pos, resolver, out); + } +} + +impl Serialize for ArrayVec +where + T: Serialize, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedVec::serialize_from_slice(self.as_slice(), serializer) + } +} + +impl Deserialize, D> + for ArchivedVec> +where + T: Archive, + Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = ArrayVec::new(); + for item in self.as_slice() { + result.push(item.deserialize(deserializer)?); + } + Ok(result) + } +} + +#[cfg(test)] +mod tests { + use crate::{archived_root, ser::Serializer, Deserialize, Infallible}; + use arrayvec::ArrayVec; + + #[test] + fn array_vec() { + use crate::ser::serializers::CoreSerializer; + + let value: ArrayVec = ArrayVec::from([10, 20, 40, 80]); + + let mut serializer = CoreSerializer::<256, 256>::default(); + serializer.serialize_value(&value).unwrap(); + let end = serializer.pos(); + let result = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::>(&result[0..end]) }; + assert_eq!(archived.as_slice(), &[10, 20, 40, 80]); + + let deserialized: ArrayVec = archived.deserialize(&mut Infallible).unwrap(); + assert_eq!(value, deserialized); + } +} diff --git a/src/impls/bitvec.rs b/src/impls/bitvec.rs new file mode 100644 index 0000000..652578c --- /dev/null +++ b/src/impls/bitvec.rs @@ -0,0 +1,183 @@ +#[cfg(feature = "bitvec_alloc")] +use crate::vec::{ArchivedVec, VecResolver}; +use crate::{ + bitvec::ArchivedBitVec, + out_field, + ser::{ScratchSpace, Serializer}, + Archive, Archived, Deserialize, Fallible, Serialize, +}; +use core::convert::{TryFrom, TryInto}; +use core::{marker::PhantomData, ops::Deref}; + +use bitvec::{prelude::*, view::BitViewSized}; + +impl ArchivedBitVec { + /// Gets the elements of the archived `BitVec` as a `BitSlice`. + pub fn as_bitslice(&self) -> &BitSlice { + self.deref() + } +} + +#[cfg(feature = "bitvec_alloc")] +impl Archive for BitVec +where + Archived: BitStore, +{ + type Archived = ArchivedBitVec, O>; + type Resolver = VecResolver; + + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.inner); + ArchivedVec::resolve_from_slice(self.as_raw_slice(), pos + fp, resolver, fo); + let (fp, fo) = out_field!(out.bit_len); + usize::resolve(&self.len(), pos + fp, (), fo); + } +} + +#[cfg(feature = "bitvec_alloc")] +impl Serialize for BitVec +where + T: BitStore + Archive + Serialize, + O: BitOrder, + S: Fallible + ?Sized + ScratchSpace + Serializer, + Archived: BitStore, +{ + fn serialize(&self, serializer: &mut S) -> Result::Error> { + let resolver = ArchivedVec::serialize_from_slice(self.as_raw_slice(), serializer)?; + usize::serialize(&self.len(), serializer)?; + + Ok(resolver) + } +} + +#[cfg(feature = "bitvec_alloc")] +impl Deserialize, D> for ArchivedBitVec, O> +where + T: BitStore + Archive, + O: BitOrder, + D: Fallible + ?Sized, + Archived: Deserialize + BitStore, +{ + fn deserialize(&self, deserializer: &mut D) -> Result, ::Error> { + let vec = ArchivedVec::deserialize(&self.inner, deserializer)?; + let bit_len = Archived::::deserialize(&self.bit_len, deserializer)?; + + let mut bitvec = BitVec::::from_vec(vec); + bitvec.truncate(bit_len); + Ok(bitvec) + } +} + +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ArchivedBitArray; 1], O = Lsb0> +where + A: BitViewSized, + O: BitOrder, +{ + inner: A, + _or: PhantomData, +} + +impl ArchivedBitArray { + /// Gets the elements of the archived `BitArray` as a `BitSlice`. + pub fn as_bitslice(&self) -> &BitSlice { + self.deref() + } +} + +impl Deref for ArchivedBitArray { + type Target = BitSlice; + + fn deref(&self) -> &Self::Target { + self.inner.view_bits::() + } +} + +impl Archive for BitArray +where + Archived: BitViewSized, + for<'a> &'a A: TryFrom<&'a [A::Store]>, +{ + type Archived = ArchivedBitArray, O>; + type Resolver = A::Resolver; + + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let arr_ref = self.as_raw_slice().try_into().ok().unwrap(); + + let (fp, fo) = out_field!(out.inner); + A::resolve(arr_ref, pos + fp, resolver, fo); + } +} + +impl Serialize for BitArray +where + A: BitViewSized + Archive + Serialize, + O: BitOrder, + S: Fallible + ?Sized + ScratchSpace + Serializer, + Archived: BitViewSized, + for<'a> &'a A: TryFrom<&'a [A::Store]>, +{ + fn serialize(&self, serializer: &mut S) -> Result::Error> { + let arr_ref = self.as_raw_slice().try_into().ok().unwrap(); + let resolver = A::serialize(arr_ref, serializer)?; + + Ok(resolver) + } +} + +impl Deserialize, D> + for ArchivedBitArray, O> +where + Archived: Deserialize + BitViewSized, +{ + fn deserialize(&self, deserializer: &mut D) -> Result, ::Error> { + let arr = Archived::::deserialize(&self.inner, deserializer)?; + Ok(arr.into()) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + archived_root, + ser::{serializers::CoreSerializer, Serializer}, + Deserialize, Infallible, + }; + use bitvec::prelude::*; + + #[test] + #[cfg(feature = "bitvec_alloc")] + fn bitvec() { + use crate::ser::serializers::CoreSerializer; + + let mut serializer = CoreSerializer::<256, 256>::default(); + let original = bitvec![1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1]; + + serializer.serialize_value(&original).unwrap(); + let end = serializer.pos(); + let buffer = serializer.into_serializer().into_inner(); + + let output = unsafe { archived_root::(&buffer[0..end]) }; + assert_eq!(&original, output.as_bitslice()); + + let deserialized: BitVec = output.deserialize(&mut Infallible).unwrap(); + assert_eq!(deserialized, original); + } + + #[test] + fn bitarr() { + let mut serializer = CoreSerializer::<256, 256>::default(); + let original = bitarr![1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1]; + + serializer.serialize_value(&original).unwrap(); + let end = serializer.pos(); + let buffer = serializer.into_serializer().into_inner(); + + let output = unsafe { archived_root::(&buffer[0..end]) }; + assert_eq!(&original[..11], &output[..11]); + + let deserialized: BitArray = output.deserialize(&mut Infallible).unwrap(); + assert_eq!(&deserialized[..11], &original[..11]); + } +} diff --git a/src/impls/core/mod.rs b/src/impls/core/mod.rs new file mode 100644 index 0000000..2e8286b --- /dev/null +++ b/src/impls/core/mod.rs @@ -0,0 +1,403 @@ +#[cfg(feature = "copy")] +use crate::copy::ArchiveCopyOptimize; +use crate::{ + ser::{ScratchSpace, Serializer}, + Archive, ArchivePointee, ArchiveUnsized, Archived, ArchivedMetadata, Deserialize, + DeserializeUnsized, Fallible, FixedUsize, Serialize, SerializeUnsized, +}; +use core::{alloc::Layout, mem::ManuallyDrop, ptr, str}; +use ptr_meta::Pointee; + +pub mod ops; +pub mod option; +pub mod primitive; +pub mod result; +pub mod time; + +impl ArchivePointee for T { + type ArchivedMetadata = (); + + #[inline] + fn pointer_metadata(_: &Self::ArchivedMetadata) -> ::Metadata {} +} + +impl ArchiveUnsized for T { + type Archived = T::Archived; + + type MetadataResolver = (); + + #[inline] + unsafe fn resolve_metadata( + &self, + _: usize, + _: Self::MetadataResolver, + _: *mut ArchivedMetadata, + ) { + } +} + +impl, S: Serializer + ?Sized> SerializeUnsized for T { + #[inline] + fn serialize_unsized(&self, serializer: &mut S) -> Result { + serializer.serialize_value(self) + } + + #[inline] + fn serialize_metadata(&self, _: &mut S) -> Result<(), S::Error> { + Ok(()) + } +} + +impl DeserializeUnsized for T::Archived +where + T::Archived: Deserialize, +{ + #[inline] + unsafe fn deserialize_unsized( + &self, + deserializer: &mut D, + mut alloc: impl FnMut(Layout) -> *mut u8, + ) -> Result<*mut (), D::Error> { + let deserialized = self.deserialize(deserializer)?; + + let layout = Layout::new::(); + if layout.size() == 0 { + Ok(ptr::NonNull::::dangling().as_ptr().cast()) + } else { + let ptr = alloc(layout).cast::(); + assert!(!ptr.is_null()); + ptr.write(deserialized); + Ok(ptr.cast()) + } + } + + #[inline] + fn deserialize_metadata(&self, _: &mut D) -> Result<::Metadata, D::Error> { + Ok(()) + } +} + +#[cfg(not(feature = "strict"))] +macro_rules! peel_tuple { + ($type:ident $index:tt, $($type_rest:ident $index_rest:tt,)*) => { impl_tuple! { $($type_rest $index_rest,)* } }; +} + +#[cfg(not(feature = "strict"))] +macro_rules! impl_tuple { + () => (); + ($($type:ident $index:tt,)+) => { + impl<$($type: Archive),+> Archive for ($($type,)+) { + type Archived = ($($type::Archived,)+); + type Resolver = ($($type::Resolver,)+); + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + $( + let (fp, fo) = out_field!(out.$index); + self.$index.resolve(pos + fp, resolver.$index, fo); + )+ + } + } + + impl<$($type: Serialize),+, S: Fallible + ?Sized> Serialize for ($($type,)+) { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + let rev = ($(self.$index.serialize(serializer)?,)+); + Ok(($(rev.$index,)+)) + } + } + + impl Deserialize<($($type,)+), D> for ($($type::Archived,)+) + where + $($type::Archived: Deserialize<$type, D>,)+ + { + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result<($($type,)+), D::Error> { + let rev = ($(&self.$index,)+); + Ok(($(rev.$index.deserialize(deserializer)?,)+)) + } + } + + peel_tuple! { $($type $index,)+ } + }; +} + +#[cfg(not(feature = "strict"))] +impl_tuple! { T11 11, T10 10, T9 9, T8 8, T7 7, T6 6, T5 5, T4 4, T3 3, T2 2, T1 1, T0 0, } + +impl Archive for [T; N] { + type Archived = [T::Archived; N]; + type Resolver = [T::Resolver; N]; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let mut resolvers = core::mem::MaybeUninit::new(resolver); + let resolvers_ptr = resolvers.as_mut_ptr().cast::(); + let out_ptr = out.cast::(); + for (i, value) in self.iter().enumerate() { + value.resolve( + pos + i * core::mem::size_of::(), + resolvers_ptr.add(i).read(), + out_ptr.add(i), + ); + } + } +} + +impl, S: Fallible + ?Sized, const N: usize> Serialize for [T; N] { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + let mut result = core::mem::MaybeUninit::::uninit(); + let result_ptr = result.as_mut_ptr().cast::(); + for (i, value) in self.iter().enumerate() { + unsafe { + result_ptr.add(i).write(value.serialize(serializer)?); + } + } + unsafe { Ok(result.assume_init()) } + } +} + +impl Deserialize<[T; N], D> for [T::Archived; N] +where + T::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result<[T; N], D::Error> { + let mut result = core::mem::MaybeUninit::<[T; N]>::uninit(); + let result_ptr = result.as_mut_ptr().cast::(); + for (i, value) in self.iter().enumerate() { + unsafe { + result_ptr.add(i).write(value.deserialize(deserializer)?); + } + } + unsafe { Ok(result.assume_init()) } + } +} + +impl ArchiveUnsized for [T] { + type Archived = [T::Archived]; + + type MetadataResolver = (); + + #[inline] + unsafe fn resolve_metadata( + &self, + _: usize, + _: Self::MetadataResolver, + out: *mut ArchivedMetadata, + ) { + out.write(to_archived!(ptr_meta::metadata(self) as FixedUsize)); + } +} + +impl ArchivePointee for [T] { + type ArchivedMetadata = Archived; + + #[inline] + fn pointer_metadata(archived: &Self::ArchivedMetadata) -> ::Metadata { + from_archived!(*archived) as usize + } +} + +impl, S: ScratchSpace + Serializer + ?Sized> SerializeUnsized for [T] { + default! { + fn serialize_unsized(&self, serializer: &mut S) -> Result { + use crate::ScratchVec; + + unsafe { + let mut resolvers = ScratchVec::new(serializer, self.len())?; + + for value in self.iter() { + resolvers.push(value.serialize(serializer)?); + } + let result = serializer.align_for::()?; + for (value, resolver) in self.iter().zip(resolvers.drain(..)) { + serializer.resolve_aligned(value, resolver)?; + } + + resolvers.free(serializer)?; + + Ok(result) + } + } + } + + default! { + #[inline] + fn serialize_metadata(&self, _: &mut S) -> Result { + Ok(()) + } + } +} + +#[cfg(feature = "copy")] +impl SerializeUnsized for [T] +where + T: Serialize + crate::copy::ArchiveCopyOptimize, + S: ScratchSpace + Serializer + ?Sized, +{ + fn serialize_unsized(&self, serializer: &mut S) -> Result { + unsafe { + let result = serializer.align_for::()?; + if !self.is_empty() { + let bytes = core::slice::from_raw_parts( + (self.as_ptr() as *const T).cast::(), + self.len() * core::mem::size_of::(), + ); + serializer.write(bytes)?; + } + Ok(result) + } + } + + #[inline] + fn serialize_metadata(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl, U, D: Fallible + ?Sized> DeserializeUnsized<[U], D> for [T] { + default! { + unsafe fn deserialize_unsized(&self, deserializer: &mut D, mut alloc: impl FnMut(Layout) -> *mut u8) -> Result<*mut (), D::Error> { + if self.is_empty() || core::mem::size_of::() == 0 { + Ok(ptr::NonNull::::dangling().as_ptr().cast()) + } else { + let result = alloc(Layout::array::(self.len()).unwrap()).cast::(); + assert!(!result.is_null()); + for (i, item) in self.iter().enumerate() { + result.add(i).write(item.deserialize(deserializer)?); + } + Ok(result.cast()) + } + } + } + + default! { + #[inline] + fn deserialize_metadata(&self, _: &mut D) -> Result<<[U] as Pointee>::Metadata, D::Error> { + Ok(ptr_meta::metadata(self)) + } + } +} + +#[cfg(feature = "copy")] +impl DeserializeUnsized<[U], D> for [T] +where + T: Deserialize, + U: ArchiveCopyOptimize, + D: Fallible + ?Sized, +{ + unsafe fn deserialize_unsized( + &self, + _: &mut D, + mut alloc: impl FnMut(Layout) -> *mut u8, + ) -> Result<*mut (), D::Error> { + if self.is_empty() || core::mem::size_of::() == 0 { + Ok(ptr::NonNull::::dangling().as_ptr().cast()) + } else { + let result = alloc(Layout::array::(self.len()).unwrap()).cast::(); + assert!(!result.is_null()); + ptr::copy_nonoverlapping(self.as_ptr(), result, self.len()); + Ok(result.cast()) + } + } + + #[inline] + fn deserialize_metadata(&self, _: &mut D) -> Result<<[T] as Pointee>::Metadata, D::Error> { + Ok(ptr_meta::metadata(self)) + } +} + +/// `str` + +impl ArchiveUnsized for str { + type Archived = str; + + type MetadataResolver = (); + + #[inline] + unsafe fn resolve_metadata( + &self, + _: usize, + _: Self::MetadataResolver, + out: *mut ArchivedMetadata, + ) { + out.write(to_archived!(ptr_meta::metadata(self) as FixedUsize)) + } +} + +impl ArchivePointee for str { + type ArchivedMetadata = Archived; + + #[inline] + fn pointer_metadata(archived: &Self::ArchivedMetadata) -> ::Metadata { + <[u8]>::pointer_metadata(archived) + } +} + +impl SerializeUnsized for str { + #[inline] + fn serialize_unsized(&self, serializer: &mut S) -> Result { + let result = serializer.pos(); + serializer.write(self.as_bytes())?; + Ok(result) + } + + #[inline] + fn serialize_metadata(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl DeserializeUnsized for ::Archived { + #[inline] + unsafe fn deserialize_unsized( + &self, + _: &mut D, + mut alloc: impl FnMut(Layout) -> *mut u8, + ) -> Result<*mut (), D::Error> { + if self.is_empty() { + Ok(ptr::null_mut()) + } else { + let bytes = alloc(Layout::array::(self.len()).unwrap()); + assert!(!bytes.is_null()); + ptr::copy_nonoverlapping(self.as_ptr(), bytes, self.len()); + Ok(bytes.cast()) + } + } + + #[inline] + fn deserialize_metadata(&self, _: &mut D) -> Result<::Metadata, D::Error> { + Ok(ptr_meta::metadata(self)) + } +} + +// `ManuallyDrop` + +impl Archive for ManuallyDrop { + type Archived = ManuallyDrop; + type Resolver = T::Resolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + T::resolve(self, pos, resolver, out.cast::()) + } +} + +impl, S: Fallible + ?Sized> Serialize for ManuallyDrop { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + T::serialize(self, serializer) + } +} + +impl Deserialize, D> for ManuallyDrop +where + T::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, ::Error> { + T::Archived::deserialize(self, deserializer).map(ManuallyDrop::new) + } +} diff --git a/src/impls/core/ops.rs b/src/impls/core/ops.rs new file mode 100644 index 0000000..5275db3 --- /dev/null +++ b/src/impls/core/ops.rs @@ -0,0 +1,249 @@ +use crate::{ + ops::{ + ArchivedRange, ArchivedRangeFrom, ArchivedRangeInclusive, ArchivedRangeTo, + ArchivedRangeToInclusive, + }, + Archive, Archived, Deserialize, Fallible, Serialize, +}; +use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; + +// RangeFull + +impl Archive for RangeFull { + type Archived = Self; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, _: *mut Self::Archived) {} +} + +impl Serialize for RangeFull { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for RangeFull { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(RangeFull) + } +} + +// Range + +impl Archive for Range { + type Archived = ArchivedRange; + type Resolver = Range; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.start); + self.start.resolve(pos + fp, resolver.start, fo); + let (fp, fo) = out_field!(out.end); + self.end.resolve(pos + fp, resolver.end, fo); + } +} + +impl, S: Fallible + ?Sized> Serialize for Range { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + Ok(Range { + start: self.start.serialize(serializer)?, + end: self.end.serialize(serializer)?, + }) + } +} + +impl Deserialize, D> for Archived> +where + T::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(Range { + start: self.start.deserialize(deserializer)?, + end: self.end.deserialize(deserializer)?, + }) + } +} + +impl> PartialEq> for ArchivedRange { + #[inline] + fn eq(&self, other: &Range) -> bool { + self.start.eq(&other.start) && self.end.eq(&other.end) + } +} + +// RangeInclusive + +impl Archive for RangeInclusive { + type Archived = ArchivedRangeInclusive; + type Resolver = Range; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.start); + self.start().resolve(pos + fp, resolver.start, fo); + let (fp, fo) = out_field!(out.end); + self.end().resolve(pos + fp, resolver.end, fo); + } +} + +impl, S: Fallible + ?Sized> Serialize for RangeInclusive { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + Ok(Range { + start: self.start().serialize(serializer)?, + end: self.end().serialize(serializer)?, + }) + } +} + +impl Deserialize, D> for Archived> +where + T: Archive, + T::Archived: Deserialize, + D: Fallible + ?Sized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(RangeInclusive::new( + self.start.deserialize(deserializer)?, + self.end.deserialize(deserializer)?, + )) + } +} + +impl> PartialEq> for ArchivedRangeInclusive { + #[inline] + fn eq(&self, other: &RangeInclusive) -> bool { + self.start.eq(other.start()) && self.end.eq(other.end()) + } +} + +// RangeFrom + +impl Archive for RangeFrom { + type Archived = ArchivedRangeFrom; + type Resolver = RangeFrom; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.start); + self.start.resolve(pos + fp, resolver.start, fo); + } +} + +impl, S: Fallible + ?Sized> Serialize for RangeFrom { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + Ok(RangeFrom { + start: self.start.serialize(serializer)?, + }) + } +} + +impl Deserialize, D> for Archived> +where + T::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(RangeFrom { + start: self.start.deserialize(deserializer)?, + }) + } +} + +impl> PartialEq> for ArchivedRangeFrom { + #[inline] + fn eq(&self, other: &RangeFrom) -> bool { + self.start.eq(&other.start) + } +} + +// RangeTo + +impl Archive for RangeTo { + type Archived = ArchivedRangeTo; + type Resolver = RangeTo; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.end); + self.end.resolve(pos + fp, resolver.end, fo); + } +} + +impl, S: Fallible + ?Sized> Serialize for RangeTo { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + Ok(RangeTo { + end: self.end.serialize(serializer)?, + }) + } +} + +impl Deserialize, D> for Archived> +where + T::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(RangeTo { + end: self.end.deserialize(deserializer)?, + }) + } +} + +impl> PartialEq> for ArchivedRangeTo { + #[inline] + fn eq(&self, other: &RangeTo) -> bool { + self.end.eq(&other.end) + } +} + +// RangeToInclusive + +impl Archive for RangeToInclusive { + type Archived = ArchivedRangeToInclusive; + type Resolver = RangeToInclusive; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.end); + self.end.resolve(pos + fp, resolver.end, fo); + } +} + +impl, S: Fallible + ?Sized> Serialize for RangeToInclusive { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + Ok(RangeToInclusive { + end: self.end.serialize(serializer)?, + }) + } +} + +impl Deserialize, D> for Archived> +where + T: Archive, + T::Archived: Deserialize, + D: Fallible + ?Sized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(RangeToInclusive { + end: self.end.deserialize(deserializer)?, + }) + } +} + +impl> PartialEq> for ArchivedRangeToInclusive { + #[inline] + fn eq(&self, other: &RangeToInclusive) -> bool { + self.end.eq(&other.end) + } +} diff --git a/src/impls/core/option.rs b/src/impls/core/option.rs new file mode 100644 index 0000000..afabd5e --- /dev/null +++ b/src/impls/core/option.rs @@ -0,0 +1,65 @@ +use crate::{option::ArchivedOption, Archive, Deserialize, Fallible, Serialize}; +use core::{hint::unreachable_unchecked, ptr}; + +#[allow(dead_code)] +#[repr(u8)] +enum ArchivedOptionTag { + None, + Some, +} + +#[repr(C)] +struct ArchivedOptionVariantNone(ArchivedOptionTag); + +#[repr(C)] +struct ArchivedOptionVariantSome(ArchivedOptionTag, T); + +impl Archive for Option { + type Archived = ArchivedOption; + type Resolver = Option; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + match resolver { + None => { + let out = out.cast::(); + ptr::addr_of_mut!((*out).0).write(ArchivedOptionTag::None); + } + Some(resolver) => { + let out = out.cast::>(); + ptr::addr_of_mut!((*out).0).write(ArchivedOptionTag::Some); + + let value = if let Some(value) = self.as_ref() { + value + } else { + unreachable_unchecked(); + }; + + let (fp, fo) = out_field!(out.1); + value.resolve(pos + fp, resolver, fo); + } + } + } +} + +impl, S: Fallible + ?Sized> Serialize for Option { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + self.as_ref() + .map(|value| value.serialize(serializer)) + .transpose() + } +} + +impl Deserialize, D> for ArchivedOption +where + T::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + match self { + ArchivedOption::Some(value) => Ok(Some(value.deserialize(deserializer)?)), + ArchivedOption::None => Ok(None), + } + } +} diff --git a/src/impls/core/primitive.rs b/src/impls/core/primitive.rs new file mode 100644 index 0000000..2cc88a1 --- /dev/null +++ b/src/impls/core/primitive.rs @@ -0,0 +1,399 @@ +use crate::{Archive, Archived, Deserialize, Fallible, FixedIsize, FixedUsize, Serialize}; +#[cfg(has_atomics)] +use core::sync::atomic::{ + AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU8, + AtomicUsize, Ordering, +}; +#[cfg(has_atomics_64)] +use core::sync::atomic::{AtomicI64, AtomicU64}; +use core::{ + marker::{PhantomData, PhantomPinned}, + num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, + }, +}; + +macro_rules! impl_primitive { + (@serialize $type:ty) => { + impl Serialize for $type { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } + } + }; + ($type:ty) => { + impl Archive for $type { + type Archived = Self; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(*self); + } + } + + impl_primitive!(@serialize $type); + + impl Deserialize<$type, D> for Archived<$type> { + #[inline] + fn deserialize(&self, _: &mut D) -> Result<$type, D::Error> { + Ok(*self) + } + } + }; + (@multibyte $type:ty) => { + const _: () = { + #[cfg(not(any(feature = "archive_le", feature = "archive_be")))] + type Archived = $type; + #[cfg(feature = "archive_le")] + type Archived = crate::rend::LittleEndian<$type>; + #[cfg(feature = "archive_be")] + type Archived = crate::rend::BigEndian<$type>; + + impl Archive for $type { + type Archived = Archived; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(to_archived!(*self as Self)); + } + } + + impl_primitive!(@serialize $type); + + impl Deserialize<$type, D> for Archived { + #[inline] + fn deserialize(&self, _: &mut D) -> Result<$type, D::Error> { + Ok(from_archived!(*self)) + } + } + }; + }; +} + +#[cfg(has_atomics)] +macro_rules! impl_atomic { + (@serialize_deserialize $type:ty) => { + impl Serialize for $type { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } + } + }; + ($type:ty, $prim:ty) => { + impl Archive for $type { + type Archived = $prim; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(self.load(Ordering::Relaxed)); + } + } + + impl_atomic!(@serialize_deserialize $type); + + impl Deserialize<$type, D> for Archived<$type> { + #[inline] + fn deserialize(&self, _: &mut D) -> Result<$type, D::Error> { + Ok((*self).into()) + } + } + }; + (@multibyte $type:ty, $prim:ty) => { + impl Archive for $type { + #[cfg(not(any(feature = "archive_le", feature = "archive_be")))] + type Archived = $prim; + #[cfg(feature = "archive_le")] + type Archived = crate::rend::LittleEndian<$prim>; + #[cfg(feature = "archive_be")] + type Archived = crate::rend::BigEndian<$prim>; + + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(to_archived!(self.load(Ordering::Relaxed))); + } + } + + impl_atomic!(@serialize_deserialize $type); + + impl Deserialize<$type, D> for Archived<$type> { + #[inline] + fn deserialize(&self, _: &mut D) -> Result<$type, D::Error> { + Ok(from_archived!(*self).into()) + } + } + }; +} + +impl_primitive!(()); +impl_primitive!(bool); +impl_primitive!(i8); +impl_primitive!(u8); +impl_primitive!(NonZeroI8); +impl_primitive!(NonZeroU8); +#[cfg(has_atomics)] +impl_atomic!(AtomicBool, bool); +#[cfg(has_atomics)] +impl_atomic!(AtomicI8, i8); +#[cfg(has_atomics)] +impl_atomic!(AtomicU8, u8); + +impl_primitive!(@multibyte i16); +impl_primitive!(@multibyte i32); +impl_primitive!(@multibyte i64); +impl_primitive!(@multibyte i128); +impl_primitive!(@multibyte u16); +impl_primitive!(@multibyte u32); +impl_primitive!(@multibyte u64); +impl_primitive!(@multibyte u128); + +impl_primitive!(@multibyte f32); +impl_primitive!(@multibyte f64); + +impl_primitive!(@multibyte char); + +impl_primitive!(@multibyte NonZeroI16); +impl_primitive!(@multibyte NonZeroI32); +impl_primitive!(@multibyte NonZeroI64); +impl_primitive!(@multibyte NonZeroI128); +impl_primitive!(@multibyte NonZeroU16); +impl_primitive!(@multibyte NonZeroU32); +impl_primitive!(@multibyte NonZeroU64); +impl_primitive!(@multibyte NonZeroU128); + +#[cfg(has_atomics)] +impl_atomic!(@multibyte AtomicI16, i16); +#[cfg(has_atomics)] +impl_atomic!(@multibyte AtomicI32, i32); +#[cfg(has_atomics_64)] +impl_atomic!(@multibyte AtomicI64, i64); +#[cfg(has_atomics)] +impl_atomic!(@multibyte AtomicU16, u16); +#[cfg(has_atomics)] +impl_atomic!(@multibyte AtomicU32, u32); +#[cfg(has_atomics_64)] +impl_atomic!(@multibyte AtomicU64, u64); + +// PhantomData + +impl Archive for PhantomData { + type Archived = PhantomData; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, _: *mut Self::Archived) {} +} + +impl Serialize for PhantomData { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize, D> for PhantomData { + #[inline] + fn deserialize(&self, _: &mut D) -> Result, D::Error> { + Ok(PhantomData) + } +} + +// PhantomPinned +impl Archive for PhantomPinned { + type Archived = PhantomPinned; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, _: *mut Self::Archived) {} +} + +impl Serialize for PhantomPinned { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for PhantomPinned { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(PhantomPinned) + } +} + +// usize + +impl Archive for usize { + type Archived = Archived; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(to_archived!(*self as FixedUsize)); + } +} + +impl Serialize for usize { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for Archived { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(from_archived!(*self) as usize) + } +} + +// isize + +impl Archive for isize { + type Archived = Archived; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(to_archived!(*self as FixedIsize)); + } +} + +impl Serialize for isize { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for Archived { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(from_archived!(*self) as isize) + } +} + +// NonZeroUsize + +type FixedNonZeroUsize = pick_size_type!(NonZeroU16, NonZeroU32, NonZeroU64); + +impl Archive for NonZeroUsize { + type Archived = Archived; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(to_archived!(FixedNonZeroUsize::new_unchecked( + self.get() as FixedUsize + ))); + } +} + +impl Serialize for NonZeroUsize { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for Archived { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(unsafe { NonZeroUsize::new_unchecked(from_archived!(*self).get() as usize) }) + } +} + +// NonZeroIsize + +type FixedNonZeroIsize = pick_size_type!(NonZeroI16, NonZeroI32, NonZeroI64); + +impl Archive for NonZeroIsize { + type Archived = Archived; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(to_archived!(FixedNonZeroIsize::new_unchecked( + self.get() as FixedIsize + ))); + } +} + +impl Serialize for NonZeroIsize { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for Archived { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(unsafe { NonZeroIsize::new_unchecked(from_archived!(*self).get() as isize) }) + } +} + +// AtomicUsize + +#[cfg(has_atomics)] +impl Archive for AtomicUsize { + type Archived = Archived; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(to_archived!(self.load(Ordering::Relaxed) as FixedUsize)); + } +} + +#[cfg(has_atomics)] +impl Serialize for AtomicUsize { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +#[cfg(has_atomics)] +impl Deserialize for Archived { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok((from_archived!(*self) as usize).into()) + } +} + +// AtomicIsize + +#[cfg(has_atomics)] +impl Archive for AtomicIsize { + type Archived = Archived; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(to_archived!(self.load(Ordering::Relaxed) as FixedIsize)); + } +} + +#[cfg(has_atomics)] +impl Serialize for AtomicIsize { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +#[cfg(has_atomics)] +impl Deserialize for Archived { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok((from_archived!(*self) as isize).into()) + } +} diff --git a/src/impls/core/result.rs b/src/impls/core/result.rs new file mode 100644 index 0000000..d1860cb --- /dev/null +++ b/src/impls/core/result.rs @@ -0,0 +1,73 @@ +use crate::{result::ArchivedResult, Archive, Deserialize, Fallible, Serialize}; +use core::{hint::unreachable_unchecked, ptr}; + +#[allow(dead_code)] +#[repr(u8)] +enum ArchivedResultTag { + Ok, + Err, +} + +#[repr(C)] +struct ArchivedResultVariantOk(ArchivedResultTag, T); + +#[repr(C)] +struct ArchivedResultVariantErr(ArchivedResultTag, E); + +impl Archive for Result { + type Archived = ArchivedResult; + type Resolver = Result; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + match resolver { + Ok(resolver) => { + let out = out.cast::>(); + ptr::addr_of_mut!((*out).0).write(ArchivedResultTag::Ok); + + let (fp, fo) = out_field!(out.1); + match self.as_ref() { + Ok(value) => value.resolve(pos + fp, resolver, fo), + Err(_) => unreachable_unchecked(), + } + } + Err(resolver) => { + let out = out.cast::>(); + ptr::addr_of_mut!((*out).0).write(ArchivedResultTag::Err); + + let (fp, fo) = out_field!(out.1); + match self.as_ref() { + Ok(_) => unreachable_unchecked(), + Err(err) => err.resolve(pos + fp, resolver, fo), + } + } + } + } +} + +impl, E: Serialize, S: Fallible + ?Sized> Serialize for Result { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + Ok(match self.as_ref() { + Ok(value) => Ok(value.serialize(serializer)?), + Err(value) => Err(value.serialize(serializer)?), + }) + } +} + +impl Deserialize, D> for ArchivedResult +where + T: Archive, + E: Archive, + D: Fallible + ?Sized, + T::Archived: Deserialize, + E::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + match self { + ArchivedResult::Ok(value) => Ok(Ok(value.deserialize(deserializer)?)), + ArchivedResult::Err(err) => Ok(Err(err.deserialize(deserializer)?)), + } + } +} diff --git a/src/impls/core/time.rs b/src/impls/core/time.rs new file mode 100644 index 0000000..48ccb2a --- /dev/null +++ b/src/impls/core/time.rs @@ -0,0 +1,33 @@ +use crate::{time::ArchivedDuration, Archive, Deserialize, Fallible, Serialize}; +use core::time::Duration; + +impl Archive for Duration { + type Archived = ArchivedDuration; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + ArchivedDuration::emplace(self.as_secs(), self.subsec_nanos(), out); + } +} + +impl Serialize for Duration { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for ArchivedDuration { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(Duration::new(self.as_secs(), self.subsec_nanos())) + } +} + +impl From for Duration { + #[inline] + fn from(duration: ArchivedDuration) -> Self { + Self::new(duration.as_secs(), duration.subsec_nanos()) + } +} diff --git a/src/impls/hashbrown/hash_map.rs b/src/impls/hashbrown/hash_map.rs new file mode 100644 index 0000000..4162013 --- /dev/null +++ b/src/impls/hashbrown/hash_map.rs @@ -0,0 +1,129 @@ +use crate::{ + collections::hash_map::{ArchivedHashMap, HashMapResolver}, + ser::{ScratchSpace, Serializer}, + Archive, Deserialize, Fallible, Serialize, +}; +use core::{ + borrow::Borrow, + hash::{BuildHasher, Hash}, +}; +use hashbrown::HashMap; + +impl Archive for HashMap +where + K::Archived: Hash + Eq, +{ + type Archived = ArchivedHashMap; + type Resolver = HashMapResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedHashMap::resolve_from_len(self.len(), pos, resolver, out); + } +} + +impl Serialize for HashMap +where + K: Serialize + Hash + Eq, + K::Archived: Hash + Eq, + V: Serialize, + S: Serializer + ScratchSpace + ?Sized, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + unsafe { ArchivedHashMap::serialize_from_iter(self.iter(), serializer) } + } +} + +impl + Deserialize, D> for ArchivedHashMap +where + K::Archived: Deserialize + Hash + Eq, + V::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = HashMap::with_capacity_and_hasher(self.len(), S::default()); + for (k, v) in self.iter() { + result.insert(k.deserialize(deserializer)?, v.deserialize(deserializer)?); + } + Ok(result) + } +} + +impl, V, AK: Hash + Eq, AV: PartialEq, S: BuildHasher> + PartialEq> for ArchivedHashMap +{ + #[inline] + fn eq(&self, other: &HashMap) -> bool { + if self.len() != other.len() { + false + } else { + self.iter() + .all(|(key, value)| other.get(key).map_or(false, |v| value.eq(v))) + } + } +} + +impl, V, AK: Hash + Eq, AV: PartialEq> + PartialEq> for HashMap +{ + #[inline] + fn eq(&self, other: &ArchivedHashMap) -> bool { + other.eq(self) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + archived_root, + ser::{serializers::AllocSerializer, Serializer}, + Deserialize, Infallible, + }; + #[cfg(all(feature = "alloc", not(feature = "std")))] + use alloc::string::String; + use hashbrown::HashMap; + + #[test] + fn index_map() { + let mut value = HashMap::new(); + value.insert(String::from("foo"), 10); + value.insert(String::from("bar"), 20); + value.insert(String::from("baz"), 40); + value.insert(String::from("bat"), 80); + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&value).unwrap(); + let result = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::>(result.as_ref()) }; + + assert_eq!(value.len(), archived.len()); + for (k, v) in value.iter() { + let (ak, av) = archived.get_key_value(k.as_str()).unwrap(); + assert_eq!(k, ak); + assert_eq!(v, av); + } + + let deserialized: HashMap = archived.deserialize(&mut Infallible).unwrap(); + assert_eq!(value, deserialized); + } + + #[cfg(feature = "validation")] + #[test] + fn validate_index_map() { + use crate::check_archived_root; + + let mut value = HashMap::new(); + value.insert(String::from("foo"), 10); + value.insert(String::from("bar"), 20); + value.insert(String::from("baz"), 40); + value.insert(String::from("bat"), 80); + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&value).unwrap(); + let result = serializer.into_serializer().into_inner(); + check_archived_root::>(result.as_ref()) + .expect("failed to validate archived index map"); + } +} diff --git a/src/impls/hashbrown/hash_set.rs b/src/impls/hashbrown/hash_set.rs new file mode 100644 index 0000000..cd62a2a --- /dev/null +++ b/src/impls/hashbrown/hash_set.rs @@ -0,0 +1,127 @@ +use crate::{ + collections::hash_set::{ArchivedHashSet, HashSetResolver}, + ser::{ScratchSpace, Serializer}, + Archive, Deserialize, Fallible, Serialize, +}; +use core::{ + borrow::Borrow, + hash::{BuildHasher, Hash}, +}; +use hashbrown::HashSet; + +impl Archive for HashSet +where + K::Archived: Hash + Eq, +{ + type Archived = ArchivedHashSet; + type Resolver = HashSetResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedHashSet::::resolve_from_len(self.len(), pos, resolver, out); + } +} + +impl Serialize for HashSet +where + K::Archived: Hash + Eq, + K: Serialize + Hash + Eq, + S: ScratchSpace + Serializer + ?Sized, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + unsafe { ArchivedHashSet::serialize_from_iter(self.iter(), serializer) } + } +} + +impl Deserialize, D> for ArchivedHashSet +where + K: Archive + Hash + Eq, + K::Archived: Deserialize + Hash + Eq, + D: Fallible + ?Sized, + S: Default + BuildHasher, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = HashSet::with_hasher(S::default()); + for k in self.iter() { + result.insert(k.deserialize(deserializer)?); + } + Ok(result) + } +} + +impl, AK: Hash + Eq, S: BuildHasher> PartialEq> + for ArchivedHashSet +{ + #[inline] + fn eq(&self, other: &HashSet) -> bool { + if self.len() != other.len() { + false + } else { + self.iter().all(|key| other.get(key).is_some()) + } + } +} + +impl, AK: Hash + Eq, S: BuildHasher> PartialEq> + for HashSet +{ + #[inline] + fn eq(&self, other: &ArchivedHashSet) -> bool { + other.eq(self) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + archived_root, + ser::{serializers::AllocSerializer, Serializer}, + Deserialize, Infallible, + }; + #[cfg(all(feature = "alloc", not(feature = "std")))] + use alloc::string::String; + use hashbrown::HashSet; + + #[test] + fn index_set() { + let mut value = HashSet::new(); + value.insert(String::from("foo")); + value.insert(String::from("bar")); + value.insert(String::from("baz")); + value.insert(String::from("bat")); + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&value).unwrap(); + let result = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::>(result.as_ref()) }; + + assert_eq!(value.len(), archived.len()); + for k in value.iter() { + let ak = archived.get(k.as_str()).unwrap(); + assert_eq!(k, ak); + } + + let deserialized: HashSet = archived.deserialize(&mut Infallible).unwrap(); + assert_eq!(value, deserialized); + } + + #[cfg(feature = "validation")] + #[test] + fn validate_index_set() { + use crate::check_archived_root; + + let mut value = HashSet::new(); + value.insert(String::from("foo")); + value.insert(String::from("bar")); + value.insert(String::from("baz")); + value.insert(String::from("bat")); + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&value).unwrap(); + let result = serializer.into_serializer().into_inner(); + check_archived_root::>(result.as_ref()) + .expect("failed to validate archived index set"); + } +} diff --git a/src/impls/hashbrown/mod.rs b/src/impls/hashbrown/mod.rs new file mode 100644 index 0000000..2bde6a0 --- /dev/null +++ b/src/impls/hashbrown/mod.rs @@ -0,0 +1,2 @@ +mod hash_map; +mod hash_set; diff --git a/src/impls/indexmap/index_map.rs b/src/impls/indexmap/index_map.rs new file mode 100644 index 0000000..32d6b24 --- /dev/null +++ b/src/impls/indexmap/index_map.rs @@ -0,0 +1,119 @@ +use crate::{ + collections::index_map::{ArchivedIndexMap, IndexMapResolver}, + ser::{ScratchSpace, Serializer}, + Archive, Deserialize, Fallible, Serialize, +}; +use core::hash::{BuildHasher, Hash}; +use indexmap::IndexMap; + +impl Archive for IndexMap { + type Archived = ArchivedIndexMap; + type Resolver = IndexMapResolver; + + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedIndexMap::resolve_from_len(self.len(), pos, resolver, out); + } +} + +impl Serialize for IndexMap +where + K: Hash + Eq + Serialize, + V: Serialize, + S: ScratchSpace + Serializer + ?Sized, + RandomState: BuildHasher, +{ + fn serialize(&self, serializer: &mut S) -> Result { + unsafe { + ArchivedIndexMap::serialize_from_iter_index( + self.iter(), + |k| self.get_index_of(k).unwrap(), + serializer, + ) + } + } +} + +impl Deserialize, D> for ArchivedIndexMap +where + K: Archive + Hash + Eq, + K::Archived: Deserialize, + V: Archive, + V::Archived: Deserialize, + D: Fallible + ?Sized, + S: Default + BuildHasher, +{ + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = IndexMap::with_capacity_and_hasher(self.len(), S::default()); + for (k, v) in self.iter() { + result.insert(k.deserialize(deserializer)?, v.deserialize(deserializer)?); + } + Ok(result) + } +} + +impl PartialEq> for ArchivedIndexMap +where + K: PartialEq, + V: PartialEq, + S: BuildHasher, +{ + fn eq(&self, other: &IndexMap) -> bool { + self.iter() + .zip(other.iter()) + .all(|((ak, av), (bk, bv))| ak == bk && av == bv) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + archived_root, + ser::{serializers::AllocSerializer, Serializer}, + Deserialize, Infallible, + }; + use indexmap::{indexmap, IndexMap}; + + #[test] + fn index_map() { + let value = indexmap! { + String::from("foo") => 10, + String::from("bar") => 20, + String::from("baz") => 40, + String::from("bat") => 80, + }; + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&value).unwrap(); + let result = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::>(result.as_ref()) }; + + assert_eq!(value.len(), archived.len()); + for (k, v) in value.iter() { + let (ak, av) = archived.get_key_value(k.as_str()).unwrap(); + assert_eq!(k, ak); + assert_eq!(v, av); + } + + let deserialized: IndexMap = archived.deserialize(&mut Infallible).unwrap(); + assert_eq!(value, deserialized); + } + + #[cfg(feature = "validation")] + #[test] + fn validate_index_map() { + use crate::check_archived_root; + + let value = indexmap! { + String::from("foo") => 10, + String::from("bar") => 20, + String::from("baz") => 40, + String::from("bat") => 80, + }; + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&value).unwrap(); + let result = serializer.into_serializer().into_inner(); + check_archived_root::>(result.as_ref()) + .expect("failed to validate archived index map"); + } +} diff --git a/src/impls/indexmap/index_set.rs b/src/impls/indexmap/index_set.rs new file mode 100644 index 0000000..d67d92d --- /dev/null +++ b/src/impls/indexmap/index_set.rs @@ -0,0 +1,108 @@ +use crate::{ + collections::index_set::{ArchivedIndexSet, IndexSetResolver}, + ser::{ScratchSpace, Serializer}, + Archive, Deserialize, Fallible, Serialize, +}; +use core::hash::{BuildHasher, Hash}; +use indexmap::IndexSet; + +impl Archive for IndexSet { + type Archived = ArchivedIndexSet; + type Resolver = IndexSetResolver; + + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedIndexSet::resolve_from_len(self.len(), pos, resolver, out); + } +} + +impl Serialize for IndexSet +where + K: Hash + Eq + Serialize, + S: ScratchSpace + Serializer + ?Sized, + RandomState: BuildHasher, +{ + fn serialize(&self, serializer: &mut S) -> Result { + unsafe { + ArchivedIndexSet::serialize_from_iter_index( + self.iter(), + |k| self.get_index_of(k).unwrap(), + serializer, + ) + } + } +} + +impl Deserialize, D> for ArchivedIndexSet +where + K: Archive + Hash + Eq, + K::Archived: Deserialize, + D: Fallible + ?Sized, + S: Default + BuildHasher, +{ + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = IndexSet::with_capacity_and_hasher(self.len(), S::default()); + for k in self.iter() { + result.insert(k.deserialize(deserializer)?); + } + Ok(result) + } +} + +impl, S: BuildHasher> PartialEq> for ArchivedIndexSet { + fn eq(&self, other: &IndexSet) -> bool { + self.iter().eq(other.iter()) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + archived_root, + ser::{serializers::AllocSerializer, Serializer}, + Deserialize, Infallible, + }; + use indexmap::{indexset, IndexSet}; + + #[test] + fn index_set() { + let value = indexset! { + String::from("foo"), + String::from("bar"), + String::from("baz"), + String::from("bat"), + }; + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&value).unwrap(); + let result = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::>(result.as_ref()) }; + + assert_eq!(value.len(), archived.len()); + for k in value.iter() { + let ak = archived.get(k.as_str()).unwrap(); + assert_eq!(k, ak); + } + + let deserialized: IndexSet = archived.deserialize(&mut Infallible).unwrap(); + assert_eq!(value, deserialized); + } + + #[cfg(feature = "validation")] + #[test] + fn validate_index_set() { + use crate::check_archived_root; + + let value = indexset! { + String::from("foo"), + String::from("bar"), + String::from("baz"), + String::from("bat"), + }; + + let mut serializer = AllocSerializer::<4096>::default(); + serializer.serialize_value(&value).unwrap(); + let result = serializer.into_serializer().into_inner(); + check_archived_root::>(result.as_ref()) + .expect("failed to validate archived index set"); + } +} diff --git a/src/impls/indexmap/mod.rs b/src/impls/indexmap/mod.rs new file mode 100644 index 0000000..ac04c20 --- /dev/null +++ b/src/impls/indexmap/mod.rs @@ -0,0 +1,2 @@ +mod index_map; +mod index_set; diff --git a/src/impls/mod.rs b/src/impls/mod.rs new file mode 100644 index 0000000..3025996 --- /dev/null +++ b/src/impls/mod.rs @@ -0,0 +1,29 @@ +#[cfg(feature = "alloc")] +mod alloc; +mod core; +#[cfg(feature = "rend")] +mod rend; +#[cfg(feature = "std")] +mod std; + +// Support for various common crates. These are primarily to get users off the ground and build some +// momentum. + +// These are NOT PLANNED to remain in rkyv for the final release. Much like serde, these +// implementations should be moved into their respective crates over time. Before adding support for +// another crate, please consider getting rkyv support in the crate instead. + +#[cfg(feature = "arrayvec")] +mod arrayvec; +#[cfg(feature = "bitvec")] +mod bitvec; +#[cfg(feature = "hashbrown")] +mod hashbrown; +#[cfg(feature = "indexmap")] +mod indexmap; +#[cfg(feature = "smallvec")] +mod smallvec; +#[cfg(feature = "tinyvec")] +mod tinyvec; +#[cfg(feature = "uuid")] +mod uuid; diff --git a/src/impls/rend.rs b/src/impls/rend.rs new file mode 100644 index 0000000..b992935 --- /dev/null +++ b/src/impls/rend.rs @@ -0,0 +1,268 @@ +use crate::{rend::*, Archive, Archived, Deserialize, Fallible, Serialize}; +#[cfg(has_atomics)] +use core::sync::atomic::Ordering; + +macro_rules! impl_rend_primitive { + ($type:ty) => { + impl Archive for $type { + type Archived = Self; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(*self); + } + } + + // Safety: rend primitives always have the same representation archived and unarchived and + // contain no padding + #[cfg(feature = "copy")] + unsafe impl crate::copy::ArchiveCopySafe for $type {} + + impl Serialize for $type { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } + } + + impl Deserialize<$type, D> for Archived<$type> { + #[inline] + fn deserialize(&self, _: &mut D) -> Result<$type, D::Error> { + Ok(*self) + } + } + }; +} + +#[cfg(has_atomics)] +macro_rules! impl_rend_atomic { + ($type:ty, $prim:ty) => { + impl Archive for $type { + type Archived = $prim; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.write(<$prim>::new(self.load(Ordering::Relaxed))); + } + } + + impl Serialize for $type { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } + } + + impl Deserialize<$type, D> for $prim { + #[inline] + fn deserialize(&self, _: &mut D) -> Result<$type, D::Error> { + Ok(self.value().into()) + } + } + }; +} + +impl_rend_primitive!(i16_be); +impl_rend_primitive!(i32_be); +impl_rend_primitive!(i64_be); +impl_rend_primitive!(i128_be); +impl_rend_primitive!(u16_be); +impl_rend_primitive!(u32_be); +impl_rend_primitive!(u64_be); +impl_rend_primitive!(u128_be); + +impl_rend_primitive!(f32_be); +impl_rend_primitive!(f64_be); + +impl_rend_primitive!(char_be); + +impl_rend_primitive!(NonZeroI16_be); +impl_rend_primitive!(NonZeroI32_be); +impl_rend_primitive!(NonZeroI64_be); +impl_rend_primitive!(NonZeroI128_be); +impl_rend_primitive!(NonZeroU16_be); +impl_rend_primitive!(NonZeroU32_be); +impl_rend_primitive!(NonZeroU64_be); +impl_rend_primitive!(NonZeroU128_be); + +#[cfg(has_atomics)] +impl_rend_atomic!(AtomicI16_be, i16_be); +#[cfg(has_atomics)] +impl_rend_atomic!(AtomicI32_be, i32_be); +#[cfg(has_atomics_64)] +impl_rend_atomic!(AtomicI64_be, i64_be); +#[cfg(has_atomics)] +impl_rend_atomic!(AtomicU16_be, u16_be); +#[cfg(has_atomics)] +impl_rend_atomic!(AtomicU32_be, u32_be); +#[cfg(has_atomics_64)] +impl_rend_atomic!(AtomicU64_be, u64_be); + +impl_rend_primitive!(i16_le); +impl_rend_primitive!(i32_le); +impl_rend_primitive!(i64_le); +impl_rend_primitive!(i128_le); +impl_rend_primitive!(u16_le); +impl_rend_primitive!(u32_le); +impl_rend_primitive!(u64_le); +impl_rend_primitive!(u128_le); + +impl_rend_primitive!(f32_le); +impl_rend_primitive!(f64_le); + +impl_rend_primitive!(char_le); + +impl_rend_primitive!(NonZeroI16_le); +impl_rend_primitive!(NonZeroI32_le); +impl_rend_primitive!(NonZeroI64_le); +impl_rend_primitive!(NonZeroI128_le); +impl_rend_primitive!(NonZeroU16_le); +impl_rend_primitive!(NonZeroU32_le); +impl_rend_primitive!(NonZeroU64_le); +impl_rend_primitive!(NonZeroU128_le); + +#[cfg(has_atomics)] +impl_rend_atomic!(AtomicI16_le, i16_le); +#[cfg(has_atomics)] +impl_rend_atomic!(AtomicI32_le, i32_le); +#[cfg(has_atomics_64)] +impl_rend_atomic!(AtomicI64_le, i64_le); +#[cfg(has_atomics)] +impl_rend_atomic!(AtomicU16_le, u16_le); +#[cfg(has_atomics)] +impl_rend_atomic!(AtomicU32_le, u32_le); +#[cfg(has_atomics_64)] +impl_rend_atomic!(AtomicU64_le, u64_le); + +#[cfg(test)] +mod tests { + use crate::{ + archived_root, ser::serializers::CoreSerializer, ser::Serializer, Deserialize, Infallible, + Serialize, + }; + use core::fmt; + + type DefaultSerializer = CoreSerializer<256, 256>; + + fn test_archive(value: &T) + where + T: fmt::Debug + PartialEq + Serialize, + T::Archived: fmt::Debug + PartialEq + Deserialize, + { + let mut serializer = DefaultSerializer::default(); + serializer + .serialize_value(value) + .expect("failed to archive value"); + let len = serializer.pos(); + let buffer = serializer.into_serializer().into_inner(); + + let archived_value = unsafe { archived_root::(&buffer[0..len]) }; + assert_eq!(archived_value, value); + let mut deserializer = Infallible; + assert_eq!( + &archived_value.deserialize(&mut deserializer).unwrap(), + value + ); + } + + #[test] + fn archive_rend() { + use crate::rend::*; + + test_archive(&f32_be::new(1234567f32)); + test_archive(&f64_be::new(12345678901234f64)); + test_archive(&i16_be::new(12345i16)); + test_archive(&i32_be::new(1234567890i32)); + test_archive(&i64_be::new(1234567890123456789i64)); + test_archive(&i128_be::new(123456789012345678901234567890123456789i128)); + test_archive(&u16_be::new(12345u16)); + test_archive(&u32_be::new(1234567890u32)); + test_archive(&u64_be::new(12345678901234567890u64)); + test_archive(&u128_be::new(123456789012345678901234567890123456789u128)); + + test_archive(&f32_le::new(1234567f32)); + test_archive(&f64_le::new(12345678901234f64)); + test_archive(&i16_le::new(12345i16)); + test_archive(&i32_le::new(1234567890i32)); + test_archive(&i64_le::new(1234567890123456789i64)); + test_archive(&i128_le::new(123456789012345678901234567890123456789i128)); + test_archive(&u16_le::new(12345u16)); + test_archive(&u32_le::new(1234567890u32)); + test_archive(&u64_le::new(12345678901234567890u64)); + test_archive(&u128_le::new(123456789012345678901234567890123456789u128)); + } + + #[test] + fn archive_rend_endianness() { + // Check representations to make sure endianness is preserved + use crate::{ + rend::{BigEndian, LittleEndian}, + ser::Serializer, + }; + + // Big endian + let value = BigEndian::::new(0x12345678); + + let mut serializer = DefaultSerializer::default(); + serializer.serialize_value(&value).unwrap(); + let buf = serializer.into_serializer().into_inner(); + + assert_eq!(&buf[0..4], &[0x12, 0x34, 0x56, 0x78]); + + // Little endian + let value = LittleEndian::::new(0x12345678i32); + + let mut serializer = DefaultSerializer::default(); + serializer.serialize_value(&value).unwrap(); + let buf = serializer.into_serializer().into_inner(); + + assert_eq!(&buf[0..4], &[0x78, 0x56, 0x34, 0x12]); + } + + #[test] + fn archive_rend_nonzero() { + use crate::rend::*; + use core::num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, NonZeroU16, NonZeroU32, + NonZeroU64, + }; + + unsafe { + test_archive(&NonZeroI16_be::new(NonZeroI16::new_unchecked(12345))); + test_archive(&NonZeroI32_be::new(NonZeroI32::new_unchecked(1234567890))); + test_archive(&NonZeroI64_be::new(NonZeroI64::new_unchecked( + 1234567890123456789, + ))); + test_archive(&NonZeroI128_be::new(NonZeroI128::new_unchecked( + 123456789012345678901234567890123456789, + ))); + test_archive(&NonZeroU16_be::new(NonZeroU16::new_unchecked(12345))); + test_archive(&NonZeroU32_be::new(NonZeroU32::new_unchecked(1234567890))); + test_archive(&NonZeroU64_be::new(NonZeroU64::new_unchecked( + 1234567890123456789, + ))); + test_archive(&NonZeroU128_be::new(NonZeroU128::new_unchecked( + 123456789012345678901234567890123456789, + ))); + + test_archive(&NonZeroI16_le::new(NonZeroI16::new_unchecked(12345))); + test_archive(&NonZeroI32_le::new(NonZeroI32::new_unchecked(1234567890))); + test_archive(&NonZeroI64_le::new(NonZeroI64::new_unchecked( + 1234567890123456789, + ))); + test_archive(&NonZeroI128_le::new(NonZeroI128::new_unchecked( + 123456789012345678901234567890123456789, + ))); + test_archive(&NonZeroU16_le::new(NonZeroU16::new_unchecked(12345))); + test_archive(&NonZeroU32_le::new(NonZeroU32::new_unchecked(1234567890))); + test_archive(&NonZeroU64_le::new(NonZeroU64::new_unchecked( + 1234567890123456789, + ))); + test_archive(&NonZeroU128_le::new(NonZeroU128::new_unchecked( + 123456789012345678901234567890123456789, + ))); + } + } +} diff --git a/src/impls/smallvec.rs b/src/impls/smallvec.rs new file mode 100644 index 0000000..50a9f1f --- /dev/null +++ b/src/impls/smallvec.rs @@ -0,0 +1,67 @@ +use crate::{ + ser::{ScratchSpace, Serializer}, + vec::{ArchivedVec, VecResolver}, + Archive, Archived, Deserialize, Fallible, Serialize, +}; +use smallvec::{Array, SmallVec}; + +impl Archive for SmallVec +where + A::Item: Archive, +{ + type Archived = ArchivedVec>; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedVec::resolve_from_slice(self.as_slice(), pos, resolver, out); + } +} + +impl Serialize for SmallVec +where + A::Item: Serialize, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedVec::serialize_from_slice(self.as_slice(), serializer) + } +} + +impl Deserialize, D> for ArchivedVec> +where + A::Item: Archive, + Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = SmallVec::new(); + for item in self.as_slice() { + result.push(item.deserialize(deserializer)?); + } + Ok(result) + } +} + +#[cfg(test)] +mod tests { + use crate::{archived_root, ser::Serializer, Deserialize, Infallible}; + use smallvec::{smallvec, SmallVec}; + + #[test] + fn small_vec() { + use crate::ser::serializers::CoreSerializer; + + let value: SmallVec<[i32; 10]> = smallvec![10, 20, 40, 80]; + + let mut serializer = CoreSerializer::<256, 256>::default(); + serializer.serialize_value(&value).unwrap(); + let end = serializer.pos(); + let result = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::>(&result[0..end]) }; + assert_eq!(archived.as_slice(), &[10, 20, 40, 80]); + + let deserialized: SmallVec<[i32; 10]> = archived.deserialize(&mut Infallible).unwrap(); + assert_eq!(value, deserialized); + } +} diff --git a/src/impls/std/collections/hash_map.rs b/src/impls/std/collections/hash_map.rs new file mode 100644 index 0000000..b2b5272 --- /dev/null +++ b/src/impls/std/collections/hash_map.rs @@ -0,0 +1,75 @@ +use crate::{ + collections::hash_map::{ArchivedHashMap, HashMapResolver}, + ser::{ScratchSpace, Serializer}, + Archive, Deserialize, Fallible, Serialize, +}; +use core::{ + borrow::Borrow, + hash::{BuildHasher, Hash}, +}; +use std::collections::HashMap; + +impl Archive for HashMap +where + K::Archived: Hash + Eq, +{ + type Archived = ArchivedHashMap; + type Resolver = HashMapResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedHashMap::resolve_from_len(self.len(), pos, resolver, out); + } +} + +impl Serialize for HashMap +where + K: Serialize + Hash + Eq, + K::Archived: Hash + Eq, + V: Serialize, + S: Serializer + ScratchSpace + ?Sized, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + unsafe { ArchivedHashMap::serialize_from_iter(self.iter(), serializer) } + } +} + +impl + Deserialize, D> for ArchivedHashMap +where + K::Archived: Deserialize + Hash + Eq, + V::Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = HashMap::with_capacity_and_hasher(self.len(), S::default()); + for (k, v) in self.iter() { + result.insert(k.deserialize(deserializer)?, v.deserialize(deserializer)?); + } + Ok(result) + } +} + +impl, V, AK: Hash + Eq, AV: PartialEq, S: BuildHasher> + PartialEq> for ArchivedHashMap +{ + #[inline] + fn eq(&self, other: &HashMap) -> bool { + if self.len() != other.len() { + false + } else { + self.iter() + .all(|(key, value)| other.get(key).map_or(false, |v| value.eq(v))) + } + } +} + +impl, V, AK: Hash + Eq, AV: PartialEq> + PartialEq> for HashMap +{ + #[inline] + fn eq(&self, other: &ArchivedHashMap) -> bool { + other.eq(self) + } +} diff --git a/src/impls/std/collections/hash_set.rs b/src/impls/std/collections/hash_set.rs new file mode 100644 index 0000000..b3f518f --- /dev/null +++ b/src/impls/std/collections/hash_set.rs @@ -0,0 +1,74 @@ +use crate::{ + collections::hash_set::{ArchivedHashSet, HashSetResolver}, + ser::{ScratchSpace, Serializer}, + Archive, Deserialize, Fallible, Serialize, +}; +use core::{ + borrow::Borrow, + hash::{BuildHasher, Hash}, +}; +use std::collections::HashSet; + +impl Archive for HashSet +where + K::Archived: Hash + Eq, +{ + type Archived = ArchivedHashSet; + type Resolver = HashSetResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedHashSet::::resolve_from_len(self.len(), pos, resolver, out); + } +} + +impl Serialize for HashSet +where + K::Archived: Hash + Eq, + K: Serialize + Hash + Eq, + S: ScratchSpace + Serializer + ?Sized, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + unsafe { ArchivedHashSet::serialize_from_iter(self.iter(), serializer) } + } +} + +impl Deserialize, D> for ArchivedHashSet +where + K: Archive + Hash + Eq, + K::Archived: Deserialize + Hash + Eq, + D: Fallible + ?Sized, + S: Default + BuildHasher, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = HashSet::with_hasher(S::default()); + for k in self.iter() { + result.insert(k.deserialize(deserializer)?); + } + Ok(result) + } +} + +impl, AK: Hash + Eq, S: BuildHasher> PartialEq> + for ArchivedHashSet +{ + #[inline] + fn eq(&self, other: &HashSet) -> bool { + if self.len() != other.len() { + false + } else { + self.iter().all(|key| other.get(key).is_some()) + } + } +} + +impl, AK: Hash + Eq, S: BuildHasher> PartialEq> + for HashSet +{ + #[inline] + fn eq(&self, other: &ArchivedHashSet) -> bool { + other.eq(self) + } +} diff --git a/src/impls/std/collections/mod.rs b/src/impls/std/collections/mod.rs new file mode 100644 index 0000000..2589922 --- /dev/null +++ b/src/impls/std/collections/mod.rs @@ -0,0 +1,2 @@ +mod hash_map; +mod hash_set; diff --git a/src/impls/std/ffi.rs b/src/impls/std/ffi.rs new file mode 100644 index 0000000..f62e9e0 --- /dev/null +++ b/src/impls/std/ffi.rs @@ -0,0 +1,121 @@ +use crate::{ + ffi::{ArchivedCString, CStringResolver}, + ser::Serializer, + Archive, ArchivePointee, ArchiveUnsized, Archived, ArchivedMetadata, Deserialize, + DeserializeUnsized, Fallible, FixedUsize, Serialize, SerializeUnsized, +}; +use core::{alloc::Layout, ptr}; +use ptr_meta::Pointee; +use std::alloc; +use std::ffi::{CStr, CString}; + +// CStr + +impl ArchiveUnsized for CStr { + type Archived = CStr; + + type MetadataResolver = (); + + #[inline] + unsafe fn resolve_metadata( + &self, + _: usize, + _: Self::MetadataResolver, + out: *mut ArchivedMetadata, + ) { + out.write(to_archived!(ptr_meta::metadata(self) as FixedUsize)) + } +} + +impl ArchivePointee for CStr { + type ArchivedMetadata = Archived; + + #[inline] + fn pointer_metadata(archived: &Self::ArchivedMetadata) -> ::Metadata { + <[u8]>::pointer_metadata(archived) + } +} + +impl SerializeUnsized for CStr { + #[inline] + fn serialize_unsized(&self, serializer: &mut S) -> Result { + let result = serializer.pos(); + serializer.write(self.to_bytes_with_nul())?; + Ok(result) + } + + #[inline] + fn serialize_metadata(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl DeserializeUnsized for ::Archived { + #[inline] + unsafe fn deserialize_unsized( + &self, + _: &mut D, + mut alloc: impl FnMut(Layout) -> *mut u8, + ) -> Result<*mut (), D::Error> { + let slice = self.to_bytes_with_nul(); + let bytes = alloc(Layout::array::(slice.len()).unwrap()); + assert!(!bytes.is_null()); + ptr::copy_nonoverlapping(slice.as_ptr(), bytes, slice.len()); + Ok(bytes.cast()) + } + + #[inline] + fn deserialize_metadata(&self, _: &mut D) -> Result<::Metadata, D::Error> { + Ok(ptr_meta::metadata(self)) + } +} + +// CString + +impl PartialEq for ArchivedCString { + #[inline] + fn eq(&self, other: &CString) -> bool { + PartialEq::eq(self.as_c_str(), other.as_c_str()) + } +} + +impl PartialEq for CString { + #[inline] + fn eq(&self, other: &ArchivedCString) -> bool { + PartialEq::eq(other.as_c_str(), self.as_c_str()) + } +} + +impl Archive for CString { + type Archived = ArchivedCString; + type Resolver = CStringResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedCString::resolve_from_c_str(self.as_c_str(), pos, resolver, out); + } +} + +impl Serialize for CString { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedCString::serialize_from_c_str(self.as_c_str(), serializer) + } +} + +impl Deserialize for Archived +where + CStr: DeserializeUnsized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result { + unsafe { + let data_address = self + .as_c_str() + .deserialize_unsized(deserializer, |layout| alloc::alloc(layout))?; + let metadata = self.as_c_str().deserialize_metadata(deserializer)?; + let ptr = ptr_meta::from_raw_parts_mut(data_address, metadata); + Ok(Box::::from_raw(ptr).into()) + } + } +} diff --git a/src/impls/std/mod.rs b/src/impls/std/mod.rs new file mode 100644 index 0000000..3d0e242 --- /dev/null +++ b/src/impls/std/mod.rs @@ -0,0 +1,4 @@ +mod collections; +mod ffi; +mod net; +mod time; diff --git a/src/impls/std/net.rs b/src/impls/std/net.rs new file mode 100644 index 0000000..1a992c8 --- /dev/null +++ b/src/impls/std/net.rs @@ -0,0 +1,698 @@ +use crate::{ + net::{ + ArchivedIpAddr, ArchivedIpv4Addr, ArchivedIpv6Addr, ArchivedSocketAddr, + ArchivedSocketAddrV4, ArchivedSocketAddrV6, + }, + Archive, Deserialize, Fallible, Serialize, +}; +use core::{cmp, ptr}; +use std::{ + io, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, +}; + +// Ipv4Addr + +impl ArchivedIpv4Addr { + /// Returns an [`Ipv4Addr`](std::net::Ipv4Addr) with the same value. + #[inline] + pub const fn as_ipv4(&self) -> Ipv4Addr { + let octets = self.octets(); + Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) + } + + /// Returns `true` if this is a broadcast address (255.255.255.255). + /// + /// See [`Ipv4Addr::is_broadcast()`](std::net::Ipv4Addr::is_broadcast()) for more details. + #[inline] + pub const fn is_broadcast(&self) -> bool { + self.as_ipv4().is_broadcast() + } + + /// Returns `true` if this address is in a range designated for documentation. + /// + /// See [`Ipv4Addr::is_documentation()`](std::net::Ipv4Addr::is_documentation()) for more details. + #[inline] + pub const fn is_documentation(&self) -> bool { + self.as_ipv4().is_documentation() + } + + /// Returns `true` if the address is link-local (169.254.0.0/16). + /// + /// See [`Ipv4Addr::is_link_local()`](std::net::Ipv4Addr::is_link_local()) for more details. + #[inline] + pub const fn is_link_local(&self) -> bool { + self.as_ipv4().is_link_local() + } + + /// Returns `true` if this is a loopback address (127.0.0.0/8). + /// + /// See [`Ipv4Addr::is_loopback()`](std::net::Ipv4Addr::is_loopback()) for more details. + #[inline] + pub const fn is_loopback(&self) -> bool { + self.as_ipv4().is_loopback() + } + + /// Returns `true` if this is a multicast address (224.0.0.0/4). + /// + /// See [`Ipv4Addr::is_multicast()`](std::net::Ipv4Addr::is_multicast()) for more details. + #[inline] + pub const fn is_multicast(&self) -> bool { + self.as_ipv4().is_multicast() + } + + /// Returns `true` if this is a private address. + /// + /// See [`Ipv4Addr::is_private()`](std::net::Ipv4Addr::is_private()) for more details. + #[inline] + pub const fn is_private(&self) -> bool { + self.as_ipv4().is_private() + } + + /// Returns `true` for the special 'unspecified' address (0.0.0.0). + /// + /// See [`Ipv4Addr::is_unspecified()`](std::net::Ipv4Addr::is_unspecified()) for more details. + #[inline] + pub const fn is_unspecified(&self) -> bool { + self.as_ipv4().is_unspecified() + } + + /// Converts this address to an IPv4-compatible [`IPv6` address](std::net::Ipv6Addr). + /// + /// See [`Ipv4Addr::to_ipv6_compatible()`](std::net::Ipv4Addr::to_ipv6_compatible()) for more + /// details. + #[inline] + #[allow(clippy::wrong_self_convention)] + pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { + self.as_ipv4().to_ipv6_compatible() + } + + /// Converts this address to an IPv4-mapped [`IPv6` address](std::net::Ipv6Addr). + /// + /// See [`Ipv4Addr::to_ipv6_mapped()`](std::net::Ipv4Addr::to_ipv6_mapped()) for more details. + #[inline] + #[allow(clippy::wrong_self_convention)] + pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { + self.as_ipv4().to_ipv6_mapped() + } +} + +impl PartialEq for ArchivedIpv4Addr { + #[inline] + fn eq(&self, other: &Ipv4Addr) -> bool { + self.as_ipv4().eq(other) + } +} + +impl PartialEq for Ipv4Addr { + #[inline] + fn eq(&self, other: &ArchivedIpv4Addr) -> bool { + other.eq(self) + } +} + +impl PartialOrd for ArchivedIpv4Addr { + #[inline] + fn partial_cmp(&self, other: &Ipv4Addr) -> Option { + self.as_ipv4().partial_cmp(other) + } +} + +impl PartialOrd for Ipv4Addr { + #[inline] + fn partial_cmp(&self, other: &ArchivedIpv4Addr) -> Option { + other.partial_cmp(self) + } +} + +impl Archive for Ipv4Addr { + type Archived = ArchivedIpv4Addr; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.cast::<[u8; 4]>().write(self.octets()); + } +} + +impl Serialize for Ipv4Addr { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for ArchivedIpv4Addr { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(self.as_ipv4()) + } +} + +// Ipv6Addr + +impl ArchivedIpv6Addr { + /// Returns an [`Ipv6Addr`](std::net::Ipv6Addr) with the same value. + #[inline] + pub const fn as_ipv6(&self) -> Ipv6Addr { + let segments = self.segments(); + Ipv6Addr::new( + segments[0], + segments[1], + segments[2], + segments[3], + segments[4], + segments[5], + segments[6], + segments[7], + ) + } + + /// Returns `true` if this is a loopback address (::1). + /// + /// See [`Ipv6Addr::is_loopback()`](std::net::Ipv6Addr::is_loopback()) for more details. + #[inline] + pub const fn is_loopback(&self) -> bool { + self.as_ipv6().is_loopback() + } + + /// Returns `true` if this is a multicast address (ff00::/8). + /// + /// See [`Ipv6Addr::is_multicast()`](std::net::Ipv6Addr::is_multicast()) for more details. + #[inline] + pub const fn is_multicast(&self) -> bool { + self.as_ipv6().is_multicast() + } + + /// Returns `true` for the special 'unspecified' address (::). + /// + /// See [`Ipv6Addr::is_unspecified()`](std::net::Ipv6Addr::is_unspecified()) for more details. + #[inline] + pub const fn is_unspecified(&self) -> bool { + self.as_ipv6().is_unspecified() + } + + /// Returns the sixteen eight-bit integers the IPv6 address consists of. + #[inline] + pub const fn octets(&self) -> [u8; 16] { + self.as_ipv6().octets() + } + + /// Converts this address to an [`IPv4` address](std::net::Ipv4Addr). Returns + /// [`None`](std::option::Option::None) if this address is neither IPv4-compatible or + /// IPv4-mapped. + #[inline] + #[allow(clippy::wrong_self_convention)] + pub const fn to_ipv4(&self) -> Option { + self.as_ipv6().to_ipv4() + } +} + +impl PartialEq for ArchivedIpv6Addr { + #[inline] + fn eq(&self, other: &Ipv6Addr) -> bool { + self.as_ipv6().eq(other) + } +} + +impl PartialEq for Ipv6Addr { + #[inline] + fn eq(&self, other: &ArchivedIpv6Addr) -> bool { + other.eq(self) + } +} + +impl PartialOrd for ArchivedIpv6Addr { + #[inline] + fn partial_cmp(&self, other: &Ipv6Addr) -> Option { + self.as_ipv6().partial_cmp(other) + } +} + +impl PartialOrd for Ipv6Addr { + #[inline] + fn partial_cmp(&self, other: &ArchivedIpv6Addr) -> Option { + other.partial_cmp(self) + } +} + +impl Archive for Ipv6Addr { + type Archived = ArchivedIpv6Addr; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + out.cast::<[u8; 16]>().write(self.octets()); + } +} + +impl Serialize for Ipv6Addr { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for ArchivedIpv6Addr { + #[inline] + fn deserialize(&self, _: &mut D) -> Result { + Ok(self.as_ipv6()) + } +} + +// IpAddr + +impl ArchivedIpAddr { + /// Returns an [`IpAddr`](std::net::IpAddr) with the same value. + #[inline] + pub const fn as_ipaddr(&self) -> IpAddr { + match self { + ArchivedIpAddr::V4(ipv4) => IpAddr::V4(ipv4.as_ipv4()), + ArchivedIpAddr::V6(ipv6) => IpAddr::V6(ipv6.as_ipv6()), + } + } + + /// Returns `true` if this is a loopback address. + /// + /// See [`IpAddr::is_loopback()`](std::net::IpAddr::is_loopback()) for more details. + #[inline] + pub const fn is_loopback(&self) -> bool { + match self { + ArchivedIpAddr::V4(ip) => ip.is_loopback(), + ArchivedIpAddr::V6(ip) => ip.is_loopback(), + } + } + + /// Returns `true` if this is a multicast address. + /// + /// See [`IpAddr::is_multicast()`](std::net::IpAddr::is_multicast()) for more details. + #[inline] + pub const fn is_multicast(&self) -> bool { + match self { + ArchivedIpAddr::V4(ip) => ip.is_multicast(), + ArchivedIpAddr::V6(ip) => ip.is_multicast(), + } + } + + /// Returns `true` for the special 'unspecified' address. + /// + /// See [`IpAddr::is_unspecified()`](std::net::IpAddr::is_unspecified()) for more details. + #[inline] + pub const fn is_unspecified(&self) -> bool { + match self { + ArchivedIpAddr::V4(ip) => ip.is_unspecified(), + ArchivedIpAddr::V6(ip) => ip.is_unspecified(), + } + } +} + +impl PartialEq for ArchivedIpAddr { + #[inline] + fn eq(&self, other: &IpAddr) -> bool { + match self { + ArchivedIpAddr::V4(self_ip) => { + if let IpAddr::V4(other_ip) = other { + self_ip.eq(other_ip) + } else { + false + } + } + ArchivedIpAddr::V6(self_ip) => { + if let IpAddr::V6(other_ip) = other { + self_ip.eq(other_ip) + } else { + false + } + } + } + } +} + +impl PartialEq for IpAddr { + #[inline] + fn eq(&self, other: &ArchivedIpAddr) -> bool { + other.eq(self) + } +} + +impl PartialOrd for ArchivedIpAddr { + #[inline] + fn partial_cmp(&self, other: &IpAddr) -> Option { + self.as_ipaddr().partial_cmp(other) + } +} + +impl PartialOrd for IpAddr { + #[inline] + fn partial_cmp(&self, other: &ArchivedIpAddr) -> Option { + other.partial_cmp(self) + } +} + +#[allow(dead_code)] +#[repr(u8)] +enum ArchivedIpAddrTag { + V4, + V6, +} + +#[repr(C)] +struct ArchivedIpAddrVariantV4(ArchivedIpAddrTag, ArchivedIpv4Addr); + +#[repr(C)] +struct ArchivedIpAddrVariantV6(ArchivedIpAddrTag, ArchivedIpv6Addr); + +impl Archive for IpAddr { + type Archived = ArchivedIpAddr; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + match self { + IpAddr::V4(ipv4_addr) => { + let out = out.cast::(); + ptr::addr_of_mut!((*out).0).write(ArchivedIpAddrTag::V4); + + let (fp, fo) = out_field!(out.1); + // resolver is guaranteed to be (), but it's better to be explicit about it + #[allow(clippy::unit_arg)] + ipv4_addr.resolve(pos + fp, resolver, fo); + } + IpAddr::V6(ipv6_addr) => { + let out = out.cast::(); + ptr::addr_of_mut!((*out).0).write(ArchivedIpAddrTag::V6); + + let (fp, fo) = out_field!(out.1); + // resolver is guaranteed to be (), but it's better to be explicit about it + #[allow(clippy::unit_arg)] + ipv6_addr.resolve(pos + fp, resolver, fo); + } + } + } +} + +impl Serialize for IpAddr { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + match self { + IpAddr::V4(ipv4_addr) => ipv4_addr.serialize(serializer), + IpAddr::V6(ipv6_addr) => ipv6_addr.serialize(serializer), + } + } +} + +impl Deserialize for ArchivedIpAddr { + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result { + match self { + ArchivedIpAddr::V4(ipv4_addr) => Ok(IpAddr::V4(ipv4_addr.deserialize(deserializer)?)), + ArchivedIpAddr::V6(ipv6_addr) => Ok(IpAddr::V6(ipv6_addr.deserialize(deserializer)?)), + } + } +} + +// SocketAddrV4 + +impl ArchivedSocketAddrV4 { + /// Returns a [`SocketAddrV4`](std::net::SocketAddrV4) with the same value. + #[inline] + pub fn as_socket_addr_v4(&self) -> SocketAddrV4 { + SocketAddrV4::new(self.ip().as_ipv4(), self.port()) + } +} + +impl ToSocketAddrs for ArchivedSocketAddrV4 { + type Iter = ::Iter; + + fn to_socket_addrs(&self) -> io::Result { + self.as_socket_addr_v4().to_socket_addrs() + } +} + +impl PartialEq for ArchivedSocketAddrV4 { + #[inline] + fn eq(&self, other: &SocketAddrV4) -> bool { + self.as_socket_addr_v4().eq(other) + } +} + +impl PartialEq for SocketAddrV4 { + #[inline] + fn eq(&self, other: &ArchivedSocketAddrV4) -> bool { + other.eq(self) + } +} + +impl PartialOrd for ArchivedSocketAddrV4 { + #[inline] + fn partial_cmp(&self, other: &SocketAddrV4) -> Option { + self.as_socket_addr_v4().partial_cmp(other) + } +} + +impl PartialOrd for SocketAddrV4 { + #[inline] + fn partial_cmp(&self, other: &ArchivedSocketAddrV4) -> Option { + other.partial_cmp(self) + } +} + +impl Archive for SocketAddrV4 { + type Archived = ArchivedSocketAddrV4; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, pos: usize, _: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.ip); + self.ip().resolve(pos + fp, (), fo); + let (fp, fo) = out_field!(out.port); + self.port().resolve(pos + fp, (), fo); + } +} + +impl Serialize for SocketAddrV4 { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for ArchivedSocketAddrV4 { + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result { + let ip = self.ip().deserialize(deserializer)?; + Ok(SocketAddrV4::new(ip, self.port())) + } +} + +// SocketAddrV6 + +impl ArchivedSocketAddrV6 { + /// Returns a [`SocketAddrV6`](std::net::SocketAddrV6) with the same value. + #[inline] + pub fn as_socket_addr_v6(&self) -> SocketAddrV6 { + SocketAddrV6::new( + self.ip().as_ipv6(), + self.port(), + self.flowinfo(), + self.scope_id(), + ) + } +} + +impl ToSocketAddrs for ArchivedSocketAddrV6 { + type Iter = ::Iter; + + fn to_socket_addrs(&self) -> io::Result { + self.as_socket_addr_v6().to_socket_addrs() + } +} + +impl PartialEq for ArchivedSocketAddrV6 { + #[inline] + fn eq(&self, other: &SocketAddrV6) -> bool { + self.as_socket_addr_v6().eq(other) + } +} + +impl PartialEq for SocketAddrV6 { + #[inline] + fn eq(&self, other: &ArchivedSocketAddrV6) -> bool { + other.eq(self) + } +} + +impl PartialOrd for ArchivedSocketAddrV6 { + #[inline] + fn partial_cmp(&self, other: &SocketAddrV6) -> Option { + self.as_socket_addr_v6().partial_cmp(other) + } +} + +impl PartialOrd for SocketAddrV6 { + #[inline] + fn partial_cmp(&self, other: &ArchivedSocketAddrV6) -> Option { + other.partial_cmp(self) + } +} + +impl Archive for SocketAddrV6 { + type Archived = ArchivedSocketAddrV6; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, pos: usize, _: Self::Resolver, out: *mut Self::Archived) { + let (fp, fo) = out_field!(out.ip); + self.ip().resolve(pos + fp, (), fo); + let (fp, fo) = out_field!(out.port); + self.port().resolve(pos + fp, (), fo); + let (fp, fo) = out_field!(out.flowinfo); + self.flowinfo().resolve(pos + fp, (), fo); + let (fp, fo) = out_field!(out.scope_id); + self.scope_id().resolve(pos + fp, (), fo); + } +} + +impl Serialize for SocketAddrV6 { + #[inline] + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for ArchivedSocketAddrV6 { + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result { + let ip = self.ip().deserialize(deserializer)?; + Ok(SocketAddrV6::new( + ip, + self.port(), + self.flowinfo(), + self.scope_id(), + )) + } +} + +// SocketAddr + +impl ArchivedSocketAddr { + /// Returns a [`SocketAddr`](std::net::SocketAddr) with the same value. + #[inline] + pub fn as_socket_addr(&self) -> SocketAddr { + match self { + ArchivedSocketAddr::V4(addr) => SocketAddr::V4(addr.as_socket_addr_v4()), + ArchivedSocketAddr::V6(addr) => SocketAddr::V6(addr.as_socket_addr_v6()), + } + } + + /// Returns the IP address associated with this socket address. + #[inline] + pub fn ip(&self) -> IpAddr { + match self { + ArchivedSocketAddr::V4(addr) => IpAddr::V4(addr.ip().as_ipv4()), + ArchivedSocketAddr::V6(addr) => IpAddr::V6(addr.ip().as_ipv6()), + } + } +} + +impl ToSocketAddrs for ArchivedSocketAddr { + type Iter = ::Iter; + + fn to_socket_addrs(&self) -> io::Result { + self.as_socket_addr().to_socket_addrs() + } +} + +impl PartialEq for ArchivedSocketAddr { + #[inline] + fn eq(&self, other: &SocketAddr) -> bool { + self.as_socket_addr().eq(other) + } +} + +impl PartialEq for SocketAddr { + #[inline] + fn eq(&self, other: &ArchivedSocketAddr) -> bool { + other.eq(self) + } +} + +impl PartialOrd for ArchivedSocketAddr { + #[inline] + fn partial_cmp(&self, other: &SocketAddr) -> Option { + self.as_socket_addr().partial_cmp(other) + } +} + +impl PartialOrd for SocketAddr { + #[inline] + fn partial_cmp(&self, other: &ArchivedSocketAddr) -> Option { + other.partial_cmp(self) + } +} + +#[allow(dead_code)] +#[repr(u8)] +enum ArchivedSocketAddrTag { + V4, + V6, +} + +#[repr(C)] +struct ArchivedSocketAddrVariantV4(ArchivedSocketAddrTag, ArchivedSocketAddrV4); + +#[repr(C)] +struct ArchivedSocketAddrVariantV6(ArchivedSocketAddrTag, ArchivedSocketAddrV6); + +impl Archive for SocketAddr { + type Archived = ArchivedSocketAddr; + type Resolver = (); + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + match self { + SocketAddr::V4(socket_addr) => { + let out = out.cast::(); + ptr::addr_of_mut!((*out).0).write(ArchivedSocketAddrTag::V4); + + let (fp, fo) = out_field!(out.1); + // resolver is guaranteed to be (), but it's better to be explicit about it + #[allow(clippy::unit_arg)] + socket_addr.resolve(pos + fp, resolver, fo); + } + SocketAddr::V6(socket_addr) => { + let out = out.cast::(); + ptr::addr_of_mut!((*out).0).write(ArchivedSocketAddrTag::V6); + + let (fp, fo) = out_field!(out.1); + // resolver is guaranteed to be (), but it's better to be explicit about it + #[allow(clippy::unit_arg)] + socket_addr.resolve(pos + fp, resolver, fo); + } + } + } +} + +impl Serialize for SocketAddr { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + match self { + SocketAddr::V4(socket_addr) => socket_addr.serialize(serializer), + SocketAddr::V6(socket_addr) => socket_addr.serialize(serializer), + } + } +} + +impl Deserialize for ArchivedSocketAddr { + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result { + match self { + ArchivedSocketAddr::V4(socket_addr) => { + Ok(SocketAddr::V4(socket_addr.deserialize(deserializer)?)) + } + ArchivedSocketAddr::V6(socket_addr) => { + Ok(SocketAddr::V6(socket_addr.deserialize(deserializer)?)) + } + } + } +} diff --git a/src/impls/std/time.rs b/src/impls/std/time.rs new file mode 100644 index 0000000..2620587 --- /dev/null +++ b/src/impls/std/time.rs @@ -0,0 +1,16 @@ +use crate::time::ArchivedDuration; +use std::time::Duration; + +impl PartialEq for ArchivedDuration { + #[inline] + fn eq(&self, other: &Duration) -> bool { + self.as_nanos() == other.as_nanos() && self.as_secs() == other.as_secs() + } +} + +impl PartialEq for Duration { + #[inline] + fn eq(&self, other: &ArchivedDuration) -> bool { + other.eq(self) + } +} diff --git a/src/impls/tinyvec.rs b/src/impls/tinyvec.rs new file mode 100644 index 0000000..4fe4a9b --- /dev/null +++ b/src/impls/tinyvec.rs @@ -0,0 +1,174 @@ +use crate::{ + ser::{ScratchSpace, Serializer}, + vec::{ArchivedVec, VecResolver}, + Archive, Archived, Deserialize, Fallible, Serialize, +}; +#[cfg(feature = "tinyvec_alloc")] +use tinyvec::TinyVec; +use tinyvec::{Array, ArrayVec, SliceVec}; + +// ArrayVec + +impl Archive for ArrayVec +where + A::Item: Archive, +{ + type Archived = ArchivedVec>; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedVec::resolve_from_slice(self.as_slice(), pos, resolver, out); + } +} + +impl Serialize for ArrayVec +where + A::Item: Serialize, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedVec::serialize_from_slice(self.as_slice(), serializer) + } +} + +impl Deserialize, D> for ArchivedVec> +where + A::Item: Archive, + Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = ArrayVec::new(); + for item in self.as_slice() { + result.push(item.deserialize(deserializer)?); + } + Ok(result) + } +} + +// SliceVec + +impl<'s, T: Archive> Archive for SliceVec<'s, T> { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedVec::resolve_from_slice(self.as_slice(), pos, resolver, out); + } +} + +impl<'s, T: Serialize, S: ScratchSpace + Serializer + ?Sized> Serialize for SliceVec<'s, T> { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedVec::serialize_from_slice(self.as_slice(), serializer) + } +} + +// SliceVec cannot be deserialized because it borrows backing memory + +// TinyVec + +#[cfg(feature = "tinyvec_alloc")] +impl Archive for TinyVec +where + A::Item: Archive, +{ + type Archived = ArchivedVec>; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedVec::resolve_from_slice(self.as_slice(), pos, resolver, out); + } +} + +#[cfg(feature = "tinyvec_alloc")] +impl Serialize for TinyVec +where + A::Item: Serialize, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + ArchivedVec::serialize_from_slice(self.as_slice(), serializer) + } +} + +#[cfg(feature = "tinyvec_alloc")] +impl Deserialize, D> for ArchivedVec> +where + A::Item: Archive, + Archived: Deserialize, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + let mut result = TinyVec::new(); + for item in self.as_slice() { + result.push(item.deserialize(deserializer)?); + } + Ok(result) + } +} + +#[cfg(test)] +mod tests { + use crate::{archived_root, ser::Serializer, Deserialize, Infallible}; + use tinyvec::{array_vec, Array, ArrayVec, SliceVec}; + + #[test] + fn array_vec() { + use crate::ser::serializers::CoreSerializer; + + let value = array_vec!([i32; 10] => 10, 20, 40, 80); + + let mut serializer = CoreSerializer::<256, 256>::default(); + serializer.serialize_value(&value).unwrap(); + let end = serializer.pos(); + let result = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::>(&result[0..end]) }; + assert_eq!(archived.as_slice(), &[10, 20, 40, 80]); + + let deserialized: ArrayVec<[i32; 10]> = archived.deserialize(&mut Infallible).unwrap(); + assert_eq!(value, deserialized); + } + + #[test] + fn slice_vec() { + use crate::ser::serializers::CoreSerializer; + + let mut backing = [0i32; 10]; + let mut value = SliceVec::from_slice_len(backing.as_slice_mut(), 0); + value.push(10); + value.push(20); + value.push(40); + value.push(80); + + let mut serializer = CoreSerializer::<256, 256>::default(); + serializer.serialize_value(&value).unwrap(); + let end = serializer.pos(); + let result = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::>(&result[0..end]) }; + assert_eq!(archived.as_slice(), &[10, 20, 40, 80]); + } + + #[cfg(feature = "tinyvec_alloc")] + #[test] + fn tiny_vec() { + use crate::ser::serializers::AllocSerializer; + #[cfg(not(feature = "std"))] + use alloc::vec; + use tinyvec::{tiny_vec, TinyVec}; + + let value = tiny_vec!([i32; 10] => 10, 20, 40, 80); + + let mut serializer = AllocSerializer::<256>::default(); + serializer.serialize_value(&value).unwrap(); + let result = serializer.into_serializer().into_inner(); + let archived = unsafe { archived_root::>(result.as_ref()) }; + assert_eq!(archived.as_slice(), &[10, 20, 40, 80]); + + let deserialized: TinyVec<[i32; 10]> = archived.deserialize(&mut Infallible).unwrap(); + assert_eq!(value, deserialized); + } +} diff --git a/src/impls/uuid.rs b/src/impls/uuid.rs new file mode 100644 index 0000000..916f7b1 --- /dev/null +++ b/src/impls/uuid.rs @@ -0,0 +1,60 @@ +use crate::{Archive, Deserialize, Fallible, Serialize}; +use uuid::Uuid; + +impl Archive for Uuid { + type Archived = Uuid; + type Resolver = (); + + unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + // Safety: Uuid is portable and has no padding + out.write(*self); + } +} + +// Safety: Uuid is portable and has no padding +#[cfg(feature = "copy")] +unsafe impl crate::copy::ArchiveCopySafe for Uuid {} + +impl Serialize for Uuid { + fn serialize(&self, _: &mut S) -> Result { + Ok(()) + } +} + +impl Deserialize for Uuid { + fn deserialize(&self, _: &mut D) -> Result { + Ok(*self) + } +} + +#[cfg(test)] +mod rkyv_tests { + use crate::{ + archived_root, + ser::{serializers::AlignedSerializer, Serializer}, + util::AlignedVec, + Deserialize, Infallible, + }; + use uuid::Uuid; + + #[test] + fn test_serialize_deserialize() { + let uuid_str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4"; + let u = Uuid::parse_str(uuid_str).unwrap(); + + let mut serializer = AlignedSerializer::new(AlignedVec::new()); + serializer + .serialize_value(&u) + .expect("failed to archive uuid"); + let buf = serializer.into_inner(); + let archived = unsafe { archived_root::(buf.as_ref()) }; + + assert_eq!(&u, archived); + + let deserialized = archived + .deserialize(&mut Infallible) + .expect("failed to deserialize uuid"); + + assert_eq!(u, deserialized); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f298221 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,753 @@ +//! # rkyv +//! +//! rkyv (*archive*) is a zero-copy deserialization framework for Rust. +//! +//! It's similar to other zero-copy deserialization frameworks such as +//! [Cap'n Proto](https://capnproto.org) and [FlatBuffers](https://google.github.io/flatbuffers). +//! However, while the former have external schemas and heavily restricted data types, rkyv allows +//! all serialized types to be defined in code and can serialize a wide variety of types that the +//! others cannot. Additionally, rkyv is designed to have little to no overhead, and in most cases +//! will perform exactly the same as native types. +//! +//! ## Design +//! +//! Like [serde](https://serde.rs), rkyv uses Rust's powerful trait system to serialize data without +//! the need for reflection. Despite having a wide array of features, you also only pay for what you +//! use. If your data checks out, the serialization process can be as simple as a `memcpy`! Like +//! serde, this allows rkyv to perform at speeds similar to handwritten serializers. +//! +//! Unlike serde, rkyv produces data that is guaranteed deserialization free. If you wrote your data +//! to disk, you can just `mmap` your file into memory, cast a pointer, and your data is ready to +//! use. This makes it ideal for high-performance and IO-bound applications. +//! +//! Limited data mutation is supported through `Pin` APIs, and archived values can be truly +//! deserialized with [`Deserialize`] if full mutation capabilities are needed. +//! +//! [The book](https://rkyv.org) has more details on the design and capabilities of rkyv. +//! +//! ## Type support +//! +//! rkyv has a hashmap implementation that is built for zero-copy deserialization, so you can +//! serialize your hashmaps with abandon. The implementation performs perfect hashing with the +//! compress, hash and displace algorithm to use as little memory as possible while still performing +//! fast lookups. +//! +//! It also comes with a B+ tree implementation that is built for maximum performance by splitting +//! data into easily-pageable 4KB segments. This makes it perfect for building immutable databases +//! and structures for bulk data. +//! +//! rkyv also has support for contextual serialization, deserialization, and validation. It can +//! properly serialize and deserialize shared pointers like `Rc` and `Arc`, and can be extended to +//! support custom contextual types. +//! +//! Finally, rkyv makes it possible to serialize trait objects and use them *as trait objects* +//! without deserialization. See the `archive_dyn` crate for more details. +//! +//! ## Tradeoffs +//! +//! While rkyv is a great format for final data, it lacks a full schema system and isn't well +//! equipped for data migration and schema upgrades. If your use case requires these capabilities, +//! you may need additional libraries the build these features on top of rkyv. You can use other +//! serialization frameworks like serde with the same types as rkyv conflict-free. +//! +//! ## Features +//! +//! - `alloc`: Enables types that require the `alloc` crate. Enabled by default. +//! - `arbitrary_enum_discriminant`: Enables the `arbitrary_enum_discriminant` feature for stable +//! multibyte enum discriminants using `archive_le` and `archive_be`. Requires nightly. +//! - `archive_be`: Forces archives into a big-endian format. This guarantees cross-endian +//! compatibility optimized for big-endian architectures. +//! - `archive_le`: Forces archives into a little-endian format. This guarantees cross-endian +//! compatibility optimized for little-endian architectures. +//! - `copy`: Enables copy optimizations for packed copyable data types. Requires nightly. +//! - `copy_unsafe`: Automatically opts all potentially copyable types into copy optimization. This +//! broadly improves performance but may cause uninitialized bytes to be copied to the output. +//! Requires nightly. +//! - `size_16`: Archives integral `*size` types as 16-bit integers. This is intended to be used +//! only for small archives and may not handle large, more general data. +//! - `size_32`: Archives integral `*size` types as 32-bit integers. Enabled by default. +//! - `size_64`: Archives integral `*size` types as 64-bit integers. This is intended to be used +//! only for very large archives and may cause unnecessary data bloat. +//! - `std`: Enables standard library support. Enabled by default. +//! - `strict`: Guarantees that types will have the same representations across platforms and +//! compilations. This is already the case in practice, but this feature provides a guarantee +//! along with C type compatibility. +//! +//! *Note*: Enabling `strict` will disable [`Archive`] implementations for tuples, as tuples +//! do not have a C type layout. Making a generic `Tuple` and deriving [`Archive`] for it +//! should provide similar functionality. +//! - `validation`: Enables validation support through `bytecheck`. +//! +//! ## Crate support +//! +//! Some common crates need to be supported by rkyv before an official integration has been made. +//! Support is provided by rkyv for these crates, but in the future crates should depend on rkyv and +//! provide their own implementations. The crates that already have support provided by rkyv should +//! work toward integrating the implementations into themselves. +//! +//! Crates supported by rkyv: +//! +//! - [`indexmap`](https://docs.rs/indexmap) +//! - [`rend`](https://docs.rs/rend) *Enabled automatically when using endian-specific archive +//! features.* +//! - [`tinyvec`](https://docs.rs/tinyvec) +//! - [`uuid`](https://docs.rs/uuid) +//! +//! Support for each of these crates can be enabled with a feature of the same name. Additionally, +//! the following external crate features are available: +//! +//! - `tinyvec_alloc`: Supports types behind the `alloc` feature in `tinyvec`. +//! - `uuid_std`: Enables the `std` feature in `uuid`. +//! +//! ## Examples +//! +//! - See [`Archive`] for examples of how to use rkyv through the derive macro and manual +//! implementation. +//! - For more details on the derive macro and its capabilities, see +//! [`Archive`](macro@Archive). +//! - Fully worked examples using rkyv are available in the +//! [`examples` directory](https://github.com/rkyv/rkyv/tree/master/examples) of the source repo. + +#![deny( + rustdoc::broken_intra_doc_links, + missing_docs, + rustdoc::missing_crate_level_docs +)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr( + feature = "copy", + feature(auto_traits), + feature(min_specialization), + feature(negative_impls), + feature(rustc_attrs) +)] +#![doc(html_favicon_url = r#" + data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' + viewBox='0 0 26.458 26.458'%3E%3Cpath d='M0 0v26.458h26.458V0zm9.175 3.772l8.107 8.106 + 2.702-2.702 2.702 13.512-13.512-2.702 2.703-2.702-8.107-8.107z'/%3E%3C/svg%3E +"#)] +#![doc(html_logo_url = r#" + data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="100" height="100" + viewBox="0 0 26.458 26.458"%3E%3Cpath d="M0 0v26.458h26.458V0zm9.175 3.772l8.107 8.106 + 2.702-2.702 2.702 13.512-13.512-2.702 2.703-2.702-8.107-8.107z"/%3E%3C/svg%3E +"#)] + +#[cfg(all(feature = "alloc", not(feature = "std")))] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +#[doc(hidden)] +#[macro_use] +pub mod macros; + +#[cfg(feature = "bitvec")] +pub mod bitvec; +pub mod boxed; +pub mod collections; +#[cfg(feature = "copy")] +pub mod copy; +pub mod de; +// This is pretty unfortunate. CStr doesn't rely on the rest of std, but it's not in core. +// If CStr ever gets moved into `core` then this module will no longer need cfg(feature = "std") +#[cfg(feature = "std")] +pub mod ffi; +mod impls; +pub mod net; +pub mod niche; +pub mod ops; +pub mod option; +pub mod rc; +pub mod rel_ptr; +pub mod result; +pub mod ser; +pub mod string; +pub mod time; +pub mod util; +#[cfg(feature = "validation")] +pub mod validation; +pub mod vec; +pub mod with; + +#[cfg(feature = "rend")] +pub use rend; + +#[cfg(feature = "validation")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "validation")))] +pub use bytecheck::{self, CheckBytes}; +use core::alloc::Layout; +use ptr_meta::Pointee; +pub use rkyv_derive::{Archive, Deserialize, Serialize}; +pub use util::*; +#[cfg(feature = "validation")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "validation")))] +pub use validation::{ + check_archived_root_with_context, check_archived_value_with_context, + validators::{check_archived_root, check_archived_value, from_bytes}, +}; + +/// A type that can produce an error. +/// +/// This trait is always implemented by serializers and deserializers. Its purpose is to provide an +/// error type without restricting what other capabilities the type must provide. +/// +/// When writing implementations for [`Serialize`] and [`Deserialize`], it's best practice to bound +/// the serializer or deserializer by `Fallible` and then require that the serialized types support +/// it (i.e. `S: Fallible, MyType: Serialize`). +pub trait Fallible { + /// The error produced by any failing methods. + type Error: 'static; +} + +/// A fallible type that cannot produce errors. +/// +/// This type can be used to serialize and deserialize types that cannot fail to serialize or +/// deserialize. +#[derive(Debug)] +pub struct Infallible; + +impl Fallible for Infallible { + type Error = core::convert::Infallible; +} + +impl Default for Infallible { + fn default() -> Self { + Infallible + } +} + +/// A type that can be used without deserializing. +/// +/// `Archive` is one of three basic traits used to work with zero-copy data and controls the layout +/// of the data in its archived zero-copy representation. The [`Serialize`] trait helps transform +/// types into that representation, and the [`Deserialize`] trait helps transform types back out. +/// +/// Types that implement `Archive` must have a well-defined archived size. Unsized types can be +/// supported using the [`ArchiveUnsized`] trait, along with [`SerializeUnsized`] and +/// [`DeserializeUnsized`]. +/// +/// Archiving is done depth-first, writing any data owned by a type before writing the data for the +/// type itself. The type must be able to create the archived type from only its own data and its +/// resolver. +/// +/// Archived data is always treated as if it is tree-shaped, with the root owning its direct +/// descendents and so on. Data that is not tree-shaped can be supported using special serializer +/// and deserializer bounds (see [`ArchivedRc`](crate::rc::ArchivedRc) for example). In a buffer of +/// serialized data, objects are laid out in *reverse order*. This means that the root object is +/// located near the end of the buffer and leaf objects are located near the beginning. +/// +/// # Examples +/// +/// Most of the time, `#[derive(Archive)]` will create an acceptable implementation. You can use the +/// `#[archive(...)]` and `#[archive_attr(...)]` attributes to control how the implementation is +/// generated. See the [`Archive`](macro@Archive) derive macro for more details. +/// +/// ``` +/// use rkyv::{Archive, Deserialize, Serialize}; +/// +/// #[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] +/// // This will generate a PartialEq impl between our unarchived and archived types +/// #[archive(compare(PartialEq))] +/// // We can pass attributes through to generated types with archive_attr +/// #[archive_attr(derive(Debug))] +/// struct Test { +/// int: u8, +/// string: String, +/// option: Option>, +/// } +/// +/// let value = Test { +/// int: 42, +/// string: "hello world".to_string(), +/// option: Some(vec![1, 2, 3, 4]), +/// }; +/// +/// // Serializing is as easy as a single function call +/// let bytes = rkyv::to_bytes::<_, 256>(&value).unwrap(); +/// +/// // Or you can customize your serialization for better performance +/// // and compatibility with #![no_std] environments +/// use rkyv::ser::{Serializer, serializers::AllocSerializer}; +/// +/// let mut serializer = AllocSerializer::<0>::default(); +/// serializer.serialize_value(&value).unwrap(); +/// let bytes = serializer.into_serializer().into_inner(); +/// +/// // You can use the safe API with the validation feature turned on, +/// // or you can use the unsafe API (shown here) for maximum performance +/// let archived = unsafe { rkyv::archived_root::(&bytes[..]) }; +/// assert_eq!(archived, &value); +/// +/// // And you can always deserialize back to the original type +/// let deserialized: Test = archived.deserialize(&mut rkyv::Infallible).unwrap(); +/// assert_eq!(deserialized, value); +/// ``` +/// +/// _Note: the safe API requires the `validation` feature._ +/// +/// Many of the core and standard library types already have `Archive` implementations available, +/// but you may need to implement `Archive` for your own types in some cases the derive macro cannot +/// handle. +/// +/// In this example, we add our own wrapper that serializes a `&'static str` as if it's owned. +/// Normally you can lean on the archived version of `String` to do most of the work, or use the +/// [`Inline`](crate::with::Inline) to do exactly this. This example does everything to demonstrate +/// how to implement `Archive` for your own types. +/// +/// ``` +/// use core::{slice, str}; +/// use rkyv::{ +/// archived_root, +/// ser::{Serializer, serializers::AlignedSerializer}, +/// out_field, +/// AlignedVec, +/// Archive, +/// Archived, +/// ArchiveUnsized, +/// MetadataResolver, +/// RelPtr, +/// Serialize, +/// SerializeUnsized, +/// }; +/// +/// struct OwnedStr { +/// inner: &'static str, +/// } +/// +/// struct ArchivedOwnedStr { +/// // This will be a relative pointer to our string +/// ptr: RelPtr, +/// } +/// +/// impl ArchivedOwnedStr { +/// // This will help us get the bytes of our type as a str again. +/// fn as_str(&self) -> &str { +/// unsafe { +/// // The as_ptr() function of RelPtr will get a pointer the str +/// &*self.ptr.as_ptr() +/// } +/// } +/// } +/// +/// struct OwnedStrResolver { +/// // This will be the position that the bytes of our string are stored at. +/// // We'll use this to resolve the relative pointer of our +/// // ArchivedOwnedStr. +/// pos: usize, +/// // The archived metadata for our str may also need a resolver. +/// metadata_resolver: MetadataResolver, +/// } +/// +/// // The Archive implementation defines the archived version of our type and +/// // determines how to turn the resolver into the archived form. The Serialize +/// // implementations determine how to make a resolver from the original value. +/// impl Archive for OwnedStr { +/// type Archived = ArchivedOwnedStr; +/// // This is the resolver we can create our Archived version from. +/// type Resolver = OwnedStrResolver; +/// +/// // The resolve function consumes the resolver and produces the archived +/// // value at the given position. +/// unsafe fn resolve( +/// &self, +/// pos: usize, +/// resolver: Self::Resolver, +/// out: *mut Self::Archived, +/// ) { +/// // We have to be careful to add the offset of the ptr field, +/// // otherwise we'll be using the position of the ArchivedOwnedStr +/// // instead of the position of the relative pointer. +/// let (fp, fo) = out_field!(out.ptr); +/// self.inner.resolve_unsized( +/// pos + fp, +/// resolver.pos, +/// resolver.metadata_resolver, +/// fo, +/// ); +/// } +/// } +/// +/// // We restrict our serializer types with Serializer because we need its +/// // capabilities to archive our type. For other types, we might need more or +/// // less restrictive bounds on the type of S. +/// impl Serialize for OwnedStr { +/// fn serialize( +/// &self, +/// serializer: &mut S +/// ) -> Result { +/// // This is where we want to write the bytes of our string and return +/// // a resolver that knows where those bytes were written. +/// // We also need to serialize the metadata for our str. +/// Ok(OwnedStrResolver { +/// pos: self.inner.serialize_unsized(serializer)?, +/// metadata_resolver: self.inner.serialize_metadata(serializer)? +/// }) +/// } +/// } +/// +/// let mut serializer = AlignedSerializer::new(AlignedVec::new()); +/// const STR_VAL: &'static str = "I'm in an OwnedStr!"; +/// let value = OwnedStr { inner: STR_VAL }; +/// // It works! +/// serializer.serialize_value(&value).expect("failed to archive test"); +/// let buf = serializer.into_inner(); +/// let archived = unsafe { archived_root::(buf.as_ref()) }; +/// // Let's make sure our data got written correctly +/// assert_eq!(archived.as_str(), STR_VAL); +/// ``` +pub trait Archive { + /// The archived representation of this type. + /// + /// In this form, the data can be used with zero-copy deserialization. + type Archived; + + /// The resolver for this type. It must contain all the additional information from serializing + /// needed to make the archived type from the normal type. + type Resolver; + + /// Creates the archived version of this value at the given position and writes it to the given + /// output. + /// + /// The output should be initialized field-by-field rather than by writing a whole struct. + /// Performing a typed copy will mark all of the padding bytes as uninitialized, but they must + /// remain set to the value they currently have. This prevents leaking uninitialized memory to + /// the final archive. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing this object + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived); +} + +/// Converts a type to its archived form. +/// +/// Objects perform any supportive serialization during [`serialize`](Serialize::serialize). For +/// types that reference nonlocal (pointed-to) data, this is when that data must be serialized to +/// the output. These types will need to bound `S` to implement [`Serializer`](ser::Serializer) and +/// any other required traits (e.g. [`SharedSerializeRegistry`](ser::SharedSerializeRegistry)). They +/// should then serialize their dependencies during `serialize`. +/// +/// See [`Archive`] for examples of implementing `Serialize`. +pub trait Serialize: Archive { + /// Writes the dependencies for the object and returns a resolver that can create the archived + /// type. + fn serialize(&self, serializer: &mut S) -> Result; +} + +/// Converts a type back from its archived form. +/// +/// Some types may require specific deserializer capabilities, such as `Rc` and `Arc`. In these +/// cases, the deserializer type `D` should be bound so that it implements traits that provide those +/// capabilities (e.g. [`SharedDeserializeRegistry`](de::SharedDeserializeRegistry)). +/// +/// This can be derived with [`Deserialize`](macro@Deserialize). +pub trait Deserialize { + /// Deserializes using the given deserializer + fn deserialize(&self, deserializer: &mut D) -> Result; +} + +/// A counterpart of [`Archive`] that's suitable for unsized types. +/// +/// Unlike `Archive`, types that implement `ArchiveUnsized` must be serialized separately from their +/// owning object. For example, whereas an `i32` might be laid out as part of a larger struct, a +/// `Box` would serialize the `i32` somewhere in the archive and the `Box` would point to it as +/// part of the larger struct. Because of this, the equivalent [`Resolver`](Archive::Resolver) type +/// for `ArchiveUnsized` is always a `usize` representing the position of the serialized value. +/// +/// `ArchiveUnsized` is automatically implemented for all types that implement [`Archive`]. Nothing +/// special needs to be done to use them with types like `Box`, `Rc`, and `Arc`. It is also already +/// implemented for slices and string slices, and the `rkyv_dyn` crate can be used to archive trait +/// objects. Other unsized types must manually implement `ArchiveUnsized`. +/// +/// # Examples +/// +/// This example shows how to manually implement `ArchiveUnsized` for an unsized type. Special care +/// must be taken to ensure that the types are laid out correctly. +/// +/// ``` +/// use core::{mem::transmute, ops::{Deref, DerefMut}}; +/// use ptr_meta::Pointee; +/// use rkyv::{ +/// from_archived, +/// to_archived, +/// archived_unsized_value, +/// ser::{serializers::AlignedSerializer, Serializer}, +/// AlignedVec, +/// Archive, +/// Archived, +/// ArchivedMetadata, +/// ArchivePointee, +/// ArchiveUnsized, +/// FixedUsize, +/// RelPtr, +/// Serialize, +/// SerializeUnsized, +/// }; +/// +/// // We're going to be dealing mostly with blocks that have a trailing slice +/// pub struct Block { +/// head: H, +/// tail: T, +/// } +/// +/// impl Pointee for Block { +/// type Metadata = usize; +/// } +/// +/// // For blocks with trailing slices, we need to store the length of the slice +/// // in the metadata. +/// pub struct BlockSliceMetadata { +/// len: Archived, +/// } +/// +/// // ArchivePointee is automatically derived for sized types because pointers +/// // to sized types don't need to store any extra information. Because we're +/// // making an unsized block, we need to define what metadata gets stored with +/// // our data pointer. +/// impl ArchivePointee for Block { +/// // This is the extra data that needs to get stored for blocks with +/// // trailing slices +/// type ArchivedMetadata = BlockSliceMetadata; +/// +/// // We need to be able to turn our archived metadata into regular +/// // metadata for our type +/// fn pointer_metadata( +/// archived: &Self::ArchivedMetadata +/// ) -> ::Metadata { +/// from_archived!(archived.len) as usize +/// } +/// } +/// +/// // We're implementing ArchiveUnsized for just Block. We can still +/// // implement Archive for blocks with sized tails and they won't conflict. +/// impl ArchiveUnsized for Block { +/// // We'll reuse our block type as our archived type. +/// type Archived = Block, [Archived]>; +/// +/// // This is where we'd put any resolve data for our metadata. +/// // Most of the time, this can just be () because most metadata is Copy, +/// // but the option is there if you need it. +/// type MetadataResolver = (); +/// +/// // Here's where we make the metadata for our pointer. +/// // This also gets the position and resolver for the metadata, but we +/// // don't need it in this case. +/// unsafe fn resolve_metadata( +/// &self, +/// _: usize, +/// _: Self::MetadataResolver, +/// out: *mut ArchivedMetadata, +/// ) { +/// unsafe { +/// out.write(BlockSliceMetadata { +/// len: to_archived!(self.tail.len() as FixedUsize), +/// }); +/// } +/// } +/// } +/// +/// // The bounds we use on our serializer type indicate that we need basic +/// // serializer capabilities, and then whatever capabilities our head and tail +/// // types need to serialize themselves. +/// impl< +/// H: Serialize, +/// T: Serialize, +/// S: Serializer + ?Sized +/// > SerializeUnsized for Block { +/// // This is where we construct our unsized type in the serializer +/// fn serialize_unsized( +/// &self, +/// serializer: &mut S +/// ) -> Result { +/// // First, we archive the head and all the tails. This will make sure +/// // that when we finally build our block, we don't accidentally mess +/// // up the structure with serialized dependencies. +/// let head_resolver = self.head.serialize(serializer)?; +/// let mut resolvers = Vec::new(); +/// for tail in self.tail.iter() { +/// resolvers.push(tail.serialize(serializer)?); +/// } +/// // Now we align our serializer for our archived type and write it. +/// // We can't align for unsized types so we treat the trailing slice +/// // like an array of 0 length for now. +/// serializer.align_for::, [Archived; 0]>>()?; +/// let result = unsafe { +/// serializer.resolve_aligned(&self.head, head_resolver)? +/// }; +/// serializer.align_for::>()?; +/// for (item, resolver) in self.tail.iter().zip(resolvers.drain(..)) { +/// unsafe { +/// serializer.resolve_aligned(item, resolver)?; +/// } +/// } +/// Ok(result) +/// } +/// +/// // This is where we serialize the metadata for our type. In this case, +/// // we do all the work in resolve and don't need to do anything here. +/// fn serialize_metadata( +/// &self, +/// serializer: &mut S +/// ) -> Result { +/// Ok(()) +/// } +/// } +/// +/// let value = Block { +/// head: "Numbers 1-4".to_string(), +/// tail: [1, 2, 3, 4], +/// }; +/// // We have a Block but we want to it to be a +/// // Block, so we need to do more pointer transmutation +/// let ptr = (&value as *const Block).cast::<()>(); +/// let unsized_value = unsafe { +/// &*transmute::<(*const (), usize), *const Block>((ptr, 4)) +/// }; +/// +/// let mut serializer = AlignedSerializer::new(AlignedVec::new()); +/// let pos = serializer.serialize_unsized_value(unsized_value) +/// .expect("failed to archive block"); +/// let buf = serializer.into_inner(); +/// +/// let archived_ref = unsafe { +/// archived_unsized_value::>(buf.as_slice(), pos) +/// }; +/// assert_eq!(archived_ref.head, "Numbers 1-4"); +/// assert_eq!(archived_ref.tail.len(), 4); +/// assert_eq!(archived_ref.tail, [1, 2, 3, 4]); +/// ``` +pub trait ArchiveUnsized: Pointee { + /// The archived counterpart of this type. Unlike `Archive`, it may be unsized. + /// + /// This type must implement [`ArchivePointee`], a trait that helps make valid pointers using + /// archived pointer metadata. + type Archived: ArchivePointee + ?Sized; + + /// The resolver for the metadata of this type. + /// + /// Because the pointer metadata must be archived with the relative pointer and not with the + /// structure itself, its resolver must be passed back to the structure holding the pointer. + type MetadataResolver; + + /// Creates the archived version of the metadata for this value at the given position and writes + /// it to the given output. + /// + /// The output should be initialized field-by-field rather than by writing a whole struct. + /// Performing a typed copy will mark all of the padding bytes as uninitialized, but they must + /// remain set to the value they currently have. This prevents leaking uninitialized memory to + /// the final archive. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing this object's metadata + unsafe fn resolve_metadata( + &self, + pos: usize, + resolver: Self::MetadataResolver, + out: *mut ArchivedMetadata, + ); + + /// Resolves a relative pointer to this value with the given `from` and `to` and writes it to + /// the given output. + /// + /// The output should be initialized field-by-field rather than by writing a whole struct. + /// Performing a typed copy will mark all of the padding bytes as uninitialized, but they must + /// remain set to the value they currently have. This prevents leaking uninitialized memory to + /// the final archive. + /// + /// # Safety + /// + /// - `from` must be the position of `out` within the archive + /// - `to` must be the position of some `Self::Archived` within the archive + /// - `resolver` must be the result of serializing this object + #[inline] + unsafe fn resolve_unsized( + &self, + from: usize, + to: usize, + resolver: Self::MetadataResolver, + out: *mut RelPtr, + ) { + RelPtr::resolve_emplace(from, to, self, resolver, out); + } +} + +/// An archived type with associated metadata for its relative pointer. +/// +/// This is mostly used in the context of smart pointers and unsized types, and is implemented for +/// all sized types by default. +pub trait ArchivePointee: Pointee { + /// The archived version of the pointer metadata for this type. + type ArchivedMetadata; + + /// Converts some archived metadata to the pointer metadata for itself. + fn pointer_metadata(archived: &Self::ArchivedMetadata) -> ::Metadata; +} + +/// A counterpart of [`Serialize`] that's suitable for unsized types. +/// +/// See [`ArchiveUnsized`] for examples of implementing `SerializeUnsized`. +pub trait SerializeUnsized: ArchiveUnsized { + /// Writes the object and returns the position of the archived type. + fn serialize_unsized(&self, serializer: &mut S) -> Result; + + /// Serializes the metadata for the given type. + fn serialize_metadata(&self, serializer: &mut S) -> Result; +} + +/// A counterpart of [`Deserialize`] that's suitable for unsized types. +pub trait DeserializeUnsized: ArchivePointee { + /// Deserializes a reference to the given value. + /// + /// # Safety + /// + /// `out` must point to memory with the layout returned by `deserialized_layout`. + unsafe fn deserialize_unsized( + &self, + deserializer: &mut D, + alloc: impl FnMut(Layout) -> *mut u8, + ) -> Result<*mut (), D::Error>; + + /// Deserializes the metadata for the given type. + fn deserialize_metadata(&self, deserializer: &mut D) -> Result; +} + +/// The native type that `usize` is converted to for archiving. +/// +/// This will be `u16`, `u32`, or `u64` when the `size_16`, `size_32`, or `size_64` features are +/// enabled, respectively. +pub type FixedUsize = pick_size_type!(u16, u32, u64); +/// The native type that `isize` is converted to for archiving. +/// +/// This will be `i16`, `i32`, or `i64` when the `size_16`, `size_32`, or `size_64` features are +/// enabled, respectively. +pub type FixedIsize = pick_size_type!(i16, i32, i64); + +/// The default raw relative pointer. +/// +/// This will use an archived [`FixedIsize`] to hold the offset. +pub type RawRelPtr = rel_ptr::RawRelPtr>; +/// The default relative pointer. +/// +/// This will use an archived [`FixedIsize`] to hold the offset. +pub type RelPtr = rel_ptr::RelPtr>; + +/// Alias for the archived version of some [`Archive`] type. +/// +/// This can be useful for reducing the lengths of type definitions. +pub type Archived = ::Archived; +/// Alias for the resolver for some [`Archive`] type. +/// +/// This can be useful for reducing the lengths of type definitions. +pub type Resolver = ::Resolver; +/// Alias for the archived metadata for some [`ArchiveUnsized`] type. +/// +/// This can be useful for reducing the lengths of type definitions. +pub type ArchivedMetadata = + <::Archived as ArchivePointee>::ArchivedMetadata; +/// Alias for the metadata resolver for some [`ArchiveUnsized`] type. +/// +/// This can be useful for reducing the lengths of type definitions. +pub type MetadataResolver = ::MetadataResolver; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..9b0f246 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,160 @@ +#[cfg(feature = "copy")] +macro_rules! default { + (#[inline] $($fn:tt)*) => { #[inline] default $($fn)* }; + ($($fn:tt)*) => { default $($fn)* }; +} + +#[cfg(not(feature = "copy"))] +macro_rules! default { + (#[inline] $($fn:tt)*) => { #[inline] $($fn)* }; + ($($fn:tt)*) => { $($fn)* }; +} + +/// Returns a tuple of `(field_pos, field_out)`, where `field_pos` is the "position", +/// i.e. offset in bytes, of the field relative to the base address of the struct and `field_out` +/// is a `*mut` that points to the field directly. +/// +/// This is essentially a convenience wrapper around [`core::ptr::addr_of_mut!`] that also +/// gives back the relative offset of the field, as these are often needed together. You will often +/// see the return values named `(fp, fo)` in internal use of this macro, which stand for `field_pos` +/// and `field_out` respectively as discussed above. +/// +/// # Example +/// +/// ``` +/// use core::mem::MaybeUninit; +/// use rkyv::out_field; +/// +/// // The macro works on repr(Rust) structs as well, but for the purposes of asserting +/// // an exact guaranteed position of each field in this example, we'll use repr(C) +/// #[repr(C)] +/// struct Example { +/// a: i32, +/// b: bool, +/// } +/// +/// let mut result = MaybeUninit::::zeroed(); +/// let out = result.as_mut_ptr(); +/// +/// let (a_pos, a_out) = out_field!(out.a); +/// assert_eq!(a_pos, 0); // guaranteed by repr(C) layout, repr(Rust) has free reign +/// unsafe { a_out.write(42); } +/// +/// let (b_pos, b_out) = out_field!(out.b); +/// assert_eq!(b_pos, 4); // guaranteed by repr(C) layout, repr(Rust) has free reign +/// unsafe { b_out.write(true); } +/// +/// let result = unsafe { result.assume_init() }; +/// assert_eq!(result.a, 42); +/// assert_eq!(result.b, true); +/// ``` +#[macro_export] +macro_rules! out_field { + ($out:ident.$field:tt) => {{ + #[allow(unused_unsafe)] + unsafe { + let fo = ::core::ptr::addr_of_mut!((*$out).$field); + (fo.cast::().offset_from($out.cast::()) as usize, fo) + } + }}; +} + +/// Returns the unarchived value of the given archived primitive. +/// +/// This macro is not needed for most use cases. Its primary purpose is to simultaneously: +/// - Convert values from (potentially) different archived primitives to their native counterparts +/// - Allow transformation in `const` contexts +/// - Prevent linter warnings from unused `into()` calls +/// +/// Users should feel free to use the more ergonomic `into()` where appropriate. +#[macro_export] +macro_rules! from_archived { + ($expr:expr) => {{ + #[cfg(not(any(feature = "archive_le", feature = "archive_be")))] + { + $expr + } + #[cfg(any(feature = "archive_le", feature = "archive_be"))] + { + ($expr).value() + } + }}; +} + +#[cfg(any(feature = "archive_le", feature = "archive_be"))] +pub use crate::rend::NativeEndian; + +/// Returns the archived value of the given archived primitive. +/// +/// This macro is not needed for most use cases. Its primary purpose is to simultaneously: +/// - Convert values from (potentially) different primitives to their archived counterparts +/// - Allow transformation in `const` contexts +/// - Prevent linter warnings from unused `into()` calls +/// +/// Users should feel free to use the more ergonomic `into()` where appropriate. +#[macro_export] +macro_rules! to_archived { + ($expr:expr) => {{ + #[cfg(not(any(feature = "archive_le", feature = "archive_be")))] + { + $expr + } + #[cfg(feature = "archive_le")] + { + $crate::macros::NativeEndian { value: $expr }.to_le() + } + #[cfg(feature = "archive_be")] + { + $crate::macros::NativeEndian { value: $expr }.to_be() + } + }}; +} + +#[cfg(not(any(feature = "size_16", feature = "size_32", feature = "size_64")))] +core::compile_error!(r#"one of ["size_16", "size_32", or "size_64"] features must be enabled"#); + +#[cfg(all(feature = "size_16", feature = "size_32"))] +core::compile_error!( + "\"size_16\" and \"size_32\" are mutually-exclusive features. You may need to set \ + `default-features = false` or compile with `--no-default-features`." +); +#[cfg(all(feature = "size_16", feature = "size_64"))] +core::compile_error!( + "\"size_16\" and \"size_64\" are mutually-exclusive features. You may need to set \ + `default-features = false` or compile with `--no-default-features`." +); +#[cfg(all(feature = "size_32", feature = "size_64"))] +core::compile_error!( + "\"size_32\" and \"size_64\" are mutually-exclusive features. You may need to set \ + `default-features = false` or compile with `--no-default-features`." +); + +#[cfg(feature = "size_16")] +macro_rules! pick_size_type { + ($s16:ty, $s32:ty, $s64:ty) => { + $s16 + }; + ($s16:ty, $s32:ty, $s64:ty,) => { + pick_size_type!($s16, $s32, $s64) + }; +} + +#[cfg(feature = "size_32")] +macro_rules! pick_size_type { + ($s16:ty, $s32:ty, $s64:ty) => { + $s32 + }; + ($s16:ty, $s32:ty, $s64:ty,) => { + pick_size_type!($s16, $s32, $s64) + }; +} + +#[cfg(feature = "size_64")] +macro_rules! pick_size_type { + ($s16:ty, $s32:ty, $s64:ty) => { + $s64 + }; + ($s16:ty, $s32:ty, $s64:ty,) => { + pick_size_type!($s16, $s32, $s64) + }; +} diff --git a/src/net.rs b/src/net.rs new file mode 100644 index 0000000..68b0f18 --- /dev/null +++ b/src/net.rs @@ -0,0 +1,171 @@ +//! Archived versions of network types. + +use crate::Archived; + +/// An archived [`Ipv4Addr`](std::net::Ipv4Addr). +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct ArchivedIpv4Addr { + octets: [Archived; 4], +} + +impl ArchivedIpv4Addr { + /// Returns the four eight-bit integers that make up this address. + #[inline] + pub const fn octets(&self) -> [u8; 4] { + self.octets + } +} + +/// An archived [`Ipv6Addr`](std::net::Ipv6Addr). +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct ArchivedIpv6Addr { + octets: [Archived; 16], +} + +impl ArchivedIpv6Addr { + /// Returns the eight 16-bit segments that make up this address. + #[inline] + pub const fn segments(&self) -> [u16; 8] { + [ + u16::from_be_bytes([self.octets[0], self.octets[1]]), + u16::from_be_bytes([self.octets[2], self.octets[3]]), + u16::from_be_bytes([self.octets[4], self.octets[5]]), + u16::from_be_bytes([self.octets[6], self.octets[7]]), + u16::from_be_bytes([self.octets[8], self.octets[9]]), + u16::from_be_bytes([self.octets[10], self.octets[11]]), + u16::from_be_bytes([self.octets[12], self.octets[13]]), + u16::from_be_bytes([self.octets[14], self.octets[15]]), + ] + } +} + +/// An archived [`IpAddr`](std::net::IpAddr). +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum ArchivedIpAddr { + /// An IPv4 address. + V4(ArchivedIpv4Addr), + /// An IPv6 address. + V6(ArchivedIpv6Addr), +} + +impl ArchivedIpAddr { + /// Returns `true` if this address is an [`IPv4` address](std::net::IpAddr::V4), and `false` + /// otherwise. + #[inline] + pub const fn is_ipv4(&self) -> bool { + matches!(self, ArchivedIpAddr::V4(_)) + } + + /// Returns `true` if this address is an [`IPv6` address](std::net::IpAddr::V6), and `false` + /// otherwise. + #[inline] + pub const fn is_ipv6(&self) -> bool { + matches!(self, ArchivedIpAddr::V6(_)) + } +} + +/// An archived [`SocketAddrV4`](std::net::SocketAddrV4). +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedSocketAddrV4 { + pub(crate) ip: ArchivedIpv4Addr, + pub(crate) port: Archived, +} + +impl ArchivedSocketAddrV4 { + /// Returns the IP address associated with this socket address. + #[inline] + pub const fn ip(&self) -> &ArchivedIpv4Addr { + &self.ip + } + + /// Returns the port number associated with this socket address. + #[inline] + pub const fn port(&self) -> u16 { + from_archived!(self.port) + } +} + +/// An archived [`SocketAddrV6`](std::net::SocketAddrV6). +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedSocketAddrV6 { + pub(crate) ip: ArchivedIpv6Addr, + pub(crate) port: Archived, + pub(crate) flowinfo: Archived, + pub(crate) scope_id: Archived, +} + +impl ArchivedSocketAddrV6 { + /// Returns the flow information associated with this address. + /// + /// See [`SocketAddrV6::flowinfo()`](std::net::SocketAddrV6::flowinfo()) for more details. + #[inline] + pub const fn flowinfo(&self) -> u32 { + from_archived!(self.flowinfo) + } + + /// Returns the IP address associated with this socket address. + #[inline] + pub const fn ip(&self) -> &ArchivedIpv6Addr { + &self.ip + } + + /// Returns the port number associated with this socket address. + #[inline] + pub const fn port(&self) -> u16 { + from_archived!(self.port) + } + + /// Returns the scope ID associated with this address. + /// + /// See [`SocketAddrV6::scope_id()`](std::net::SocketAddrV6::scope_id()) for more details. + #[inline] + pub const fn scope_id(&self) -> u32 { + from_archived!(self.scope_id) + } +} + +/// An archived [`SocketAddr`](std::net::SocketAddr). +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum ArchivedSocketAddr { + /// An IPv4 socket address. + V4(ArchivedSocketAddrV4), + /// An IPv6 socket address. + V6(ArchivedSocketAddrV6), +} + +impl ArchivedSocketAddr { + /// Returns the port number associated with this socket address. + #[inline] + pub fn port(&self) -> u16 { + match self { + ArchivedSocketAddr::V4(addr) => addr.port(), + ArchivedSocketAddr::V6(addr) => addr.port(), + } + } + + /// Returns `true` if the [IP address](std::net::IpAddr) in this `ArchivedSocketAddr` is an + /// [`IPv4` address](std::net::IpAddr::V4), and `false` otherwise. + #[inline] + pub fn is_ipv4(&self) -> bool { + matches!(self, ArchivedSocketAddr::V4(_)) + } + + /// Returns `true` if the [IP address](std::net::IpAddr) in this `ArchivedSocketAddr` is an + /// [`IPv6` address](std::net::IpAddr::V6), and `false` otherwise. + #[inline] + pub fn is_ipv6(&self) -> bool { + matches!(self, ArchivedSocketAddr::V6(_)) + } +} diff --git a/src/niche/mod.rs b/src/niche/mod.rs new file mode 100644 index 0000000..d267974 --- /dev/null +++ b/src/niche/mod.rs @@ -0,0 +1,5 @@ +//! Manually niched type replacements. + +#[cfg(feature = "alloc")] +pub mod option_box; +pub mod option_nonzero; diff --git a/src/niche/option_box.rs b/src/niche/option_box.rs new file mode 100644 index 0000000..bcabe48 --- /dev/null +++ b/src/niche/option_box.rs @@ -0,0 +1,213 @@ +//! A niched archived `Option>` that uses less space. + +use crate::{ + boxed::{ArchivedBox, BoxResolver}, + ser::Serializer, + ArchivePointee, ArchiveUnsized, SerializeUnsized, +}; +use core::{ + cmp::{self, Eq, Ord, PartialEq, PartialOrd}, + fmt, hash, + hint::unreachable_unchecked, + ops::Deref, + pin::Pin, +}; + +/// A niched archived `Option>`. +/// +/// It uses less space by storing the `None` variant as a null pointer. +#[repr(transparent)] +pub struct ArchivedOptionBox { + inner: ArchivedBox, +} + +impl ArchivedOptionBox { + /// Returns `true` if the option box is a `None` value. + #[inline] + pub fn is_none(&self) -> bool { + self.as_ref().is_none() + } + + /// Returns `true` if the option box is a `Some` value. + #[inline] + pub fn is_some(&self) -> bool { + self.as_ref().is_some() + } + + /// Converts to an `Option<&ArchivedBox>`. + #[inline] + pub fn as_ref(&self) -> Option<&ArchivedBox> { + if self.inner.is_null() { + None + } else { + Some(&self.inner) + } + } + + /// Converts to an `Option<&mut ArchivedBox>`. + #[inline] + pub fn as_mut(&mut self) -> Option<&mut ArchivedBox> { + if self.inner.is_null() { + None + } else { + Some(&mut self.inner) + } + } + + /// Converts from `Pin<&ArchivedOptionBox>` to `Option>>`. + #[inline] + pub fn as_pin_ref(self: Pin<&Self>) -> Option>> { + unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } + } + + /// Converts from `Pin<&mut ArchivedOption>` to `Option>>`. + #[inline] + pub fn as_pin_mut(self: Pin<&mut Self>) -> Option>> { + unsafe { + Pin::get_unchecked_mut(self) + .as_mut() + .map(|x| Pin::new_unchecked(x)) + } + } + + /// Returns an iterator over the possibly contained value. + #[inline] + pub fn iter(&self) -> Iter<'_, ArchivedBox> { + Iter { + inner: self.as_ref(), + } + } + + /// Returns a mutable iterator over the possibly contained value. + #[inline] + pub fn iter_mut(&mut self) -> IterMut<'_, ArchivedBox> { + IterMut { + inner: self.as_mut(), + } + } + + /// Converts from `&ArchivedOptionBox` to `Option<&T>`. + /// + /// Leaves the original `ArchivedOptionBox` in-place, creating a new one with a reference to the + /// original one. + #[inline] + pub fn as_deref(&self) -> Option<&T> { + self.as_ref().map(|x| (*x).deref()) + } +} + +impl ArchivedOptionBox +where + T::ArchivedMetadata: Default, +{ + /// Resolves an `ArchivedOptionBox` from an `Option<&T>`. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing `field` + #[inline] + pub unsafe fn resolve_from_option + ?Sized>( + field: Option<&U>, + pos: usize, + resolver: OptionBoxResolver, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.inner); + if let Some(value) = field { + let resolver = if let OptionBoxResolver::Some(metadata_resolver) = resolver { + metadata_resolver + } else { + unreachable_unchecked(); + }; + + ArchivedBox::resolve_from_ref(value, pos + fp, resolver, fo) + } else { + ArchivedBox::emplace_null(pos + fp, fo); + } + } + + /// Serializes an `ArchivedOptionBox` from an `Option<&T>`. + #[inline] + pub fn serialize_from_option( + field: Option<&U>, + serializer: &mut S, + ) -> Result, S::Error> + where + U: SerializeUnsized + ?Sized, + S: Serializer + ?Sized, + { + if let Some(value) = field { + Ok(OptionBoxResolver::Some(ArchivedBox::serialize_from_ref( + value, serializer, + )?)) + } else { + Ok(OptionBoxResolver::None) + } + } +} + +impl fmt::Debug for ArchivedOptionBox +where + T::ArchivedMetadata: fmt::Debug, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.as_ref() { + Some(inner) => inner.fmt(f), + None => f.debug_tuple("None").finish(), + } + } +} + +impl Eq for ArchivedOptionBox {} + +impl hash::Hash for ArchivedOptionBox { + #[inline] + fn hash(&self, state: &mut H) { + self.as_ref().hash(state) + } +} + +impl Ord for ArchivedOptionBox { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.as_ref().cmp(&other.as_ref()) + } +} + +impl PartialEq for ArchivedOptionBox { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_ref().eq(&other.as_ref()) + } +} + +impl PartialOrd for ArchivedOptionBox { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.as_ref().partial_cmp(&other.as_ref()) + } +} + +/// An iterator over a reference to the `Some` variant of an `ArchivedOptionBox`. +/// +/// This iterator yields one value if the `ArchivedOptionBox` is a `Some`, otherwise none. +/// +/// This `struct` is created by the [`ArchivedOptionBox::iter`] function. +pub type Iter<'a, T> = crate::option::Iter<'a, T>; + +/// An iterator over a mutable reference to the `Some` variant of an `ArchivedOptionBox`. +/// +/// This iterator yields one value if the `ArchivedOptionBox` is a `Some`, otherwise none. +/// +/// This `struct` is created by the [`ArchivedOptionBox::iter_mut`] function. +pub type IterMut<'a, T> = crate::option::IterMut<'a, T>; + +/// The resolver for [`ArchivedOptionBox`]. +pub enum OptionBoxResolver { + /// The `ArchivedOptionBox` was `None` + None, + /// The resolver for the `ArchivedBox` + Some(BoxResolver), +} diff --git a/src/niche/option_nonzero.rs b/src/niche/option_nonzero.rs new file mode 100644 index 0000000..a2b434c --- /dev/null +++ b/src/niche/option_nonzero.rs @@ -0,0 +1,195 @@ +//! Niched archived `Option` integers that use less space. + +use crate::Archived; +use core::{ + cmp, fmt, hash, + num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16, + NonZeroU32, NonZeroU64, NonZeroU8, + }, + pin::Pin, +}; + +macro_rules! impl_archived_option_nonzero { + ($ar:ident, $nz:ty, $ne:ty) => { + #[doc = concat!("A niched archived `Option<", stringify!($nz), ">`")] + #[repr(transparent)] + pub struct $ar { + inner: Archived<$ne>, + } + + impl $ar { + /// Returns `true` if the option is a `None` value. + #[inline] + pub fn is_none(&self) -> bool { + self.inner == 0 + } + + /// Returns `true` if the option is a `Some` value. + #[inline] + pub fn is_some(&self) -> bool { + self.inner != 0 + } + + #[doc = concat!("Converts to an `Option<&Archived<", stringify!($nz), ">>`")] + pub fn as_ref(&self) -> Option<&Archived<$nz>> { + if self.inner != 0 { + let as_nonzero = unsafe { + // SAFETY: NonZero types have the same memory layout and bit patterns as + // their integer counterparts, regardless of endianness + &*(&self.inner as *const _ as *const Archived<$nz>) + }; + Some(as_nonzero) + } else { + None + } + } + + #[doc = concat!("Converts to an `Option<&mut Archived<", stringify!($nz), ">>`")] + pub fn as_mut(&mut self) -> Option<&mut Archived<$nz>> { + if self.inner != 0 { + let as_nonzero = unsafe { + // SAFETY: NonZero types have the same memory layout and bit patterns as + // their integer counterparts, regardless of endianness + &mut *(&mut self.inner as *mut _ as *mut Archived<$nz>) + }; + Some(as_nonzero) + } else { + None + } + } + + #[doc = concat!("Converts from `Pin<&ArchivedOption", stringify!($nz), ">` to `Option>>`.")] + #[inline] + pub fn as_pin_ref(self: Pin<&Self>) -> Option>> { + unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } + } + + #[doc = concat!("Converts from `Pin<&mut ArchivedOption", stringify!($nz), ">` to `Option>>`.")] + #[inline] + pub fn as_pin_mut(self: Pin<&mut Self>) -> Option>> { + unsafe { + Pin::get_unchecked_mut(self) + .as_mut() + .map(|x| Pin::new_unchecked(x)) + } + } + + /// Returns an iterator over the possibly contained value. + #[inline] + pub fn iter(&self) -> Iter<'_, Archived<$nz>> { + Iter { + inner: self.as_ref(), + } + } + + /// Returns a mutable iterator over the possibly contained value. + #[inline] + pub fn iter_mut(&mut self) -> IterMut<'_, Archived<$nz>> { + IterMut { + inner: self.as_mut(), + } + } + + /// Inserts `v` into the option if it is `None`, then returns a mutable + /// reference to the contained value. + #[inline] + pub fn get_or_insert(&mut self, v: $nz) -> &mut Archived<$nz> { + self.get_or_insert_with(move || v) + } + + /// Inserts a value computed from `f` into the option if it is `None`, then + /// returns a mutable reference to the contained value. + #[inline] + pub fn get_or_insert_with $nz>(&mut self, f: F) -> &mut Archived<$nz> { + if self.inner == 0 { + self.inner = f().get().into(); + } + unsafe { + // SAFETY: self.inner is nonzero + &mut *(&mut self.inner as *mut _ as *mut Archived<$nz>) + } + } + + /// Resolves an `ArchivedOptionNonZero` from an `Option`. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + #[inline] + pub unsafe fn resolve_from_option(field: Option<$nz>, out: *mut Self) { + let (_, fo) = out_field!(out.inner); + if let Some(nz) = field { + fo.write(nz.get().into()); + } else { + fo.write((0 as $ne).into()); + } + } + } + + impl fmt::Debug for $ar { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.as_ref() { + Some(inner) => inner.fmt(f), + None => f.debug_tuple("None").finish(), + } + } + } + + impl Eq for $ar {} + + impl hash::Hash for $ar { + #[inline] + fn hash(&self, state: &mut H) { + self.as_ref().hash(state) + } + } + + impl Ord for $ar { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.as_ref().cmp(&other.as_ref()) + } + } + + impl PartialEq for $ar { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_ref().eq(&other.as_ref()) + } + } + + impl PartialOrd for $ar { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.as_ref().partial_cmp(&other.as_ref()) + } + } + }; +} + +impl_archived_option_nonzero!(ArchivedOptionNonZeroI8, NonZeroI8, i8); +impl_archived_option_nonzero!(ArchivedOptionNonZeroI16, NonZeroI16, i16); +impl_archived_option_nonzero!(ArchivedOptionNonZeroI32, NonZeroI32, i32); +impl_archived_option_nonzero!(ArchivedOptionNonZeroI64, NonZeroI64, i64); +impl_archived_option_nonzero!(ArchivedOptionNonZeroI128, NonZeroI128, i128); + +impl_archived_option_nonzero!(ArchivedOptionNonZeroU8, NonZeroU8, u8); +impl_archived_option_nonzero!(ArchivedOptionNonZeroU16, NonZeroU16, u16); +impl_archived_option_nonzero!(ArchivedOptionNonZeroU32, NonZeroU32, u32); +impl_archived_option_nonzero!(ArchivedOptionNonZeroU64, NonZeroU64, u64); +impl_archived_option_nonzero!(ArchivedOptionNonZeroU128, NonZeroU128, u128); + +/// An iterator over a reference to the `Some` variant of an `ArchivedOptionNonZero` integer. +/// +/// This iterator yields one value if the `ArchivedOptionNonZero` integer is a `Some`, otherwise +/// none. +pub type Iter<'a, T> = crate::option::Iter<'a, T>; + +/// An iterator over a mutable reference to the `Some` variant of an `ArchivedOptionNonZero` +/// integer. +/// +/// This iterator yields one value if the `ArchivedOptionNonZero` integer is a `Some`, otherwise +/// none. +pub type IterMut<'a, T> = crate::option::IterMut<'a, T>; diff --git a/src/ops.rs b/src/ops.rs new file mode 100644 index 0000000..d1a3b64 --- /dev/null +++ b/src/ops.rs @@ -0,0 +1,238 @@ +//! Archived versions of `ops` types. + +use core::{ + cmp, fmt, + ops::{Bound, RangeBounds}, +}; + +/// An archived [`Range`](::core::ops::Range). +#[derive(Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedRange { + /// The lower bound of the range (inclusive). + pub start: T, + /// The upper bound of the range (inclusive). + pub end: T, +} + +impl fmt::Debug for ArchivedRange { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(fmt)?; + write!(fmt, "..")?; + self.end.fmt(fmt)?; + Ok(()) + } +} + +impl> ArchivedRange { + /// Returns `true` if `item` is contained in the range. + #[inline] + pub fn contains(&self, item: &U) -> bool + where + T: PartialOrd, + U: PartialOrd + ?Sized, + { + >::contains(self, item) + } + + /// Returns `true` if the range contains no items. + #[inline] + pub fn is_empty(&self) -> bool { + match self.start.partial_cmp(&self.end) { + None | Some(cmp::Ordering::Greater) | Some(cmp::Ordering::Equal) => true, + Some(cmp::Ordering::Less) => false, + } + } +} + +impl RangeBounds for ArchivedRange { + #[inline] + fn start_bound(&self) -> Bound<&T> { + Bound::Included(&self.start) + } + + #[inline] + fn end_bound(&self) -> Bound<&T> { + Bound::Excluded(&self.end) + } +} + +// RangeInclusive + +/// An archived [`RangeInclusive`](::core::ops::RangeInclusive). +#[derive(Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedRangeInclusive { + /// The lower bound of the range (inclusive). + pub start: T, + /// The upper bound of the range (inclusive). + pub end: T, +} + +impl fmt::Debug for ArchivedRangeInclusive { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(fmt)?; + write!(fmt, "..=")?; + self.end.fmt(fmt)?; + Ok(()) + } +} + +impl> ArchivedRangeInclusive { + /// Returns `true` if `item` is contained in the range. + #[inline] + pub fn contains(&self, item: &U) -> bool + where + T: PartialOrd, + U: PartialOrd + ?Sized, + { + >::contains(self, item) + } + + /// Returns `true` if the range contains no items. + #[inline] + pub fn is_empty(&self) -> bool { + match self.start.partial_cmp(&self.end) { + None | Some(cmp::Ordering::Greater) => true, + Some(cmp::Ordering::Less) | Some(cmp::Ordering::Equal) => false, + } + } +} + +impl RangeBounds for ArchivedRangeInclusive { + #[inline] + fn start_bound(&self) -> Bound<&T> { + Bound::Included(&self.start) + } + + #[inline] + fn end_bound(&self) -> Bound<&T> { + Bound::Included(&self.end) + } +} + +/// An archived [`RangeFrom`](::core::ops::RangeFrom). +#[derive(Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedRangeFrom { + /// The lower bound of the range (inclusive). + pub start: T, +} + +impl fmt::Debug for ArchivedRangeFrom { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(fmt)?; + write!(fmt, "..")?; + Ok(()) + } +} + +impl> ArchivedRangeFrom { + /// Returns `true` if `item` is contained in the range. + #[inline] + pub fn contains(&self, item: &U) -> bool + where + T: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } +} + +impl RangeBounds for ArchivedRangeFrom { + #[inline] + fn start_bound(&self) -> Bound<&T> { + Bound::Included(&self.start) + } + + #[inline] + fn end_bound(&self) -> Bound<&T> { + Bound::Unbounded + } +} + +/// An archived [`RangeTo`](::core::ops::RangeTo). +#[derive(Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedRangeTo { + /// The upper bound of the range (exclusive). + pub end: T, +} + +impl fmt::Debug for ArchivedRangeTo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "..")?; + self.end.fmt(fmt)?; + Ok(()) + } +} + +impl> ArchivedRangeTo { + /// Returns `true` if `item` is contained in the range. + #[inline] + pub fn contains(&self, item: &U) -> bool + where + T: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } +} + +impl RangeBounds for ArchivedRangeTo { + #[inline] + fn start_bound(&self) -> Bound<&T> { + Bound::Unbounded + } + + #[inline] + fn end_bound(&self) -> Bound<&T> { + Bound::Excluded(&self.end) + } +} + +/// An archived [`RangeToInclusive`](::core::ops::RangeToInclusive). +#[derive(Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedRangeToInclusive { + /// The upper bound of the range (inclusive). + pub end: T, +} + +impl fmt::Debug for ArchivedRangeToInclusive { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "..=")?; + self.end.fmt(fmt)?; + Ok(()) + } +} + +impl> ArchivedRangeToInclusive { + /// Returns `true` if `item` is contained in the range. + #[inline] + pub fn contains(&self, item: &U) -> bool + where + T: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } +} + +impl RangeBounds for ArchivedRangeToInclusive { + #[inline] + fn start_bound(&self) -> Bound<&T> { + Bound::Unbounded + } + + #[inline] + fn end_bound(&self) -> Bound<&T> { + Bound::Included(&self.end) + } +} diff --git a/src/option.rs b/src/option.rs new file mode 100644 index 0000000..c6de1fe --- /dev/null +++ b/src/option.rs @@ -0,0 +1,252 @@ +//! An archived version of `Option`. + +use core::{ + cmp, hash, + iter::DoubleEndedIterator, + mem, + ops::{Deref, DerefMut}, + pin::Pin, +}; + +/// An archived [`Option`]. +/// +/// It functions identically to [`Option`] but has a different internal +/// representation to allow for archiving. +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[repr(u8)] +pub enum ArchivedOption { + /// No value + None, + /// Some value `T` + Some(T), +} + +impl ArchivedOption { + /// Returns `true` if the option is a `None` value. + #[inline] + pub fn is_none(&self) -> bool { + match self { + ArchivedOption::None => true, + ArchivedOption::Some(_) => false, + } + } + + /// Returns `true` if the option is a `Some` value. + #[inline] + pub fn is_some(&self) -> bool { + match self { + ArchivedOption::None => false, + ArchivedOption::Some(_) => true, + } + } + + /// Converts to an `Option<&T>`. + #[inline] + pub const fn as_ref(&self) -> Option<&T> { + match self { + ArchivedOption::None => None, + ArchivedOption::Some(value) => Some(value), + } + } + + /// Converts to an `Option<&mut T>`. + #[inline] + pub fn as_mut(&mut self) -> Option<&mut T> { + match self { + ArchivedOption::None => None, + ArchivedOption::Some(value) => Some(value), + } + } + + /// Converts from `Pin<&ArchivedOption>` to `Option>`. + #[inline] + pub fn as_pin_ref(self: Pin<&Self>) -> Option> { + unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } + } + + /// Converts from `Pin<&mut ArchivedOption>` to `Option>`. + #[inline] + pub fn as_pin_mut(self: Pin<&mut Self>) -> Option> { + unsafe { + Pin::get_unchecked_mut(self) + .as_mut() + .map(|x| Pin::new_unchecked(x)) + } + } + + /// Returns an iterator over the possibly contained value. + #[inline] + pub const fn iter(&self) -> Iter<'_, T> { + Iter { + inner: self.as_ref(), + } + } + + /// Returns a mutable iterator over the possibly contained value. + #[inline] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { + inner: self.as_mut(), + } + } + + /// Inserts `v` into the option if it is `None`, then returns a mutable + /// reference to the contained value. + #[inline] + pub fn get_or_insert(&mut self, v: T) -> &mut T { + self.get_or_insert_with(move || v) + } + + /// Inserts a value computed from `f` into the option if it is `None`, then + /// returns a mutable reference to the contained value. + #[inline] + pub fn get_or_insert_with T>(&mut self, f: F) -> &mut T { + if let ArchivedOption::Some(ref mut value) = self { + value + } else { + *self = ArchivedOption::Some(f()); + self.as_mut().unwrap() + } + } +} + +impl ArchivedOption { + /// Converts from `&ArchivedOption` to `Option<&T::Target>`. + /// + /// Leaves the original `ArchivedOption` in-place, creating a new one with a reference to the + /// original one, additionally coercing the contents via `Deref`. + #[inline] + pub fn as_deref(&self) -> Option<&::Target> { + self.as_ref().map(|x| x.deref()) + } +} + +impl ArchivedOption { + /// Converts from `&mut ArchivedOption` to `Option<&mut T::Target>`. + /// + /// Leaves the original `ArchivedOption` in-place, creating a new `Option` with a mutable + /// reference to the inner type's `Deref::Target` type. + #[inline] + pub fn as_deref_mut(&mut self) -> Option<&mut ::Target> { + self.as_mut().map(|x| x.deref_mut()) + } +} + +impl Eq for ArchivedOption {} + +impl hash::Hash for ArchivedOption { + #[inline] + fn hash(&self, state: &mut H) { + self.as_ref().hash(state) + } +} + +impl Ord for ArchivedOption { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.as_ref().cmp(&other.as_ref()) + } +} + +impl PartialEq for ArchivedOption { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_ref().eq(&other.as_ref()) + } +} + +impl PartialOrd for ArchivedOption { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.as_ref().partial_cmp(&other.as_ref()) + } +} + +impl> PartialEq> for ArchivedOption { + #[inline] + fn eq(&self, other: &Option) -> bool { + if let ArchivedOption::Some(self_value) = self { + if let Some(other_value) = other { + self_value.eq(other_value) + } else { + false + } + } else { + other.is_none() + } + } +} + +impl, U> PartialEq> for Option { + #[inline] + fn eq(&self, other: &ArchivedOption) -> bool { + other.eq(self) + } +} + +impl From for ArchivedOption { + /// Moves `val` into a new [`Some`]. + /// + /// # Examples + /// + /// ``` + /// # use rkyv::option::ArchivedOption; + /// let o: ArchivedOption = ArchivedOption::from(67); + /// + /// assert_eq!(Some(67), o); + /// ``` + fn from(val: T) -> ArchivedOption { + ArchivedOption::Some(val) + } +} + +/// An iterator over a reference to the `Some` variant of an `ArchivedOption`. +/// +/// This iterator yields one value if the `ArchivedOption` is a `Some`, otherwise none. +/// +/// This `struct` is created by the [`ArchivedOption::iter`] function. +pub struct Iter<'a, T> { + pub(crate) inner: Option<&'a T>, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + let mut result = None; + mem::swap(&mut self.inner, &mut result); + result + } +} + +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + fn next_back(&mut self) -> Option { + self.next() + } +} + +/// An iterator over a mutable reference to the `Some` variant of an `ArchivedOption`. +/// +/// This iterator yields one value if the `ArchivedOption` is a `Some`, otherwise none. +/// +/// This `struct` is created by the [`ArchivedOption::iter_mut`] function. +pub struct IterMut<'a, T> { + pub(crate) inner: Option<&'a mut T>, +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + let mut result = None; + mem::swap(&mut self.inner, &mut result); + result + } +} + +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + fn next_back(&mut self) -> Option { + self.next() + } +} diff --git a/src/rc/mod.rs b/src/rc/mod.rs new file mode 100644 index 0000000..4e32322 --- /dev/null +++ b/src/rc/mod.rs @@ -0,0 +1,270 @@ +//! Archived versions of shared pointers. + +#[cfg(feature = "validation")] +pub mod validation; + +use crate::{ + ser::{Serializer, SharedSerializeRegistry}, + ArchivePointee, ArchiveUnsized, MetadataResolver, RelPtr, SerializeUnsized, +}; +use core::{borrow::Borrow, cmp, fmt, hash, marker::PhantomData, ops::Deref, pin::Pin, ptr}; + +/// An archived `Rc`. +/// +/// This is a thin wrapper around a [`RelPtr`] to the archived type paired with a "flavor" type. +/// Because there may be many varieties of shared pointers and they may not be used together, the +/// flavor helps check that memory is not being shared incorrectly during validation. +#[repr(transparent)] +pub struct ArchivedRc(RelPtr, PhantomData); + +impl ArchivedRc { + /// Gets the value of the `ArchivedRc`. + #[inline] + pub fn get(&self) -> &T { + unsafe { &*self.0.as_ptr() } + } + + /// Gets the pinned mutable value of this `ArchivedRc`. + /// + /// # Safety + /// + /// Any other `ArchivedRc` pointers to the same value must not be dereferenced for the duration + /// of the returned borrow. + #[inline] + pub unsafe fn get_pin_mut_unchecked(self: Pin<&mut Self>) -> Pin<&mut T> { + self.map_unchecked_mut(|s| &mut *s.0.as_mut_ptr()) + } + + /// Resolves an archived `Rc` from a given reference. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing `value` + #[inline] + pub unsafe fn resolve_from_ref + ?Sized>( + value: &U, + pos: usize, + resolver: RcResolver>, + out: *mut Self, + ) { + let (fp, fo) = out_field!(out.0); + value.resolve_unsized(pos + fp, resolver.pos, resolver.metadata_resolver, fo); + } + + /// Serializes an archived `Rc` from a given reference. + #[inline] + pub fn serialize_from_ref< + U: SerializeUnsized + ?Sized, + S: Serializer + SharedSerializeRegistry + ?Sized, + >( + value: &U, + serializer: &mut S, + ) -> Result>, S::Error> { + let pos = serializer.serialize_shared(value)?; + + // The positions of serialized `Rc` values must be unique. If we didn't + // write any data by serializing `value`, pad the serializer by a byte + // to ensure that our position will be unique. + if serializer.pos() == pos { + serializer.pad(1)?; + } + + Ok(RcResolver { + pos, + metadata_resolver: value.serialize_metadata(serializer)?, + }) + } +} + +impl AsRef for ArchivedRc { + #[inline] + fn as_ref(&self) -> &T { + self.get() + } +} + +impl Borrow for ArchivedRc { + #[inline] + fn borrow(&self) -> &T { + self.get() + } +} + +impl fmt::Debug for ArchivedRc { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl Deref for ArchivedRc { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl fmt::Display for ArchivedRc { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +impl Eq for ArchivedRc {} + +impl hash::Hash for ArchivedRc { + fn hash(&self, state: &mut H) { + self.get().hash(state) + } +} + +impl Ord for ArchivedRc { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.get().cmp(other.get()) + } +} + +impl PartialEq> for ArchivedRc +where + T: ArchivePointee + PartialEq + ?Sized, + U: ArchivePointee + ?Sized, +{ + fn eq(&self, other: &ArchivedRc) -> bool { + self.get().eq(other.get()) + } +} + +impl PartialOrd> for ArchivedRc +where + T: ArchivePointee + PartialOrd + ?Sized, + U: ArchivePointee + ?Sized, +{ + fn partial_cmp(&self, other: &ArchivedRc) -> Option { + self.get().partial_cmp(other.get()) + } +} + +impl fmt::Pointer for ArchivedRc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.0.base(), f) + } +} + +/// The resolver for `Rc`. +pub struct RcResolver { + pos: usize, + metadata_resolver: T, +} + +/// An archived `rc::Weak`. +/// +/// This is essentially just an optional [`ArchivedRc`]. +#[repr(u8)] +pub enum ArchivedRcWeak { + /// A null weak pointer + None, + /// A weak pointer to some shared pointer + Some(ArchivedRc), +} + +impl ArchivedRcWeak { + /// Attempts to upgrade the weak pointer to an `ArchivedArc`. + /// + /// Returns `None` if a null weak pointer was serialized. + #[inline] + pub fn upgrade(&self) -> Option<&ArchivedRc> { + match self { + ArchivedRcWeak::None => None, + ArchivedRcWeak::Some(r) => Some(r), + } + } + + /// Attempts to upgrade a pinned mutable weak pointer. + #[inline] + pub fn upgrade_pin_mut(self: Pin<&mut Self>) -> Option>> { + unsafe { + match self.get_unchecked_mut() { + ArchivedRcWeak::None => None, + ArchivedRcWeak::Some(r) => Some(Pin::new_unchecked(r)), + } + } + } + + /// Resolves an archived `Weak` from a given optional reference. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing `value` + #[inline] + pub unsafe fn resolve_from_ref + ?Sized>( + value: Option<&U>, + pos: usize, + resolver: RcWeakResolver>, + out: *mut Self, + ) { + match resolver { + RcWeakResolver::None => { + let out = out.cast::(); + ptr::addr_of_mut!((*out).0).write(ArchivedRcWeakTag::None); + } + RcWeakResolver::Some(resolver) => { + let out = out.cast::>(); + ptr::addr_of_mut!((*out).0).write(ArchivedRcWeakTag::Some); + + let (fp, fo) = out_field!(out.1); + ArchivedRc::resolve_from_ref(value.unwrap(), pos + fp, resolver, fo); + } + } + } + + /// Serializes an archived `Weak` from a given optional reference. + #[inline] + pub fn serialize_from_ref( + value: Option<&U>, + serializer: &mut S, + ) -> Result>, S::Error> + where + U: SerializeUnsized + ?Sized, + S: Serializer + SharedSerializeRegistry + ?Sized, + { + Ok(match value { + None => RcWeakResolver::None, + Some(r) => RcWeakResolver::Some(ArchivedRc::::serialize_from_ref(r, serializer)?), + }) + } +} + +impl fmt::Debug for ArchivedRcWeak { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "(Weak)") + } +} + +/// The resolver for `rc::Weak`. +pub enum RcWeakResolver { + /// The weak pointer was null + None, + /// The weak pointer was to some shared pointer + Some(RcResolver), +} + +#[allow(dead_code)] +#[repr(u8)] +enum ArchivedRcWeakTag { + None, + Some, +} + +#[repr(C)] +struct ArchivedRcWeakVariantNone(ArchivedRcWeakTag); + +#[repr(C)] +struct ArchivedRcWeakVariantSome( + ArchivedRcWeakTag, + ArchivedRc, +); diff --git a/src/rc/validation.rs b/src/rc/validation.rs new file mode 100644 index 0000000..76bf7d5 --- /dev/null +++ b/src/rc/validation.rs @@ -0,0 +1,179 @@ +//! Validation implementations for shared pointers. + +use super::{ArchivedRc, ArchivedRcWeak, ArchivedRcWeakTag, ArchivedRcWeakVariantSome}; +use crate::{ + validation::{ArchiveContext, LayoutRaw, SharedContext}, + ArchivePointee, RelPtr, +}; +use bytecheck::{CheckBytes, Error}; +use core::{any::TypeId, convert::Infallible, fmt, ptr}; +use ptr_meta::Pointee; + +/// Errors that can occur while checking archived shared pointers. +#[derive(Debug)] +pub enum SharedPointerError { + /// An error occurred while checking the bytes of a shared value + PointerCheckBytesError(T), + /// An error occurred while checking the bytes of a shared reference + ValueCheckBytesError(R), + /// A context error occurred + ContextError(C), +} + +impl fmt::Display for SharedPointerError +where + T: fmt::Display, + R: fmt::Display, + C: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SharedPointerError::PointerCheckBytesError(e) => e.fmt(f), + SharedPointerError::ValueCheckBytesError(e) => e.fmt(f), + SharedPointerError::ContextError(e) => e.fmt(f), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for SharedPointerError + where + T: Error + 'static, + R: Error + 'static, + C: Error + 'static, + { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + SharedPointerError::PointerCheckBytesError(e) => Some(e as &dyn Error), + SharedPointerError::ValueCheckBytesError(e) => Some(e as &dyn Error), + SharedPointerError::ContextError(e) => Some(e as &dyn Error), + } + } + } +}; + +/// Errors that can occur while checking archived weak pointers. +#[derive(Debug)] +pub enum WeakPointerError { + /// The weak pointer had an invalid tag + InvalidTag(u8), + /// An error occurred while checking the underlying shared pointer + CheckBytes(SharedPointerError), +} + +impl fmt::Display for WeakPointerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + WeakPointerError::InvalidTag(tag) => { + write!(f, "archived weak had invalid tag: {}", tag) + } + WeakPointerError::CheckBytes(e) => e.fmt(f), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for WeakPointerError + where + T: Error + 'static, + R: Error + 'static, + C: Error + 'static, + { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + WeakPointerError::InvalidTag(_) => None, + WeakPointerError::CheckBytes(e) => Some(e as &dyn Error), + } + } + } +}; + +impl From for WeakPointerError { + fn from(_: Infallible) -> Self { + unsafe { core::hint::unreachable_unchecked() } + } +} + +impl CheckBytes for ArchivedRc +where + T: ArchivePointee + CheckBytes + LayoutRaw + Pointee + ?Sized + 'static, + C: ArchiveContext + SharedContext + ?Sized, + T::ArchivedMetadata: CheckBytes, + C::Error: Error, + F: 'static, +{ + type Error = + SharedPointerError<>::Error, T::Error, C::Error>; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + let rel_ptr = RelPtr::::manual_check_bytes(value.cast(), context) + .map_err(SharedPointerError::PointerCheckBytesError)?; + let ptr = context + .check_rel_ptr(rel_ptr) + .map_err(SharedPointerError::ContextError)?; + + let type_id = TypeId::of::(); + if context + .register_shared_ptr(ptr.cast(), type_id) + .map_err(SharedPointerError::ContextError)? + { + context + .bounds_check_subtree_ptr(ptr) + .map_err(SharedPointerError::ContextError)?; + + let range = context + .push_prefix_subtree(ptr) + .map_err(SharedPointerError::ContextError)?; + T::check_bytes(ptr, context).map_err(SharedPointerError::ValueCheckBytesError)?; + context + .pop_prefix_range(range) + .map_err(SharedPointerError::ContextError)?; + } + Ok(&*value) + } +} + +impl ArchivedRcWeakTag { + const TAG_NONE: u8 = ArchivedRcWeakTag::None as u8; + const TAG_SOME: u8 = ArchivedRcWeakTag::Some as u8; +} + +impl CheckBytes for ArchivedRcWeak +where + T: ArchivePointee + CheckBytes + LayoutRaw + Pointee + ?Sized + 'static, + C: ArchiveContext + SharedContext + ?Sized, + T::ArchivedMetadata: CheckBytes, + C::Error: Error, + F: 'static, +{ + type Error = + WeakPointerError<>::Error, T::Error, C::Error>; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + let tag = *u8::check_bytes(value.cast::(), context)?; + match tag { + ArchivedRcWeakTag::TAG_NONE => (), + ArchivedRcWeakTag::TAG_SOME => { + let value = value.cast::>(); + ArchivedRc::::check_bytes(ptr::addr_of!((*value).1), context) + .map_err(WeakPointerError::CheckBytes)?; + } + _ => return Err(WeakPointerError::InvalidTag(tag)), + } + Ok(&*value) + } +} diff --git a/src/rel_ptr/mod.rs b/src/rel_ptr/mod.rs new file mode 100644 index 0000000..5936ee4 --- /dev/null +++ b/src/rel_ptr/mod.rs @@ -0,0 +1,508 @@ +//! Relative pointer implementations and options. + +#[cfg(feature = "validation")] +mod validation; + +use crate::{ArchivePointee, ArchiveUnsized, Archived}; +use core::{ + convert::TryFrom, + fmt, + marker::{PhantomData, PhantomPinned}, + ptr, +}; + +/// An error where the distance between two positions cannot be represented by the offset type. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum OffsetError { + /// The offset overflowed the range of `isize` + IsizeOverflow, + /// The offset is too far for the offset type of the relative pointer + ExceedsStorageRange, +} + +impl fmt::Display for OffsetError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + OffsetError::IsizeOverflow => write!(f, "the offset overflowed the range of `isize`"), + OffsetError::ExceedsStorageRange => write!( + f, + "the offset is too far for the offset type of the relative pointer" + ), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for OffsetError {} + +/// Calculates the offset between two positions as an `isize`. +/// +/// This function exists solely to get the distance between two `usizes` as an `isize` with a full +/// range of values. +/// +/// # Examples +/// +/// ``` +/// use rkyv::rel_ptr::{signed_offset, OffsetError}; +/// +/// assert_eq!(signed_offset(0, 1), Ok(1)); +/// assert_eq!(signed_offset(1, 0), Ok(-1)); +/// assert_eq!(signed_offset(0, isize::MAX as usize), Ok(isize::MAX)); +/// assert_eq!(signed_offset(isize::MAX as usize, 0), Ok(-isize::MAX)); +/// assert_eq!(signed_offset(0, isize::MAX as usize + 1), Err(OffsetError::IsizeOverflow)); +/// assert_eq!(signed_offset(isize::MAX as usize + 1, 0), Ok(isize::MIN)); +/// assert_eq!(signed_offset(0, isize::MAX as usize + 2), Err(OffsetError::IsizeOverflow)); +/// assert_eq!(signed_offset(isize::MAX as usize + 2, 0), Err(OffsetError::IsizeOverflow)); +/// ``` +#[inline] +pub fn signed_offset(from: usize, to: usize) -> Result { + let (result, overflow) = to.overflowing_sub(from); + if (!overflow && result <= (isize::MAX as usize)) + || (overflow && result >= (isize::MIN as usize)) + { + Ok(result as isize) + } else { + Err(OffsetError::IsizeOverflow) + } +} + +/// A offset that can be used with [`RawRelPtr`]. +pub trait Offset: Copy { + /// Creates a new offset between a `from` position and a `to` position. + fn between(from: usize, to: usize) -> Result; + + /// Gets the offset as an `isize`. + fn to_isize(&self) -> isize; +} + +macro_rules! impl_offset { + ($ty:ty) => { + impl Offset for $ty { + #[inline] + fn between(from: usize, to: usize) -> Result { + // pointer::add and pointer::offset require that the computed offsets cannot + // overflow an isize, which is why we're using signed_offset instead of checked_sub + // for unsized types + Self::try_from(signed_offset(from, to)?) + .map_err(|_| OffsetError::ExceedsStorageRange) + } + + #[inline] + fn to_isize(&self) -> isize { + // We're guaranteed that our offset will not exceed the the capacity of an `isize` + *self as isize + } + } + }; + (@endian $ty:ty) => { + impl Offset for Archived<$ty> { + #[inline] + fn between(from: usize, to: usize) -> Result { + // pointer::add and pointer::offset require that the computed offsets cannot + // overflow an isize, which is why we're using signed_offset instead of checked_sub + // for unsized types + <$ty>::try_from(signed_offset(from, to)?) + .map(|x| to_archived!(x)) + .map_err(|_| OffsetError::ExceedsStorageRange) + } + + #[inline] + fn to_isize(&self) -> isize { + // We're guaranteed that our offset will not exceed the the capacity of an `isize` + from_archived!(*self) as isize + } + } + }; +} + +impl_offset!(i8); +impl_offset!(@endian i16); +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +impl_offset!(@endian i32); +#[cfg(target_pointer_width = "64")] +impl_offset!(@endian i64); +impl_offset!(u8); +impl_offset!(@endian u16); +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +impl_offset!(@endian u32); +#[cfg(target_pointer_width = "64")] +impl_offset!(@endian u64); + +/// Errors that can occur while creating raw relative pointers. +#[derive(Debug)] +pub enum RelPtrError { + /// The given `from` and `to` positions for the relative pointer failed to form a valid offset. + /// + /// This is probably because the distance between them could not be represented by the offset + /// type. + OffsetError, +} + +/// An untyped pointer which resolves relative to its position in memory. +/// +/// This is the most fundamental building block in rkyv. It allows the construction and use of +/// pointers that can be safely relocated as long as the source and target are moved together. This +/// is what allows memory to be moved from disk into memory and accessed without decoding. +/// +/// Regular pointers are *absolute*, meaning that the pointee can be moved without invalidating the +/// pointer. However, the target cannot be moved or the pointer is invalidated. +/// +/// Relative pointers are *relative*, meaning that the pointee can be moved with the target without +/// invalidating the pointer. However, if either the pointee or the target move independently, the +/// pointer will be invalidated. +#[repr(transparent)] +pub struct RawRelPtr { + offset: O, + _phantom: PhantomPinned, +} + +impl RawRelPtr { + /// Attempts to create a new `RawRelPtr` in-place between the given `from` and `to` positions. + /// + /// # Safety + /// + /// - `out` must be located at position `from` + /// - `to` must be a position within the archive + #[inline] + pub unsafe fn try_emplace(from: usize, to: usize, out: *mut Self) -> Result<(), OffsetError> { + let offset = O::between(from, to)?; + ptr::addr_of_mut!((*out).offset).write(offset); + Ok(()) + } + + /// Creates a new `RawRelPtr` in-place between the given `from` and `to` positions. + /// + /// # Safety + /// + /// - `out` must be located at position `from` + /// - `to` must be a position within the archive + /// - The offset between `from` and `to` must fit in an `isize` and not exceed the offset + /// storage + #[inline] + pub unsafe fn emplace(from: usize, to: usize, out: *mut Self) { + Self::try_emplace(from, to, out).unwrap(); + } + + /// Gets the base pointer for the relative pointer. + #[inline] + pub fn base(&self) -> *const u8 { + (self as *const Self).cast::() + } + + /// Gets the mutable base pointer for the relative pointer. + #[inline] + pub fn base_mut(&mut self) -> *mut u8 { + (self as *mut Self).cast::() + } + + /// Gets the offset of the relative pointer from its base. + #[inline] + pub fn offset(&self) -> isize { + self.offset.to_isize() + } + + /// Gets whether the offset of the relative pointer is 0. + #[inline] + pub fn is_null(&self) -> bool { + self.offset() == 0 + } + + /// Calculates the memory address being pointed to by this relative pointer. + #[inline] + pub fn as_ptr(&self) -> *const () { + unsafe { self.base().offset(self.offset()).cast() } + } + + /// Returns an unsafe mutable pointer to the memory address being pointed to + /// by this relative pointer. + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut () { + unsafe { self.base_mut().offset(self.offset()).cast() } + } +} + +impl fmt::Debug for RawRelPtr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawRelPtr") + .field("offset", &self.offset) + .finish() + } +} + +impl fmt::Pointer for RawRelPtr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.as_ptr(), f) + } +} + +/// A raw relative pointer that uses an archived `i8` as the underlying offset. +pub type RawRelPtrI8 = RawRelPtr>; +/// A raw relative pointer that uses an archived `i16` as the underlying offset. +pub type RawRelPtrI16 = RawRelPtr>; +/// A raw relative pointer that uses an archived `i32` as the underlying offset. +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +pub type RawRelPtrI32 = RawRelPtr>; +/// A raw relative pointer that uses an archived `i64` as the underlying offset. +#[cfg(target_pointer_width = "64")] +pub type RawRelPtrI64 = RawRelPtr>; + +/// A raw relative pointer that uses an archived `u8` as the underlying offset. +pub type RawRelPtrU8 = RawRelPtr>; +/// A raw relative pointer that uses an archived `u16` as the underlying offset. +pub type RawRelPtrU16 = RawRelPtr>; +/// A raw relative pointer that uses an archived `u32` as the underlying offset. +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +pub type RawRelPtrU32 = RawRelPtr>; +/// A raw relative pointer that uses an archived `u64` as the underlying offset. +#[cfg(target_pointer_width = "64")] +pub type RawRelPtrU64 = RawRelPtr>; + +// TODO: implement for NonZero types + +/// A pointer which resolves to relative to its position in memory. +/// +/// This is a strongly-typed version of [`RawRelPtr`]. +/// +/// See [`Archive`](crate::Archive) for an example of creating one. +pub struct RelPtr { + raw_ptr: RawRelPtr, + metadata: T::ArchivedMetadata, + _phantom: PhantomData, +} + +impl RelPtr { + /// Attempts to create a relative pointer from one position to another. + /// + /// # Safety + /// + /// - `from` must be the position of `out` within the archive + /// - `to` must be the position of some valid `T` + #[inline] + pub unsafe fn try_emplace(from: usize, to: usize, out: *mut Self) -> Result<(), OffsetError> { + let (fp, fo) = out_field!(out.raw_ptr); + // Skip metadata since sized T is guaranteed to be () + RawRelPtr::try_emplace(from + fp, to, fo) + } + + /// Creates a relative pointer from one position to another. + /// + /// # Panics + /// + /// - The offset between `from` and `to` does not fit in an `isize` + /// - The offset between `from` and `to` exceeds the offset storage + /// + /// # Safety + /// + /// - `from` must be the position of `out` within the archive + /// - `to` must be the position of some valid `T` + #[inline] + pub unsafe fn emplace(from: usize, to: usize, out: *mut Self) { + Self::try_emplace(from, to, out).unwrap(); + } +} + +impl RelPtr +where + T::ArchivedMetadata: Default, +{ + /// Attempts to create a null relative pointer with default metadata. + /// + /// # Safety + /// + /// `pos` must be the position of `out` within the archive. + #[inline] + pub unsafe fn try_emplace_null(pos: usize, out: *mut Self) -> Result<(), OffsetError> { + let (fp, fo) = out_field!(out.raw_ptr); + RawRelPtr::try_emplace(pos + fp, pos, fo)?; + let (_, fo) = out_field!(out.metadata); + fo.write(Default::default()); + Ok(()) + } + + /// Creates a null relative pointer with default metadata. + /// + /// # Panics + /// + /// - An offset of `0` does not fit in an `isize` + /// - An offset of `0` exceeds the offset storage + /// + /// # Safety + /// + /// `pos` must be the position of `out` within the archive. + #[inline] + pub unsafe fn emplace_null(pos: usize, out: *mut Self) { + Self::try_emplace_null(pos, out).unwrap() + } +} + +impl RelPtr { + /// Attempts to create a relative pointer from one position to another. + /// + /// # Safety + /// + /// - `from` must be the position of `out` within the archive + /// - `to` must be the position of some valid `T` + /// - `value` must be the value being serialized + /// - `metadata_resolver` must be the result of serializing the metadata of `value` + #[inline] + pub unsafe fn try_resolve_emplace + ?Sized>( + from: usize, + to: usize, + value: &U, + metadata_resolver: U::MetadataResolver, + out: *mut Self, + ) -> Result<(), OffsetError> { + let (fp, fo) = out_field!(out.raw_ptr); + RawRelPtr::try_emplace(from + fp, to, fo)?; + let (fp, fo) = out_field!(out.metadata); + value.resolve_metadata(from + fp, metadata_resolver, fo); + Ok(()) + } + + /// Creates a relative pointer from one position to another. + /// + /// # Panics + /// + /// - The offset between `from` and `to` does not fit in an `isize` + /// - The offset between `from` and `to` exceeds the offset storage + /// + /// # Safety + /// + /// - `from` must be the position of `out` within the archive + /// - `to` must be the position of some valid `T` + /// - `value` must be the value being serialized + /// - `metadata_resolver` must be the result of serializing the metadata of `value` + #[inline] + pub unsafe fn resolve_emplace + ?Sized>( + from: usize, + to: usize, + value: &U, + metadata_resolver: U::MetadataResolver, + out: *mut Self, + ) { + Self::try_resolve_emplace(from, to, value, metadata_resolver, out).unwrap(); + } + + /// Attempts to create a relative pointer from one position to another given + /// raw pointer metadata. + /// + /// This does the same thing as [`RelPtr::try_resolve_emplace`] but you must supply + /// the [`::ArchivedMetadata`][ArchivePointee::ArchivedMetadata] + /// yourself directly rather than through an implementation of [`ArchiveUnsized`] on some + /// value. + /// + /// # Safety + /// + /// - `from` must be the position of `out` within the archive + /// - `to` must be the position of some valid `T` + /// - `value` must be the value being serialized + /// - `archived_metadata` must produce valid metadata for the pointee of the resulting + /// `RelPtr` (the thing being pointed at) when [`::pointer_metadata(archived_metadata)`][ArchivePointee::pointer_metadata] + /// is called. + pub unsafe fn try_resolve_emplace_from_raw_parts( + from: usize, + to: usize, + archived_metadata: ::ArchivedMetadata, + out: *mut Self, + ) -> Result<(), OffsetError> { + let (fp, fo) = out_field!(out.raw_ptr); + RawRelPtr::try_emplace(from + fp, to, fo)?; + let (_fp, fo) = out_field!(out.metadata); + *fo = archived_metadata; + Ok(()) + } + + /// Creates a relative pointer from one position to another given + /// raw pointer metadata. + /// + /// This does the same thing as [`RelPtr::resolve_emplace`] but you must supply + /// the [`::ArchivedMetadata`][ArchivePointee::ArchivedMetadata] + /// yourself directly rather than through an implementation of [`ArchiveUnsized`] on some + /// value. + /// + /// # Panics + /// + /// - The offset between `from` and `to` does not fit in an `isize` + /// - The offset between `from` and `to` exceeds the offset storage + /// + /// # Safety + /// + /// - `from` must be the position of `out` within the archive + /// - `to` must be the position of some valid `T` + /// - `value` must be the value being serialized + /// - `archived_metadata` must produce valid metadata for the pointee of the resulting + /// `RelPtr` (the thing being pointed at) when [`::pointer_metadata(archived_metadata)`][ArchivePointee::pointer_metadata] + /// is called. + pub unsafe fn resolve_emplace_from_raw_parts( + from: usize, + to: usize, + archived_metadata: ::ArchivedMetadata, + out: *mut Self, + ) { + Self::try_resolve_emplace_from_raw_parts(from, to, archived_metadata, out).unwrap(); + } + + /// Gets the base pointer for the relative pointer. + #[inline] + pub fn base(&self) -> *const u8 { + self.raw_ptr.base() + } + + /// Gets the mutable base pointer for the relative pointer. + #[inline] + pub fn base_mut(&mut self) -> *mut u8 { + self.raw_ptr.base_mut() + } + + /// Gets the offset of the relative pointer from its base. + #[inline] + pub fn offset(&self) -> isize { + self.raw_ptr.offset() + } + + /// Gets whether the offset of the relative pointer is 0. + #[inline] + pub fn is_null(&self) -> bool { + self.raw_ptr.is_null() + } + + /// Gets the metadata of the relative pointer. + #[inline] + pub fn metadata(&self) -> &T::ArchivedMetadata { + &self.metadata + } + + /// Calculates the memory address being pointed to by this relative pointer. + #[inline] + pub fn as_ptr(&self) -> *const T { + ptr_meta::from_raw_parts(self.raw_ptr.as_ptr(), T::pointer_metadata(&self.metadata)) + } + + /// Returns an unsafe mutable pointer to the memory address being pointed to by this relative + /// pointer. + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + ptr_meta::from_raw_parts_mut( + self.raw_ptr.as_mut_ptr(), + T::pointer_metadata(&self.metadata), + ) + } +} + +impl fmt::Debug for RelPtr +where + T::ArchivedMetadata: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RelPtr") + .field("raw_ptr", &self.raw_ptr) + .field("metadata", &self.metadata) + .finish() + } +} + +impl fmt::Pointer for RelPtr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.as_ptr(), f) + } +} diff --git a/src/rel_ptr/validation.rs b/src/rel_ptr/validation.rs new file mode 100644 index 0000000..27a897a --- /dev/null +++ b/src/rel_ptr/validation.rs @@ -0,0 +1,62 @@ +//! Validation implementations for relative pointers + +use crate::{ + rel_ptr::{Offset, RawRelPtr, RelPtr}, + ArchivePointee, Fallible, +}; +use bytecheck::CheckBytes; +use core::{ + convert::Infallible, + marker::{PhantomData, PhantomPinned}, + ptr, +}; + +impl RawRelPtr { + /// Checks the bytes of the given raw relative pointer. + /// + /// This is done rather than implementing `CheckBytes` to force users to manually write their + /// `CheckBytes` implementation since they need to also provide the ownership model of their + /// memory. + /// + /// # Safety + /// + /// The given pointer must be aligned and point to enough bytes to represent a `RawRelPtr`. + #[inline] + pub unsafe fn manual_check_bytes<'a, C: Fallible + ?Sized>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Infallible> + where + O: CheckBytes, + { + O::check_bytes(ptr::addr_of!((*value).offset), context).unwrap(); + PhantomPinned::check_bytes(ptr::addr_of!((*value)._phantom), context).unwrap(); + Ok(&*value) + } +} + +impl RelPtr { + /// Checks the bytes of the given relative pointer. + /// + /// This is done rather than implementing `CheckBytes` to force users to manually write their + /// `CheckBytes` implementation since they need to also provide the ownership model of their + /// memory. + /// + /// # Safety + /// + /// The given pointer must be aligned and point to enough bytes to represent a `RelPtr`. + #[inline] + pub unsafe fn manual_check_bytes<'a, C: Fallible + ?Sized>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, >::Error> + where + O: CheckBytes, + T::ArchivedMetadata: CheckBytes, + { + RawRelPtr::manual_check_bytes(ptr::addr_of!((*value).raw_ptr), context).unwrap(); + T::ArchivedMetadata::check_bytes(ptr::addr_of!((*value).metadata), context)?; + PhantomData::::check_bytes(ptr::addr_of!((*value)._phantom), context).unwrap(); + Ok(&*value) + } +} diff --git a/src/result.rs b/src/result.rs new file mode 100644 index 0000000..e427cb9 --- /dev/null +++ b/src/result.rs @@ -0,0 +1,208 @@ +//! An archived version of `Result`. + +use core::{ + cmp::{Ord, Ordering, PartialOrd}, + hash, mem, + ops::{Deref, DerefMut}, +}; + +/// An archived [`Result`] that represents either success ([`Ok`](ArchivedResult::Ok)) or failure +/// ([`Err`](ArchivedResult::Err)). +#[derive(Debug)] +#[cfg_attr(feature = "validation", derive(bytecheck::CheckBytes))] +#[repr(u8)] +pub enum ArchivedResult { + /// Contains the success value + Ok(T), + /// Contains the error value + Err(E), +} + +impl ArchivedResult { + /// Returns `true` if the result is [`Ok`](ArchivedResult::Ok). + #[inline] + pub const fn is_ok(&self) -> bool { + matches!(self, ArchivedResult::Ok(_)) + } + + /// Returns `true` if the result is [`Err`](ArchivedResult::Err). + #[inline] + pub const fn is_err(&self) -> bool { + matches!(self, ArchivedResult::Err(_)) + } + + /// Returns a `Result` containing the success and error values of this `ArchivedResult`. + #[inline] + pub fn as_ref(&self) -> Result<&T, &E> { + match self { + ArchivedResult::Ok(value) => Ok(value), + ArchivedResult::Err(err) => Err(err), + } + } + + /// Converts from `&mut ArchivedResult` to `Result<&mut T, &mut E>`. + #[inline] + pub fn as_mut(&mut self) -> Result<&mut T, &mut E> { + match self { + ArchivedResult::Ok(value) => Ok(value), + ArchivedResult::Err(err) => Err(err), + } + } + + /// Returns an iterator over the possibly contained value. + /// + /// The iterator yields one value if the result is `ArchivedResult::Ok`, otherwise none. + #[inline] + pub fn iter(&self) -> Iter<'_, T> { + Iter { + inner: self.as_ref().ok(), + } + } + + /// Returns a mutable iterator over the possibly contained value. + /// + /// The iterator yields one value if the result is `ArchivedResult::Ok`, otherwise none. + #[inline] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { + inner: self.as_mut().ok(), + } + } +} + +impl ArchivedResult { + /// Converts from `&ArchivedResult` to `Result<&::Target, &E>`. + /// + /// Coerces the `Ok` variant of the original `ArchivedResult` via `Deref` and returns the new + /// `Result`. + #[inline] + pub fn as_deref(&self) -> Result<&::Target, &E> { + match self { + ArchivedResult::Ok(value) => Ok(value.deref()), + ArchivedResult::Err(err) => Err(err), + } + } +} + +impl ArchivedResult { + /// Converts from `&mut ArchivedResult` to `Result<&mut ::Target, &mut E>`. + /// + /// Coerces the `Ok` variant of the original `ArchivedResult` via `DerefMut` and returns the new + /// `Result`. + #[inline] + pub fn as_deref_mut(&mut self) -> Result<&mut ::Target, &mut E> { + match self { + ArchivedResult::Ok(value) => Ok(value.deref_mut()), + ArchivedResult::Err(err) => Err(err), + } + } +} + +/// An iterator over a reference to the `Ok` variant of an [`ArchivedResult`]. +/// +/// The iterator yields one value if the result is `Ok`, otherwise none. +/// +/// Created by [`ArchivedResult::iter`]. +pub struct Iter<'a, T> { + inner: Option<&'a T>, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + let mut result = None; + mem::swap(&mut self.inner, &mut result); + result + } +} + +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + fn next_back(&mut self) -> Option { + self.next() + } +} + +/// An iterator over a mutable reference to the `Ok` variant of an [`ArchivedResult`]. +/// +/// The iterator yields one value if the result is `Ok`, otherwise none. +/// +/// Created by [`ArchivedResult::iter_mut`]. +pub struct IterMut<'a, T> { + inner: Option<&'a mut T>, +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + let mut result = None; + mem::swap(&mut self.inner, &mut result); + result + } +} + +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + fn next_back(&mut self) -> Option { + self.next() + } +} + +impl Eq for ArchivedResult {} + +impl hash::Hash for ArchivedResult { + #[inline] + fn hash(&self, state: &mut H) { + self.as_ref().hash(state) + } +} + +impl Ord for ArchivedResult { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.as_ref().cmp(&other.as_ref()) + } +} + +impl PartialEq for ArchivedResult { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_ref().eq(&other.as_ref()) + } +} + +impl PartialOrd for ArchivedResult { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.as_ref().partial_cmp(&other.as_ref()) + } +} + +impl, E, F: PartialEq> PartialEq> for ArchivedResult { + #[inline] + fn eq(&self, other: &Result) -> bool { + match self { + ArchivedResult::Ok(self_value) => { + if let Ok(other_value) = other { + self_value.eq(other_value) + } else { + false + } + } + ArchivedResult::Err(self_err) => { + if let Err(other_err) = other { + self_err.eq(other_err) + } else { + false + } + } + } + } +} + +impl, U, E: PartialEq, F> PartialEq> for Result { + #[inline] + fn eq(&self, other: &ArchivedResult) -> bool { + other.eq(self) + } +} diff --git a/src/ser/mod.rs b/src/ser/mod.rs new file mode 100644 index 0000000..cae71f0 --- /dev/null +++ b/src/ser/mod.rs @@ -0,0 +1,188 @@ +//! Serialization traits, serializers, and adapters. + +pub mod serializers; + +use crate::{Archive, ArchiveUnsized, Fallible, RelPtr, Serialize, SerializeUnsized}; +use core::{alloc::Layout, mem, ptr::NonNull, slice}; + +/// A byte sink that knows where it is. +/// +/// A type that is [`io::Write`](std::io::Write) can be wrapped in a +/// [`WriteSerializer`](serializers::WriteSerializer) to equip it with `Serializer`. +/// +/// It's important that the memory for archived objects is properly aligned before attempting to +/// read objects out of it; use an [`AlignedVec`](crate::AlignedVec) or the +/// [`AlignedBytes`](crate::AlignedBytes) wrappers if they are appropriate. +pub trait Serializer: Fallible { + /// Returns the current position of the serializer. + fn pos(&self) -> usize; + + /// Attempts to write the given bytes to the serializer. + fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error>; + + /// Advances the given number of bytes as padding. + #[inline] + fn pad(&mut self, padding: usize) -> Result<(), Self::Error> { + const MAX_ZEROES: usize = 32; + const ZEROES: [u8; MAX_ZEROES] = [0; MAX_ZEROES]; + debug_assert!(padding < MAX_ZEROES); + + self.write(&ZEROES[0..padding]) + } + + /// Aligns the position of the serializer to the given alignment. + #[inline] + fn align(&mut self, align: usize) -> Result { + let mask = align - 1; + debug_assert_eq!(align & mask, 0); + + self.pad((align - (self.pos() & mask)) & mask)?; + Ok(self.pos()) + } + + /// Aligns the position of the serializer to be suitable to write the given type. + #[inline] + fn align_for(&mut self) -> Result { + self.align(mem::align_of::()) + } + + /// Resolves the given value with its resolver and writes the archived type. + /// + /// Returns the position of the written archived type. + /// + /// # Safety + /// + /// - `resolver` must be the result of serializing `value` + /// - The serializer must be aligned for a `T::Archived` + unsafe fn resolve_aligned( + &mut self, + value: &T, + resolver: T::Resolver, + ) -> Result { + let pos = self.pos(); + debug_assert_eq!(pos & (mem::align_of::() - 1), 0); + + let mut resolved = mem::MaybeUninit::::uninit(); + resolved.as_mut_ptr().write_bytes(0, 1); + value.resolve(pos, resolver, resolved.as_mut_ptr()); + + let data = resolved.as_ptr().cast::(); + let len = mem::size_of::(); + self.write(slice::from_raw_parts(data, len))?; + Ok(pos) + } + + /// Archives the given object and returns the position it was archived at. + #[inline] + fn serialize_value>(&mut self, value: &T) -> Result { + let resolver = value.serialize(self)?; + self.align_for::()?; + unsafe { self.resolve_aligned(value, resolver) } + } + + /// Resolves the given reference with its resolver and writes the archived reference. + /// + /// Returns the position of the written archived `RelPtr`. + /// + /// # Safety + /// + /// - `metadata_resolver` must be the result of serializing the metadata of `value` + /// - `to` must be the position of the serialized `value` within the archive + /// - The serializer must be aligned for a `RelPtr` + unsafe fn resolve_unsized_aligned( + &mut self, + value: &T, + to: usize, + metadata_resolver: T::MetadataResolver, + ) -> Result { + let from = self.pos(); + debug_assert_eq!(from & (mem::align_of::>() - 1), 0); + + let mut resolved = mem::MaybeUninit::>::uninit(); + resolved.as_mut_ptr().write_bytes(0, 1); + value.resolve_unsized(from, to, metadata_resolver, resolved.as_mut_ptr()); + + let data = resolved.as_ptr().cast::(); + let len = mem::size_of::>(); + self.write(slice::from_raw_parts(data, len))?; + Ok(from) + } + + /// Archives a reference to the given object and returns the position it was archived at. + #[inline] + fn serialize_unsized_value + ?Sized>( + &mut self, + value: &T, + ) -> Result { + let to = value.serialize_unsized(self)?; + let metadata_resolver = value.serialize_metadata(self)?; + self.align_for::>()?; + unsafe { self.resolve_unsized_aligned(value, to, metadata_resolver) } + } +} + +// Someday this can probably be replaced with alloc::Allocator + +/// A serializer that can allocate scratch space. +pub trait ScratchSpace: Fallible { + /// Allocates scratch space of the requested size. + /// + /// # Safety + /// + /// `layout` must have non-zero size. + unsafe fn push_scratch(&mut self, layout: Layout) -> Result, Self::Error>; + + /// Deallocates previously allocated scratch space. + /// + /// # Safety + /// + /// - `ptr` must be the scratch memory last allocated with `push_scratch`. + /// - `layout` must be the same layout that was used to allocate that block of memory. + unsafe fn pop_scratch(&mut self, ptr: NonNull, layout: Layout) -> Result<(), Self::Error>; +} + +/// A registry that tracks serialized shared memory. +/// +/// This trait is required to serialize shared pointers. +pub trait SharedSerializeRegistry: Fallible { + /// Gets the position of a previously-added shared pointer. + /// + /// Returns `None` if the pointer has not yet been added. + fn get_shared_ptr(&self, value: *const u8) -> Option; + + /// Gets the position of a previously-added shared value. + /// + /// Returns `None` if the value has not yet been added. + #[inline] + fn get_shared(&self, value: &T) -> Option { + self.get_shared_ptr(value as *const T as *const u8) + } + + /// Adds the position of a shared pointer to the registry. + fn add_shared_ptr(&mut self, value: *const u8, pos: usize) -> Result<(), Self::Error>; + + /// Adds the position of a shared value to the registry. + #[inline] + fn add_shared(&mut self, value: &T, pos: usize) -> Result<(), Self::Error> { + self.add_shared_ptr(value as *const T as *const u8, pos) + } + + /// Archives the given shared value and returns its position. If the value has already been + /// added then it returns the position of the previously added value. + #[inline] + fn serialize_shared + ?Sized>( + &mut self, + value: &T, + ) -> Result + where + Self: Serializer, + { + if let Some(pos) = self.get_shared(value) { + Ok(pos) + } else { + let pos = value.serialize_unsized(self)?; + self.add_shared(value, pos)?; + Ok(pos) + } + } +} diff --git a/src/ser/serializers/alloc.rs b/src/ser/serializers/alloc.rs new file mode 100644 index 0000000..acb959d --- /dev/null +++ b/src/ser/serializers/alloc.rs @@ -0,0 +1,407 @@ +use crate::{ + ser::{serializers::BufferScratch, ScratchSpace, Serializer, SharedSerializeRegistry}, + AlignedBytes, AlignedVec, Archive, ArchiveUnsized, Fallible, RelPtr, +}; +#[cfg(not(feature = "std"))] +use ::alloc::{alloc, boxed::Box, vec::Vec}; +#[cfg(feature = "std")] +use ::std::alloc; +use core::{ + alloc::Layout, + borrow::{Borrow, BorrowMut}, + convert::Infallible, + fmt, mem, + ptr::NonNull, +}; +#[cfg(not(feature = "std"))] +use hashbrown::hash_map; +#[cfg(feature = "std")] +use std::collections::hash_map; + +/// A serializer made specifically to work with [`AlignedVec`](crate::util::AlignedVec). +/// +/// This serializer makes it easier for the compiler to perform emplacement optimizations and may +/// give better performance than a basic `WriteSerializer`. +#[derive(Debug)] +pub struct AlignedSerializer { + inner: A, +} + +impl> AlignedSerializer { + /// Creates a new `AlignedSerializer` by wrapping a `Borrow`. + #[inline] + pub fn new(inner: A) -> Self { + Self { inner } + } + + /// Consumes the serializer and returns the underlying type. + #[inline] + pub fn into_inner(self) -> A { + self.inner + } +} + +impl Default for AlignedSerializer { + #[inline] + fn default() -> Self { + Self { + inner: A::default(), + } + } +} + +impl Fallible for AlignedSerializer { + type Error = Infallible; +} + +impl + BorrowMut> Serializer for AlignedSerializer { + #[inline] + fn pos(&self) -> usize { + self.inner.borrow().len() + } + + #[inline] + fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error> { + self.inner.borrow_mut().extend_from_slice(bytes); + Ok(()) + } + + #[inline] + unsafe fn resolve_aligned( + &mut self, + value: &T, + resolver: T::Resolver, + ) -> Result { + let pos = self.pos(); + debug_assert_eq!(pos & (mem::align_of::() - 1), 0); + let vec = self.inner.borrow_mut(); + let additional = mem::size_of::(); + vec.reserve(additional); + vec.set_len(vec.len() + additional); + + let ptr = vec.as_mut_ptr().add(pos).cast::(); + ptr.write_bytes(0, 1); + value.resolve(pos, resolver, ptr); + + Ok(pos) + } + + #[inline] + unsafe fn resolve_unsized_aligned( + &mut self, + value: &T, + to: usize, + metadata_resolver: T::MetadataResolver, + ) -> Result { + let from = self.pos(); + debug_assert_eq!(from & (mem::align_of::>() - 1), 0); + let vec = self.inner.borrow_mut(); + let additional = mem::size_of::>(); + vec.reserve(additional); + vec.set_len(vec.len() + additional); + + let ptr = vec.as_mut_ptr().add(from).cast::>(); + ptr.write_bytes(0, 1); + + value.resolve_unsized(from, to, metadata_resolver, ptr); + Ok(from) + } +} + +/// Fixed-size scratch space allocated on the heap. +#[derive(Debug)] +pub struct HeapScratch { + inner: BufferScratch>>, +} + +impl HeapScratch { + /// Creates a new heap scratch space. + pub fn new() -> Self { + if N != 0 { + unsafe { + let layout = Layout::new::>(); + let ptr = alloc::alloc_zeroed(layout).cast::>(); + assert!(!ptr.is_null()); + let buf = Box::from_raw(ptr); + Self { + inner: BufferScratch::new(buf), + } + } + } else { + Self { + inner: BufferScratch::new(Box::default()), + } + } + } + + /// Gets the memory layout of the heap-allocated space. + pub fn layout() -> Layout { + unsafe { Layout::from_size_align_unchecked(N, 1) } + } +} + +impl Default for HeapScratch { + fn default() -> Self { + Self::new() + } +} + +impl Fallible for HeapScratch { + type Error = > as Fallible>::Error; +} + +impl ScratchSpace for HeapScratch { + #[inline] + unsafe fn push_scratch(&mut self, layout: Layout) -> Result, Self::Error> { + self.inner.push_scratch(layout) + } + + #[inline] + unsafe fn pop_scratch(&mut self, ptr: NonNull, layout: Layout) -> Result<(), Self::Error> { + self.inner.pop_scratch(ptr, layout) + } +} + +/// Errors that can occur when allocating with the global allocator. +#[derive(Debug)] +pub enum AllocScratchError { + /// The amount of scratch space requested exceeded the maximum limit + ExceededLimit { + /// The amount of scratch space requested + requested: usize, + /// The amount of scratch space remaining + remaining: usize, + }, + /// Scratch space was not popped in reverse order. + NotPoppedInReverseOrder { + /// The pointer of the allocation that was expected to be next + expected: *mut u8, + /// The layout of the allocation that was expected to be next + expected_layout: Layout, + /// The pointer that was popped instead + actual: *mut u8, + /// The layout of the pointer that was popped instead + actual_layout: Layout, + }, + /// There are no allocations to pop + NoAllocationsToPop, +} + +// SAFETY: AllocScratchError is safe to send to another thread +// This trait is not automatically implemented because the enum contains a pointer +unsafe impl Send for AllocScratchError {} + +// SAFETY: AllocScratchError is safe to share between threads +// This trait is not automatically implemented because the enum contains a pointer +unsafe impl Sync for AllocScratchError {} + +impl fmt::Display for AllocScratchError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ExceededLimit { requested, remaining } => write!( + f, + "exceeded the maximum limit of scratch space: requested {}, remaining {}", + requested, remaining + ), + Self::NotPoppedInReverseOrder { + expected, + expected_layout, + actual, + actual_layout, + } => write!( + f, + "scratch space was not popped in reverse order: expected {:p} with size {} and align {}, found {:p} with size {} and align {}", + expected, expected_layout.size(), expected_layout.align(), actual, actual_layout.size(), actual_layout.align() + ), + Self::NoAllocationsToPop => write!( + f, "attempted to pop scratch space but there were no allocations to pop" + ), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for AllocScratchError {} +}; + +/// Scratch space that always uses the global allocator. +/// +/// This allocator will panic if scratch is popped that it did not allocate. For this reason, it +/// should only ever be used as a fallback allocator. +#[derive(Debug)] +pub struct AllocScratch { + remaining: Option, + allocations: Vec<(*mut u8, Layout)>, +} + +// SAFETY: AllocScratch is safe to send to another thread +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Send for AllocScratch {} + +// SAFETY: AllocScratch is safe to share between threads +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Sync for AllocScratch {} + +impl AllocScratch { + /// Creates a new scratch allocator with no allocation limit. + pub fn new() -> Self { + Self { + remaining: None, + allocations: Vec::new(), + } + } + + /// Creates a new scratch allocator with the given allocation limit. + pub fn with_limit(limit: usize) -> Self { + Self { + remaining: Some(limit), + allocations: Vec::new(), + } + } +} + +impl Drop for AllocScratch { + fn drop(&mut self) { + for (ptr, layout) in self.allocations.drain(..).rev() { + unsafe { + alloc::dealloc(ptr, layout); + } + } + } +} + +impl Default for AllocScratch { + fn default() -> Self { + Self::new() + } +} + +impl Fallible for AllocScratch { + type Error = AllocScratchError; +} + +impl ScratchSpace for AllocScratch { + #[inline] + unsafe fn push_scratch(&mut self, layout: Layout) -> Result, Self::Error> { + if let Some(remaining) = self.remaining { + if remaining < layout.size() { + return Err(AllocScratchError::ExceededLimit { + requested: layout.size(), + remaining, + }); + } + } + let result_ptr = alloc::alloc(layout); + assert!(!result_ptr.is_null()); + self.allocations.push((result_ptr, layout)); + let result_slice = ptr_meta::from_raw_parts_mut(result_ptr.cast(), layout.size()); + let result = NonNull::new_unchecked(result_slice); + Ok(result) + } + + #[inline] + unsafe fn pop_scratch(&mut self, ptr: NonNull, layout: Layout) -> Result<(), Self::Error> { + if let Some(&(last_ptr, last_layout)) = self.allocations.last() { + if ptr.as_ptr() == last_ptr && layout == last_layout { + alloc::dealloc(ptr.as_ptr(), layout); + self.allocations.pop(); + Ok(()) + } else { + Err(AllocScratchError::NotPoppedInReverseOrder { + expected: last_ptr, + expected_layout: last_layout, + actual: ptr.as_ptr(), + actual_layout: layout, + }) + } + } else { + Err(AllocScratchError::NoAllocationsToPop) + } + } +} + +/// An error that can occur while serializing shared pointers. +#[derive(Debug)] +pub enum SharedSerializeMapError { + /// A shared pointer was added multiple times + DuplicateSharedPointer(*const u8), +} + +// SAFETY: SharedSerializeMapError is safe to send to another thread +// This trait is not automatically implemented because the enum contains a pointer +unsafe impl Send for SharedSerializeMapError {} + +// SAFETY: SharedSerializeMapError is safe to share between threads +// This trait is not automatically implemented because the enum contains a pointer +unsafe impl Sync for SharedSerializeMapError {} + +impl fmt::Display for SharedSerializeMapError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::DuplicateSharedPointer(p) => write!(f, "duplicate shared pointer: {:p}", p), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for SharedSerializeMapError {} +}; + +/// An adapter that adds shared serialization support to a serializer. +#[derive(Debug)] +pub struct SharedSerializeMap { + shared_resolvers: hash_map::HashMap<*const u8, usize>, +} + +// SAFETY: SharedSerializeMap is safe to send to another thread +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Send for SharedSerializeMap {} + +// SAFETY: SharedSerializeMap is safe to share between threads +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Sync for SharedSerializeMap {} + +impl SharedSerializeMap { + /// Creates a new shared registry map. + #[inline] + pub fn new() -> Self { + Self { + shared_resolvers: hash_map::HashMap::new(), + } + } +} + +impl Default for SharedSerializeMap { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Fallible for SharedSerializeMap { + type Error = SharedSerializeMapError; +} + +impl SharedSerializeRegistry for SharedSerializeMap { + fn get_shared_ptr(&self, value: *const u8) -> Option { + self.shared_resolvers.get(&value).copied() + } + + fn add_shared_ptr(&mut self, value: *const u8, pos: usize) -> Result<(), Self::Error> { + match self.shared_resolvers.entry(value) { + hash_map::Entry::Occupied(_) => { + Err(SharedSerializeMapError::DuplicateSharedPointer(value)) + } + hash_map::Entry::Vacant(e) => { + e.insert(pos); + Ok(()) + } + } + } +} diff --git a/src/ser/serializers/core.rs b/src/ser/serializers/core.rs new file mode 100644 index 0000000..adec592 --- /dev/null +++ b/src/ser/serializers/core.rs @@ -0,0 +1,422 @@ +use crate::{ + ser::{ScratchSpace, Serializer}, + Fallible, +}; +use core::{ + alloc::Layout, + fmt, + ops::DerefMut, + ptr::{copy_nonoverlapping, NonNull}, +}; + +/// The error type returned by an [`BufferSerializer`]. +#[derive(Debug)] +pub enum BufferSerializerError { + /// Writing has overflowed the internal buffer. + Overflow { + /// The position of the serializer + pos: usize, + /// The number of bytes needed + bytes_needed: usize, + /// The total length of the archive + archive_len: usize, + }, +} + +impl fmt::Display for BufferSerializerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Overflow { + pos, + bytes_needed, + archive_len, + } => write!( + f, + "writing has overflowed the serializer buffer: pos {}, needed {}, total length {}", + pos, bytes_needed, archive_len + ), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for BufferSerializerError {} +}; + +/// Wraps a byte buffer and equips it with [`Serializer`]. +/// +/// Common uses include archiving in `#![no_std]` environments and archiving small objects without +/// allocating. +/// +/// # Examples +/// ``` +/// use rkyv::{ +/// archived_value, +/// ser::{Serializer, serializers::BufferSerializer}, +/// AlignedBytes, +/// AlignedVec, +/// Archive, +/// Archived, +/// Serialize, +/// }; +/// +/// #[derive(Archive, Serialize)] +/// enum Event { +/// Spawn, +/// Speak(String), +/// Die, +/// } +/// +/// let mut serializer = BufferSerializer::new(AlignedBytes([0u8; 256])); +/// let pos = serializer.serialize_value(&Event::Speak("Help me!".to_string())) +/// .expect("failed to archive event"); +/// let buf = serializer.into_inner(); +/// let archived = unsafe { archived_value::(buf.as_ref(), pos) }; +/// if let Archived::::Speak(message) = archived { +/// assert_eq!(message.as_str(), "Help me!"); +/// } else { +/// panic!("archived event was of the wrong type"); +/// } +/// ``` +#[derive(Debug)] +pub struct BufferSerializer { + inner: T, + pos: usize, +} + +impl BufferSerializer { + /// Creates a new archive buffer from a byte buffer. + #[inline] + pub fn new(inner: T) -> Self { + Self::with_pos(inner, 0) + } + + /// Creates a new archive buffer from a byte buffer. The buffer will start writing at the given + /// position, but the buffer must contain all bytes (otherwise the alignments of types may not + /// be correct). + #[inline] + pub fn with_pos(inner: T, pos: usize) -> Self { + Self { inner, pos } + } + + /// Consumes the serializer and returns the underlying type. + #[inline] + pub fn into_inner(self) -> T { + self.inner + } +} + +impl Default for BufferSerializer { + #[inline] + fn default() -> Self { + Self::new(T::default()) + } +} + +impl Fallible for BufferSerializer { + type Error = BufferSerializerError; +} + +impl> Serializer for BufferSerializer { + #[inline] + fn pos(&self) -> usize { + self.pos + } + + fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error> { + let end_pos = self.pos + bytes.len(); + let archive_len = self.inner.as_mut().len(); + if end_pos > archive_len { + Err(BufferSerializerError::Overflow { + pos: self.pos, + bytes_needed: bytes.len(), + archive_len, + }) + } else { + unsafe { + copy_nonoverlapping( + bytes.as_ptr(), + self.inner.as_mut().as_mut_ptr().add(self.pos), + bytes.len(), + ); + } + self.pos = end_pos; + Ok(()) + } + } +} + +/// Errors that can occur when using a fixed-size allocator. +/// +/// Pairing a fixed-size allocator with a fallback allocator can help prevent running out of scratch +/// space unexpectedly. +#[derive(Debug)] +pub enum FixedSizeScratchError { + /// The allocator ran out of scratch space. + OutOfScratch(Layout), + /// Scratch space was not popped in reverse order. + NotPoppedInReverseOrder { + /// The current position of the start of free memory + pos: usize, + /// The next position according to the erroneous pop + next_pos: usize, + /// The size of the memory according to the erroneous pop + next_size: usize, + }, + /// The given allocation did not belong to the scratch allocator. + UnownedAllocation, +} + +impl fmt::Display for FixedSizeScratchError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::OutOfScratch(layout) => write!( + f, + "out of scratch: requested scratch space with size {} and align {}", + layout.size(), + layout.align() + ), + Self::NotPoppedInReverseOrder { + pos, + next_pos, + next_size, + } => write!( + f, + "scratch space was not popped in reverse order: pos {}, next pos {}, next size {}", + pos, next_pos, next_size + ), + Self::UnownedAllocation => write!(f, "unowned allocation"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for FixedSizeScratchError {} + +/// Scratch space that allocates within a buffer. +#[derive(Debug)] +pub struct BufferScratch { + buffer: T, + pos: usize, + // TODO: Compute this pointer eagerly in a future version of rkyv. + ptr: Option>, +} + +unsafe impl Send for BufferScratch where T: Send {} +unsafe impl Sync for BufferScratch where T: Sync {} + +impl BufferScratch { + /// Creates a new buffer scratch allocator. + pub fn new(buffer: T) -> Self { + Self { + buffer, + pos: 0, + ptr: None, + } + } + + /// Resets the scratch space to its initial state. + pub fn clear(&mut self) { + self.pos = 0; + } + + /// Consumes the buffer scratch allocator, returning the underlying buffer. + pub fn into_inner(self) -> T { + self.buffer + } +} + +impl Default for BufferScratch { + fn default() -> Self { + Self::new(T::default()) + } +} + +impl Fallible for BufferScratch { + type Error = FixedSizeScratchError; +} + +impl, U: AsMut<[u8]>> ScratchSpace for BufferScratch { + #[inline] + unsafe fn push_scratch(&mut self, layout: Layout) -> Result, Self::Error> { + if self.ptr.is_none() { + self.ptr = Some(NonNull::from(self.buffer.as_mut())); + } + let bytes = self.ptr.unwrap().as_ptr(); + + let start = bytes.cast::().add(self.pos); + let pad = match (start as usize) & (layout.align() - 1) { + 0 => 0, + x => layout.align() - x, + }; + if pad + layout.size() <= ptr_meta::metadata(bytes) - self.pos { + self.pos += pad; + let result_slice = ptr_meta::from_raw_parts_mut( + bytes.cast::().add(self.pos).cast(), + layout.size(), + ); + let result = NonNull::new_unchecked(result_slice); + self.pos += layout.size(); + Ok(result) + } else { + Err(FixedSizeScratchError::OutOfScratch(layout)) + } + } + + #[inline] + unsafe fn pop_scratch(&mut self, ptr: NonNull, layout: Layout) -> Result<(), Self::Error> { + let bytes = self.ptr.unwrap().as_ptr(); + + let ptr = ptr.as_ptr(); + if ptr >= bytes.cast::() && ptr < bytes.cast::().add(ptr_meta::metadata(bytes)) { + let next_pos = ptr.offset_from(bytes.cast::()) as usize; + if next_pos + layout.size() <= self.pos { + self.pos = next_pos; + Ok(()) + } else { + Err(FixedSizeScratchError::NotPoppedInReverseOrder { + pos: self.pos, + next_pos, + next_size: layout.size(), + }) + } + } else { + Err(FixedSizeScratchError::UnownedAllocation) + } + } +} + +/// Allocates scratch space with a main and backup scratch. +#[derive(Debug)] +pub struct FallbackScratch { + main: M, + fallback: F, +} + +impl FallbackScratch { + /// Creates fallback scratch from a main and backup scratch. + pub fn new(main: M, fallback: F) -> Self { + Self { main, fallback } + } +} + +impl Default for FallbackScratch { + fn default() -> Self { + Self { + main: M::default(), + fallback: F::default(), + } + } +} + +impl Fallible for FallbackScratch { + type Error = F::Error; +} + +impl ScratchSpace for FallbackScratch { + #[inline] + unsafe fn push_scratch(&mut self, layout: Layout) -> Result, Self::Error> { + self.main + .push_scratch(layout) + .or_else(|_| self.fallback.push_scratch(layout)) + } + + #[inline] + unsafe fn pop_scratch(&mut self, ptr: NonNull, layout: Layout) -> Result<(), Self::Error> { + self.main + .pop_scratch(ptr, layout) + .or_else(|_| self.fallback.pop_scratch(ptr, layout)) + } +} + +/// A passthrough scratch space allocator that tracks scratch space usage. +#[derive(Debug)] +pub struct ScratchTracker { + inner: T, + bytes_allocated: usize, + allocations: usize, + max_bytes_allocated: usize, + max_allocations: usize, + max_alignment: usize, +} + +impl ScratchTracker { + /// Creates a new scratch tracker from the given inner scratch space. + pub fn new(inner: T) -> Self { + Self { + inner, + bytes_allocated: 0, + allocations: 0, + max_bytes_allocated: 0, + max_allocations: 0, + max_alignment: 1, + } + } + + /// Returns the maximum number of bytes that were concurrently allocated during serialization. + pub fn max_bytes_allocated(&self) -> usize { + self.max_bytes_allocated + } + + /// Returns the maximum number of concurrent allocations during serialization. + pub fn max_allocations(&self) -> usize { + self.max_allocations + } + + /// Returns the maximum alignment of scratch space requested during serialization. + pub fn max_alignment(&self) -> usize { + self.max_alignment + } + + /// Returns the minimum buffer size required to serialize the same data. + /// + /// This calculation takes into account packing efficiency for slab allocated scratch space. It + /// is not exact, and has an error bound of `max_allocations * (max_alignment - 1)` bytes. This + /// should be suitably small for most use cases. + pub fn min_buffer_size(&self) -> usize { + self.max_bytes_allocated + self.min_buffer_size_max_error() + } + + /// Returns the maximum error term for the minimum buffer size calculation. + pub fn min_buffer_size_max_error(&self) -> usize { + self.max_allocations * (self.max_alignment - 1) + } +} + +impl Fallible for ScratchTracker { + type Error = T::Error; +} + +impl ScratchSpace for ScratchTracker { + #[inline] + unsafe fn push_scratch(&mut self, layout: Layout) -> Result, Self::Error> { + let result = self.inner.push_scratch(layout)?; + + self.bytes_allocated += layout.size(); + self.allocations += 1; + self.max_bytes_allocated = usize::max(self.bytes_allocated, self.max_bytes_allocated); + self.max_allocations = usize::max(self.allocations, self.max_allocations); + self.max_alignment = usize::max(self.max_alignment, layout.align()); + + Ok(result) + } + + #[inline] + unsafe fn pop_scratch(&mut self, ptr: NonNull, layout: Layout) -> Result<(), Self::Error> { + self.inner.pop_scratch(ptr, layout)?; + + self.bytes_allocated -= layout.size(); + self.allocations -= 1; + + Ok(()) + } +} + +impl From for ScratchTracker { + fn from(inner: T) -> Self { + Self::new(inner) + } +} diff --git a/src/ser/serializers/mod.rs b/src/ser/serializers/mod.rs new file mode 100644 index 0000000..8c0d7a0 --- /dev/null +++ b/src/ser/serializers/mod.rs @@ -0,0 +1,234 @@ +//! Serializers that can be used standalone and provide basic capabilities. + +#[cfg(feature = "alloc")] +mod alloc; +mod core; +#[cfg(feature = "std")] +mod std; + +#[cfg(feature = "alloc")] +use crate::AlignedVec; +use crate::{ + ser::{ScratchSpace, Serializer, SharedSerializeRegistry}, + AlignedBytes, Archive, ArchiveUnsized, Fallible, Infallible, +}; +use ::core::{alloc::Layout, fmt, ptr::NonNull}; + +#[doc(inline)] +#[cfg(feature = "alloc")] +pub use self::alloc::*; +#[doc(inline)] +pub use self::core::*; +#[doc(inline)] +#[cfg(feature = "std")] +pub use self::std::*; + +/// The default serializer error. +#[derive(Debug)] +pub enum CompositeSerializerError { + /// An error occurred while serializing + SerializerError(S), + /// An error occurred while using scratch space + ScratchSpaceError(C), + /// An error occurred while serializing shared memory + SharedError(H), +} + +impl fmt::Display for CompositeSerializerError +where + S: fmt::Display, + C: fmt::Display, + H: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::SerializerError(e) => write!(f, "serialization error: {}", e), + Self::ScratchSpaceError(e) => write!(f, "scratch space error: {}", e), + Self::SharedError(e) => write!(f, "shared memory error: {}", e), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use ::std::error::Error; + + impl Error for CompositeSerializerError + where + S: Error + 'static, + C: Error + 'static, + H: Error + 'static, + { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::SerializerError(e) => Some(e as &dyn Error), + Self::ScratchSpaceError(e) => Some(e as &dyn Error), + Self::SharedError(e) => Some(e as &dyn Error), + } + } + } +}; + +/// A serializer built from composeable pieces. +#[derive(Debug)] +pub struct CompositeSerializer { + serializer: S, + scratch: C, + shared: H, +} + +impl CompositeSerializer { + /// Creates a new composite serializer from serializer, scratch, and shared components. + #[inline] + pub fn new(serializer: S, scratch: C, shared: H) -> Self { + Self { + serializer, + scratch, + shared, + } + } + + /// Consumes the composite serializer and returns the components. + #[inline] + pub fn into_components(self) -> (S, C, H) { + (self.serializer, self.scratch, self.shared) + } + + /// Consumes the composite serializer and returns the serializer. + /// + /// The scratch space and shared component are discarded. + #[inline] + pub fn into_serializer(self) -> S { + self.serializer + } +} + +impl Default for CompositeSerializer { + #[inline] + fn default() -> Self { + Self { + serializer: S::default(), + scratch: C::default(), + shared: H::default(), + } + } +} + +impl Fallible for CompositeSerializer { + type Error = CompositeSerializerError; +} + +impl Serializer for CompositeSerializer { + #[inline] + fn pos(&self) -> usize { + self.serializer.pos() + } + + #[inline] + fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error> { + self.serializer + .write(bytes) + .map_err(CompositeSerializerError::SerializerError) + } + + #[inline] + fn pad(&mut self, padding: usize) -> Result<(), Self::Error> { + self.serializer + .pad(padding) + .map_err(CompositeSerializerError::SerializerError) + } + + #[inline] + fn align(&mut self, align: usize) -> Result { + self.serializer + .align(align) + .map_err(CompositeSerializerError::SerializerError) + } + + #[inline] + fn align_for(&mut self) -> Result { + self.serializer + .align_for::() + .map_err(CompositeSerializerError::SerializerError) + } + + #[inline] + unsafe fn resolve_aligned( + &mut self, + value: &T, + resolver: T::Resolver, + ) -> Result { + self.serializer + .resolve_aligned::(value, resolver) + .map_err(CompositeSerializerError::SerializerError) + } + + #[inline] + unsafe fn resolve_unsized_aligned( + &mut self, + value: &T, + to: usize, + metadata_resolver: T::MetadataResolver, + ) -> Result { + self.serializer + .resolve_unsized_aligned(value, to, metadata_resolver) + .map_err(CompositeSerializerError::SerializerError) + } +} + +impl ScratchSpace for CompositeSerializer { + #[inline] + unsafe fn push_scratch(&mut self, layout: Layout) -> Result, Self::Error> { + self.scratch + .push_scratch(layout) + .map_err(CompositeSerializerError::ScratchSpaceError) + } + + #[inline] + unsafe fn pop_scratch(&mut self, ptr: NonNull, layout: Layout) -> Result<(), Self::Error> { + self.scratch + .pop_scratch(ptr, layout) + .map_err(CompositeSerializerError::ScratchSpaceError) + } +} + +impl SharedSerializeRegistry + for CompositeSerializer +{ + #[inline] + fn get_shared_ptr(&self, value: *const u8) -> Option { + self.shared.get_shared_ptr(value) + } + + #[inline] + fn add_shared_ptr(&mut self, value: *const u8, pos: usize) -> Result<(), Self::Error> { + self.shared + .add_shared_ptr(value, pos) + .map_err(CompositeSerializerError::SharedError) + } +} + +/// A serializer suitable for environments where allocations cannot be made. +/// +/// `CoreSerializer` takes two arguments: the amount of serialization memory to allocate and the +/// amount of scratch space to allocate. If you run out of either while serializing, the serializer +/// will return an error. +pub type CoreSerializer = CompositeSerializer< + BufferSerializer>, + BufferScratch>, + Infallible, +>; + +/// A general-purpose serializer suitable for environments where allocations can be made. +/// +/// `AllocSerializer` takes one argument: the amount of scratch space to allocate before spilling +/// allocations over into heap memory. A large amount of scratch space may result in some of it not +/// being used, but too little scratch space will result in many allocations and decreased +/// performance. You should consider your use case carefully when determining how much scratch space +/// to pre-allocate. +#[cfg(feature = "alloc")] +pub type AllocSerializer = CompositeSerializer< + AlignedSerializer, + FallbackScratch, AllocScratch>, + SharedSerializeMap, +>; diff --git a/src/ser/serializers/std.rs b/src/ser/serializers/std.rs new file mode 100644 index 0000000..c879e88 --- /dev/null +++ b/src/ser/serializers/std.rs @@ -0,0 +1,61 @@ +use crate::{ser::Serializer, Fallible}; +use std::io; + +/// Wraps a type that implements [`io::Write`](std::io::Write) and equips it with [`Serializer`]. +/// +/// # Examples +/// ``` +/// use rkyv::ser::{serializers::WriteSerializer, Serializer}; +/// +/// let mut serializer = WriteSerializer::new(Vec::new()); +/// assert_eq!(serializer.pos(), 0); +/// serializer.write(&[0u8, 1u8, 2u8, 3u8]); +/// assert_eq!(serializer.pos(), 4); +/// let buf = serializer.into_inner(); +/// assert_eq!(buf.len(), 4); +/// assert_eq!(buf, vec![0u8, 1u8, 2u8, 3u8]); +/// ``` +#[derive(Debug)] +pub struct WriteSerializer { + inner: W, + pos: usize, +} + +impl WriteSerializer { + /// Creates a new serializer from a writer. + #[inline] + pub fn new(inner: W) -> Self { + Self::with_pos(inner, 0) + } + + /// Creates a new serializer from a writer, and assumes that the underlying writer is currently + /// at the given position. + #[inline] + pub fn with_pos(inner: W, pos: usize) -> Self { + Self { inner, pos } + } + + /// Consumes the serializer and returns the internal writer used to create it. + #[inline] + pub fn into_inner(self) -> W { + self.inner + } +} + +impl Fallible for WriteSerializer { + type Error = io::Error; +} + +impl Serializer for WriteSerializer { + #[inline] + fn pos(&self) -> usize { + self.pos + } + + #[inline] + fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error> { + self.inner.write_all(bytes)?; + self.pos += bytes.len(); + Ok(()) + } +} diff --git a/src/string/mod.rs b/src/string/mod.rs new file mode 100644 index 0000000..1bb65d9 --- /dev/null +++ b/src/string/mod.rs @@ -0,0 +1,271 @@ +//! Archived versions of string types. + +pub mod repr; + +use crate::{Fallible, SerializeUnsized}; +use core::{ + borrow::Borrow, + cmp, fmt, hash, + ops::{Deref, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, + pin::Pin, + str, +}; +use repr::{ArchivedStringRepr, INLINE_CAPACITY}; + +/// An archived [`String`]. +/// +/// This has inline and out-of-line representations. Short strings will use the available space +/// inside the structure to store the string, and long strings will store a +/// [`RelPtr`](crate::RelPtr) to a `str` instead. +#[repr(transparent)] +pub struct ArchivedString(repr::ArchivedStringRepr); + +impl ArchivedString { + /// Extracts a string slice containing the entire `ArchivedString`. + #[inline] + pub fn as_str(&self) -> &str { + self.0.as_str() + } + + /// Extracts a pinned mutable string slice containing the entire `ArchivedString`. + #[inline] + pub fn pin_mut_str(self: Pin<&mut Self>) -> Pin<&mut str> { + unsafe { self.map_unchecked_mut(|s| s.0.as_mut_str()) } + } + + /// Resolves an archived string from a given `str`. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing `value` + #[inline] + pub unsafe fn resolve_from_str( + value: &str, + pos: usize, + resolver: StringResolver, + out: *mut Self, + ) { + if value.len() <= repr::INLINE_CAPACITY { + ArchivedStringRepr::emplace_inline(value, out.cast()); + } else { + ArchivedStringRepr::emplace_out_of_line(value, pos, resolver.pos, out.cast()); + } + } + + /// Serializes an archived string from a given `str`. + #[inline] + pub fn serialize_from_str( + value: &str, + serializer: &mut S, + ) -> Result + where + str: SerializeUnsized, + { + if value.len() <= INLINE_CAPACITY { + Ok(StringResolver { pos: 0 }) + } else { + Ok(StringResolver { + pos: value.serialize_unsized(serializer)?, + }) + } + } +} + +impl AsRef for ArchivedString { + #[inline] + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl Borrow for ArchivedString { + #[inline] + fn borrow(&self) -> &str { + self.as_str() + } +} + +impl fmt::Debug for ArchivedString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_str(), f) + } +} + +impl Deref for ArchivedString { + type Target = str; + + #[inline] + fn deref(&self) -> &Self::Target { + self.as_str() + } +} + +impl fmt::Display for ArchivedString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_str(), f) + } +} + +impl Eq for ArchivedString {} + +impl hash::Hash for ArchivedString { + #[inline] + fn hash(&self, state: &mut H) { + self.as_str().hash(state) + } +} + +macro_rules! impl_index { + ($index:ty) => { + impl Index<$index> for ArchivedString { + type Output = str; + + #[inline] + fn index(&self, index: $index) -> &Self::Output { + self.as_str().index(index) + } + } + }; +} + +impl_index!(Range); +impl_index!(RangeFrom); +impl_index!(RangeFull); +impl_index!(RangeInclusive); +impl_index!(RangeTo); +impl_index!(RangeToInclusive); + +impl Ord for ArchivedString { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.as_str().cmp(other.as_str()) + } +} + +impl PartialEq for ArchivedString { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_str() == other.as_str() + } +} + +impl PartialOrd for ArchivedString { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.as_str().partial_cmp(other.as_str()) + } +} + +impl PartialEq<&str> for ArchivedString { + #[inline] + fn eq(&self, other: &&str) -> bool { + PartialEq::eq(self.as_str(), *other) + } +} + +impl PartialEq for ArchivedString { + #[inline] + fn eq(&self, other: &str) -> bool { + PartialEq::eq(self.as_str(), other) + } +} + +impl PartialEq for &str { + #[inline] + fn eq(&self, other: &ArchivedString) -> bool { + PartialEq::eq(other.as_str(), *self) + } +} + +impl PartialEq for str { + #[inline] + fn eq(&self, other: &ArchivedString) -> bool { + PartialEq::eq(other.as_str(), self) + } +} + +impl PartialOrd<&str> for ArchivedString { + #[inline] + fn partial_cmp(&self, other: &&str) -> Option { + self.as_str().partial_cmp(*other) + } +} + +impl PartialOrd for ArchivedString { + #[inline] + fn partial_cmp(&self, other: &str) -> Option { + self.as_str().partial_cmp(other) + } +} + +impl PartialOrd for &str { + #[inline] + fn partial_cmp(&self, other: &ArchivedString) -> Option { + self.partial_cmp(&other.as_str()) + } +} + +impl PartialOrd for str { + #[inline] + fn partial_cmp(&self, other: &ArchivedString) -> Option { + self.partial_cmp(other.as_str()) + } +} + +/// The resolver for `String`. +pub struct StringResolver { + pos: usize, +} + +#[cfg(feature = "validation")] +const _: () = { + use crate::validation::{owned::OwnedPointerError, ArchiveContext}; + use bytecheck::{CheckBytes, Error}; + + impl CheckBytes for ArchivedString + where + C::Error: Error + 'static, + { + type Error = OwnedPointerError< + >::Error, + >::Error, + C::Error, + >; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + // The repr is always valid + let repr = ArchivedStringRepr::check_bytes(value.cast(), context) + .map_err(OwnedPointerError::PointerCheckBytesError)?; + + if repr.is_inline() { + str::check_bytes(repr.as_str_ptr(), context) + .map_err(OwnedPointerError::ValueCheckBytesError)?; + } else { + let base = value.cast(); + let offset = repr.out_of_line_offset(); + let metadata = repr.len(); + + let ptr = context + .check_subtree_ptr::(base, offset, metadata) + .map_err(OwnedPointerError::ContextError)?; + + let range = context + .push_prefix_subtree(ptr) + .map_err(OwnedPointerError::ContextError)?; + str::check_bytes(ptr, context).map_err(OwnedPointerError::ValueCheckBytesError)?; + context + .pop_prefix_range(range) + .map_err(OwnedPointerError::ContextError)?; + } + + Ok(&*value) + } + } +}; diff --git a/src/string/repr.rs b/src/string/repr.rs new file mode 100644 index 0000000..3f744b3 --- /dev/null +++ b/src/string/repr.rs @@ -0,0 +1,201 @@ +//! An archived string representation that supports inlining short strings. + +use crate::{Archived, FixedIsize, FixedUsize}; +use core::{marker::PhantomPinned, mem, ptr, slice, str}; + +const OFFSET_BYTES: usize = mem::size_of::(); + +#[derive(Clone, Copy)] +#[repr(C)] +struct OutOfLineRepr { + len: Archived, + // Offset is always stored in little-endian format to put the sign bit at the end. + // This representation is optimized for little-endian architectures. + offset: [u8; OFFSET_BYTES], + _phantom: PhantomPinned, +} + +/// The maximum number of bytes that can be inlined. +pub const INLINE_CAPACITY: usize = mem::size_of::() - 1; + +#[derive(Clone, Copy)] +#[repr(C)] +struct InlineRepr { + bytes: [u8; INLINE_CAPACITY], + len: u8, +} + +/// An archived string representation that can inline short strings. +pub union ArchivedStringRepr { + out_of_line: OutOfLineRepr, + inline: InlineRepr, +} + +impl ArchivedStringRepr { + /// Returns whether the representation is inline. + #[inline] + pub fn is_inline(&self) -> bool { + unsafe { self.inline.len & 0x80 == 0 } + } + + /// Returns the offset of the representation. + /// + /// # Safety + /// + /// The internal representation must be out-of-line. + #[inline] + pub unsafe fn out_of_line_offset(&self) -> isize { + FixedIsize::from_le_bytes(self.out_of_line.offset) as isize + } + + /// Returns a pointer to the bytes of the string. + #[inline] + pub fn as_ptr(&self) -> *const u8 { + unsafe { + if self.is_inline() { + self.inline.bytes.as_ptr() + } else { + (self as *const Self) + .cast::() + .offset(self.out_of_line_offset()) + } + } + } + + /// Returns a mutable pointer to the bytes of the string. + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut u8 { + unsafe { + if self.is_inline() { + self.inline.bytes.as_mut_ptr() + } else { + (self as *mut Self) + .cast::() + .offset(self.out_of_line_offset()) + } + } + } + + /// Returns the length of the string. + #[inline] + pub fn len(&self) -> usize { + unsafe { + if self.is_inline() { + self.inline.len as usize + } else { + from_archived!(self.out_of_line.len) as usize + } + } + } + + /// Returns whether the string is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns a pointer to the string as a `str`. + #[cfg(feature = "validation")] + #[inline] + pub fn as_str_ptr(&self) -> *const str { + ptr_meta::from_raw_parts(self.as_ptr().cast(), self.len()) + } + + /// Returns a slice of the bytes of the string. + #[inline] + pub fn bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) } + } + + /// Returns a mutable slice of the bytes of the string. + #[inline] + pub fn bytes_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) } + } + + /// Returns a reference to the string as a `str`. + #[inline] + pub fn as_str(&self) -> &str { + unsafe { str::from_utf8_unchecked(self.bytes()) } + } + + /// Returns a mutable reference to the string as a `str`. + #[inline] + pub fn as_mut_str(&mut self) -> &mut str { + unsafe { str::from_utf8_unchecked_mut(self.bytes_mut()) } + } + + /// Emplaces a new inline representation for the given `str`. + /// + /// # Safety + /// + /// - The length of `str` must be less than or equal to [`INLINE_CAPACITY`]. + /// - `out` must point to a valid location to write the inline representation. + #[inline] + pub unsafe fn emplace_inline(value: &str, out: *mut Self) { + let out_bytes = ptr::addr_of_mut!((*out).inline.bytes); + ptr::copy_nonoverlapping(value.as_bytes().as_ptr(), out_bytes.cast(), value.len()); + + let out_len = ptr::addr_of_mut!((*out).inline.len); + *out_len = value.len() as u8; + } + + /// Emplaces a new out-of-line representation for the given `str`. + /// + /// # Safety + /// + /// - The length of `str` must be greater than [`INLINE_CAPACITY`]. + /// - `pos` must be the location of the representation within the archive. + /// - `target` must be the location of the serialized bytes of the string. + /// - `out` must point to a valid location to write the out-of-line representation. + #[inline] + pub unsafe fn emplace_out_of_line(value: &str, pos: usize, target: usize, out: *mut Self) { + let out_len = ptr::addr_of_mut!((*out).out_of_line.len); + out_len.write(to_archived!(value.len() as FixedUsize)); + + let out_offset = ptr::addr_of_mut!((*out).out_of_line.offset); + let offset = crate::rel_ptr::signed_offset(pos, target).unwrap(); + *out_offset = (offset as FixedIsize).to_le_bytes(); + } +} + +#[cfg(feature = "validation")] +const _: () = { + use crate::Fallible; + use bytecheck::CheckBytes; + use core::fmt; + + /// An error resulting from an invalid string representation. + /// + /// Strings that are inline must have a length of at most [`INLINE_CAPACITY`]. + #[derive(Debug)] + pub struct CheckStringReprError; + + impl fmt::Display for CheckStringReprError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "String representation was inline but the length was too large" + ) + } + } + + #[cfg(feature = "std")] + impl std::error::Error for CheckStringReprError {} + + impl CheckBytes for ArchivedStringRepr { + type Error = CheckStringReprError; + + #[inline] + unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> { + // The fields of `ArchivedStringRepr` are always valid + let repr = &*value; + + if repr.is_inline() && repr.len() > INLINE_CAPACITY { + Err(CheckStringReprError) + } else { + Ok(repr) + } + } + } +}; diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..0886d16 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,152 @@ +//! Archived versions of `time` types. + +use crate::Archived; + +/// An archived [`Duration`](core::time::Duration). +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedDuration { + secs: Archived, + nanos: Archived, +} + +const NANOS_PER_SEC: u32 = 1_000_000_000; +const NANOS_PER_MILLI: u32 = 1_000_000; +const NANOS_PER_MICRO: u32 = 1_000; +const MILLIS_PER_SEC: u64 = 1_000; +const MICROS_PER_SEC: u64 = 1_000_000; + +impl ArchivedDuration { + /// Returns the number of _whole_ seconds contained by this + /// `ArchivedDuration`. + /// + /// The returned value does not include the fractional (nanosecond) part of the duration, which + /// can be obtained using [`subsec_nanos`]. + /// + /// [`subsec_nanos`]: ArchivedDuration::subsec_nanos + #[inline] + pub const fn as_secs(&self) -> u64 { + from_archived!(self.secs) + } + + /// Returns the fractional part of this `ArchivedDuration`, in whole milliseconds. + /// + /// This method does **not** return the length of the duration when represented by milliseconds. + /// The returned number always represents a fractional portion of a second (i.e., it is less + /// than one thousand). + #[inline] + pub const fn subsec_millis(&self) -> u32 { + from_archived!(self.nanos) / NANOS_PER_MILLI + } + + /// Returns the fractional part of this `ArchivedDuration`, in whole microseconds. + /// + /// This method does **not** return the length of the duration when represented by microseconds. + /// The returned number always represents a fractional portion of a second (i.e., it is less + /// than one million). + #[inline] + pub const fn subsec_micros(&self) -> u32 { + from_archived!(self.nanos) / NANOS_PER_MICRO + } + + /// Returns the fractional part of this `Duration`, in nanoseconds. + /// + /// This method does **not** return the length of the duration when represented by nanoseconds. + /// The returned number always represents a fractional portion of a second (i.e., it is less + /// than one billion). + #[inline] + pub const fn subsec_nanos(&self) -> u32 { + from_archived!(self.nanos) + } + + /// Returns the total number of whole milliseconds contained by this `ArchivedDuration`. + #[inline] + pub const fn as_millis(&self) -> u128 { + self.as_secs() as u128 * MILLIS_PER_SEC as u128 + + (self.subsec_nanos() / NANOS_PER_MILLI) as u128 + } + + /// Returns the total number of whole microseconds contained by this `ArchivedDuration`. + #[inline] + pub const fn as_micros(&self) -> u128 { + self.as_secs() as u128 * MICROS_PER_SEC as u128 + + (self.subsec_nanos() / NANOS_PER_MICRO) as u128 + } + + /// Returns the total number of nanoseconds contained by this `ArchivedDuration`. + #[inline] + pub const fn as_nanos(&self) -> u128 { + self.as_secs() as u128 * NANOS_PER_SEC as u128 + self.subsec_nanos() as u128 + } + + /// Returns the number of seconds contained by this `ArchivedDuration` as `f64`. + /// + /// The returned value does include the fractional (nanosecond) part of the duration. + #[inline] + pub fn as_secs_f64(&self) -> f64 { + (self.as_secs() as f64) + (self.subsec_nanos() as f64) / (NANOS_PER_SEC as f64) + } + + /// Returns the number of seconds contained by this `ArchivedDuration` as `f32`. + /// + /// The returned value does include the fractional (nanosecond) part of the duration. + #[inline] + pub fn as_secs_f32(&self) -> f32 { + (self.as_secs() as f32) + (self.subsec_nanos() as f32) / (NANOS_PER_SEC as f32) + } + + /// Constructs an archived duration at the given position. + /// + /// # Safety + /// + /// `out` must point to memory suitable for holding an `ArchivedDuration`. + #[inline] + pub unsafe fn emplace(secs: u64, nanos: u32, out: *mut ArchivedDuration) { + use core::ptr::addr_of_mut; + + addr_of_mut!((*out).secs).write(to_archived!(secs)); + addr_of_mut!((*out).nanos).write(to_archived!(nanos)); + } +} + +#[cfg(feature = "validation")] +const _: () = { + use crate::Fallible; + use bytecheck::CheckBytes; + use core::fmt; + + /// An error resulting from an invalid duration. + /// + /// Durations must have a `secs` and `nanos` that when combined do not overflow a `u64`. + #[derive(Debug)] + pub struct DurationError; + + impl fmt::Display for DurationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Duration error: nanos field is greater than 1 billion and overflows the seconds counter") + } + } + + #[cfg(feature = "std")] + impl std::error::Error for DurationError {} + + impl CheckBytes for ArchivedDuration { + type Error = DurationError; + + #[inline] + unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> { + // The fields of `ArchivedDuration` are always valid + let duration = &*value; + let secs = from_archived!(duration.secs); + + if secs + .checked_add((duration.nanos / 1_000_000_000) as u64) + .is_none() + { + Err(DurationError) + } else { + Ok(duration) + } + } + } +}; diff --git a/src/util/aligned_vec.rs b/src/util/aligned_vec.rs new file mode 100644 index 0000000..edba10a --- /dev/null +++ b/src/util/aligned_vec.rs @@ -0,0 +1,984 @@ +use crate::vec::VecResolver; +use crate::{ + ser::{ScratchSpace, Serializer}, + vec::ArchivedVec, + Archive, Archived, Serialize, +}; + +#[cfg(not(feature = "std"))] +use ::alloc::{alloc, boxed::Box, vec::Vec}; +use core::borrow::{Borrow, BorrowMut}; +use core::{ + fmt, + ops::{Deref, DerefMut, Index, IndexMut}, + ptr::NonNull, + slice, +}; +#[cfg(feature = "std")] +use std::{alloc, io}; + +/// A vector of bytes that aligns its memory to 16 bytes. +/// +/// The alignment also applies to `ArchivedAlignedVec`, which is useful for aligning opaque bytes inside of an archived data +/// type. +/// +/// ``` +/// # use rkyv::{archived_value, AlignedBytes, AlignedVec, Archive, Serialize}; +/// # use rkyv::ser::Serializer; +/// # use rkyv::ser::serializers::CoreSerializer; +/// # +/// #[derive(Archive, Serialize)] +/// struct HasAlignedBytes { +/// pub bytes: AlignedVec, +/// } +/// +/// let mut serializer = CoreSerializer::<256, 0>::default(); +/// +/// // Write a single byte to force re-alignment. +/// serializer.write(&[0]).unwrap(); +/// assert_eq!(serializer.pos(), 1); +/// +/// let mut bytes = AlignedVec::new(); +/// bytes.extend_from_slice(&[1, 2, 3]); +/// let pos = serializer.serialize_value(&HasAlignedBytes { bytes }).unwrap(); +/// +/// // Make sure we can recover the archived type with the expected alignment. +/// let buf = serializer.into_serializer().into_inner(); +/// let archived = unsafe { archived_value::(buf.as_ref(), pos) }; +/// assert_eq!(archived.bytes.as_slice(), &[1, 2, 3]); +/// assert_eq!(archived.bytes.as_ptr().align_offset(16), 0); +/// ``` +pub struct AlignedVec { + ptr: NonNull, + cap: usize, + len: usize, +} + +impl Drop for AlignedVec { + #[inline] + fn drop(&mut self) { + if self.cap != 0 { + unsafe { + alloc::dealloc(self.ptr.as_ptr(), self.layout()); + } + } + } +} + +impl AlignedVec { + /// The alignment of the vector + pub const ALIGNMENT: usize = 16; + + /// Maximum capacity of the vector. + /// Dictated by the requirements of + /// [`alloc::Layout`](https://doc.rust-lang.org/alloc/alloc/struct.Layout.html). + /// "`size`, when rounded up to the nearest multiple of `align`, must not overflow `isize` + /// (i.e. the rounded value must be less than or equal to `isize::MAX`)". + pub const MAX_CAPACITY: usize = isize::MAX as usize - (Self::ALIGNMENT - 1); + + /// Constructs a new, empty `AlignedVec`. + /// + /// The vector will not allocate until elements are pushed into it. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// ``` + #[inline] + pub fn new() -> Self { + AlignedVec { + ptr: NonNull::dangling(), + cap: 0, + len: 0, + } + } + + /// Constructs a new, empty `AlignedVec` with the specified capacity. + /// + /// The vector will be able to hold exactly `capacity` bytes without reallocating. If + /// `capacity` is 0, the vector will not allocate. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::with_capacity(10); + /// + /// // The vector contains no items, even though it has capacity for more + /// assert_eq!(vec.len(), 0); + /// assert_eq!(vec.capacity(), 10); + /// + /// // These are all done without reallocating... + /// for i in 0..10 { + /// vec.push(i); + /// } + /// assert_eq!(vec.len(), 10); + /// assert_eq!(vec.capacity(), 10); + /// + /// // ...but this may make the vector reallocate + /// vec.push(11); + /// assert_eq!(vec.len(), 11); + /// assert!(vec.capacity() >= 11); + /// ``` + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + if capacity == 0 { + Self::new() + } else { + assert!( + capacity <= Self::MAX_CAPACITY, + "`capacity` cannot exceed isize::MAX - 15" + ); + let ptr = unsafe { + let layout = alloc::Layout::from_size_align_unchecked(capacity, Self::ALIGNMENT); + let ptr = alloc::alloc(layout); + if ptr.is_null() { + alloc::handle_alloc_error(layout); + } + NonNull::new_unchecked(ptr) + }; + Self { + ptr, + cap: capacity, + len: 0, + } + } + } + + #[inline] + fn layout(&self) -> alloc::Layout { + unsafe { alloc::Layout::from_size_align_unchecked(self.cap, Self::ALIGNMENT) } + } + + /// Clears the vector, removing all values. + /// + /// Note that this method has no effect on the allocated capacity of the vector. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut v = AlignedVec::new(); + /// v.extend_from_slice(&[1, 2, 3, 4]); + /// + /// v.clear(); + /// + /// assert!(v.is_empty()); + /// ``` + #[inline] + pub fn clear(&mut self) { + self.len = 0; + } + + /// Change capacity of vector. + /// + /// Will set capacity to exactly `new_cap`. + /// Can be used to either grow or shrink capacity. + /// Backing memory will be reallocated. + /// + /// Usually the safe methods `reserve` or `reserve_exact` are a better choice. + /// This method only exists as a micro-optimization for very performance-sensitive + /// code where where the calculation of capacity required has already been + /// performed, and you want to avoid doing it again, or if you want to implement + /// a different growth strategy. + /// + /// # Safety + /// + /// - `new_cap` must be less than or equal to [`MAX_CAPACITY`](AlignedVec::MAX_CAPACITY) + /// - `new_cap` must be greater than or equal to [`len()`](AlignedVec::len) + #[inline] + pub unsafe fn change_capacity(&mut self, new_cap: usize) { + debug_assert!(new_cap <= Self::MAX_CAPACITY); + debug_assert!(new_cap >= self.len); + + if new_cap > 0 { + let new_ptr = if self.cap > 0 { + let new_ptr = alloc::realloc(self.ptr.as_ptr(), self.layout(), new_cap); + if new_ptr.is_null() { + alloc::handle_alloc_error(alloc::Layout::from_size_align_unchecked( + new_cap, + Self::ALIGNMENT, + )); + } + new_ptr + } else { + let layout = alloc::Layout::from_size_align_unchecked(new_cap, Self::ALIGNMENT); + let new_ptr = alloc::alloc(layout); + if new_ptr.is_null() { + alloc::handle_alloc_error(layout); + } + new_ptr + }; + self.ptr = NonNull::new_unchecked(new_ptr); + self.cap = new_cap; + } else if self.cap > 0 { + alloc::dealloc(self.ptr.as_ptr(), self.layout()); + self.ptr = NonNull::dangling(); + self.cap = 0; + } + } + + /// Shrinks the capacity of the vector as much as possible. + /// + /// It will drop down as close as possible to the length but the allocator may still inform the + /// vector that there is space for a few more elements. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::with_capacity(10); + /// vec.extend_from_slice(&[1, 2, 3]); + /// assert_eq!(vec.capacity(), 10); + /// vec.shrink_to_fit(); + /// assert!(vec.capacity() >= 3); + /// + /// vec.clear(); + /// vec.shrink_to_fit(); + /// assert!(vec.capacity() == 0); + /// ``` + #[inline] + pub fn shrink_to_fit(&mut self) { + if self.cap != self.len { + // New capacity cannot exceed max as it's shrinking + unsafe { self.change_capacity(self.len) }; + } + } + + /// Returns an unsafe mutable pointer to the vector's buffer. + /// + /// The caller must ensure that the vector outlives the pointer this function returns, or else + /// it will end up pointing to garbage. Modifying the vector may cause its buffer to be + /// reallocated, which would also make any pointers to it invalid. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// // Allocate vecotr big enough for 4 bytes. + /// let size = 4; + /// let mut x = AlignedVec::with_capacity(size); + /// let x_ptr = x.as_mut_ptr(); + /// + /// // Initialize elements via raw pointer writes, then set length. + /// unsafe { + /// for i in 0..size { + /// *x_ptr.add(i) = i as u8; + /// } + /// x.set_len(size); + /// } + /// assert_eq!(&*x, &[0, 1, 2, 3]); + /// ``` + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.ptr.as_ptr() + } + + /// Extracts a mutable slice of the entire vector. + /// + /// Equivalent to `&mut s[..]`. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// vec.extend_from_slice(&[1, 2, 3, 4, 5]); + /// assert_eq!(vec.as_mut_slice().len(), 5); + /// for i in 0..5 { + /// assert_eq!(vec.as_mut_slice()[i], i as u8 + 1); + /// vec.as_mut_slice()[i] = i as u8; + /// assert_eq!(vec.as_mut_slice()[i], i as u8); + /// } + /// ``` + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } + } + + /// Returns a raw pointer to the vector's buffer. + /// + /// The caller must ensure that the vector outlives the pointer this function returns, or else + /// it will end up pointing to garbage. Modifying the vector may cause its buffer to be + /// reallocated, which would also make any pointers to it invalid. + /// + /// The caller must also ensure that the memory the pointer (non-transitively) points to is + /// never written to (except inside an `UnsafeCell`) using this pointer or any pointer derived + /// from it. If you need to mutate the contents of the slice, use + /// [`as_mut_ptr`](AlignedVec::as_mut_ptr). + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut x = AlignedVec::new(); + /// x.extend_from_slice(&[1, 2, 4]); + /// let x_ptr = x.as_ptr(); + /// + /// unsafe { + /// for i in 0..x.len() { + /// assert_eq!(*x_ptr.add(i), 1 << i); + /// } + /// } + /// ``` + #[inline] + pub fn as_ptr(&self) -> *const u8 { + self.ptr.as_ptr() + } + + /// Extracts a slice containing the entire vector. + /// + /// Equivalent to `&s[..]`. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// vec.extend_from_slice(&[1, 2, 3, 4, 5]); + /// assert_eq!(vec.as_slice().len(), 5); + /// for i in 0..5 { + /// assert_eq!(vec.as_slice()[i], i as u8 + 1); + /// } + /// ``` + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } + } + + /// Returns the number of elements the vector can hold without reallocating. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let vec = AlignedVec::with_capacity(10); + /// assert_eq!(vec.capacity(), 10); + /// ``` + #[inline] + pub fn capacity(&self) -> usize { + self.cap + } + + /// Reserves capacity for at least `additional` more bytes to be inserted into the given + /// `AlignedVec`. The collection may reserve more space to avoid frequent reallocations. After + /// calling `reserve`, capacity will be greater than or equal to `self.len() + additional`. Does + /// nothing if capacity is already sufficient. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX - 15` bytes. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// vec.push(1); + /// vec.reserve(10); + /// assert!(vec.capacity() >= 11); + /// ``` + #[inline] + pub fn reserve(&mut self, additional: usize) { + // Cannot wrap because capacity always exceeds len, + // but avoids having to handle potential overflow here + let remaining = self.cap.wrapping_sub(self.len); + if additional > remaining { + self.do_reserve(additional); + } + } + + /// Extend capacity after `reserve` has found it's necessary. + /// + /// Actually performing the extension is in this separate function marked + /// `#[cold]` to hint to compiler that this branch is not often taken. + /// This keeps the path for common case where capacity is already sufficient + /// as fast as possible, and makes `reserve` more likely to be inlined. + /// This is the same trick that Rust's `Vec::reserve` uses. + #[cold] + fn do_reserve(&mut self, additional: usize) { + let new_cap = self + .len + .checked_add(additional) + .expect("cannot reserve a larger AlignedVec"); + unsafe { self.grow_capacity_to(new_cap) }; + } + + /// Grows total capacity of vector to `new_cap` or more. + /// + /// Capacity after this call will be `new_cap` rounded up to next power of 2, + /// unless that would exceed maximum capacity, in which case capacity + /// is capped at the maximum. + /// + /// This is same growth strategy used by `reserve`, `push` and `extend_from_slice`. + /// + /// Usually the safe methods `reserve` or `reserve_exact` are a better choice. + /// This method only exists as a micro-optimization for very performance-sensitive + /// code where where the calculation of capacity required has already been + /// performed, and you want to avoid doing it again. + /// + /// Maximum capacity is `isize::MAX - 15` bytes. + /// + /// # Panics + /// + /// Panics if `new_cap` exceeds `isize::MAX - 15` bytes. + /// + /// # Safety + /// + /// - `new_cap` must be greater than current [`capacity()`](AlignedVec::capacity) + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// vec.push(1); + /// unsafe { vec.grow_capacity_to(50) }; + /// assert_eq!(vec.len(), 1); + /// assert_eq!(vec.capacity(), 64); + /// ``` + #[inline] + pub unsafe fn grow_capacity_to(&mut self, new_cap: usize) { + debug_assert!(new_cap > self.cap); + + let new_cap = if new_cap > (isize::MAX as usize + 1) >> 1 { + // Rounding up to next power of 2 would result in `isize::MAX + 1` or higher, + // which exceeds max capacity. So cap at max instead. + assert!( + new_cap <= Self::MAX_CAPACITY, + "cannot reserve a larger AlignedVec" + ); + Self::MAX_CAPACITY + } else { + // Cannot overflow due to check above + new_cap.next_power_of_two() + }; + self.change_capacity(new_cap); + } + + /// Resizes the Vec in-place so that len is equal to new_len. + /// + /// If new_len is greater than len, the Vec is extended by the difference, with each additional slot filled with value. If new_len is less than len, the Vec is simply truncated. + /// + /// # Panics + /// + /// Panics if the new length exceeds `isize::MAX - 15` bytes. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// vec.push(3); + /// vec.resize(3, 2); + /// assert_eq!(vec.as_slice(), &[3, 2, 2]); + /// + /// let mut vec = AlignedVec::new(); + /// vec.extend_from_slice(&[1, 2, 3, 4]); + /// vec.resize(2, 0); + /// assert_eq!(vec.as_slice(), &[1, 2]); + /// ``` + #[inline] + pub fn resize(&mut self, new_len: usize, value: u8) { + if new_len > self.len { + let additional = new_len - self.len; + self.reserve(additional); + unsafe { + core::ptr::write_bytes(self.ptr.as_ptr().add(self.len), value, additional); + } + } + unsafe { + self.set_len(new_len); + } + } + + /// Returns `true` if the vector contains no elements. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut v = Vec::new(); + /// assert!(v.is_empty()); + /// + /// v.push(1); + /// assert!(!v.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns the number of elements in the vector, also referred to as its 'length'. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut a = AlignedVec::new(); + /// a.extend_from_slice(&[1, 2, 3]); + /// assert_eq!(a.len(), 3); + /// ``` + #[inline] + pub fn len(&self) -> usize { + self.len + } + + /// Copies and appends all bytes in a slice to the `AlignedVec`. + /// + /// The elements of the slice are appended in-order. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// vec.push(1); + /// vec.extend_from_slice(&[2, 3, 4]); + /// assert_eq!(vec.as_slice(), &[1, 2, 3, 4]); + /// ``` + #[inline] + pub fn extend_from_slice(&mut self, other: &[u8]) { + if !other.is_empty() { + self.reserve(other.len()); + unsafe { + core::ptr::copy_nonoverlapping( + other.as_ptr(), + self.as_mut_ptr().add(self.len()), + other.len(), + ); + } + self.len += other.len(); + } + } + + /// Removes the last element from a vector and returns it, or `None` if it is empty. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// vec.extend_from_slice(&[1, 2, 3]); + /// assert_eq!(vec.pop(), Some(3)); + /// assert_eq!(vec.as_slice(), &[1, 2]); + /// ``` + #[inline] + pub fn pop(&mut self) -> Option { + if self.len == 0 { + None + } else { + let result = self[self.len - 1]; + self.len -= 1; + Some(result) + } + } + + /// Appends an element to the back of a collection. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX - 15` bytes. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// vec.extend_from_slice(&[1, 2]); + /// vec.push(3); + /// assert_eq!(vec.as_slice(), &[1, 2, 3]); + /// ``` + #[inline] + pub fn push(&mut self, value: u8) { + if self.len == self.cap { + self.reserve_for_push(); + } + + unsafe { + self.as_mut_ptr().add(self.len).write(value); + self.len += 1; + } + } + + /// Extend capacity by at least 1 byte after `push` has found it's necessary. + /// + /// Actually performing the extension is in this separate function marked + /// `#[cold]` to hint to compiler that this branch is not often taken. + /// This keeps the path for common case where capacity is already sufficient + /// as fast as possible, and makes `push` more likely to be inlined. + /// This is the same trick that Rust's `Vec::push` uses. + #[cold] + fn reserve_for_push(&mut self) { + // `len` is always less than `isize::MAX`, so no possibility of overflow here + let new_cap = self.len + 1; + unsafe { self.grow_capacity_to(new_cap) }; + } + + /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the + /// given `AlignedVec`. After calling `reserve_exact`, capacity will be greater than or equal + /// to `self.len() + additional`. Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it requests. Therefore, + /// capacity can not be relied upon to be precisely minimal. Prefer reserve if future insertions + /// are expected. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `isize::MAX - 15`. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::new(); + /// vec.push(1); + /// vec.reserve_exact(10); + /// assert!(vec.capacity() >= 11); + /// ``` + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + // This function does not use the hot/cold paths trick that `reserve` + // and `push` do, on assumption that user probably knows this will require + // an increase in capacity. Otherwise, they'd likely use `reserve`. + let new_cap = self + .len + .checked_add(additional) + .expect("cannot reserve a larger AlignedVec"); + if new_cap > self.cap { + assert!( + new_cap <= Self::MAX_CAPACITY, + "cannot reserve a larger AlignedVec" + ); + unsafe { self.change_capacity(new_cap) }; + } + } + + /// Forces the length of the vector to `new_len`. + /// + /// This is a low-level operation that maintains none of the normal invariants of the type. + /// + /// # Safety + /// + /// - `new_len` must be less than or equal to [`capacity()`](AlignedVec::capacity) + /// - The elements at `old_len..new_len` must be initialized + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::with_capacity(3); + /// vec.extend_from_slice(&[1, 2, 3]); + /// + /// // SAFETY: + /// // 1. `old_len..0` is empty to no elements need to be initialized. + /// // 2. `0 <= capacity` always holds whatever capacity is. + /// unsafe { + /// vec.set_len(0); + /// } + /// ``` + #[inline] + pub unsafe fn set_len(&mut self, new_len: usize) { + debug_assert!(new_len <= self.capacity()); + + self.len = new_len; + } + + /// Converts the vector into `Box<[u8]>`. + /// + /// This method reallocates and copies the underlying bytes. Any excess capacity is dropped. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut v = AlignedVec::new(); + /// v.extend_from_slice(&[1, 2, 3]); + /// + /// let slice = v.into_boxed_slice(); + /// ``` + /// + /// Any excess capacity is removed: + /// + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut vec = AlignedVec::with_capacity(10); + /// vec.extend_from_slice(&[1, 2, 3]); + /// + /// assert_eq!(vec.capacity(), 10); + /// let slice = vec.into_boxed_slice(); + /// assert_eq!(slice.len(), 3); + /// ``` + #[inline] + pub fn into_boxed_slice(self) -> Box<[u8]> { + self.into_vec().into_boxed_slice() + } + + /// Converts the vector into `Vec`. + /// + /// This method reallocates and copies the underlying bytes. Any excess capacity is dropped. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let mut v = AlignedVec::new(); + /// v.extend_from_slice(&[1, 2, 3]); + /// + /// let vec = v.into_vec(); + /// assert_eq!(vec.len(), 3); + /// assert_eq!(vec.as_slice(), &[1, 2, 3]); + /// ``` + #[inline] + pub fn into_vec(self) -> Vec { + Vec::from(self.as_ref()) + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::io::{ErrorKind, Read}; + + impl AlignedVec { + /// Reads all bytes until EOF from `r` and appends them to this `AlignedVec`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// # Examples + /// ``` + /// use rkyv::AlignedVec; + /// + /// let source = (0..4096).map(|x| (x % 256) as u8).collect::>(); + /// let mut bytes = AlignedVec::new(); + /// bytes.extend_from_reader(&mut source.as_slice()).unwrap(); + /// + /// assert_eq!(bytes.len(), 4096); + /// assert_eq!(bytes[0], 0); + /// assert_eq!(bytes[100], 100); + /// assert_eq!(bytes[2945], 129); + /// ``` + pub fn extend_from_reader( + &mut self, + r: &mut R, + ) -> std::io::Result { + let start_len = self.len(); + let start_cap = self.capacity(); + + // Extra initialized bytes from previous loop iteration. + let mut initialized = 0; + loop { + if self.len() == self.capacity() { + // No available capacity, reserve some space. + self.reserve(32); + } + + let read_buf_start = unsafe { self.as_mut_ptr().add(self.len) }; + let read_buf_len = self.capacity() - self.len(); + + // Initialize the uninitialized portion of the available space. + unsafe { + // The first `initialized` bytes don't need to be zeroed. + // This leaves us `read_buf_len - initialized` bytes to zero + // starting at `initialized`. + core::ptr::write_bytes( + read_buf_start.add(initialized), + 0, + read_buf_len - initialized, + ); + } + + // The entire read buffer is now initialized, so we can create a + // mutable slice of it. + let read_buf = + unsafe { core::slice::from_raw_parts_mut(read_buf_start, read_buf_len) }; + + match r.read(read_buf) { + Ok(read) => { + // We filled `read` additional bytes. + unsafe { + self.set_len(self.len() + read); + } + initialized = read_buf_len - read; + + if read == 0 { + return Ok(self.len() - start_len); + } + } + Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + } + + if self.len() == self.capacity() && self.capacity() == start_cap { + // The buffer might be an exact fit. Let's read into a probe buffer + // and see if it returns `Ok(0)`. If so, we've avoided an + // unnecessary doubling of the capacity. But if not, append the + // probe buffer to the primary buffer and let its capacity grow. + let mut probe = [0u8; 32]; + + loop { + match r.read(&mut probe) { + Ok(0) => return Ok(self.len() - start_len), + Ok(n) => { + self.extend_from_slice(&probe[..n]); + break; + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + } + } + } + } + } + } +}; + +impl From for Vec { + #[inline] + fn from(aligned: AlignedVec) -> Self { + aligned.to_vec() + } +} + +impl Archive for AlignedVec { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedVec::resolve_from_slice(self.as_slice(), pos, resolver, out); + } +} + +impl AsMut<[u8]> for AlignedVec { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + self.as_mut_slice() + } +} + +impl AsRef<[u8]> for AlignedVec { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_slice() + } +} + +impl Borrow<[u8]> for AlignedVec { + #[inline] + fn borrow(&self) -> &[u8] { + self.as_slice() + } +} + +impl BorrowMut<[u8]> for AlignedVec { + #[inline] + fn borrow_mut(&mut self) -> &mut [u8] { + self.as_mut_slice() + } +} + +impl Clone for AlignedVec { + #[inline] + fn clone(&self) -> Self { + unsafe { + let mut result = AlignedVec::with_capacity(self.len); + result.len = self.len; + core::ptr::copy_nonoverlapping(self.as_ptr(), result.as_mut_ptr(), self.len); + result + } + } +} + +impl fmt::Debug for AlignedVec { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_slice().fmt(f) + } +} + +impl Default for AlignedVec { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Deref for AlignedVec { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl DerefMut for AlignedVec { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut_slice() + } +} + +impl> Index for AlignedVec { + type Output = >::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + &self.as_slice()[index] + } +} + +impl> IndexMut for AlignedVec { + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.as_mut_slice()[index] + } +} + +#[cfg(feature = "std")] +impl io::Write for AlignedVec { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + let len = bufs.iter().map(|b| b.len()).sum(); + self.reserve(len); + for buf in bufs { + self.extend_from_slice(buf); + } + Ok(len) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.extend_from_slice(buf); + Ok(()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +// SAFETY: AlignedVec is safe to send to another thread +unsafe impl Send for AlignedVec {} + +impl Serialize for AlignedVec { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + serializer.align(Self::ALIGNMENT)?; + ArchivedVec::>::serialize_from_slice(self.as_slice(), serializer) + } +} + +// SAFETY: AlignedVec is safe to share between threads +unsafe impl Sync for AlignedVec {} + +impl Unpin for AlignedVec {} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..2008fb9 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,336 @@ +//! Utilities for common archive operations. +//! +//! ## Buffer access +//! +//! Helper functions to get the root object of an archive under certain conditions. +//! +//! ## Alignment +//! +//! Alignment helpers ensure that byte buffers are properly aligned when accessing and deserializing +//! data. + +#[cfg(feature = "alloc")] +mod aligned_vec; +mod scratch_vec; + +#[cfg(feature = "alloc")] +use crate::{ + de::deserializers::SharedDeserializeMap, + ser::{serializers::AllocSerializer, Serializer}, + Fallible, +}; +use crate::{Archive, ArchiveUnsized, Deserialize, RelPtr, Serialize}; +use core::{ + mem, + ops::{Deref, DerefMut}, + pin::Pin, +}; + +#[doc(inline)] +#[cfg(feature = "alloc")] +pub use self::aligned_vec::*; +#[doc(inline)] +pub use self::scratch_vec::*; + +#[cfg(debug_assertions)] +#[inline] +fn check_alignment(ptr: *const u8) { + let expect_align = core::mem::align_of::(); + let actual_align = (ptr as usize) & (expect_align - 1); + debug_assert_eq!( + actual_align, + 0, + concat!( + "unaligned buffer, expected alignment {} but found alignment {}\n", + "help: rkyv requires byte buffers to be aligned to access the data inside.\n", + " Using an ALignedVec or manually aligning your data with #[align(...)]\n", + " may resolve this issue.", + ), + expect_align, + 1 << actual_align.trailing_zeros() + ); +} + +/// Casts an archived value from the given byte slice at the given position. +/// +/// This helps avoid situations where lifetimes get inappropriately assigned and allow buffer +/// mutation after getting archived value references. +/// +/// # Safety +/// +/// A `T::Archived` must be archived at the given position in the byte slice. +#[inline] +pub unsafe fn archived_value(bytes: &[u8], pos: usize) -> &T::Archived { + #[cfg(debug_assertions)] + check_alignment::(bytes.as_ptr()); + + &*bytes.as_ptr().add(pos).cast() +} + +/// Casts a mutable archived value from the given byte slice at the given position. +/// +/// This helps avoid situations where lifetimes get inappropriately assigned and allow buffer +/// mutation after getting archived value references. +/// +/// # Safety +/// +/// A `T::Archived` must be archived at the given position in the byte slice. +#[inline] +pub unsafe fn archived_value_mut( + bytes: Pin<&mut [u8]>, + pos: usize, +) -> Pin<&mut T::Archived> { + #[cfg(debug_assertions)] + check_alignment::(bytes.as_ptr()); + + Pin::new_unchecked(&mut *bytes.get_unchecked_mut().as_mut_ptr().add(pos).cast()) +} + +/// Casts a [`RelPtr`] to the given unsized type from the given byte slice at the given position and +/// returns the value it points to. +/// +/// This helps avoid situations where lifetimes get inappropriately assigned and allow buffer +/// mutation after getting archived value references. +/// +/// # Safety +/// +/// A `RelPtr` must be archived at the given position in the byte slice. +#[inline] +pub unsafe fn archived_unsized_value( + bytes: &[u8], + pos: usize, +) -> &T::Archived { + #[cfg(debug_assertions)] + check_alignment::>(bytes.as_ptr()); + + let rel_ptr = &*bytes.as_ptr().add(pos).cast::>(); + &*rel_ptr.as_ptr() +} + +/// Casts a mutable [`RelPtr`] to the given unsized type from the given byte slice at the given +/// position and returns the value it points to. +/// +/// This helps avoid situations where lifetimes get inappropriately assigned and allow buffer +/// mutation after getting archived value references. +/// +/// # Safety +/// +/// A `RelPtr` must be archived at the given position in the byte slice. +#[inline] +pub unsafe fn archived_unsized_value_mut( + bytes: Pin<&mut [u8]>, + pos: usize, +) -> Pin<&mut T::Archived> { + #[cfg(debug_assertions)] + check_alignment::>(bytes.as_ptr()); + + let rel_ptr = &mut *bytes + .get_unchecked_mut() + .as_mut_ptr() + .add(pos) + .cast::>(); + Pin::new_unchecked(&mut *rel_ptr.as_mut_ptr()) +} + +/// Casts an archived value from the given byte slice by calculating the root position. +/// +/// This is a wrapper for [`archived_value`](crate::archived_value) that calculates the correct +/// position of the root using the length of the byte slice. If your byte slice is not guaranteed to +/// end immediately after the root object, you may need to store the position of the root object +/// returned from [`serialize_value`](crate::ser::Serializer::serialize_value). +/// +/// # Safety +/// +/// - The byte slice must represent an archived object +/// - The root of the object must be stored at the end of the slice (this is the default behavior) +#[inline] +pub unsafe fn archived_root(bytes: &[u8]) -> &T::Archived { + archived_value::(bytes, bytes.len() - mem::size_of::()) +} + +/// Casts a mutable archived value from the given byte slice by calculating the root position. +/// +/// This is a wrapper for [`archived_value_mut`](crate::archived_value_mut) that calculates the +/// correct position of the root using the length of the byte slice. If your byte slice is not +/// guaranteed to end immediately after the root object, you may need to store the position of the +/// root object returned from [`serialize_value`](crate::ser::Serializer::serialize_value). +/// +/// # Safety +/// +/// - The byte slice must represent an archived object +/// - The root of the object must be stored at the end of the slice (this is the default behavior) +#[inline] +pub unsafe fn archived_root_mut( + bytes: Pin<&mut [u8]>, +) -> Pin<&mut T::Archived> { + let pos = bytes.len() - mem::size_of::(); + archived_value_mut::(bytes, pos) +} + +/// Casts a [`RelPtr`] to the given unsized type from the given byte slice by calculating the root +/// position. +/// +/// This is a wrapper for [`archived_unsized_value`](crate::archived_unsized_value) that calculates +/// the correct position of the root using the length of the byte slice. If your byte slice is not +/// guaranteed to end immediately after the root object, you may need to store the position of the +/// root object returned from +/// [`serialize_unsized_value`](crate::ser::Serializer::serialize_unsized_value). +/// +/// # Safety +/// +/// - The byte slice must represent an archived object +/// - The root of the object must be stored at the end of the slice (this is the default behavior) +#[inline] +pub unsafe fn archived_unsized_root(bytes: &[u8]) -> &T::Archived { + archived_unsized_value::(bytes, bytes.len() - mem::size_of::>()) +} + +/// Casts a [`RelPtr`] to the given unsized type from the given byte slice by calculating the root +/// position. +/// +/// This is a wrapper for [`archived_unsized_value_mut`](crate::archived_unsized_value_mut) that +/// calculates the correct position of the root using the length of the byte slice. If your byte +/// slice is not guaranteed to end immediately after the root object, you may need to store the +/// position of the root object returned from +/// [`serialize_unsized_value`](crate::ser::Serializer::serialize_unsized_value). +/// +/// # Safety +/// +/// - The byte slice must represent an archived object +/// - The root of the object must be stored at the end of the slice (this is the default behavior) +#[inline] +pub unsafe fn archived_unsized_root_mut( + bytes: Pin<&mut [u8]>, +) -> Pin<&mut T::Archived> { + let pos = bytes.len() - mem::size_of::>(); + archived_unsized_value_mut::(bytes, pos) +} + +/// A buffer of bytes aligned to 16 bytes. +/// +/// # Examples +/// +/// ``` +/// use core::mem; +/// use rkyv::AlignedBytes; +/// +/// assert_eq!(mem::align_of::(), 1); +/// assert_eq!(mem::align_of::>(), 16); +/// ``` +#[derive(Archive, Clone, Copy, Debug, Deserialize, Serialize)] +#[archive(crate = "crate")] +#[repr(C, align(16))] +pub struct AlignedBytes(pub [u8; N]); + +impl Default for AlignedBytes { + fn default() -> Self { + Self([0; N]) + } +} + +impl Deref for AlignedBytes { + type Target = [u8; N]; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for AlignedBytes { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsRef<[u8]> for AlignedBytes { + #[inline] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsMut<[u8]> for AlignedBytes { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_mut() + } +} + +/// Serializes the given value and returns the resulting bytes. +/// +/// The const generic parameter `N` specifies the number of bytes to pre-allocate as scratch space. +/// Choosing a good default value for your data can be difficult without any data, so consider using +/// [`ScratchTracker`](crate::ser::serializers::ScratchTracker) to determine how much scratch space +/// is typically used. +/// +/// This function is only available with the `alloc` feature because it uses a general-purpose +/// serializer. In no-alloc and high-performance environments, the serializer should be customized +/// for the specific situation. +/// +/// # Examples +/// ``` +/// let value = vec![1, 2, 3, 4]; +/// +/// let bytes = rkyv::to_bytes::<_, 1024>(&value).expect("failed to serialize vec"); +/// // SAFETY: +/// // - The byte slice represents an archived object +/// // - The root of the object is stored at the end of the slice +/// let deserialized = unsafe { +/// rkyv::from_bytes_unchecked::>(&bytes) +/// .expect("failed to deserialize vec") +/// }; +/// +/// assert_eq!(deserialized, value); +/// ``` +#[cfg(feature = "alloc")] +#[inline] +pub fn to_bytes( + value: &T, +) -> Result as Fallible>::Error> +where + T: Serialize>, +{ + let mut serializer = AllocSerializer::::default(); + serializer.serialize_value(value)?; + Ok(serializer.into_serializer().into_inner()) +} + +/// Deserializes a value from the given bytes. +/// +/// This function is only available with the `alloc` feature because it uses a general-purpose +/// deserializer. In no-alloc and high-performance environments, the deserializer should be +/// customized for the specific situation. +/// +/// # Safety +/// +/// - The byte slice must represent an archived object +/// - The root of the object must be stored at the end of the slice (this is the default behavior) +/// +/// # Examples +/// ``` +/// let value = vec![1, 2, 3, 4]; +/// +/// let bytes = rkyv::to_bytes::<_, 1024>(&value).expect("failed to serialize vec"); +/// // SAFETY: +/// // - The byte slice represents an archived object +/// // - The root of the object is stored at the end of the slice +/// let deserialized = unsafe { +/// rkyv::from_bytes_unchecked::>(&bytes) +/// .expect("failed to deserialize vec") +/// }; +/// +/// assert_eq!(deserialized, value); +/// ``` +#[cfg(feature = "alloc")] +#[inline] +pub unsafe fn from_bytes_unchecked( + bytes: &[u8], +) -> Result::Error> +where + T: Archive, + T::Archived: Deserialize, +{ + archived_root::(bytes).deserialize(&mut SharedDeserializeMap::default()) +} diff --git a/src/util/scratch_vec.rs b/src/util/scratch_vec.rs new file mode 100644 index 0000000..12167f6 --- /dev/null +++ b/src/util/scratch_vec.rs @@ -0,0 +1,495 @@ +use crate::ser::ScratchSpace; +use core::{ + alloc::Layout, + borrow::{Borrow, BorrowMut}, + fmt, + mem::MaybeUninit, + ops, + ptr::NonNull, + slice, +}; + +/// A vector view into serializer scratch space. +pub struct ScratchVec { + ptr: NonNull, + cap: usize, + len: usize, +} + +impl Drop for ScratchVec { + fn drop(&mut self) { + for i in 0..self.len { + unsafe { + core::ptr::drop_in_place(self.ptr.as_ptr().add(i)); + } + } + } +} + +// SAFETY: ScratchVec is safe to send to another thread is T is safe to send to another thread +unsafe impl Send for ScratchVec {} + +// SAFETY: ScratchVec is safe to share between threads if T is safe to share between threads +unsafe impl Sync for ScratchVec {} + +impl ScratchVec { + /// Constructs a new, empty `ScratchVec` with the specified capacity. + /// + /// The vector will be able to hold exactly `capacity` elements. If `capacity` is 0, the vector + /// will not allocate. + /// + /// # Safety + /// + /// - The vector must not outlive the given scratch space. + /// - Vectors must be dropped in the reverse order they are allocated. + #[inline] + pub unsafe fn new( + scratch_space: &mut S, + capacity: usize, + ) -> Result { + let layout = Layout::array::(capacity).unwrap(); + if layout.size() == 0 { + Ok(Self { + ptr: NonNull::dangling(), + cap: capacity, + len: 0, + }) + } else { + let ptr = scratch_space.push_scratch(layout)?; + Ok(Self { + ptr: ptr.cast(), + cap: capacity, + len: 0, + }) + } + } + + /// Frees the memory associated with the scratch vec and releases it back to the scratch space. + /// + /// This must be called when serialization succeeds, but may be omitted when serialization + /// fails. In that case, the elements of the scratch vec will be dropped but the memory will not + /// be popped. It is the duty of the scratch space in that case to ensure that memory resources + /// are properly cleaned up. + /// + /// # Safety + /// + /// The given scratch space must be the same one used to allocate the scratch vec. + #[inline] + pub unsafe fn free( + self, + scratch_space: &mut S, + ) -> Result<(), S::Error> { + let layout = self.layout(); + if layout.size() != 0 { + let ptr = self.ptr.cast(); + core::mem::drop(self); + scratch_space.pop_scratch(ptr, layout)?; + } + Ok(()) + } + + #[inline] + fn layout(&self) -> Layout { + Layout::array::(self.cap).unwrap() + } + + /// Clears the vector, removing all values. + /// + /// Note that this method has no effect on the allocated capacity of the vector. + #[inline] + pub fn clear(&mut self) { + self.len = 0; + } + + /// Returns an unsafe mutable pointer to the vector's buffer. + /// + /// The caller must ensure that the vector outlives the pointer this function returns, or else + /// it will end up pointing to garbage. + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self.ptr.as_ptr() + } + + /// Extracts a mutable slice of the entire vector. + /// + /// Equivalent to `&mut s[..]`. + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [T] { + unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } + } + + /// Returns a raw pointer to the vector's buffer. + /// + /// The caller must ensure that the vector outlives the pointer this functions returns, or else + /// it will end up pointing to garbage. + /// + /// The caller must also ensure that the memory the pointer (non-transitively) points to is + /// never written to (except inside an `UnsafeCell`) using this pointer or any pointer derived + /// from it. If you need to mutate the contents of the slice, use + /// [`as_mut_ptr`](ScratchVec::as_mut_ptr). + #[inline] + pub fn as_ptr(&self) -> *const T { + self.ptr.as_ptr() + } + + /// Extracts a slice containing the entire vector. + /// + /// Equivalent to `&s[..]`. + #[inline] + pub fn as_slice(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } + } + + /// Returns the number of elements the vector can hole without reallocating. + #[inline] + pub fn capacity(&self) -> usize { + self.cap + } + + /// Ensures that there is capacity for at least `additional` more elements to be inserted into + /// the `ScratchVec`. + /// + /// # Panics + /// + /// Panics if the required capacity exceeds the available capacity. + #[inline] + pub fn reserve(&mut self, additional: usize) { + if self.len + additional > self.cap { + panic!("reserve requested more capacity than the scratch vec has available"); + } + } + + /// Returns `true` if the vector contains no elements. + #[inline] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns the number of elements in the vector, also referred to as its `length`. + #[inline] + pub fn len(&self) -> usize { + self.len + } + + /// Copies and appends all elements in a slice to the `ScratchVec`. + /// + /// The elements of the slice are appended in-order. + #[inline] + pub fn extend_from_slice(&mut self, other: &[T]) { + if !other.is_empty() { + self.reserve(other.len()); + unsafe { + core::ptr::copy_nonoverlapping( + other.as_ptr(), + self.as_mut_ptr().add(self.len()), + other.len(), + ); + } + self.len += other.len(); + } + } + + /// Removes the last element from a vector and returns it, or `None` if it is empty. + #[inline] + pub fn pop(&mut self) -> Option { + if self.len == 0 { + None + } else { + unsafe { + self.len -= 1; + Some(self.as_ptr().add(self.len()).read()) + } + } + } + + /// Appends an element to the back of a collection. + #[inline] + pub fn push(&mut self, value: T) { + unsafe { + self.reserve(1); + self.as_mut_ptr().add(self.len).write(value); + self.len += 1; + } + } + + /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the + /// given `AlignedVec`. After calling `reserve_exact`, capacity will be greater than or equal + /// to `self.len() + additional`. Does nothing if the capacity is already sufficient. + /// + /// # Panics + /// + /// Panics if the required capacity exceeds the available capacity. + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.reserve(additional); + } + + /// Forces the length of the vector to `new_len`. + /// + /// This is a low-level operation that maintains none of the normal invariants of the type. + /// + /// # Safety + /// + /// - `new_len` must be less than or equal to [`capacity()`](ScratchVec::capacity) + /// - The elements at `old_len..new_len` must be initialized + #[inline] + pub unsafe fn set_len(&mut self, new_len: usize) { + debug_assert!(new_len <= self.capacity()); + + self.len = new_len; + } + + // This is taken from `slice::range`, which is not yet stable. + #[inline] + fn drain_range(range: R, bounds: ops::RangeTo) -> ops::Range + where + R: ops::RangeBounds, + { + let len = bounds.end; + + let start: ops::Bound<&usize> = range.start_bound(); + let start = match start { + ops::Bound::Included(&start) => start, + ops::Bound::Excluded(start) => start + .checked_add(1) + .unwrap_or_else(|| panic!("attempted to index slice from after maximum usize")), + ops::Bound::Unbounded => 0, + }; + + let end: ops::Bound<&usize> = range.end_bound(); + let end = match end { + ops::Bound::Included(end) => end + .checked_add(1) + .unwrap_or_else(|| panic!("attempted to index slice up to maximum usize")), + ops::Bound::Excluded(&end) => end, + ops::Bound::Unbounded => len, + }; + + if start > end { + panic!("slice index starts at {} but ends at {}", start, end); + } + if end > len { + panic!( + "range start index {} out of range for slice of length {}", + end, len + ); + } + + ops::Range { start, end } + } + + /// Creates a draining iterator that removes the specified range in the vector and yields the + /// removed items. + /// + /// When the iterator **is** dropped, all elements in the range are removed from the vector, + /// even if the iterator was not fully consumed. If the iterator **is not** dropped (with + /// `mem::forget` for example), it is unspecified how many elements are removed. + /// + /// # Panics + /// + /// Panics if the starting point is greater than the end point or if the end point is greater + /// than the length of the vector. + #[inline] + pub fn drain>(&mut self, range: R) -> Drain<'_, T> { + let len = self.len(); + let ops::Range { start, end } = Self::drain_range(range, ..len); + + unsafe { + self.set_len(start); + let range_slice = slice::from_raw_parts_mut(self.as_mut_ptr().add(start), end - start); + Drain { + tail_start: end, + tail_len: len - end, + iter: range_slice.iter(), + vec: NonNull::from(self), + } + } + } +} + +impl ScratchVec> { + /// Assuming that all the elements are initialized, removes the `MaybeUninit` wrapper from the + /// vector. + /// + /// # Safety + /// + /// It is up to the caller to guarantee that the `MaybeUninit` elements really are in an + /// initialized state. Calling this when the content is not yet fully initialized causes + /// undefined behavior. + #[inline] + pub fn assume_init(self) -> ScratchVec { + ScratchVec { + ptr: self.ptr.cast(), + cap: self.cap, + len: self.len, + } + } +} + +impl AsMut<[T]> for ScratchVec { + #[inline] + fn as_mut(&mut self) -> &mut [T] { + self.as_mut_slice() + } +} + +impl AsRef<[T]> for ScratchVec { + #[inline] + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +impl Borrow<[T]> for ScratchVec { + #[inline] + fn borrow(&self) -> &[T] { + self.as_slice() + } +} + +impl BorrowMut<[T]> for ScratchVec { + #[inline] + fn borrow_mut(&mut self) -> &mut [T] { + self.as_mut_slice() + } +} + +impl fmt::Debug for ScratchVec { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_slice().fmt(f) + } +} + +impl ops::Deref for ScratchVec { + type Target = [T]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl ops::DerefMut for ScratchVec { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut_slice() + } +} + +impl> ops::Index for ScratchVec { + type Output = >::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + &self.as_slice()[index] + } +} + +impl> ops::IndexMut for ScratchVec { + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.as_mut_slice()[index] + } +} + +/// A draining iterator for `ScratchVec`. +/// +/// This `struct` is created by [`ScratchVec::drain`]. See its documentation for more. +pub struct Drain<'a, T: 'a> { + tail_start: usize, + tail_len: usize, + iter: slice::Iter<'a, T>, + vec: NonNull>, +} + +impl fmt::Debug for Drain<'_, T> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Drain").field(&self.iter.as_slice()).finish() + } +} + +impl Drain<'_, T> { + /// Returns the remaining items of this iterator as a slice. + #[inline] + pub fn as_slice(&self) -> &[T] { + self.iter.as_slice() + } +} + +impl AsRef<[T]> for Drain<'_, T> { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +impl Iterator for Drain<'_, T> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.iter + .next() + .map(|elt| unsafe { core::ptr::read(elt as *const _) }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl DoubleEndedIterator for Drain<'_, T> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter + .next_back() + .map(|elt| unsafe { core::ptr::read(elt as *const _) }) + } +} + +impl Drop for Drain<'_, T> { + fn drop(&mut self) { + /// Continues dropping the remaining elements in the `Drain`, then moves back the + /// un-`Drain`ed elements to restore the original `Vec`. + struct DropGuard<'r, 'a, T>(&'r mut Drain<'a, T>); + + impl<'r, 'a, T> Drop for DropGuard<'r, 'a, T> { + fn drop(&mut self) { + // Continue the same loop we have below. If the loop already finished, this does + // nothing. + self.0.for_each(drop); + + if self.0.tail_len > 0 { + unsafe { + let source_vec = self.0.vec.as_mut(); + // memmove back untouched tail, update to new length + let start = source_vec.len(); + let tail = self.0.tail_start; + if tail != start { + let src = source_vec.as_ptr().add(tail); + let dst = source_vec.as_mut_ptr().add(start); + core::ptr::copy(src, dst, self.0.tail_len); + } + source_vec.set_len(start + self.0.tail_len); + } + } + } + } + + // exhaust self first + while let Some(item) = self.next() { + let guard = DropGuard(self); + drop(item); + core::mem::forget(guard); + } + + // Drop a `DropGuard` to move back the non-drained tail of `self`. + DropGuard(self); + } +} + +impl ExactSizeIterator for Drain<'_, T> {} + +impl core::iter::FusedIterator for Drain<'_, T> {} diff --git a/src/validation/mod.rs b/src/validation/mod.rs new file mode 100644 index 0000000..8504404 --- /dev/null +++ b/src/validation/mod.rs @@ -0,0 +1,371 @@ +//! Validation implementations and helper types. + +pub mod owned; +pub mod validators; + +use crate::{Archive, ArchivePointee, CheckBytes, Fallible, RelPtr}; +use core::{alloc::Layout, alloc::LayoutError, any::TypeId, fmt}; +use ptr_meta::Pointee; +#[cfg(feature = "std")] +use std::error::Error; + +// Replace this trait with core::mem::{align_of_val_raw, size_of_val_raw} when they get stabilized. + +/// Gets the layout of a type from its pointee type and metadata. +pub trait LayoutRaw +where + Self: Pointee, +{ + /// Gets the layout of the type. + fn layout_raw(metadata: ::Metadata) -> Result; +} + +impl LayoutRaw for T { + #[inline] + fn layout_raw(_: ::Metadata) -> Result { + Ok(Layout::new::()) + } +} + +impl LayoutRaw for [T] { + #[inline] + fn layout_raw(metadata: ::Metadata) -> Result { + Layout::array::(metadata) + } +} + +impl LayoutRaw for str { + #[inline] + fn layout_raw(metadata: ::Metadata) -> Result { + Layout::array::(metadata) + } +} + +#[cfg(feature = "std")] +impl LayoutRaw for ::std::ffi::CStr { + #[inline] + fn layout_raw(metadata: ::Metadata) -> Result { + Layout::array::<::std::os::raw::c_char>(metadata) + } +} + +/// A context that can validate nonlocal archive memory. +pub trait ArchiveContext: Fallible { + /// A prefix range from an archive context. + /// + /// Ranges must be popped in the reverse order they are pushed. + type PrefixRange: 'static; + + /// A suffix range from an archive context. + /// + /// Ranges must be popped in the reverse order they are pushed. + type SuffixRange: 'static; + + /// Checks that a relative pointer points to an address within the archive. + /// + /// The returned pointer is not guaranteed to point to an object that is contained completely + /// within the archive. Use [`bounds_check_layout`](ArchiveContext::bounds_check_layout) to + /// verify that an object with some layout is located at the target address. + /// + /// # Safety + /// + /// - `base` must be inside the archive this validator was created for. + unsafe fn bounds_check_ptr( + &mut self, + base: *const u8, + offset: isize, + ) -> Result<*const u8, Self::Error>; + + /// Checks that a given pointer can be dereferenced. + /// + /// The returned pointer is guaranteed to be located within the archive. This means that the + /// returned pointer is safe to check, but may be vulnerable to memory overlap and recursion + /// attacks unless the subtree range is properly restricted. Use `check_subtree_ptr` to perform + /// the subtree range check as well. + /// + /// # Safety + /// + /// - `data_address` must be inside the archive this validator was created for. + /// - `layout` must be the layout for the given pointer. + unsafe fn bounds_check_layout( + &mut self, + data_address: *const u8, + layout: &Layout, + ) -> Result<(), Self::Error>; + + /// Checks that the given relative pointer can be dereferenced. + /// + /// The returned pointer is guaranteed to be located within the archive. This means that the + /// returned pointer is safe to check, but may be vulnerable to memory overlap and recursion + /// attacks unless the subtree range is properly restricted. Use `check_subtree_ptr` to perform + /// the subtree range check as well. + /// + /// # Safety + /// + /// - `base` must be inside the archive this validator was created for. + /// - `metadata` must be the metadata for the pointer defined by `base` and `offset`. + #[inline] + unsafe fn check_ptr( + &mut self, + base: *const u8, + offset: isize, + metadata: T::Metadata, + ) -> Result<*const T, Self::Error> { + let data_address = self.bounds_check_ptr(base, offset)?; + let layout = T::layout_raw(metadata).map_err(Self::wrap_layout_error)?; + let ptr = ptr_meta::from_raw_parts(data_address.cast(), metadata); + self.bounds_check_layout(data_address, &layout)?; + Ok(ptr) + } + + /// Checks that the given `RelPtr` can be dereferenced. + /// + /// The returned pointer is guaranteed to be located within the archive. This means that the + /// returned pointer is safe to check, but may be vulnerable to memory overlap and recursion + /// attacks unless the subtree range is properly restricted. Use `check_subtree_ptr` to perform + /// the subtree range check as well. + /// + /// # Safety + /// + /// - `rel_ptr` must be inside the archive this validator was created for. + #[inline] + unsafe fn check_rel_ptr( + &mut self, + rel_ptr: &RelPtr, + ) -> Result<*const T, Self::Error> { + let metadata = T::pointer_metadata(rel_ptr.metadata()); + self.check_ptr(rel_ptr.base(), rel_ptr.offset(), metadata) + } + + /// Checks that the given data address and layout is located completely within the subtree + /// range. + /// + /// # Safety + /// + /// - `data_address` must be inside the archive this validator was created for. + unsafe fn bounds_check_subtree_ptr_layout( + &mut self, + data_address: *const u8, + layout: &Layout, + ) -> Result<(), Self::Error>; + + /// Checks that the given pointer is located completely within the subtree range. + /// + /// # Safety + /// + /// - `ptr` must be inside the archive this validator was created for. + #[inline] + unsafe fn bounds_check_subtree_ptr( + &mut self, + ptr: *const T, + ) -> Result<(), Self::Error> { + let layout = T::layout_raw(ptr_meta::metadata(ptr)).map_err(Self::wrap_layout_error)?; + self.bounds_check_subtree_ptr_layout(ptr.cast(), &layout) + } + + /// Checks that the given relative pointer to a subtree can be dereferenced. + /// + /// # Safety + /// + /// - `base` must be inside the archive this validator was created for. + /// - `metadata` must be the metadata for the pointer defined by `base` and `offset`. + #[inline] + unsafe fn check_subtree_ptr( + &mut self, + base: *const u8, + offset: isize, + metadata: T::Metadata, + ) -> Result<*const T, Self::Error> { + let ptr = self.check_ptr(base, offset, metadata)?; + self.bounds_check_subtree_ptr(ptr)?; + Ok(ptr) + } + + /// Checks that the given `RelPtr` to a subtree can be dereferenced. + /// + /// # Safety + /// + /// - `rel_ptr` must be inside the archive this validator was created for. + #[inline] + unsafe fn check_subtree_rel_ptr( + &mut self, + rel_ptr: &RelPtr, + ) -> Result<*const T, Self::Error> { + let ptr = self.check_rel_ptr(rel_ptr)?; + self.bounds_check_subtree_ptr(ptr)?; + Ok(ptr) + } + + /// Pushes a new subtree range onto the validator and starts validating it. + /// + /// After calling `push_subtree_claim_to`, the validator will have a subtree range starting at + /// the original start and ending at `root`. After popping the returned range, the validator + /// will have a subtree range starting at `end` and ending at the original end. + /// + /// # Safety + /// + /// `root` and `end` must be located inside the archive. + unsafe fn push_prefix_subtree_range( + &mut self, + root: *const u8, + end: *const u8, + ) -> Result; + + /// Pushes a new subtree range onto the validator and starts validating it. + /// + /// The claimed range spans from the end of `start` to the end of the current subobject range. + /// + /// # Safety + /// + /// `` must be located inside the archive. + #[inline] + unsafe fn push_prefix_subtree( + &mut self, + root: *const T, + ) -> Result { + let layout = T::layout_raw(ptr_meta::metadata(root)).map_err(Self::wrap_layout_error)?; + self.push_prefix_subtree_range(root as *const u8, (root as *const u8).add(layout.size())) + } + + /// Pops the given range, restoring the original state with the pushed range removed. + /// + /// If the range was not popped in reverse order, an error is returned. + fn pop_prefix_range(&mut self, range: Self::PrefixRange) -> Result<(), Self::Error>; + + /// Pushes a new subtree range onto the validator and starts validating it. + /// + /// After calling `push_prefix_subtree_range`, the validator will have a subtree range starting + /// at `start` and ending at `root`. After popping the returned range, the validator will have a + /// subtree range starting at the original start and ending at `start`. + /// + /// # Safety + /// + /// `start` and `root` must be located inside the archive. + unsafe fn push_suffix_subtree_range( + &mut self, + start: *const u8, + root: *const u8, + ) -> Result; + + /// Finishes the given range, restoring the original state with the pushed range removed. + /// + /// If the range was not popped in reverse order, an error is returned. + fn pop_suffix_range(&mut self, range: Self::SuffixRange) -> Result<(), Self::Error>; + + /// Wraps a layout error in an ArchiveContext error + fn wrap_layout_error(error: LayoutError) -> Self::Error; + + /// Verifies that all outstanding claims have been returned. + fn finish(&mut self) -> Result<(), Self::Error>; +} + +/// A context that can validate shared archive memory. +/// +/// Shared pointers require this kind of context to validate. +pub trait SharedContext: Fallible { + /// Registers the given `ptr` as a shared pointer with the given type. + /// + /// Returns `true` if the pointer was newly-registered and `check_bytes` should be called. + fn register_shared_ptr(&mut self, ptr: *const u8, type_id: TypeId) + -> Result; +} + +/// Errors that can occur when checking an archive. +#[derive(Debug)] +pub enum CheckArchiveError { + /// An error that occurred while validating an object + CheckBytesError(T), + /// A context error occurred + ContextError(C), +} + +impl fmt::Display for CheckArchiveError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CheckArchiveError::CheckBytesError(e) => write!(f, "check bytes error: {}", e), + CheckArchiveError::ContextError(e) => write!(f, "context error: {}", e), + } + } +} + +#[cfg(feature = "std")] +impl Error for CheckArchiveError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + CheckArchiveError::CheckBytesError(e) => Some(e as &dyn Error), + CheckArchiveError::ContextError(e) => Some(e as &dyn Error), + } + } +} + +/// The error type that can be produced by checking the given type with the given validator. +pub type CheckTypeError = + CheckArchiveError<>::Error, ::Error>; + +// TODO: change this to be the public-facing API (uses pos: isize instead of pos: usize) +#[inline] +fn internal_check_archived_value_with_context<'a, T, C>( + buf: &'a [u8], + pos: isize, + context: &mut C, +) -> Result<&'a T::Archived, CheckTypeError> +where + T: Archive, + T::Archived: CheckBytes + Pointee, + C: ArchiveContext + ?Sized, +{ + unsafe { + let ptr = context + .check_subtree_ptr(buf.as_ptr(), pos, ()) + .map_err(CheckArchiveError::ContextError)?; + + let range = context + .push_prefix_subtree(ptr) + .map_err(CheckArchiveError::ContextError)?; + let result = + CheckBytes::check_bytes(ptr, context).map_err(CheckArchiveError::CheckBytesError)?; + context + .pop_prefix_range(range) + .map_err(CheckArchiveError::ContextError)?; + + context.finish().map_err(CheckArchiveError::ContextError)?; + Ok(result) + } +} + +/// Checks the given archive with an additional context. +/// +/// See [`check_archived_value`](crate::validation::validators::check_archived_value) for more details. +#[inline] +pub fn check_archived_value_with_context<'a, T, C>( + buf: &'a [u8], + pos: usize, + context: &mut C, +) -> Result<&'a T::Archived, CheckTypeError> +where + T: Archive, + T::Archived: CheckBytes + Pointee, + C: ArchiveContext + ?Sized, +{ + internal_check_archived_value_with_context::(buf, pos as isize, context) +} + +/// Checks the given archive with an additional context. +/// +/// See [`check_archived_value`](crate::validation::validators::check_archived_value) for more details. +#[inline] +pub fn check_archived_root_with_context<'a, T, C>( + buf: &'a [u8], + context: &mut C, +) -> Result<&'a T::Archived, CheckTypeError> +where + T: Archive, + T::Archived: CheckBytes + Pointee, + C: ArchiveContext + ?Sized, +{ + internal_check_archived_value_with_context::( + buf, + buf.len() as isize - core::mem::size_of::() as isize, + context, + ) +} diff --git a/src/validation/owned.rs b/src/validation/owned.rs new file mode 100644 index 0000000..5ea07b5 --- /dev/null +++ b/src/validation/owned.rs @@ -0,0 +1,56 @@ +//! Common validation utilities for owned containers (`Box`, `String`, `Vec`, etc.). + +use crate::{ArchivePointee, Fallible}; +use bytecheck::CheckBytes; +use core::fmt; +#[cfg(feature = "std")] +use std::error::Error; + +/// Errors that can occur while chechking archived owned pointers +#[derive(Debug)] +pub enum OwnedPointerError { + /// The pointer failed to validate due to invalid metadata. + PointerCheckBytesError(T), + /// The value pointed to by the owned pointer was invalid. + ValueCheckBytesError(R), + /// An error occurred from the validation context. + ContextError(C), +} + +impl fmt::Display for OwnedPointerError +where + T: fmt::Display, + R: fmt::Display, + C: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + OwnedPointerError::PointerCheckBytesError(e) => e.fmt(f), + OwnedPointerError::ValueCheckBytesError(e) => e.fmt(f), + OwnedPointerError::ContextError(e) => e.fmt(f), + } + } +} + +#[cfg(feature = "std")] +impl Error for OwnedPointerError +where + T: Error + 'static, + R: Error + 'static, + C: Error + 'static, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + OwnedPointerError::PointerCheckBytesError(e) => Some(e as &dyn Error), + OwnedPointerError::ValueCheckBytesError(e) => Some(e as &dyn Error), + OwnedPointerError::ContextError(e) => Some(e as &dyn Error), + } + } +} + +/// The [`OwnedPointerError`] for an owned `T` being checked with a some context `C`. +pub type CheckOwnedPointerError = OwnedPointerError< + <::ArchivedMetadata as CheckBytes>::Error, + >::Error, + ::Error, +>; diff --git a/src/validation/validators/archive.rs b/src/validation/validators/archive.rs new file mode 100644 index 0000000..42e72bd --- /dev/null +++ b/src/validation/validators/archive.rs @@ -0,0 +1,434 @@ +//! The provided implementation for `ArchiveContext`. + +use crate::{validation::ArchiveContext, Fallible}; +use core::{ + alloc::{Layout, LayoutError}, + fmt, + ops::Range, +}; + +/// Errors that can occur when checking archive memory. +#[derive(Debug)] +pub enum ArchiveError { + /// Computing the target of a relative pointer overflowed + Overflow { + /// The base pointer + base: *const u8, + /// The offset + offset: isize, + }, + /// The archive is under-aligned for one of the types inside + Underaligned { + /// The expected alignment of the archive + expected_align: usize, + /// The actual alignment of the archive + actual_align: usize, + }, + /// A pointer pointed outside the bounds of the archive + OutOfBounds { + /// The base of the relative pointer + base: *const u8, + /// The offset of the relative pointer + offset: isize, + /// The pointer range of the archive + range: Range<*const u8>, + }, + /// There wasn't enough space for the desired type at the pointed location + Overrun { + /// The pointer to the type + ptr: *const u8, + /// The desired size of the type + size: usize, + /// The pointer range of the archive + range: Range<*const u8>, + }, + /// The pointer wasn't aligned properly for the desired type + Unaligned { + /// The pointer to the type + ptr: *const u8, + /// The required alignment of the type + align: usize, + }, + /// The pointer wasn't within the subtree range + SubtreePointerOutOfBounds { + /// The pointer to the subtree + ptr: *const u8, + /// The subtree range + subtree_range: Range<*const u8>, + }, + /// There wasn't enough space in the subtree range for the desired type at the pointed location + SubtreePointerOverrun { + /// The pointer to the subtree type, + ptr: *const u8, + /// The desired size of the type + size: usize, + /// The subtree range + subtree_range: Range<*const u8>, + }, + /// A subtree range was popped out of order. + /// + /// Subtree ranges must be popped in the reverse of the order they are pushed. + RangePoppedOutOfOrder { + /// The expected depth of the range + expected_depth: usize, + /// The actual depth of the range + actual_depth: usize, + }, + /// A subtree range was not popped before validation concluded. + UnpoppedSubtreeRanges { + /// The depth of the last subtree that was pushed + last_range: usize, + }, + /// The maximum subtree depth was reached or exceeded. + ExceededMaximumSubtreeDepth { + /// The maximum depth that subtrees may be validated down to + max_subtree_depth: usize, + }, + /// A layout error occurred + LayoutError { + /// A layout error + layout_error: LayoutError, + }, +} + +// SAFETY: ArchiveError is safe to send to another thread +// This trait is not automatically implemented because the enum contains a pointer +unsafe impl Send for ArchiveError {} + +// SAFETY: ArchiveError is safe to share between threads +// This trait is not automatically implemented because the enum contains a pointer +unsafe impl Sync for ArchiveError {} + +impl fmt::Display for ArchiveError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ArchiveError::Overflow { base, offset } => write!( + f, + "relative pointer overflowed: base {:p} offset {}", + base, offset + ), + ArchiveError::Underaligned { + expected_align, + actual_align, + } => write!( + f, + "archive underaligned: need alignment {} but have alignment {}", + expected_align, actual_align + ), + ArchiveError::OutOfBounds { + base, + offset, + range, + } => write!( + f, + "pointer out of bounds: base {:p} offset {} not in range {:p}..{:p}", + base, offset, range.start, range.end + ), + ArchiveError::Overrun { ptr, size, range } => write!( + f, + "pointer overran buffer: ptr {:p} size {} in range {:p}..{:p}", + ptr, size, range.start, range.end + ), + ArchiveError::Unaligned { ptr, align } => write!( + f, + "unaligned pointer: ptr {:p} unaligned for alignment {}", + ptr, align + ), + ArchiveError::SubtreePointerOutOfBounds { ptr, subtree_range } => write!( + f, + "subtree pointer out of bounds: ptr {:p} not in range {:p}..{:p}", + ptr, subtree_range.start, subtree_range.end + ), + ArchiveError::SubtreePointerOverrun { + ptr, + size, + subtree_range, + } => write!( + f, + "subtree pointer overran range: ptr {:p} size {} in range {:p}..{:p}", + ptr, size, subtree_range.start, subtree_range.end + ), + ArchiveError::RangePoppedOutOfOrder { + expected_depth, + actual_depth, + } => write!( + f, + "subtree range popped out of order: expected depth {}, actual depth {}", + expected_depth, actual_depth + ), + ArchiveError::UnpoppedSubtreeRanges { last_range } => { + write!(f, "unpopped subtree ranges: last range {}", last_range) + } + ArchiveError::ExceededMaximumSubtreeDepth { max_subtree_depth } => write!( + f, + "pushed a subtree range that exceeded the maximum subtree depth of {}", + max_subtree_depth + ), + ArchiveError::LayoutError { layout_error } => { + write!(f, "a layout error occurred: {}", layout_error) + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ArchiveError {} + +/// A prefix range from an [`ArchiveValidator`]. +#[derive(Debug)] +pub struct PrefixRange { + range: Range<*const u8>, + depth: usize, +} + +// SAFETY: PrefixRange is safe to send to another thread +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Send for PrefixRange {} + +// SAFETY: PrefixRange is safe to share between threads +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Sync for PrefixRange {} + +/// A suffix range from an [`ArchiveValidator`]. +#[derive(Debug)] +pub struct SuffixRange { + start: *const u8, + depth: usize, +} + +// SAFETY: SuffixRange is safe to send to another thread +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Send for SuffixRange {} + +// SAFETY: SuffixRange is safe to share between threads +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Sync for SuffixRange {} + +/// A validator that can verify archives with nonlocal memory. +#[derive(Debug)] +pub struct ArchiveValidator<'a> { + bytes: &'a [u8], + subtree_range: Range<*const u8>, + subtree_depth: usize, + max_subtree_depth: usize, +} + +// SAFETY: ArchiveValidator is safe to send to another thread +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl<'a> Send for ArchiveValidator<'a> {} + +// SAFETY: ArchiveValidator is safe to share between threads +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl<'a> Sync for ArchiveValidator<'a> {} + +impl<'a> ArchiveValidator<'a> { + /// Creates a new bounds validator for the given bytes. + #[inline] + pub fn new(bytes: &'a [u8]) -> Self { + Self::with_max_depth(bytes, usize::MAX) + } + + /// Crates a new bounds validator for the given bytes with a maximum validation depth. + #[inline] + pub fn with_max_depth(bytes: &'a [u8], max_subtree_depth: usize) -> Self { + Self { + bytes, + subtree_range: bytes.as_ptr_range(), + subtree_depth: 0, + max_subtree_depth, + } + } + + /// Returns the log base 2 of the alignment of the archive. + /// + /// An archive that is 2-aligned will return 1, 4-aligned will return 2, 8-aligned will return 3 + /// and so on. + #[inline] + pub fn log_alignment(&self) -> usize { + (self.bytes.as_ptr() as usize).trailing_zeros() as usize + } + + /// Returns the alignment of the archive. + #[inline] + pub fn alignment(&self) -> usize { + 1 << self.log_alignment() + } +} + +impl<'a> Fallible for ArchiveValidator<'a> { + type Error = ArchiveError; +} + +impl<'a> ArchiveContext for ArchiveValidator<'a> { + type PrefixRange = PrefixRange; + type SuffixRange = SuffixRange; + + #[inline] + unsafe fn bounds_check_ptr( + &mut self, + base: *const u8, + offset: isize, + ) -> Result<*const u8, Self::Error> { + let base_pos = base.offset_from(self.bytes.as_ptr()); + let target_pos = base_pos + .checked_add(offset) + .ok_or(ArchiveError::Overflow { base, offset })?; + if target_pos < 0 || target_pos as usize > self.bytes.len() { + Err(ArchiveError::OutOfBounds { + base, + offset, + range: self.bytes.as_ptr_range(), + }) + } else { + Ok(base.offset(offset)) + } + } + + #[inline] + unsafe fn bounds_check_layout( + &mut self, + data_address: *const u8, + layout: &Layout, + ) -> Result<(), Self::Error> { + if self.alignment() < layout.align() { + Err(ArchiveError::Underaligned { + expected_align: layout.align(), + actual_align: self.alignment(), + }) + } else if (data_address as usize) & (layout.align() - 1) != 0 { + Err(ArchiveError::Unaligned { + ptr: data_address, + align: layout.align(), + }) + } else { + let available_space = self.bytes.as_ptr_range().end.offset_from(data_address) as usize; + if available_space < layout.size() { + Err(ArchiveError::Overrun { + ptr: data_address, + size: layout.size(), + range: self.bytes.as_ptr_range(), + }) + } else { + Ok(()) + } + } + } + + #[inline] + unsafe fn bounds_check_subtree_ptr_layout( + &mut self, + data_address: *const u8, + layout: &Layout, + ) -> Result<(), Self::Error> { + if layout.size() == 0 { + if data_address < self.subtree_range.start || data_address > self.subtree_range.end { + Err(ArchiveError::SubtreePointerOutOfBounds { + ptr: data_address, + subtree_range: self.subtree_range.clone(), + }) + } else { + Ok(()) + } + } else if !self.subtree_range.contains(&data_address) { + Err(ArchiveError::SubtreePointerOutOfBounds { + ptr: data_address, + subtree_range: self.subtree_range.clone(), + }) + } else { + let available_space = self.subtree_range.end.offset_from(data_address) as usize; + if available_space < layout.size() { + Err(ArchiveError::SubtreePointerOverrun { + ptr: data_address, + size: layout.size(), + subtree_range: self.subtree_range.clone(), + }) + } else { + Ok(()) + } + } + } + + #[inline] + unsafe fn push_prefix_subtree_range( + &mut self, + root: *const u8, + end: *const u8, + ) -> Result { + if self.subtree_depth >= self.max_subtree_depth { + Err(ArchiveError::ExceededMaximumSubtreeDepth { + max_subtree_depth: self.max_subtree_depth, + }) + } else { + let result = PrefixRange { + range: Range { + start: end, + end: self.subtree_range.end, + }, + depth: self.subtree_depth, + }; + self.subtree_depth += 1; + self.subtree_range.end = root; + Ok(result) + } + } + + #[inline] + fn pop_prefix_range(&mut self, range: PrefixRange) -> Result<(), Self::Error> { + if self.subtree_depth - 1 != range.depth { + Err(ArchiveError::RangePoppedOutOfOrder { + expected_depth: self.subtree_depth - 1, + actual_depth: range.depth, + }) + } else { + self.subtree_range = range.range; + self.subtree_depth = range.depth; + Ok(()) + } + } + + #[inline] + unsafe fn push_suffix_subtree_range( + &mut self, + start: *const u8, + root: *const u8, + ) -> Result { + let result = SuffixRange { + start: self.subtree_range.start, + depth: self.subtree_depth, + }; + self.subtree_depth += 1; + self.subtree_range.start = start; + self.subtree_range.end = root; + Ok(result) + } + + #[inline] + fn pop_suffix_range(&mut self, range: SuffixRange) -> Result<(), Self::Error> { + if self.subtree_depth - 1 != range.depth { + Err(ArchiveError::RangePoppedOutOfOrder { + expected_depth: self.subtree_depth - 1, + actual_depth: range.depth, + }) + } else { + self.subtree_range.end = self.subtree_range.start; + self.subtree_range.start = range.start; + self.subtree_depth = range.depth; + Ok(()) + } + } + + #[inline] + fn finish(&mut self) -> Result<(), Self::Error> { + if self.subtree_depth != 0 { + Err(ArchiveError::UnpoppedSubtreeRanges { + last_range: self.subtree_depth - 1, + }) + } else { + Ok(()) + } + } + + fn wrap_layout_error(layout_error: core::alloc::LayoutError) -> Self::Error { + ArchiveError::LayoutError { layout_error } + } +} diff --git a/src/validation/validators/mod.rs b/src/validation/validators/mod.rs new file mode 100644 index 0000000..db73ca2 --- /dev/null +++ b/src/validation/validators/mod.rs @@ -0,0 +1,238 @@ +//! Validators that can check archived types. + +mod archive; +mod shared; +mod util; + +use crate::{ + validation::{ + check_archived_root_with_context, check_archived_value_with_context, ArchiveContext, + CheckTypeError, SharedContext, + }, + Archive, Fallible, +}; +pub use archive::*; +use bytecheck::CheckBytes; +use core::{ + alloc::{Layout, LayoutError}, + any::TypeId, + fmt, +}; +pub use shared::*; +pub use util::*; + +/// The default validator error. +#[derive(Debug)] +pub enum DefaultValidatorError { + /// An archive validator error occurred. + ArchiveError(ArchiveError), + /// A shared validator error occurred. + SharedError(SharedError), +} + +impl fmt::Display for DefaultValidatorError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ArchiveError(e) => write!(f, "{}", e), + Self::SharedError(e) => write!(f, "{}", e), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for DefaultValidatorError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::ArchiveError(e) => Some(e as &dyn Error), + Self::SharedError(e) => Some(e as &dyn Error), + } + } + } +}; + +/// The default validator. +#[derive(Debug)] +pub struct DefaultValidator<'a> { + archive: ArchiveValidator<'a>, + shared: SharedValidator, +} + +impl<'a> DefaultValidator<'a> { + /// Creates a new validator from a byte range. + #[inline] + pub fn new(bytes: &'a [u8]) -> Self { + Self { + archive: ArchiveValidator::new(bytes), + shared: SharedValidator::new(), + } + } +} + +impl<'a> Fallible for DefaultValidator<'a> { + type Error = DefaultValidatorError; +} + +impl<'a> ArchiveContext for DefaultValidator<'a> { + type PrefixRange = as ArchiveContext>::PrefixRange; + type SuffixRange = as ArchiveContext>::SuffixRange; + + #[inline] + unsafe fn bounds_check_ptr( + &mut self, + base: *const u8, + offset: isize, + ) -> Result<*const u8, Self::Error> { + self.archive + .bounds_check_ptr(base, offset) + .map_err(DefaultValidatorError::ArchiveError) + } + + #[inline] + unsafe fn bounds_check_layout( + &mut self, + data_address: *const u8, + layout: &Layout, + ) -> Result<(), Self::Error> { + self.archive + .bounds_check_layout(data_address, layout) + .map_err(DefaultValidatorError::ArchiveError) + } + + #[inline] + unsafe fn bounds_check_subtree_ptr_layout( + &mut self, + data_address: *const u8, + layout: &Layout, + ) -> Result<(), Self::Error> { + self.archive + .bounds_check_subtree_ptr_layout(data_address, layout) + .map_err(DefaultValidatorError::ArchiveError) + } + + #[inline] + unsafe fn push_prefix_subtree_range( + &mut self, + root: *const u8, + end: *const u8, + ) -> Result { + self.archive + .push_prefix_subtree_range(root, end) + .map_err(DefaultValidatorError::ArchiveError) + } + + #[inline] + fn pop_prefix_range(&mut self, range: PrefixRange) -> Result<(), Self::Error> { + self.archive + .pop_prefix_range(range) + .map_err(DefaultValidatorError::ArchiveError) + } + + #[inline] + unsafe fn push_suffix_subtree_range( + &mut self, + start: *const u8, + root: *const u8, + ) -> Result { + self.archive + .push_suffix_subtree_range(start, root) + .map_err(DefaultValidatorError::ArchiveError) + } + + #[inline] + fn pop_suffix_range(&mut self, range: SuffixRange) -> Result<(), Self::Error> { + self.archive + .pop_suffix_range(range) + .map_err(DefaultValidatorError::ArchiveError) + } + + #[inline] + fn finish(&mut self) -> Result<(), Self::Error> { + self.archive + .finish() + .map_err(DefaultValidatorError::ArchiveError) + } + + #[inline] + fn wrap_layout_error(error: LayoutError) -> Self::Error { + DefaultValidatorError::ArchiveError(ArchiveValidator::wrap_layout_error(error)) + } +} + +impl<'a> SharedContext for DefaultValidator<'a> { + #[inline] + fn register_shared_ptr( + &mut self, + ptr: *const u8, + type_id: TypeId, + ) -> Result { + self.shared + .register_shared_ptr(ptr, type_id) + .map_err(DefaultValidatorError::SharedError) + } +} + +/// Checks the given archive at the given position for an archived version of the given type. +/// +/// This is a safe alternative to [`archived_value`](crate::archived_value) for types that implement +/// `CheckBytes`. +/// +/// # Examples +/// ``` +/// use rkyv::{ +/// check_archived_value, +/// ser::{Serializer, serializers::AlignedSerializer}, +/// AlignedVec, +/// Archive, +/// Serialize, +/// }; +/// use bytecheck::CheckBytes; +/// +/// #[derive(Archive, Serialize)] +/// #[archive_attr(derive(CheckBytes))] +/// struct Example { +/// name: String, +/// value: i32, +/// } +/// +/// let value = Example { +/// name: "pi".to_string(), +/// value: 31415926, +/// }; +/// +/// let mut serializer = AlignedSerializer::new(AlignedVec::new()); +/// let pos = serializer.serialize_value(&value) +/// .expect("failed to archive test"); +/// let buf = serializer.into_inner(); +/// let archived = check_archived_value::(buf.as_ref(), pos).unwrap(); +/// ``` +#[inline] +pub fn check_archived_value<'a, T: Archive>( + bytes: &'a [u8], + pos: usize, +) -> Result<&T::Archived, CheckTypeError>> +where + T::Archived: CheckBytes>, +{ + let mut validator = DefaultValidator::new(bytes); + check_archived_value_with_context::(bytes, pos, &mut validator) +} + +/// Checks the given archive at the given position for an archived version of the given type. +/// +/// This is a safe alternative to [`archived_value`](crate::archived_value) for types that implement +/// `CheckBytes`. +/// +/// See [`check_archived_value`] for more details. +#[inline] +pub fn check_archived_root<'a, T: Archive>( + bytes: &'a [u8], +) -> Result<&'a T::Archived, CheckTypeError>> +where + T::Archived: CheckBytes>, +{ + let mut validator = DefaultValidator::new(bytes); + check_archived_root_with_context::(bytes, &mut validator) +} diff --git a/src/validation/validators/shared.rs b/src/validation/validators/shared.rs new file mode 100644 index 0000000..c575387 --- /dev/null +++ b/src/validation/validators/shared.rs @@ -0,0 +1,106 @@ +//! Validators add validation capabilities by wrapping and extending basic validators. + +use crate::{validation::SharedContext, Fallible}; +use core::{any::TypeId, fmt}; + +#[cfg(not(feature = "std"))] +use hashbrown::HashMap; +#[cfg(feature = "std")] +use std::collections::HashMap; + +/// Errors that can occur when checking shared memory. +#[derive(Debug)] +pub enum SharedError { + /// Multiple pointers exist to the same location with different types + TypeMismatch { + /// A previous type that the location was checked as + previous: TypeId, + /// The current type that the location is checked as + current: TypeId, + }, +} + +impl fmt::Display for SharedError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SharedError::TypeMismatch { previous, current } => write!( + f, + "the same memory region has been claimed as two different types ({:?} and {:?})", + previous, current + ), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use std::error::Error; + + impl Error for SharedError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + SharedError::TypeMismatch { .. } => None, + } + } + } +}; + +/// A validator that can verify shared memory. +#[derive(Debug)] +pub struct SharedValidator { + shared: HashMap<*const u8, TypeId>, +} + +// SAFETY: SharedValidator is safe to send to another thread +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Send for SharedValidator {} + +// SAFETY: SharedValidator is safe to share between threads +// This trait is not automatically implemented because the struct contains a pointer +unsafe impl Sync for SharedValidator {} + +impl SharedValidator { + /// Wraps the given context and adds shared memory validation. + #[inline] + pub fn new() -> Self { + Self { + // TODO: consider deferring this to avoid the overhead of constructing + shared: HashMap::new(), + } + } +} + +impl Default for SharedValidator { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Fallible for SharedValidator { + type Error = SharedError; +} + +impl SharedContext for SharedValidator { + #[inline] + fn register_shared_ptr( + &mut self, + ptr: *const u8, + type_id: TypeId, + ) -> Result { + if let Some(previous_type_id) = self.shared.get(&ptr) { + if previous_type_id != &type_id { + Err(SharedError::TypeMismatch { + previous: *previous_type_id, + current: type_id, + }) + } else { + Ok(false) + } + } else { + self.shared.insert(ptr, type_id); + Ok(true) + } + } +} diff --git a/src/validation/validators/util.rs b/src/validation/validators/util.rs new file mode 100644 index 0000000..cb6e62f --- /dev/null +++ b/src/validation/validators/util.rs @@ -0,0 +1,74 @@ +use crate::{ + check_archived_root, + de::deserializers::SharedDeserializeMap, + validation::validators::{CheckTypeError, DefaultValidator}, + Archive, Deserialize, Fallible, +}; +use ::bytecheck::CheckBytes; +use ::core::fmt; + +/// Errors that can occur while deserializing from bytes. +#[derive(Debug)] +pub enum CheckDeserializeError { + /// A validation error occurred. + CheckBytesError(C), + /// A deserialization error occurred. + DeserializeError(D), +} + +impl fmt::Display for CheckDeserializeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CheckBytesError(e) => write!(f, "{}", e), + Self::DeserializeError(e) => write!(f, "{}", e), + } + } +} + +#[cfg(feature = "std")] +const _: () = { + use ::std::error::Error; + + impl Error for CheckDeserializeError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::CheckBytesError(e) => Some(e as &dyn Error), + Self::DeserializeError(e) => Some(e as &dyn Error), + } + } + } +}; + +/// The error type for [`from_bytes`]. +pub type FromBytesError<'a, T> = CheckDeserializeError< + CheckTypeError<::Archived, DefaultValidator<'a>>, + ::Error, +>; + +/// Checks and deserializes a value from the given bytes. +/// +/// This function is only available with the `alloc` and `validation` features because it uses a +/// general-purpose deserializer and performs validation on the data before deserializing. In +/// no-alloc and high-performance environments, the deserializer should be customized for the +/// specific situation. +/// +/// # Examples +/// ``` +/// let value = vec![1, 2, 3, 4]; +/// +/// let bytes = rkyv::to_bytes::<_, 1024>(&value).expect("failed to serialize vec"); +/// let deserialized = rkyv::from_bytes::>(&bytes).expect("failed to deserialize vec"); +/// +/// assert_eq!(deserialized, value); +/// ``` +#[inline] +pub fn from_bytes<'a, T>(bytes: &'a [u8]) -> Result> +where + T: Archive, + T::Archived: 'a + CheckBytes> + Deserialize, +{ + check_archived_root::<'a, T>(bytes) + .map_err(CheckDeserializeError::CheckBytesError)? + .deserialize(&mut SharedDeserializeMap::default()) + .map_err(CheckDeserializeError::DeserializeError) +} diff --git a/src/vec/mod.rs b/src/vec/mod.rs new file mode 100644 index 0000000..893c0b7 --- /dev/null +++ b/src/vec/mod.rs @@ -0,0 +1,358 @@ +//! An archived version of `Vec`. + +mod raw; + +use crate::{ + ser::{ScratchSpace, Serializer}, + Archive, Archived, RelPtr, Serialize, SerializeUnsized, +}; +use core::{ + borrow::Borrow, + cmp, fmt, hash, + ops::{Deref, Index, IndexMut}, + pin::Pin, + slice::SliceIndex, +}; + +pub use self::raw::*; + +/// An archived [`Vec`]. +/// +/// This uses a [`RelPtr`] to a `[T]` under the hood. Unlike +/// [`ArchivedString`](crate::string::ArchivedString), it does not have an inline representation. +#[cfg_attr(feature = "strict", repr(C))] +pub struct ArchivedVec { + ptr: RelPtr, + len: Archived, +} + +impl ArchivedVec { + /// Returns a pointer to the first element of the archived vec. + #[inline] + pub fn as_ptr(&self) -> *const T { + self.ptr.as_ptr() + } + + /// Returns the number of elements in the archived vec. + #[inline] + pub fn len(&self) -> usize { + from_archived!(self.len) as usize + } + + /// Returns whether the archived vec is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Gets the elements of the archived vec as a slice. + #[inline] + pub fn as_slice(&self) -> &[T] { + unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) } + } + + /// Gets the elements of the archived vec as a pinned mutable slice. + #[inline] + pub fn pin_mut_slice(self: Pin<&mut Self>) -> Pin<&mut [T]> { + unsafe { + self.map_unchecked_mut(|s| core::slice::from_raw_parts_mut(s.ptr.as_mut_ptr(), s.len())) + } + } + + // This method can go away once pinned slices have indexing support + // https://github.com/rust-lang/rust/pull/78370 + + /// Gets the element at the given index ot this archived vec as a pinned mutable reference. + #[inline] + pub fn index_pin(self: Pin<&mut Self>, index: I) -> Pin<&mut <[T] as Index>::Output> + where + [T]: IndexMut, + { + unsafe { self.pin_mut_slice().map_unchecked_mut(|s| &mut s[index]) } + } + + /// Resolves an archived `Vec` from a given slice. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing `value` + #[inline] + pub unsafe fn resolve_from_slice>( + slice: &[U], + pos: usize, + resolver: VecResolver, + out: *mut Self, + ) { + Self::resolve_from_len(slice.len(), pos, resolver, out); + } + + /// Resolves an archived `Vec` from a given length. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must bet he result of serializing `value` + #[inline] + pub unsafe fn resolve_from_len(len: usize, pos: usize, resolver: VecResolver, out: *mut Self) { + let (fp, fo) = out_field!(out.ptr); + RelPtr::emplace(pos + fp, resolver.pos, fo); + let (fp, fo) = out_field!(out.len); + usize::resolve(&len, pos + fp, (), fo); + } + + /// Serializes an archived `Vec` from a given slice. + #[inline] + pub fn serialize_from_slice, S: Serializer + ?Sized>( + slice: &[U], + serializer: &mut S, + ) -> Result + where + // This bound is necessary only in no-alloc, no-std situations + // SerializeUnsized is only implemented for U: Serialize in that case + [U]: SerializeUnsized, + { + Ok(VecResolver { + pos: slice.serialize_unsized(serializer)?, + }) + } + + /// Serializes an archived `Vec` from a given slice by directly copying bytes. + /// + /// # Safety + /// + /// The type being serialized must be copy-safe. Copy-safe types must be trivially copyable + /// (have the same archived and unarchived representations) and contain no padding bytes. In + /// situations where copying uninitialized bytes the output is acceptable, this function may be + /// used with types that contain padding bytes. + #[inline] + pub unsafe fn serialize_copy_from_slice( + slice: &[U], + serializer: &mut S, + ) -> Result + where + U: Serialize, + S: Serializer + ?Sized, + { + use ::core::{mem::size_of, slice::from_raw_parts}; + + let pos = serializer.align_for::()?; + + let bytes = from_raw_parts(slice.as_ptr().cast::(), size_of::() * slice.len()); + serializer.write(bytes)?; + + Ok(VecResolver { pos }) + } + + /// Serializes an archived `Vec` from a given iterator. + /// + /// This method is unable to perform copy optimizations; prefer + /// [`serialize_from_slice`](ArchivedVec::serialize_from_slice) when possible. + #[inline] + pub fn serialize_from_iter( + iter: I, + serializer: &mut S, + ) -> Result + where + U: Serialize, + B: Borrow, + I: ExactSizeIterator, + S: ScratchSpace + Serializer + ?Sized, + { + use crate::ScratchVec; + + unsafe { + let mut resolvers = ScratchVec::new(serializer, iter.len())?; + + for value in iter { + let resolver = value.borrow().serialize(serializer)?; + resolvers.push((value, resolver)); + } + let pos = serializer.align_for::()?; + for (value, resolver) in resolvers.drain(..) { + serializer.resolve_aligned(value.borrow(), resolver)?; + } + + resolvers.free(serializer)?; + + Ok(VecResolver { pos }) + } + } +} + +impl AsRef<[T]> for ArchivedVec { + #[inline] + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +impl Borrow<[T]> for ArchivedVec { + #[inline] + fn borrow(&self) -> &[T] { + self.as_slice() + } +} + +impl fmt::Debug for ArchivedVec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.as_slice()).finish() + } +} + +impl Deref for ArchivedVec { + type Target = [T]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl Eq for ArchivedVec {} + +impl hash::Hash for ArchivedVec { + #[inline] + fn hash(&self, state: &mut H) { + self.as_slice().hash(state) + } +} + +impl> Index for ArchivedVec { + type Output = <[T] as Index>::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + self.as_slice().index(index) + } +} + +impl Ord for ArchivedVec { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.as_slice().cmp(other.as_slice()) + } +} + +impl, U> PartialEq> for ArchivedVec { + #[inline] + fn eq(&self, other: &ArchivedVec) -> bool { + self.as_slice().eq(other.as_slice()) + } +} + +impl, U, const N: usize> PartialEq<[U; N]> for ArchivedVec { + #[inline] + fn eq(&self, other: &[U; N]) -> bool { + self.as_slice().eq(&other[..]) + } +} + +impl, U, const N: usize> PartialEq> for [U; N] { + #[inline] + fn eq(&self, other: &ArchivedVec) -> bool { + other.eq(self) + } +} + +impl, U> PartialEq<[U]> for ArchivedVec { + #[inline] + fn eq(&self, other: &[U]) -> bool { + self.as_slice().eq(other) + } +} + +impl, U> PartialEq> for [T] { + #[inline] + fn eq(&self, other: &ArchivedVec) -> bool { + self.eq(other.as_slice()) + } +} + +impl PartialOrd> for ArchivedVec { + #[inline] + fn partial_cmp(&self, other: &ArchivedVec) -> Option { + self.as_slice().partial_cmp(other.as_slice()) + } +} + +impl PartialOrd<[T]> for ArchivedVec { + #[inline] + fn partial_cmp(&self, other: &[T]) -> Option { + self.as_slice().partial_cmp(other) + } +} + +impl PartialOrd> for [T] { + #[inline] + fn partial_cmp(&self, other: &ArchivedVec) -> Option { + self.partial_cmp(other.as_slice()) + } +} + +/// The resolver for [`ArchivedVec`]. +pub struct VecResolver { + pos: usize, +} + +#[cfg(feature = "validation")] +const _: () = { + use crate::validation::{ + owned::{CheckOwnedPointerError, OwnedPointerError}, + ArchiveContext, + }; + use bytecheck::{CheckBytes, Error}; + + impl ArchivedVec { + /// Checks the bytes of the `ArchivedVec` with the given element checking function. + /// + /// # Safety + /// + /// `check_elements` must ensure that the pointer given to it contains only valid data. + pub unsafe fn check_bytes_with<'a, C, F>( + value: *const Self, + context: &mut C, + check_elements: F, + ) -> Result<&'a Self, CheckOwnedPointerError<[T], C>> + where + T: CheckBytes, + C: ArchiveContext + ?Sized, + F: FnOnce(*const [T], &mut C) -> Result<(), <[T] as CheckBytes>::Error>, + { + let rel_ptr = RelPtr::<[T]>::manual_check_bytes(value.cast(), context) + .map_err(OwnedPointerError::PointerCheckBytesError)?; + let ptr = context + .check_subtree_rel_ptr(rel_ptr) + .map_err(OwnedPointerError::ContextError)?; + + let range = context + .push_prefix_subtree(ptr) + .map_err(OwnedPointerError::ContextError)?; + check_elements(ptr, context).map_err(OwnedPointerError::ValueCheckBytesError)?; + context + .pop_prefix_range(range) + .map_err(OwnedPointerError::ContextError)?; + + Ok(&*value) + } + } + + impl CheckBytes for ArchivedVec + where + T: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, + { + type Error = CheckOwnedPointerError<[T], C>; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + Self::check_bytes_with::(value, context, |v, c| { + <[T]>::check_bytes(v, c).map(|_| ()) + }) + } + } +}; diff --git a/src/vec/raw.rs b/src/vec/raw.rs new file mode 100644 index 0000000..e317d58 --- /dev/null +++ b/src/vec/raw.rs @@ -0,0 +1,225 @@ +use crate::{ + ser::Serializer, + vec::{ArchivedVec, VecResolver}, + Archive, Serialize, +}; +use core::{ + borrow::Borrow, + cmp, + ops::{Deref, Index, IndexMut}, + pin::Pin, + slice::SliceIndex, +}; + +/// An archived [`Vec`]. +/// +/// This uses a [`RelPtr`](crate::rel_ptr::RelPtr) to a `[T]` under the hood. Unlike +/// [`ArchivedString`](crate::string::ArchivedString), it does not have an inline representation. +#[derive(Hash, Eq, Debug)] +#[repr(transparent)] +pub struct RawArchivedVec { + inner: ArchivedVec, +} + +impl RawArchivedVec { + /// Returns a pointer to the first element of the archived vec. + #[inline] + pub fn as_ptr(&self) -> *const T { + self.inner.as_ptr() + } + + /// Returns the number of elements in the archived vec. + #[inline] + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Returns whether the archived vec is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Gets the elements of the archived vec as a slice. + #[inline] + pub fn as_slice(&self) -> &[T] { + self.inner.as_slice() + } + + /// Gets the elements of the archived vec as a pinned mutable slice. + #[inline] + pub fn pin_mut_slice(self: Pin<&mut Self>) -> Pin<&mut [T]> { + unsafe { self.map_unchecked_mut(|s| &mut s.inner).pin_mut_slice() } + } + + // This method can go away once pinned slices have indexing support + // https://github.com/rust-lang/rust/pull/78370 + + /// Gets the element at the given index ot this archived vec as a pinned mutable reference. + #[inline] + pub fn index_pin(self: Pin<&mut Self>, index: I) -> Pin<&mut <[T] as Index>::Output> + where + [T]: IndexMut, + { + unsafe { self.map_unchecked_mut(|s| &mut s.inner).index_pin(index) } + } + + /// Resolves an archived `Vec` from a given slice. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing `value` with + /// [`serialize_copy_from_slice`](RawArchivedVec::serialize_copy_from_slice). + #[inline] + pub unsafe fn resolve_from_slice>( + slice: &[U], + pos: usize, + resolver: VecResolver, + out: *mut Self, + ) { + ArchivedVec::resolve_from_slice(slice, pos, resolver, out.cast()); + } + + /// Serializes an archived `Vec` from a given slice by directly copying bytes. + /// + /// # Safety + /// + /// The type being serialized must be copy-safe. Copy-safe types must be trivially copyable + /// (have the same archived and unarchived representations) and contain no padding bytes. In + /// situations where copying uninitialized bytes the output is acceptable, this function may be + /// used with types that contain padding bytes. + /// + /// Additionally, the type being serialized must not require any validation. All bit patterns + /// must represent valid values. + #[inline] + pub unsafe fn serialize_copy_from_slice( + slice: &[U], + serializer: &mut S, + ) -> Result + where + U: Serialize, + S: Serializer + ?Sized, + { + ArchivedVec::serialize_copy_from_slice(slice, serializer) + } +} + +impl AsRef<[T]> for RawArchivedVec { + #[inline] + fn as_ref(&self) -> &[T] { + self.inner.as_ref() + } +} + +impl Borrow<[T]> for RawArchivedVec { + #[inline] + fn borrow(&self) -> &[T] { + self.inner.borrow() + } +} + +impl Deref for RawArchivedVec { + type Target = [T]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.inner.deref() + } +} + +impl> Index for RawArchivedVec { + type Output = <[T] as Index>::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + self.inner.index(index) + } +} + +impl, U> PartialEq> for RawArchivedVec { + #[inline] + fn eq(&self, other: &RawArchivedVec) -> bool { + self.inner.eq(&other.inner) + } +} + +impl, U, const N: usize> PartialEq<[U; N]> for RawArchivedVec { + #[inline] + fn eq(&self, other: &[U; N]) -> bool { + self.inner.eq(&other[..]) + } +} + +impl, U, const N: usize> PartialEq> for [U; N] { + #[inline] + fn eq(&self, other: &RawArchivedVec) -> bool { + self.eq(&other.inner) + } +} + +impl, U> PartialEq<[U]> for RawArchivedVec { + #[inline] + fn eq(&self, other: &[U]) -> bool { + self.inner.eq(other) + } +} + +impl, U> PartialEq> for [T] { + #[inline] + fn eq(&self, other: &RawArchivedVec) -> bool { + self.eq(&other.inner) + } +} + +impl PartialOrd> for RawArchivedVec { + #[inline] + fn partial_cmp(&self, other: &RawArchivedVec) -> Option { + self.inner.partial_cmp(&other.inner) + } +} + +impl Ord for RawArchivedVec { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.inner.cmp(&other.inner) + } +} + +impl PartialOrd<[T]> for RawArchivedVec { + #[inline] + fn partial_cmp(&self, other: &[T]) -> Option { + self.inner.partial_cmp(other) + } +} + +impl PartialOrd> for [T] { + #[inline] + fn partial_cmp(&self, other: &RawArchivedVec) -> Option { + self.partial_cmp(&other.inner) + } +} + +#[cfg(feature = "validation")] +const _: () = { + use crate::validation::{owned::CheckOwnedPointerError, ArchiveContext}; + use bytecheck::{CheckBytes, Error}; + + impl CheckBytes for RawArchivedVec + where + T: CheckBytes, + C: ArchiveContext + ?Sized, + C::Error: Error, + { + type Error = CheckOwnedPointerError<[T], C>; + + #[inline] + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + ArchivedVec::::check_bytes_with::(value.cast(), context, |_, _| Ok(()))?; + Ok(&*value) + } + } +}; diff --git a/src/with/alloc.rs b/src/with/alloc.rs new file mode 100644 index 0000000..8e616d6 --- /dev/null +++ b/src/with/alloc.rs @@ -0,0 +1,604 @@ +use crate::{ + boxed::{ArchivedBox, BoxResolver}, + collections::util::Entry, + niche::option_box::{ArchivedOptionBox, OptionBoxResolver}, + ser::{ScratchSpace, Serializer}, + string::{ArchivedString, StringResolver}, + vec::{ArchivedVec, RawArchivedVec, VecResolver}, + with::{ + ArchiveWith, AsOwned, AsVec, CopyOptimize, DeserializeWith, Map, Niche, Raw, RefAsBox, + SerializeWith, With, + }, + Archive, ArchiveUnsized, ArchivedMetadata, Deserialize, DeserializeUnsized, Fallible, + MetadataResolver, Serialize, SerializeUnsized, +}; +use ::core::marker::PhantomData; +#[cfg(not(feature = "std"))] +use alloc::{ + borrow::Cow, + boxed::Box, + collections::{BTreeMap, BTreeSet}, + vec::Vec, +}; +#[cfg(feature = "std")] +use std::{ + borrow::Cow, + boxed::Box, + collections::{BTreeMap, BTreeSet}, +}; + +// Map for Vecs + +impl ArchiveWith> for Map +where + A: ArchiveWith, +{ + type Archived = ArchivedVec<>::Archived>; + type Resolver = VecResolver; + + unsafe fn resolve_with( + field: &Vec, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedVec::resolve_from_len(field.len(), pos, resolver, out) + } +} + +impl SerializeWith, S> for Map +where + S: Fallible + ScratchSpace + Serializer + ?Sized, + A: ArchiveWith + SerializeWith, +{ + fn serialize_with(field: &Vec, s: &mut S) -> Result { + // Wrapper for O so that we have an Archive and Serialize implementation + // and ArchivedVec::serialize_from_* is happy about the bound constraints + struct RefWrapper<'o, A, O>(&'o O, PhantomData); + + impl, O> Archive for RefWrapper<'_, A, O> { + type Archived = >::Archived; + type Resolver = >::Resolver; + + unsafe fn resolve( + &self, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + A::resolve_with(self.0, pos, resolver, out) + } + } + + impl Serialize for RefWrapper<'_, A, O> + where + A: ArchiveWith + SerializeWith, + S: Fallible + Serializer + ?Sized, + { + fn serialize(&self, s: &mut S) -> Result { + A::serialize_with(self.0, s) + } + } + + let iter = field + .iter() + .map(|value| RefWrapper::<'_, A, O>(value, PhantomData)); + + ArchivedVec::serialize_from_iter(iter, s) + } +} + +impl DeserializeWith>::Archived>, Vec, D> for Map +where + A: ArchiveWith + DeserializeWith<>::Archived, O, D>, + D: Fallible + ?Sized, +{ + fn deserialize_with( + field: &ArchivedVec<>::Archived>, + d: &mut D, + ) -> Result, D::Error> { + field + .iter() + .map(|value| >::deserialize_with(value, d)) + .collect() + } +} + +// AsOwned + +impl<'a, F: Archive + Clone> ArchiveWith> for AsOwned { + type Archived = F::Archived; + type Resolver = F::Resolver; + + #[inline] + unsafe fn resolve_with( + field: &Cow<'a, F>, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + field.resolve(pos, resolver, out); + } +} + +impl<'a, F: Serialize + Clone, S: Fallible + ?Sized> SerializeWith, S> for AsOwned { + #[inline] + fn serialize_with(field: &Cow<'a, F>, serializer: &mut S) -> Result { + field.serialize(serializer) + } +} + +impl DeserializeWith for AsOwned +where + T::Archived: Deserialize, +{ + #[inline] + fn deserialize_with(field: &T::Archived, deserializer: &mut D) -> Result { + field.deserialize(deserializer) + } +} + +impl<'a, T: Archive + Clone> ArchiveWith> for AsOwned { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve_with( + field: &Cow<'a, [T]>, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedVec::resolve_from_slice(field, pos, resolver, out); + } +} + +impl<'a, T: Serialize + Clone, S: ScratchSpace + Serializer + ?Sized> + SerializeWith, S> for AsOwned +{ + #[inline] + fn serialize_with( + field: &Cow<'a, [T]>, + serializer: &mut S, + ) -> Result { + ArchivedVec::serialize_from_slice(field, serializer) + } +} + +impl<'a, T, D> DeserializeWith, Cow<'a, [T]>, D> for AsOwned +where + T: Archive + Clone, + T::Archived: Deserialize, + D: Fallible + ?Sized, +{ + #[inline] + fn deserialize_with( + field: &ArchivedVec, + deserializer: &mut D, + ) -> Result, D::Error> { + Ok(Cow::Owned(field.deserialize(deserializer)?)) + } +} + +impl<'a> ArchiveWith> for AsOwned { + type Archived = ArchivedString; + type Resolver = StringResolver; + + #[inline] + unsafe fn resolve_with( + field: &Cow<'a, str>, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedString::resolve_from_str(field, pos, resolver, out); + } +} + +impl<'a, S: Serializer + ?Sized> SerializeWith, S> for AsOwned { + #[inline] + fn serialize_with( + field: &Cow<'a, str>, + serializer: &mut S, + ) -> Result { + ArchivedString::serialize_from_str(field, serializer) + } +} + +impl<'a, D: Fallible + ?Sized> DeserializeWith, D> for AsOwned { + #[inline] + fn deserialize_with( + field: &ArchivedString, + deserializer: &mut D, + ) -> Result, D::Error> { + Ok(Cow::Owned(field.deserialize(deserializer)?)) + } +} + +#[cfg(feature = "std")] +const _: () = { + use crate::ffi::{ArchivedCString, CStringResolver}; + use std::ffi::CStr; + + impl<'a> ArchiveWith> for AsOwned { + type Archived = ArchivedCString; + type Resolver = CStringResolver; + + #[inline] + unsafe fn resolve_with( + field: &Cow<'a, CStr>, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedCString::resolve_from_c_str(field, pos, resolver, out); + } + } + + impl<'a, S: Serializer + ?Sized> SerializeWith, S> for AsOwned { + #[inline] + fn serialize_with( + field: &Cow<'a, CStr>, + serializer: &mut S, + ) -> Result { + ArchivedCString::serialize_from_c_str(field, serializer) + } + } + + impl<'a, D: Fallible + ?Sized> DeserializeWith, D> for AsOwned { + #[inline] + fn deserialize_with( + field: &ArchivedCString, + deserializer: &mut D, + ) -> Result, D::Error> { + Ok(Cow::Owned(field.deserialize(deserializer)?)) + } + } +}; + +// AsVec + +impl ArchiveWith> for AsVec { + type Archived = ArchivedVec>; + type Resolver = VecResolver; + + unsafe fn resolve_with( + field: &BTreeMap, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedVec::resolve_from_len(field.len(), pos, resolver, out); + } +} + +impl SerializeWith, S> for AsVec +where + K: Serialize, + V: Serialize, + S: ScratchSpace + Serializer + ?Sized, +{ + fn serialize_with( + field: &BTreeMap, + serializer: &mut S, + ) -> Result { + ArchivedVec::serialize_from_iter( + field.iter().map(|(key, value)| Entry { key, value }), + serializer, + ) + } +} + +impl DeserializeWith>, BTreeMap, D> + for AsVec +where + K: Archive + Ord, + V: Archive, + K::Archived: Deserialize, + V::Archived: Deserialize, + D: Fallible + ?Sized, +{ + fn deserialize_with( + field: &ArchivedVec>, + deserializer: &mut D, + ) -> Result, D::Error> { + let mut result = BTreeMap::new(); + for entry in field.iter() { + result.insert( + entry.key.deserialize(deserializer)?, + entry.value.deserialize(deserializer)?, + ); + } + Ok(result) + } +} + +impl ArchiveWith> for AsVec { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + unsafe fn resolve_with( + field: &BTreeSet, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedVec::resolve_from_len(field.len(), pos, resolver, out); + } +} + +impl SerializeWith, S> for AsVec +where + T: Serialize, + S: ScratchSpace + Serializer + ?Sized, +{ + fn serialize_with(field: &BTreeSet, serializer: &mut S) -> Result { + ArchivedVec::::serialize_from_iter::(field.iter(), serializer) + } +} + +impl DeserializeWith, BTreeSet, D> for AsVec +where + T: Archive + Ord, + T::Archived: Deserialize, + D: Fallible + ?Sized, +{ + fn deserialize_with( + field: &ArchivedVec, + deserializer: &mut D, + ) -> Result, D::Error> { + let mut result = BTreeSet::new(); + for key in field.iter() { + result.insert(key.deserialize(deserializer)?); + } + Ok(result) + } +} + +// Niche + +impl ArchiveWith>> for Niche +where + ArchivedMetadata: Default, +{ + type Archived = ArchivedOptionBox; + type Resolver = OptionBoxResolver; + + unsafe fn resolve_with( + field: &Option>, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedOptionBox::resolve_from_option(field.as_deref(), pos, resolver, out); + } +} + +impl SerializeWith>, S> for Niche +where + T: SerializeUnsized + ?Sized, + S: Serializer + ?Sized, + ArchivedMetadata: Default, +{ + fn serialize_with( + field: &Option>, + serializer: &mut S, + ) -> Result { + ArchivedOptionBox::serialize_from_option(field.as_deref(), serializer) + } +} + +impl DeserializeWith, Option>, D> for Niche +where + T: ArchiveUnsized + ?Sized, + T::Archived: DeserializeUnsized, + D: Fallible + ?Sized, +{ + fn deserialize_with( + field: &ArchivedOptionBox, + deserializer: &mut D, + ) -> Result>, D::Error> { + if let Some(value) = field.as_ref() { + Ok(Some(value.deserialize(deserializer)?)) + } else { + Ok(None) + } + } +} + +// CopyOptimize + +impl ArchiveWith> for CopyOptimize { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + unsafe fn resolve_with( + field: &Vec, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedVec::resolve_from_len(field.len(), pos, resolver, out); + } +} + +impl SerializeWith, S> for CopyOptimize +where + T: Serialize, + S: Serializer + ?Sized, +{ + fn serialize_with(field: &Vec, serializer: &mut S) -> Result { + use ::core::mem::size_of; + + // Basic debug assert that T and T::Archived are at least the same size + debug_assert_eq!(size_of::(), size_of::()); + + unsafe { ArchivedVec::serialize_copy_from_slice(field.as_slice(), serializer) } + } +} + +impl DeserializeWith, Vec, D> for CopyOptimize +where + T: Archive, + T::Archived: Deserialize, + D: Fallible + ?Sized, +{ + fn deserialize_with(field: &ArchivedVec, _: &mut D) -> Result, D::Error> { + use ::core::{mem::size_of, ptr::copy_nonoverlapping}; + + // Basic debug assert that T and T::Archived are at least the same size + debug_assert_eq!(size_of::(), size_of::()); + + let mut result = Vec::with_capacity(field.len()); + unsafe { + copy_nonoverlapping(field.as_ptr().cast(), result.as_mut_ptr(), field.len()); + result.set_len(field.len()); + } + + Ok(result) + } +} + +impl ArchiveWith> for CopyOptimize { + type Archived = ArchivedBox<[T::Archived]>; + type Resolver = BoxResolver>; + + unsafe fn resolve_with( + field: &Box<[T]>, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedBox::resolve_from_ref(&**field, pos, resolver, out); + } +} + +impl SerializeWith, S> for CopyOptimize +where + T: Serialize, + S: Serializer + ?Sized, +{ + fn serialize_with(field: &Box<[T]>, serializer: &mut S) -> Result { + use ::core::mem::size_of; + + // Basic debug assert that T and T::Archived are at least the same size + debug_assert_eq!(size_of::(), size_of::()); + + unsafe { ArchivedBox::<[T::Archived]>::serialize_copy_from_slice(field, serializer) } + } +} + +impl DeserializeWith, Box<[T]>, D> for CopyOptimize +where + T: Archive, + T::Archived: Deserialize, + D: Fallible + ?Sized, +{ + fn deserialize_with( + field: &ArchivedBox<[T::Archived]>, + _: &mut D, + ) -> Result, D::Error> { + use ::core::{mem::size_of, ptr::copy_nonoverlapping}; + + // Basic debug assert that T and T::Archived are at least the same size + debug_assert_eq!(size_of::(), size_of::()); + + let mut result = Vec::with_capacity(field.len()); + unsafe { + copy_nonoverlapping(field.as_ptr().cast(), result.as_mut_ptr(), field.len()); + result.set_len(field.len()); + } + + Ok(result.into_boxed_slice()) + } +} + +impl<'a, T: Archive> ArchiveWith> for CopyOptimize { + type Archived = ArchivedBox<[T::Archived]>; + type Resolver = BoxResolver>; + + unsafe fn resolve_with( + field: &With<&'a [T], RefAsBox>, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedBox::resolve_from_ref(*field.as_ref(), pos, resolver, out); + } +} + +impl<'a, T, S> SerializeWith, S> for CopyOptimize +where + T: Serialize, + S: Serializer + ?Sized, +{ + fn serialize_with( + field: &With<&'a [T], RefAsBox>, + serializer: &mut S, + ) -> Result { + use ::core::mem::size_of; + + // Basic debug assert that T and T::Archived are at least the same size + debug_assert_eq!(size_of::(), size_of::()); + + unsafe { + ArchivedBox::<[T::Archived]>::serialize_copy_from_slice(field.as_ref(), serializer) + } + } +} + +// Raw + +impl ArchiveWith> for Raw { + type Archived = RawArchivedVec; + type Resolver = VecResolver; + + unsafe fn resolve_with( + field: &Vec, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + RawArchivedVec::resolve_from_slice(field.as_slice(), pos, resolver, out); + } +} + +impl SerializeWith, S> for Raw +where + T: Serialize, + S: Serializer + ?Sized, +{ + fn serialize_with(field: &Vec, serializer: &mut S) -> Result { + use ::core::mem::size_of; + + // Basic debug assert that T and T::Archived are at least the same size + debug_assert_eq!(size_of::(), size_of::()); + + unsafe { ArchivedVec::serialize_copy_from_slice(field.as_slice(), serializer) } + } +} + +impl DeserializeWith, Vec, D> for Raw +where + T: Archive, + T::Archived: Deserialize, + D: Fallible + ?Sized, +{ + fn deserialize_with( + field: &RawArchivedVec, + _: &mut D, + ) -> Result, D::Error> { + use ::core::{mem::size_of, ptr::copy_nonoverlapping}; + + // Basic debug assert that T and T::Archived are at least the same size + debug_assert_eq!(size_of::(), size_of::()); + + let mut result = Vec::with_capacity(field.len()); + unsafe { + copy_nonoverlapping(field.as_ptr().cast(), result.as_mut_ptr(), field.len()); + result.set_len(field.len()); + } + + Ok(result) + } +} diff --git a/src/with/atomic.rs b/src/with/atomic.rs new file mode 100644 index 0000000..b833d9a --- /dev/null +++ b/src/with/atomic.rs @@ -0,0 +1,184 @@ +use crate::{ + with::{ArchiveWith, Atomic, DeserializeWith, SerializeWith, With}, + Archived, Fallible, +}; +use core::sync::atomic::{ + AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicU16, AtomicU32, AtomicU8, Ordering, +}; +#[cfg(has_atomics_64)] +use core::sync::atomic::{AtomicI64, AtomicU64}; + +macro_rules! impl_atomic { + (@serialize_deserialize $type:ty) => { + impl SerializeWith<$type, S> for Atomic { + #[inline] + fn serialize_with(_: &$type, _: &mut S) -> Result { + Ok(()) + } + } + }; + ($type:ty) => { + impl ArchiveWith<$type> for Atomic { + type Archived = $type; + type Resolver = (); + + #[inline] + unsafe fn resolve_with(field: &$type, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + (&*out).store(field.load(Ordering::Relaxed), Ordering::Relaxed); + } + } + + impl_atomic!(@serialize_deserialize $type); + + impl DeserializeWith<$type, $type, D> for Atomic { + #[inline] + fn deserialize_with(field: &$type, _: &mut D) -> Result<$type, D::Error> { + Ok(field.load(Ordering::Relaxed).into()) + } + } + }; + (@multibyte $type:ty) => { + impl ArchiveWith<$type> for Atomic { + #[cfg(not(any(feature = "archive_le", feature = "archive_be")))] + type Archived = $type; + #[cfg(feature = "archive_le")] + type Archived = crate::rend::LittleEndian<$type>; + #[cfg(feature = "archive_be")] + type Archived = crate::rend::BigEndian<$type>; + + type Resolver = (); + + #[inline] + unsafe fn resolve_with(field: &$type, _: usize, _: Self::Resolver, out: *mut Self::Archived) { + (&*out).store(field.load(Ordering::Relaxed), Ordering::Relaxed); + } + } + + impl_atomic!(@serialize_deserialize $type); + + impl DeserializeWith>, $type, D> for Atomic { + #[inline] + fn deserialize_with(field: &Archived>, _: &mut D) -> Result<$type, D::Error> { + Ok(field.load(Ordering::Relaxed).into()) + } + } + }; +} + +impl_atomic!(AtomicBool); +impl_atomic!(AtomicI8); +impl_atomic!(AtomicU8); + +impl_atomic!(@multibyte AtomicI16); +impl_atomic!(@multibyte AtomicI32); +#[cfg(has_atomics_64)] +impl_atomic!(@multibyte AtomicI64); +impl_atomic!(@multibyte AtomicU16); +impl_atomic!(@multibyte AtomicU32); +#[cfg(has_atomics_64)] +impl_atomic!(@multibyte AtomicU64); + +// AtomicUsize + +// We can't implement Archive for AtomicUsize if the platform does not have 64-bit atomics but the +// size type is 64-bit +#[cfg(any(has_atomics_64, not(feature = "size_64")))] +const _: () = { + use crate::FixedUsize; + use core::sync::atomic::AtomicUsize; + + #[cfg(not(has_atomics_64))] + type FixedAtomicUsize = pick_size_type!(AtomicU16, AtomicU32, ()); + #[cfg(has_atomics_64)] + type FixedAtomicUsize = pick_size_type!(AtomicU16, AtomicU32, AtomicU64); + + impl ArchiveWith for Atomic { + type Archived = Archived>; + type Resolver = (); + + #[inline] + unsafe fn resolve_with( + field: &AtomicUsize, + _: usize, + _: Self::Resolver, + out: *mut Self::Archived, + ) { + (*out).store( + field.load(Ordering::Relaxed) as FixedUsize, + Ordering::Relaxed, + ); + } + } + + impl SerializeWith for Atomic { + #[inline] + fn serialize_with(_: &AtomicUsize, _: &mut S) -> Result { + Ok(()) + } + } + + impl + DeserializeWith<>::Archived, AtomicUsize, D> + for Atomic + { + #[inline] + fn deserialize_with( + field: &>::Archived, + _: &mut D, + ) -> Result { + Ok((field.load(Ordering::Relaxed) as usize).into()) + } + } +}; + +// AtomicIsize + +// We can't implement Archive for AtomicIsize if the platform does not have 64-bit atomics but the +// size type is 64-bit +#[cfg(any(has_atomics_64, not(feature = "size_64")))] +const _: () = { + use crate::FixedIsize; + use core::sync::atomic::AtomicIsize; + + #[cfg(not(has_atomics_64))] + type FixedAtomicIsize = pick_size_type!(AtomicI16, AtomicI32, ()); + #[cfg(has_atomics_64)] + type FixedAtomicIsize = pick_size_type!(AtomicI16, AtomicI32, AtomicI64); + + impl ArchiveWith for Atomic { + type Archived = Archived>; + type Resolver = (); + + #[inline] + unsafe fn resolve_with( + field: &AtomicIsize, + _: usize, + _: Self::Resolver, + out: *mut Self::Archived, + ) { + (*out).store( + field.load(Ordering::Relaxed) as FixedIsize, + Ordering::Relaxed, + ); + } + } + + impl SerializeWith for Atomic { + #[inline] + fn serialize_with(_: &AtomicIsize, _: &mut S) -> Result { + Ok(()) + } + } + + impl DeserializeWith>, AtomicIsize, D> + for Atomic + { + #[inline] + fn deserialize_with( + field: &Archived>, + _: &mut D, + ) -> Result { + Ok((field.load(Ordering::Relaxed) as isize).into()) + } + } +}; diff --git a/src/with/core.rs b/src/with/core.rs new file mode 100644 index 0000000..31fd642 --- /dev/null +++ b/src/with/core.rs @@ -0,0 +1,434 @@ +use crate::{ + boxed::{ArchivedBox, BoxResolver}, + niche::option_nonzero::{ + ArchivedOptionNonZeroI128, ArchivedOptionNonZeroI16, ArchivedOptionNonZeroI32, + ArchivedOptionNonZeroI64, ArchivedOptionNonZeroI8, ArchivedOptionNonZeroU128, + ArchivedOptionNonZeroU16, ArchivedOptionNonZeroU32, ArchivedOptionNonZeroU64, + ArchivedOptionNonZeroU8, + }, + option::ArchivedOption, + with::{ + ArchiveWith, AsBox, DeserializeWith, Inline, Map, Niche, RefAsBox, SerializeWith, Skip, + Unsafe, + }, + Archive, ArchiveUnsized, Deserialize, Fallible, Serialize, SerializeUnsized, +}; +use ::core::{ + cell::{Cell, UnsafeCell}, + convert::TryInto, + hint::unreachable_unchecked, + num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, + }, + ptr, +}; + +// Map for Options + +// Copy-paste from Option's impls for the most part +impl ArchiveWith> for Map +where + A: ArchiveWith, +{ + type Archived = ArchivedOption<>::Archived>; + type Resolver = Option<>::Resolver>; + + unsafe fn resolve_with( + field: &Option, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + match resolver { + None => { + let out = out.cast::(); + ptr::addr_of_mut!((*out).0).write(ArchivedOptionTag::None); + } + Some(resolver) => { + let out = out.cast::>::Archived>>(); + ptr::addr_of_mut!((*out).0).write(ArchivedOptionTag::Some); + + let value = if let Some(value) = field.as_ref() { + value + } else { + unreachable_unchecked(); + }; + + let (fp, fo) = out_field!(out.1); + A::resolve_with(value, pos + fp, resolver, fo); + } + } + } +} + +impl SerializeWith, S> for Map +where + S: Fallible + ?Sized, + A: ArchiveWith + SerializeWith, +{ + fn serialize_with(field: &Option, s: &mut S) -> Result { + field + .as_ref() + .map(|value| A::serialize_with(value, s)) + .transpose() + } +} + +impl DeserializeWith>::Archived>, Option, D> + for Map +where + D: Fallible + ?Sized, + A: ArchiveWith + DeserializeWith<>::Archived, O, D>, +{ + fn deserialize_with( + field: &ArchivedOption<>::Archived>, + d: &mut D, + ) -> Result, D::Error> { + match field { + ArchivedOption::Some(value) => Ok(Some(A::deserialize_with(value, d)?)), + ArchivedOption::None => Ok(None), + } + } +} + +#[repr(u8)] +enum ArchivedOptionTag { + None, + Some, +} + +#[repr(C)] +struct ArchivedOptionVariantNone(ArchivedOptionTag); + +#[repr(C)] +struct ArchivedOptionVariantSome(ArchivedOptionTag, T); + +// Inline + +impl ArchiveWith<&F> for Inline { + type Archived = F::Archived; + type Resolver = F::Resolver; + + #[inline] + unsafe fn resolve_with( + field: &&F, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + field.resolve(pos, resolver, out); + } +} + +impl, S: Fallible + ?Sized> SerializeWith<&F, S> for Inline { + #[inline] + fn serialize_with(field: &&F, serializer: &mut S) -> Result { + field.serialize(serializer) + } +} + +// RefAsBox + +impl ArchiveWith<&F> for RefAsBox { + type Archived = ArchivedBox; + type Resolver = BoxResolver; + + #[inline] + unsafe fn resolve_with( + field: &&F, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedBox::resolve_from_ref(*field, pos, resolver, out); + } +} + +impl + ?Sized, S: Fallible + ?Sized> SerializeWith<&F, S> for RefAsBox { + #[inline] + fn serialize_with(field: &&F, serializer: &mut S) -> Result { + ArchivedBox::serialize_from_ref(*field, serializer) + } +} + +// AsBox + +impl ArchiveWith for AsBox { + type Archived = ArchivedBox; + type Resolver = BoxResolver; + + #[inline] + unsafe fn resolve_with( + field: &F, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedBox::resolve_from_ref(field, pos, resolver, out); + } +} + +impl + ?Sized, S: Fallible + ?Sized> SerializeWith for AsBox { + #[inline] + fn serialize_with(field: &F, serializer: &mut S) -> Result { + ArchivedBox::serialize_from_ref(field, serializer) + } +} + +impl DeserializeWith, F, D> for AsBox +where + F::Archived: Deserialize, +{ + #[inline] + fn deserialize_with( + field: &ArchivedBox, + deserializer: &mut D, + ) -> Result { + field.get().deserialize(deserializer) + } +} + +// Niche + +macro_rules! impl_nonzero_niche { + ($ar:ty, $nz:ty, $ne:ty) => { + impl ArchiveWith> for Niche { + type Archived = $ar; + type Resolver = (); + + #[inline] + unsafe fn resolve_with( + field: &Option<$nz>, + _: usize, + _: Self::Resolver, + out: *mut Self::Archived, + ) { + <$ar>::resolve_from_option(*field, out); + } + } + + impl SerializeWith, S> for Niche { + #[inline] + fn serialize_with(_: &Option<$nz>, _: &mut S) -> Result { + Ok(()) + } + } + + impl DeserializeWith<$ar, Option<$nz>, D> for Niche { + #[inline] + fn deserialize_with(field: &$ar, _: &mut D) -> Result, D::Error> { + Ok(field.as_ref().map(|x| (*x).into())) + } + } + }; +} + +impl_nonzero_niche!(ArchivedOptionNonZeroI8, NonZeroI8, i8); +impl_nonzero_niche!(ArchivedOptionNonZeroI16, NonZeroI16, i16); +impl_nonzero_niche!(ArchivedOptionNonZeroI32, NonZeroI32, i32); +impl_nonzero_niche!(ArchivedOptionNonZeroI64, NonZeroI64, i64); +impl_nonzero_niche!(ArchivedOptionNonZeroI128, NonZeroI128, i128); + +type FixedNonZeroIsize = pick_size_type!(NonZeroI16, NonZeroI32, NonZeroI64); +type ArchivedOptionNonZeroIsize = pick_size_type!( + ArchivedOptionNonZeroI16, + ArchivedOptionNonZeroI32, + ArchivedOptionNonZeroI64, +); + +impl ArchiveWith> for Niche { + type Archived = ArchivedOptionNonZeroIsize; + type Resolver = (); + + #[inline] + unsafe fn resolve_with( + field: &Option, + _: usize, + _: Self::Resolver, + out: *mut Self::Archived, + ) { + let f = field.as_ref().map(|&x| x.try_into().unwrap()); + ArchivedOptionNonZeroIsize::resolve_from_option(f, out); + } +} + +impl SerializeWith, S> for Niche { + #[inline] + fn serialize_with(_: &Option, _: &mut S) -> Result { + Ok(()) + } +} + +impl DeserializeWith, D> + for Niche +{ + #[inline] + fn deserialize_with( + field: &ArchivedOptionNonZeroIsize, + _: &mut D, + ) -> Result, D::Error> { + // This conversion is necessary with archive_be and archive_le + #[allow(clippy::useless_conversion)] + Ok(field + .as_ref() + .map(|x| FixedNonZeroIsize::from(*x).try_into().unwrap())) + } +} + +impl_nonzero_niche!(ArchivedOptionNonZeroU8, NonZeroU8, u8); +impl_nonzero_niche!(ArchivedOptionNonZeroU16, NonZeroU16, u16); +impl_nonzero_niche!(ArchivedOptionNonZeroU32, NonZeroU32, u32); +impl_nonzero_niche!(ArchivedOptionNonZeroU64, NonZeroU64, u64); +impl_nonzero_niche!(ArchivedOptionNonZeroU128, NonZeroU128, u128); + +type FixedNonZeroUsize = pick_size_type!(NonZeroU16, NonZeroU32, NonZeroU64); +type ArchivedOptionNonZeroUsize = pick_size_type!( + ArchivedOptionNonZeroU16, + ArchivedOptionNonZeroU32, + ArchivedOptionNonZeroU64, +); + +impl ArchiveWith> for Niche { + type Archived = ArchivedOptionNonZeroUsize; + type Resolver = (); + + #[inline] + unsafe fn resolve_with( + field: &Option, + _: usize, + _: Self::Resolver, + out: *mut Self::Archived, + ) { + let f = field.as_ref().map(|&x| x.try_into().unwrap()); + ArchivedOptionNonZeroUsize::resolve_from_option(f, out); + } +} + +impl SerializeWith, S> for Niche { + #[inline] + fn serialize_with(_: &Option, _: &mut S) -> Result { + Ok(()) + } +} + +impl DeserializeWith, D> + for Niche +{ + #[inline] + fn deserialize_with( + field: &ArchivedOptionNonZeroUsize, + _: &mut D, + ) -> Result, D::Error> { + // This conversion is necessary with archive_be and archive_le + #[allow(clippy::useless_conversion)] + Ok(field + .as_ref() + .map(|x| FixedNonZeroUsize::from(*x).try_into().unwrap())) + } +} + +// Unsafe + +impl ArchiveWith> for Unsafe { + type Archived = UnsafeCell; + type Resolver = F::Resolver; + + #[inline] + unsafe fn resolve_with( + field: &UnsafeCell, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + F::resolve(&*field.get(), pos, resolver, out.cast()); + } +} + +impl, S: Fallible + ?Sized> SerializeWith, S> for Unsafe { + #[inline] + fn serialize_with( + field: &UnsafeCell, + serializer: &mut S, + ) -> Result { + unsafe { (*field.get()).serialize(serializer) } + } +} + +impl DeserializeWith, UnsafeCell, D> + for Unsafe +where + F::Archived: Deserialize, +{ + #[inline] + fn deserialize_with( + field: &UnsafeCell, + deserializer: &mut D, + ) -> Result, D::Error> { + unsafe { + (*field.get()) + .deserialize(deserializer) + .map(|x| UnsafeCell::new(x)) + } + } +} + +impl ArchiveWith> for Unsafe { + type Archived = Cell; + type Resolver = F::Resolver; + + #[inline] + unsafe fn resolve_with( + field: &Cell, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + F::resolve(&*field.as_ptr(), pos, resolver, out.cast()); + } +} + +impl, S: Fallible + ?Sized> SerializeWith, S> for Unsafe { + #[inline] + fn serialize_with(field: &Cell, serializer: &mut S) -> Result { + unsafe { (*field.as_ptr()).serialize(serializer) } + } +} + +impl DeserializeWith, Cell, D> for Unsafe +where + F::Archived: Deserialize, +{ + #[inline] + fn deserialize_with( + field: &Cell, + deserializer: &mut D, + ) -> Result, D::Error> { + unsafe { + (*field.as_ptr()) + .deserialize(deserializer) + .map(|x| Cell::new(x)) + } + } +} + +// Skip + +impl ArchiveWith for Skip { + type Archived = (); + type Resolver = (); + + unsafe fn resolve_with(_: &F, _: usize, _: Self::Resolver, _: *mut Self::Archived) {} +} + +impl SerializeWith for Skip { + fn serialize_with(_: &F, _: &mut S) -> Result<(), S::Error> { + Ok(()) + } +} + +impl DeserializeWith<(), F, D> for Skip { + fn deserialize_with(_: &(), _: &mut D) -> Result { + Ok(Default::default()) + } +} diff --git a/src/with/mod.rs b/src/with/mod.rs new file mode 100644 index 0000000..6bf1440 --- /dev/null +++ b/src/with/mod.rs @@ -0,0 +1,714 @@ +//! Wrapper type support and commonly used wrappers. +//! +//! Wrappers can be applied with the `#[with(...)]` attribute in the +//! [`Archive`](macro@crate::Archive) macro. See [`With`] for examples. + +#[cfg(feature = "alloc")] +mod alloc; +#[cfg(has_atomics)] +mod atomic; +mod core; +#[cfg(feature = "std")] +mod std; + +#[cfg(feature = "alloc")] +pub use self::alloc::*; +#[cfg(feature = "std")] +pub use self::std::*; + +use crate::{Archive, Deserialize, Fallible, Serialize}; +use ::core::{fmt, marker::PhantomData, mem::transmute, ops::Deref}; + +/// A transparent wrapper for archived fields. +/// +/// This is used by the `#[with(...)]` attribute in the [`Archive`](macro@crate::Archive) macro to +/// create transparent serialization wrappers. Those wrappers leverage [`ArchiveWith`] to change +/// how the type is archived, serialized, and deserialized. +/// +/// When a field is serialized, a reference to the field (i.e. `&T`) can be cast to a reference to a +/// wrapping `With` (i.e. `With`) and serialized instead. This is safe to do because +/// `With` is a transparent wrapper and is shaped exactly the same as the underlying field. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::Inline}; +/// +/// #[derive(Archive)] +/// struct Example<'a> { +/// // This will archive as if it were With<&'a i32, Inline>. That will delegate the archival +/// // to the ArchiveWith implementation of Inline for &T. +/// #[with(Inline)] +/// a: &'a i32, +/// } +/// ``` +#[repr(transparent)] +#[derive(Debug)] +pub struct With { + _phantom: PhantomData, + field: F, +} + +impl With { + /// Casts a `With` reference from a reference to the underlying field. + /// + /// This is always safe to do because `With` is a transparent wrapper. + #[inline] + pub fn cast(field: &F) -> &'_ With { + // Safety: transmuting from an unsized type reference to a reference to a transparent + // wrapper is safe because they both have the same data address and metadata + #[allow(clippy::transmute_ptr_to_ptr)] + unsafe { + transmute(field) + } + } +} + +impl With { + /// Unwraps a `With` into the underlying field. + #[inline] + pub fn into_inner(self) -> F { + self.field + } +} + +impl AsRef for With { + fn as_ref(&self) -> &F { + &self.field + } +} + +/// A variant of [`Archive`] that works with [`With`] wrappers. +/// +/// Creating a wrapper allows users to customize how fields are archived easily without changing the +/// unarchived type. +/// +/// This trait allows wrapper types to transparently change the archive behaviors for struct fields. +/// When a field is serialized, its reference may be converted to a [`With`] reference, and that +/// reference may be serialized instead. `With` references look for implementations of `ArchiveWith` +/// to determine how a wrapped field should be treated. +/// +/// # Example +/// +/// ``` +/// use rkyv::{ +/// archived_root, +/// ser::{ +/// serializers::AllocSerializer, +/// Serializer, +/// }, +/// with::{ +/// ArchiveWith, +/// DeserializeWith, +/// SerializeWith, +/// }, +/// Archive, +/// Archived, +/// Deserialize, +/// Fallible, +/// Infallible, +/// Resolver, +/// Serialize, +/// }; +/// +/// struct Incremented; +/// +/// impl ArchiveWith for Incremented { +/// type Archived = Archived; +/// type Resolver = Resolver; +/// +/// unsafe fn resolve_with(field: &i32, pos: usize, _: (), out: *mut Self::Archived) { +/// let incremented = field + 1; +/// incremented.resolve(pos, (), out); +/// } +/// } +/// +/// impl SerializeWith for Incremented +/// where +/// i32: Serialize, +/// { +/// fn serialize_with(field: &i32, serializer: &mut S) -> Result { +/// let incremented = field + 1; +/// incremented.serialize(serializer) +/// } +/// } +/// +/// impl DeserializeWith, i32, D> for Incremented +/// where +/// Archived: Deserialize, +/// { +/// fn deserialize_with(field: &Archived, deserializer: &mut D) -> Result { +/// Ok(field.deserialize(deserializer)? - 1) +/// } +/// } +/// +/// #[derive(Archive, Deserialize, Serialize)] +/// struct Example { +/// #[with(Incremented)] +/// a: i32, +/// // Another i32 field, but not incremented this time +/// b: i32, +/// } +/// +/// let value = Example { +/// a: 4, +/// b: 9, +/// }; +/// +/// let mut serializer = AllocSerializer::<4096>::default(); +/// serializer.serialize_value(&value).unwrap(); +/// let buf = serializer.into_serializer().into_inner(); +/// +/// let archived = unsafe { archived_root::(buf.as_ref()) }; +/// // The wrapped field has been incremented +/// assert_eq!(archived.a, 5); +/// // ... and the unwrapped field has not +/// assert_eq!(archived.b, 9); +/// +/// let deserialized: Example = archived.deserialize(&mut Infallible).unwrap(); +/// // The wrapped field is back to normal +/// assert_eq!(deserialized.a, 4); +/// // ... and the unwrapped field is unchanged +/// assert_eq!(deserialized.b, 9); +/// ``` +pub trait ArchiveWith { + /// The archived type of a `With`. + type Archived; + /// The resolver of a `With`. + type Resolver; + + /// Resolves the archived type using a reference to the field type `F`. + /// + /// # Safety + /// + /// - `pos` must be the position of `out` within the archive + /// - `resolver` must be the result of serializing `field` + unsafe fn resolve_with( + field: &F, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ); +} + +impl> Archive for With { + type Archived = W::Archived; + type Resolver = W::Resolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + W::resolve_with(&self.field, pos, resolver, out.cast()); + } +} + +/// A variant of `Serialize` that works with `With` wrappers. +pub trait SerializeWith: ArchiveWith { + /// Serializes the field type `F` using the given serializer. + fn serialize_with(field: &F, serializer: &mut S) -> Result; +} + +impl, S: Fallible + ?Sized> Serialize for With { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + W::serialize_with(&self.field, serializer) + } +} + +/// A variant of `Deserialize` that works with `With` wrappers. +pub trait DeserializeWith { + /// Deserializes the field type `F` using the given deserializer. + fn deserialize_with(field: &F, deserializer: &mut D) -> Result; +} + +impl Deserialize, D> for F +where + F: ?Sized, + W: DeserializeWith, + D: Fallible + ?Sized, +{ + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(With { + _phantom: PhantomData, + field: W::deserialize_with(self, deserializer)?, + }) + } +} + +/// A wrapper to make a type immutable. +#[repr(transparent)] +#[derive(Debug)] +pub struct Immutable(T); + +impl Immutable { + /// Gets the underlying immutable value. + #[inline] + pub fn value(&self) -> &T { + &self.0 + } +} + +impl Deref for Immutable { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[cfg(feature = "validation")] +const _: () = { + use bytecheck::CheckBytes; + + impl + ?Sized, C: ?Sized> CheckBytes for Immutable { + type Error = T::Error; + + unsafe fn check_bytes<'a>( + value: *const Self, + context: &mut C, + ) -> Result<&'a Self, Self::Error> { + CheckBytes::check_bytes(::core::ptr::addr_of!((*value).0), context)?; + Ok(&*value) + } + } +}; + +/// A generic wrapper that allows wrapping an `Option`. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::{Map, RefAsBox}}; +/// +/// #[derive(Archive)] +/// struct Example<'a> { +/// #[with(Map)] +/// option: Option<&'a i32>, +/// #[with(Map)] +/// vec: Vec<&'a i32>, +/// } +/// ``` +#[derive(Debug)] +pub struct Map { + _type: PhantomData, +} + +/// A wrapper that archives an atomic with an underlying atomic. +/// +/// By default, atomics are archived with an underlying integer. +/// +/// # Safety +/// +/// This wrapper is only safe to use when the backing memory for wrapped types is mutable. +/// +/// # Example +/// +/// ``` +/// # #[cfg(has_atomics)] +/// use core::sync::atomic::AtomicU32; +/// use rkyv::{Archive, with::Atomic}; +/// +/// # #[cfg(has_atomics)] +/// #[derive(Archive)] +/// struct Example { +/// #[with(Atomic)] +/// a: AtomicU32, +/// } +/// ``` +#[derive(Debug)] +pub struct Atomic; + +/// A wrapper that serializes a reference inline. +/// +/// References serialized with `Inline` cannot be deserialized because the struct cannot own the +/// deserialized value. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::Inline}; +/// +/// #[derive(Archive)] +/// struct Example<'a> { +/// #[with(Inline)] +/// a: &'a i32, +/// } +/// ``` +#[derive(Debug)] +pub struct Inline; + +/// A wrapper that serializes a reference as if it were boxed. +/// +/// Unlike [`Inline`], unsized references can be serialized with `Boxed`. +/// +/// References serialized with `Boxed` cannot be deserialized because the struct cannot own the +/// deserialized value. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::Boxed}; +/// +/// #[derive(Archive)] +/// struct Example<'a> { +/// #[with(Boxed)] +/// a: &'a str, +/// } +/// ``` +#[deprecated = "Use `RefAsBox` for references, or `AsBox` for direct fields"] +pub type Boxed = RefAsBox; + +/// A wrapper that serializes a field into a box. +/// +/// This functions similarly to [`RefAsBox`], but is for regular fields instead of references. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::AsBox}; +/// +/// #[derive(Archive)] +/// struct Example { +/// #[with(AsBox)] +/// a: i32, +/// #[with(AsBox)] +/// b: str, +/// } +/// ``` +#[derive(Debug)] +pub struct AsBox; + +/// A wrapper that serializes a reference as if it were boxed. +/// +/// Unlike [`Inline`], unsized references can be serialized with `RefAsBox`. +/// +/// References serialized with `RefAsBox` cannot be deserialized because the struct cannot own the +/// deserialized value. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::RefAsBox}; +/// +/// #[derive(Archive)] +/// struct Example<'a> { +/// #[with(RefAsBox)] +/// a: &'a i32, +/// #[with(RefAsBox)] +/// b: &'a str, +/// } +/// ``` +#[derive(Debug)] +pub struct RefAsBox; + +/// A wrapper that attempts to convert a type to and from UTF-8. +/// +/// Types like `OsString` and `PathBuf` aren't guaranteed to be encoded as UTF-8, but they usually +/// are anyway. Using this wrapper will archive them as if they were regular `String`s. +/// +/// Regular serializers don't support the custom error handling needed for this type by default. To +/// use this wrapper, a custom serializer with an error type satisfying +/// `::Error: From` must be provided. +/// +/// # Example +/// +/// ``` +/// use std::{ffi::OsString, path::PathBuf}; +/// use rkyv::{Archive, with::AsString}; +/// +/// #[derive(Archive)] +/// struct Example { +/// #[with(AsString)] +/// os_string: OsString, +/// #[with(AsString)] +/// path: PathBuf, +/// } +/// ``` +#[derive(Debug)] +pub struct AsString; + +/// Errors that can occur when serializing a [`AsString`] wrapper. +#[derive(Debug)] +pub enum AsStringError { + /// The `OsString` or `PathBuf` was not valid UTF-8. + InvalidUTF8, +} + +impl fmt::Display for AsStringError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid UTF-8") + } +} + +#[cfg(feature = "std")] +impl ::std::error::Error for AsStringError {} + +/// A wrapper that locks a lock and serializes the value immutably. +/// +/// This wrapper can panic under very specific circumstances when: +/// +/// 1. `serialize_with` is called and succeeds in locking the value to serialize it. +/// 2. Another thread locks the value and panics, poisoning the lock +/// 3. `resolve_with` is called and gets a poisoned value. +/// +/// Unfortunately, it's not possible to work around this issue. If your code absolutely must not +/// panic under any circumstances, it's recommended that you lock your values and then serialize +/// them while locked. +/// +/// Additionally, mutating the data protected by a mutex between the serialize and resolve steps may +/// cause undefined behavior in the resolve step. **Uses of this wrapper should be considered +/// unsafe** with the requirement that the data not be mutated between these two steps. +/// +/// Regular serializers don't support the custom error handling needed for this type by default. To +/// use this wrapper, a custom serializer with an error type satisfying +/// `::Error: From` must be provided. +/// +/// # Example +/// +/// ``` +/// use std::sync::Mutex; +/// use rkyv::{Archive, with::Lock}; +/// +/// #[derive(Archive)] +/// struct Example { +/// #[with(Lock)] +/// a: Mutex, +/// } +/// ``` +#[derive(Debug)] +pub struct Lock; + +/// Errors that can occur while serializing a [`Lock`] wrapper +#[derive(Debug)] +pub enum LockError { + /// The mutex was poisoned + Poisoned, +} + +impl fmt::Display for LockError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "lock poisoned") + } +} + +#[cfg(feature = "std")] +impl ::std::error::Error for LockError {} + +/// A wrapper that serializes a `Cow` as if it were owned. +/// +/// # Example +/// +/// ``` +/// use std::borrow::Cow; +/// use rkyv::{Archive, with::AsOwned}; +/// +/// #[derive(Archive)] +/// struct Example<'a> { +/// #[with(AsOwned)] +/// a: Cow<'a, str>, +/// } +/// ``` +#[derive(Debug)] +pub struct AsOwned; + +/// A wrapper that serializes associative containers as a `Vec` of key-value pairs. +/// +/// This provides faster serialization for containers like `HashMap` and `BTreeMap` by serializing +/// the key-value pairs directly instead of building a data structure in the buffer. +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// use rkyv::{Archive, with::AsVec}; +/// +/// #[derive(Archive)] +/// struct Example { +/// #[with(AsVec)] +/// values: HashMap, +/// } +/// ``` +#[derive(Debug)] +pub struct AsVec; + +/// A wrapper that niches some type combinations. +/// +/// A common type combination is `Option>`. By using a null pointer, the archived version can +/// save some space on-disk. +/// +/// # Example +/// +/// ``` +/// use core::mem::size_of; +/// use rkyv::{Archive, Archived, with::Niche}; +/// +/// #[derive(Archive)] +/// struct BasicExample { +/// value: Option>, +/// } +/// +/// #[derive(Archive)] +/// struct NichedExample { +/// #[with(Niche)] +/// value: Option>, +/// } +/// +/// assert!(size_of::>() > size_of::>()); +/// ``` +#[derive(Debug)] +pub struct Niche; + +/// A wrapper that provides specialized, performant implementations of serialization and +/// deserialization. +/// +/// This wrapper can be used with containers like `Vec`, but care must be taken to ensure that they +/// contain copy-safe types. Copy-safe types must be trivially copyable (have the same archived and +/// unarchived representations) and contain no padding bytes. In situations where copying +/// uninitialized bytes the output is acceptable, this wrapper may be used with containers of types +/// that contain padding bytes. +/// +/// # Safety +/// +/// Using this wrapper with containers containing non-copy-safe types may result in undefined +/// behavior. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::CopyOptimize}; +/// +/// #[derive(Archive)] +/// struct Example { +/// #[with(CopyOptimize)] +/// bytes: Vec, +/// } +/// ``` +#[derive(Debug)] +pub struct CopyOptimize; + +/// A wrapper that converts a [`SystemTime`](::std::time::SystemTime) to a +/// [`Duration`](::std::time::Duration) since [`UNIX_EPOCH`](::std::time::UNIX_EPOCH). +/// +/// If the serialized time occurs before the UNIX epoch, serialization will panic during `resolve`. +/// The resulting archived time will be an [`ArchivedDuration`](crate::time::ArchivedDuration) +/// relative to the UNIX epoch. +/// +/// Regular serializers don't support the custom error handling needed for this type by default. To +/// use this wrapper, a custom serializer with an error type satisfying +/// `::Error: From` must be provided. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::UnixTimestamp}; +/// use std::time::SystemTime; +/// +/// #[derive(Archive)] +/// struct Example { +/// #[with(UnixTimestamp)] +/// time: SystemTime, +/// } +#[derive(Debug)] +pub struct UnixTimestamp; + +/// Errors that can occur when serializing a [`UnixTimestamp`] wrapper. +#[derive(Debug)] +pub enum UnixTimestampError { + /// The `SystemTime` occurred prior to the UNIX epoch. + TimeBeforeUnixEpoch, +} + +impl fmt::Display for UnixTimestampError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "time occurred before the UNIX epoch") + } +} + +#[cfg(feature = "std")] +impl ::std::error::Error for UnixTimestampError {} + +/// A wrapper that provides an optimized bulk data array. This is primarily intended for large +/// amounts of raw data, like bytes, floats, or integers. +/// +/// This wrapper can be used with containers like `Vec`, but care must be taken to ensure that they +/// contain copy-safe types. Copy-safe types must be trivially copyable (have the same archived and +/// unarchived representations) and contain no padding bytes. In situations where copying +/// uninitialized bytes the output is acceptable, this wrapper may be used with containers of types +/// that contain padding bytes. +/// +/// Unlike [`CopyOptimize`], this wrapper will also skip validation for its elements. If the +/// elements of the container can have any invalid bit patterns (e.g. `char`, `bool`, complex +/// containers, etc.), then using `Raw` in an insecure setting can lead to undefined behavior. Take +/// great caution! +/// +/// # Safety +/// +/// Using this wrapper with containers containing non-copy-safe types or types that require +/// validation may result in undefined behavior. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::Raw}; +/// +/// #[derive(Archive)] +/// struct Example { +/// #[with(Raw)] +/// bytes: Vec, +/// #[with(Raw)] +/// vertices: Vec<[f32; 3]>, +/// } +/// ``` +#[derive(Debug)] +pub struct Raw; + +/// A wrapper that allows serialize-unsafe types to be serialized. +/// +/// Types like `Cell` and `UnsafeCell` may contain serializable types, but have unsafe access +/// semantics due to interior mutability. They may be safe to serialize, but only under conditions +/// that rkyv is unable to guarantee. +/// +/// This wrapper enables serializing these types, and places the burden of verifying that their +/// access semantics are used safely on the user. +/// +/// # Safety +/// +/// Using this wrapper on types with interior mutability can create races conditions or allow access +/// to data in an invalid state if access semantics are not followed properly. During serialization, +/// the data must not be modified. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::Unsafe}; +/// use core::cell::{Cell, UnsafeCell}; +/// +/// #[derive(Archive)] +/// struct Example { +/// #[with(Unsafe)] +/// cell: Cell, +/// #[with(Unsafe)] +/// unsafe_cell: UnsafeCell, +/// } +/// ``` +#[derive(Debug)] +pub struct Unsafe; + +/// A wrapper that skips serializing a field. +/// +/// Skipped fields must implement `Default` to be deserialized. +/// +/// # Example +/// +/// ``` +/// use rkyv::{Archive, with::Skip}; +/// +/// #[derive(Archive)] +/// struct Example { +/// #[with(Skip)] +/// a: u32, +/// } +/// ``` +#[derive(Debug)] +pub struct Skip; diff --git a/src/with/std.rs b/src/with/std.rs new file mode 100644 index 0000000..1d08b21 --- /dev/null +++ b/src/with/std.rs @@ -0,0 +1,328 @@ +use crate::{ + collections::util::Entry, + ser::{ScratchSpace, Serializer}, + string::{ArchivedString, StringResolver}, + time::ArchivedDuration, + vec::{ArchivedVec, VecResolver}, + with::{ + ArchiveWith, AsString, AsStringError, AsVec, DeserializeWith, Immutable, Lock, LockError, + SerializeWith, UnixTimestamp, UnixTimestampError, + }, + Archive, Deserialize, Fallible, Serialize, SerializeUnsized, +}; +use core::{hash::Hash, str::FromStr}; +use std::{ + collections::{HashMap, HashSet}, + ffi::OsString, + path::PathBuf, + sync::{Mutex, RwLock}, + time::{SystemTime, UNIX_EPOCH}, +}; + +// AsString + +impl ArchiveWith for AsString { + type Archived = ArchivedString; + type Resolver = StringResolver; + + #[inline] + unsafe fn resolve_with( + field: &OsString, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + // It's safe to unwrap here because if the OsString wasn't valid UTF-8 it would have failed + // to serialize + ArchivedString::resolve_from_str(field.to_str().unwrap(), pos, resolver, out); + } +} + +impl SerializeWith for AsString +where + S::Error: From, + str: SerializeUnsized, +{ + #[inline] + fn serialize_with(field: &OsString, serializer: &mut S) -> Result { + ArchivedString::serialize_from_str( + field.to_str().ok_or(AsStringError::InvalidUTF8)?, + serializer, + ) + } +} + +impl DeserializeWith for AsString { + #[inline] + fn deserialize_with(field: &ArchivedString, _: &mut D) -> Result { + Ok(OsString::from_str(field.as_str()).unwrap()) + } +} + +impl ArchiveWith for AsString { + type Archived = ArchivedString; + type Resolver = StringResolver; + + #[inline] + unsafe fn resolve_with( + field: &PathBuf, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + // It's safe to unwrap here because if the OsString wasn't valid UTF-8 it would have failed + // to serialize + ArchivedString::resolve_from_str(field.to_str().unwrap(), pos, resolver, out); + } +} + +impl SerializeWith for AsString +where + S::Error: From, + str: SerializeUnsized, +{ + #[inline] + fn serialize_with(field: &PathBuf, serializer: &mut S) -> Result { + ArchivedString::serialize_from_str( + field.to_str().ok_or(AsStringError::InvalidUTF8)?, + serializer, + ) + } +} + +impl DeserializeWith for AsString { + #[inline] + fn deserialize_with(field: &ArchivedString, _: &mut D) -> Result { + Ok(PathBuf::from_str(field.as_str()).unwrap()) + } +} + +// Lock + +impl ArchiveWith> for Lock { + type Archived = Immutable; + type Resolver = F::Resolver; + + #[inline] + unsafe fn resolve_with( + field: &Mutex, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + // Unfortunately, we have to unwrap here because resolve must be infallible + // + // An alternative would be to only implement ArchiveWith for Arc>, copy an Arc into + // the resolver, and hold the guard in there as well (as a reference to the internal mutex). + // This unfortunately will cause a deadlock if two Arcs to the same Mutex are serialized + // before the first is resolved. The compromise is, unfortunately, to just unwrap poison + // errors here and document it. + field.lock().unwrap().resolve(pos, resolver, out.cast()); + } +} + +impl, S: Fallible + ?Sized> SerializeWith, S> for Lock +where + S::Error: From, +{ + #[inline] + fn serialize_with(field: &Mutex, serializer: &mut S) -> Result { + field + .lock() + .map_err(|_| LockError::Poisoned)? + .serialize(serializer) + } +} + +impl DeserializeWith, Mutex, D> for Lock +where + F: Deserialize, + D: Fallible + ?Sized, +{ + #[inline] + fn deserialize_with(field: &Immutable, deserializer: &mut D) -> Result, D::Error> { + Ok(Mutex::new(field.value().deserialize(deserializer)?)) + } +} + +impl ArchiveWith> for Lock { + type Archived = Immutable; + type Resolver = F::Resolver; + + #[inline] + unsafe fn resolve_with( + field: &RwLock, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + // Unfortunately, we have to unwrap here because resolve must be infallible + // + // An alternative would be to only implement ArchiveWith for Arc>, copy a an Arc + // into the resolver, and hold the guard in there as well (as a reference to the internal + // mutex). This unfortunately will cause a deadlock if two Arcs to the same Mutex are + // serialized before the first is resolved. The compromise is, unfortunately, to just + // unwrap poison errors here and document it. + field.read().unwrap().resolve(pos, resolver, out.cast()); + } +} + +impl, S: Fallible + ?Sized> SerializeWith, S> for Lock +where + S::Error: From, +{ + #[inline] + fn serialize_with(field: &RwLock, serializer: &mut S) -> Result { + field + .read() + .map_err(|_| LockError::Poisoned)? + .serialize(serializer) + } +} + +impl DeserializeWith, RwLock, D> for Lock +where + F: Deserialize, + D: Fallible + ?Sized, +{ + #[inline] + fn deserialize_with(field: &Immutable, deserializer: &mut D) -> Result, D::Error> { + Ok(RwLock::new(field.value().deserialize(deserializer)?)) + } +} + +// AsVec + +impl ArchiveWith> for AsVec { + type Archived = ArchivedVec>; + type Resolver = VecResolver; + + unsafe fn resolve_with( + field: &HashMap, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedVec::resolve_from_len(field.len(), pos, resolver, out); + } +} + +impl SerializeWith, S> for AsVec +where + K: Serialize, + V: Serialize, + S: ScratchSpace + Serializer + ?Sized, +{ + fn serialize_with( + field: &HashMap, + serializer: &mut S, + ) -> Result { + ArchivedVec::serialize_from_iter( + field.iter().map(|(key, value)| Entry { key, value }), + serializer, + ) + } +} + +impl DeserializeWith>, HashMap, D> + for AsVec +where + K: Archive + Hash + Eq, + V: Archive, + K::Archived: Deserialize, + V::Archived: Deserialize, + D: Fallible + ?Sized, +{ + fn deserialize_with( + field: &ArchivedVec>, + deserializer: &mut D, + ) -> Result, D::Error> { + let mut result = HashMap::new(); + for entry in field.iter() { + result.insert( + entry.key.deserialize(deserializer)?, + entry.value.deserialize(deserializer)?, + ); + } + Ok(result) + } +} + +impl ArchiveWith> for AsVec { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + unsafe fn resolve_with( + field: &HashSet, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + ArchivedVec::resolve_from_len(field.len(), pos, resolver, out); + } +} + +impl SerializeWith, S> for AsVec +where + T: Serialize, + S: ScratchSpace + Serializer + ?Sized, +{ + fn serialize_with(field: &HashSet, serializer: &mut S) -> Result { + ArchivedVec::::serialize_from_iter::(field.iter(), serializer) + } +} + +impl DeserializeWith, HashSet, D> for AsVec +where + T: Archive + Hash + Eq, + T::Archived: Deserialize, + D: Fallible + ?Sized, +{ + fn deserialize_with( + field: &ArchivedVec, + deserializer: &mut D, + ) -> Result, D::Error> { + let mut result = HashSet::new(); + for key in field.iter() { + result.insert(key.deserialize(deserializer)?); + } + Ok(result) + } +} + +// UnixTimestamp + +impl ArchiveWith for UnixTimestamp { + type Archived = ArchivedDuration; + type Resolver = (); + + #[inline] + unsafe fn resolve_with( + field: &SystemTime, + pos: usize, + resolver: Self::Resolver, + out: *mut Self::Archived, + ) { + // We already checked the duration during serialize_with + let duration = field.duration_since(UNIX_EPOCH).unwrap(); + Archive::resolve(&duration, pos, resolver, out); + } +} + +impl SerializeWith for UnixTimestamp +where + S::Error: From, +{ + fn serialize_with(field: &SystemTime, _: &mut S) -> Result { + field + .duration_since(UNIX_EPOCH) + .map_err(|_| UnixTimestampError::TimeBeforeUnixEpoch)?; + Ok(()) + } +} + +impl DeserializeWith for UnixTimestamp { + fn deserialize_with(field: &ArchivedDuration, _: &mut D) -> Result { + Ok(UNIX_EPOCH + (*field).into()) + } +} -- 2.34.1