Serde_AnyMap: A serializable map that stores and retrieves elements by type
SerdeAnyMap provides map-like data structures that can store values of different types and can be serialized and deserialized with Serde. The values are stored and retrieved using their TypeId as the key, making it a powerful tool for dynamic, type-safe data storage.
There are two main types provided by this crate:
SerdeAnyMap: A simple map fromTypeIdto a value of that type.NamedSerdeAnyMap: A map fromTypeIdto a further map ofStringnames to values, allowing you to store multiple instances of the same type under different names.
How to Use
1. Add serde_anymap to your dependencies
[]
= "0.1.0"
# For automatic type registration (recommended)
= { = "0.1.0", = ["serdeany_autoreg"] }
2. Define Your Types and Implement SerdeAny
To be stored in a SerdeAnyMap, your types must implement the SerdeAny trait. The easiest way to do this is using the #[derive(SerdeAny)] macro from the LibAFL_Derive crate or by using the impl_serdeany! macro on the type, instead. Your types must also derive Serialize and Deserialize.
use ;
use impl_serdeany;
impl_serdeany!;
;
impl_serdeany!;
3. Use SerdeAnyMap
You can now insert instances of your types into the map and retrieve them by type.
# use ;
# use impl_serdeany;
#
#
# impl_serdeany!;
#
# ;
# impl_serdeany!;
use SerdeAnyMap;
let mut map = new;
map.insert;
map.insert;
// Retrieve values by type
assert_eq!;
assert_eq!;
// You can also get mutable access
let config = map..unwrap;
config.port = 9090;
assert_eq!;
4. Serialization and Deserialization
The real power of serde_anymap is its ability to serialize and deserialize these heterogeneous maps.
Important: For deserialization to work, the types you are deserializing must be registered. See the "Type Registration" section below.
# use ;
# use ;
#
#
# impl_serdeany!;
#
# ;
# impl_serdeany!;
# let mut map = new;
# map.insert;
# map.insert;
// This is only needed if you don't use the `serdeany_autoreg` feature
unsafe
// Serialize the map to a JSON string
let serialized = to_string_pretty.unwrap;
println!;
// Deserialize it back
let deserialized: SerdeAnyMap = from_str.unwrap;
assert!;
assert_eq!;
assert_eq!;
Type Registration
For serde_anymap to deserialize a generic dyn SerdeAny trait object, it needs a way to map a serialized type identifier back to a concrete type. This is done via a global type registry.
Automatic Registration (Recommended)
The easiest way to handle registration is to enable the serdeany_autoreg feature. This uses the ctor crate to automatically run registration code for each type when your program starts. The impl_serdeany! macro handles this for you.
# In your Cargo.toml
= { = "0.1.0", = ["serdeany_autoreg"] }
With this feature, you don't need to do anything else. Just use impl_serdeany! and it works.
Manual Registration
If you cannot use serdeany_autoreg, you must register your types manually at the start of your program.
use RegistryBuilder;
// This must be done before any deserialization happens.
// It is safe to call multiple times.
unsafe
// Your application logic...
NamedSerdeAnyMap
If you need to store multiple objects of the same type, you can use NamedSerdeAnyMap, which adds a string name as a key.
# use ;
# use ;
#
# ;
# impl_serdeany!;
#
# unsafe
let mut named_map = new;
named_map.insert;
named_map.insert;
assert_eq!;
assert_eq!;
let serialized = to_string.unwrap;
let deserialized: NamedSerdeAnyMap = from_str.unwrap;
assert_eq!;
Features
serdeany_autoreg: Enables automatic type registration at program startup. Highly recommended.stable_anymap: Uses the type name (&'static str) as the key instead ofTypeId. This makes the serialized output more stable across different compilations, but it can be slightly slower and may cause issues if you have types with the same name in different modules.
The LibAFL Project
The LibAFL project is part of AFLplusplus and maintained by
- Andrea Fioraldi andrea@aflplus.plus
- Dominik Maier dominik@aflplus.plus
- s1341 github@shmarya.net
- Dongjia Zhang toka@aflplus.plus
- Addison Crump me@addisoncrump.info
Contributing
For bugs, feel free to open issues or contact us directly. Thank you for your support. <3
Even though we will gladly assist you in finishing up your PR, try to
- keep all the crates compiling with stable rust (hide the eventual non-stable code under
cfgs.) - run
cargo nightly fmton your code before pushing - check the output of
cargo clippy --allor./clippy.sh - run
cargo build --no-default-featuresto check forno_stdcompatibility (and possibly add#[cfg(feature = "std")]) to hide parts of your code.
Some parts in this list may sound hard, but don't be afraid to open a PR if you cannot fix them by yourself. We will gladly assist.