NamedSerdeAnyMap observers
This commit is contained in:
parent
4e1d844b7d
commit
ff983ecf23
@ -85,14 +85,15 @@ where
|
||||
|
||||
/// Runs the input and triggers observers and feedback
|
||||
fn evaluate_input(&mut self, input: &I) -> Result<u32, AflError> {
|
||||
self.reset_observers()?;
|
||||
self.executor_mut().reset_observers()?;
|
||||
self.executor_mut().run_target(&input)?;
|
||||
self.set_executions(self.executions() + 1);
|
||||
self.post_exec_observers()?;
|
||||
self.executor_mut().post_exec_observers()?;
|
||||
|
||||
let mut fitness = 0;
|
||||
let observers = self.executor().observers();
|
||||
for feedback in self.feedbacks_mut() {
|
||||
fitness += feedback.is_interesting(&input)?;
|
||||
fitness += feedback.is_interesting(&input, observers)?;
|
||||
}
|
||||
Ok(fitness)
|
||||
}
|
||||
@ -225,14 +226,6 @@ where
|
||||
&mut self.metadatas
|
||||
}
|
||||
|
||||
fn observers(&self) -> &[Rc<RefCell<dyn Observer>>] {
|
||||
&self.observers
|
||||
}
|
||||
|
||||
fn observers_mut(&mut self) -> &mut Vec<Rc<RefCell<dyn Observer>>> {
|
||||
&mut self.observers
|
||||
}
|
||||
|
||||
fn feedbacks(&self) -> &[Box<dyn Feedback<I>>] {
|
||||
&self.feedbacks
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use core::ptr;
|
||||
|
||||
use crate::executors::{Executor, ExitKind};
|
||||
use crate::inputs::Input;
|
||||
use crate::metamap::NamedAnyMap;
|
||||
use crate::serde_anymap::NamedSerdeAnyMap;
|
||||
use crate::observers::Observer;
|
||||
use crate::AflError;
|
||||
|
||||
@ -14,7 +14,7 @@ where
|
||||
I: Input,
|
||||
{
|
||||
harness: HarnessFunction<I>,
|
||||
observers: NamedAnyMap<dyn Observer>,
|
||||
observers: NamedSerdeAnyMap<dyn Observer>,
|
||||
}
|
||||
|
||||
static mut CURRENT_INMEMORY_EXECUTOR_PTR: *const c_void = ptr::null();
|
||||
@ -35,11 +35,11 @@ where
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn observers(&self) -> &NamedAnyMap<dyn Observer> {
|
||||
fn observers(&self) -> &NamedSerdeAnyMap<dyn Observer> {
|
||||
&self.observers
|
||||
}
|
||||
|
||||
fn observers_mut(&mut self) -> &mut NamedAnyMap<dyn Observer> {
|
||||
fn observers_mut(&mut self) -> &mut NamedSerdeAnyMap<dyn Observer> {
|
||||
&mut self.observers
|
||||
}
|
||||
}
|
||||
@ -55,7 +55,7 @@ where
|
||||
}
|
||||
Self {
|
||||
harness: harness_fn,
|
||||
observers: NamedAnyMap::new(),
|
||||
observers: NamedSerdeAnyMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
pub mod inmemory;
|
||||
|
||||
use crate::inputs::Input;
|
||||
use crate::metamap::NamedAnyMap;
|
||||
use crate::serde_anymap::NamedSerdeAnyMap;
|
||||
use crate::observers::Observer;
|
||||
use crate::AflError;
|
||||
|
||||
@ -12,8 +12,6 @@ pub enum ExitKind {
|
||||
Timeout,
|
||||
}
|
||||
|
||||
// TODO unbox input
|
||||
|
||||
pub trait Executor<I>
|
||||
where
|
||||
I: Input,
|
||||
@ -22,33 +20,26 @@ where
|
||||
fn run_target(&mut self, input: &I) -> Result<ExitKind, AflError>;
|
||||
|
||||
/// Get the linked observers
|
||||
fn observers(&self) -> &NamedAnyMap<dyn Observer>;
|
||||
fn observers(&self) -> &NamedSerdeAnyMap<dyn Observer>;
|
||||
|
||||
/// Get the linked observers
|
||||
fn observers_mut(&mut self) -> &mut NamedAnyMap<dyn Observer>;
|
||||
fn observers_mut(&mut self) -> &mut NamedSerdeAnyMap<dyn Observer>;
|
||||
|
||||
/// Add a linked observer
|
||||
fn add_observer(&mut self, observer: Box<dyn Observer>) {
|
||||
self.observers_mut().insert(observer, observer.name());
|
||||
let name = observer.name();
|
||||
self.observers_mut().insert(observer, name);
|
||||
}
|
||||
|
||||
/// Reset the state of all the observes linked to this executor
|
||||
fn reset_observers(&mut self) -> Result<(), AflError> {
|
||||
for typeid in self.observers().all_typeids() {
|
||||
for observer in self.observers_mut().all_by_typeid_mut(typeid).unwrap() {
|
||||
observer.reset()?;
|
||||
}
|
||||
}
|
||||
self.observers_mut().for_each_mut(|_, x| Ok(x.reset()?))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run the post exec hook for all the observes linked to this executor
|
||||
fn post_exec_observers(&mut self) -> Result<(), AflError> {
|
||||
for typeid in self.observers().all_typeids() {
|
||||
for observer in self.observers_mut().all_by_typeid_mut(typeid).unwrap() {
|
||||
observer.post_exec()?;
|
||||
}
|
||||
}
|
||||
self.observers_mut().for_each_mut(|_, x| Ok(x.post_exec()?))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,11 @@
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec::Vec;
|
||||
use core::any::Any;
|
||||
use core::cell::RefCell;
|
||||
use core::marker::PhantomData;
|
||||
use num::Integer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::corpus::{Testcase, TestcaseMetadata};
|
||||
use crate::executors::Executor;
|
||||
use crate::inputs::Input;
|
||||
use crate::observers::MapObserver;
|
||||
use crate::serde_anymap::SerdeAny;
|
||||
use crate::observers::{Observer, MapObserver};
|
||||
use crate::serde_anymap::NamedSerdeAnyMap;
|
||||
use crate::AflError;
|
||||
|
||||
pub trait Feedback<I>
|
||||
@ -18,7 +13,7 @@ where
|
||||
I: Input,
|
||||
{
|
||||
/// is_interesting should return the "Interestingness" from 0 to 255 (percent times 2.55)
|
||||
fn is_interesting(&mut self, input: &I, executor: &dyn Executor<I>) -> Result<u32, AflError>;
|
||||
fn is_interesting(&mut self, input: &I, observers: &NamedSerdeAnyMap<dyn Observer>) -> Result<u32, AflError>;
|
||||
|
||||
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||
fn append_metadata(&mut self, _testcase: &mut Testcase<I>) -> Result<(), AflError> {
|
||||
@ -100,21 +95,20 @@ impl<T, R, O, I> Feedback<I> for MapFeedback<T, R, O>
|
||||
where
|
||||
T: Integer + Copy + 'static,
|
||||
R: Reducer<T>,
|
||||
O: MapObserver<T>,
|
||||
O: MapObserver<T> + 'static,
|
||||
I: Input,
|
||||
{
|
||||
fn is_interesting(&mut self, _input: &I, executor: &dyn Executor<I>) -> Result<u32, AflError> {
|
||||
fn is_interesting(&mut self, _input: &I, observers: &NamedSerdeAnyMap<dyn Observer>) -> Result<u32, AflError> {
|
||||
let mut interesting = 0;
|
||||
// TODO optimize
|
||||
let size = self.map_observer.borrow().map().len();
|
||||
let mut history_map = self.history_map.borrow_mut();
|
||||
let observer = self.map_observer.borrow();
|
||||
let observer = observers.get::<O>(self.name).unwrap();
|
||||
let size = observer.map().len();
|
||||
for i in 0..size {
|
||||
let history = history_map[i];
|
||||
let history = self.history_map[i];
|
||||
let item = observer.map()[i];
|
||||
let reduced = R::reduce(history, item);
|
||||
if history != reduced {
|
||||
history_map[i] = reduced;
|
||||
self.history_map[i] = reduced;
|
||||
interesting += 1;
|
||||
}
|
||||
}
|
||||
@ -160,6 +154,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct MapNoveltiesMetadata {
|
||||
novelties: Vec<usize>,
|
||||
@ -290,9 +285,10 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub type MaxMapFeedback<T, O> = MapFeedback<T, MaxReducer<T>, O>;
|
||||
pub type MinMapFeedback<T, O> = MapFeedback<T, MinReducer<T>, O>;
|
||||
|
||||
pub type MaxMapTrackerFeedback<T, O> = MapFeedback<T, MaxReducer<T>, O>;
|
||||
pub type MinMapTrackerFeedback<T, O> = MapFeedback<T, MinReducer<T>, O>;
|
||||
//pub type MaxMapTrackerFeedback<T, O> = MapFeedback<T, MaxReducer<T>, O>;
|
||||
//pub type MinMapTrackerFeedback<T, O> = MapFeedback<T, MinReducer<T>, O>;
|
||||
|
@ -291,7 +291,7 @@ where
|
||||
pub fn by_typeid_mut(&mut self, name: &'static str, typeid: &TypeId) -> Option<&mut B> {
|
||||
match self.map.get_mut(typeid) {
|
||||
None => None,
|
||||
Some(h) => h.get(&name).map(|x| x.as_mut()),
|
||||
Some(h) => h.get_mut(&name).map(|x| x.as_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,14 @@ extern crate num;
|
||||
use core::any::Any;
|
||||
use core::slice::from_raw_parts_mut;
|
||||
use num::Integer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::metamap::AsAny;
|
||||
use crate::serde_anymap::{SerdeAny, SliceMut};
|
||||
use crate::AflError;
|
||||
|
||||
/// Observers observe different information about the target.
|
||||
/// They can then be used by various sorts of feedback.
|
||||
pub trait Observer: Any + AsAny {
|
||||
pub trait Observer: SerdeAny + 'static {
|
||||
fn flush(&mut self) -> Result<(), AflError> {
|
||||
Ok(())
|
||||
}
|
||||
@ -35,7 +36,7 @@ where
|
||||
fn map_mut(&mut self) -> &mut [T];
|
||||
|
||||
/// Get the initial value for reset()
|
||||
fn initial(&self) -> &T;
|
||||
fn initial(&self) -> T;
|
||||
|
||||
/// Get the initial value for reset()
|
||||
fn initial_mut(&mut self) -> &mut T;
|
||||
@ -46,25 +47,27 @@ where
|
||||
/// Reset the map
|
||||
fn reset_map(&mut self) -> Result<(), AflError> {
|
||||
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
|
||||
let initial = self.initial();
|
||||
for i in self.map_mut().iter_mut() {
|
||||
*i = T::zero();
|
||||
*i = initial;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct StdMapObserver<T>
|
||||
where
|
||||
T: Integer + Copy + 'static,
|
||||
{
|
||||
map: &'static mut [T],
|
||||
map: SliceMut<'static, T>,
|
||||
initial: T,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl<T> Observer for StdMapObserver<T>
|
||||
where
|
||||
T: Integer + Copy,
|
||||
T: Integer + Copy + 'static + serde::Serialize,
|
||||
{
|
||||
fn reset(&mut self) -> Result<(), AflError> {
|
||||
self.reset_map()
|
||||
@ -75,9 +78,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsAny for StdMapObserver<T>
|
||||
impl<T> SerdeAny for StdMapObserver<T>
|
||||
where
|
||||
T: Integer + Copy,
|
||||
T: Integer + Copy + 'static + serde::Serialize,
|
||||
{
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
@ -92,15 +95,21 @@ where
|
||||
T: Integer + Copy,
|
||||
{
|
||||
fn map(&self) -> &[T] {
|
||||
&self.map
|
||||
match &self.map {
|
||||
SliceMut::Ref(r) => r,
|
||||
SliceMut::Owned(v) => v.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
fn map_mut(&mut self) -> &mut [T] {
|
||||
&mut self.map
|
||||
match &mut self.map {
|
||||
SliceMut::Ref(r) => r,
|
||||
SliceMut::Owned(v) => v.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
fn initial(&self) -> &T {
|
||||
&self.initial
|
||||
fn initial(&self) -> T {
|
||||
self.initial
|
||||
}
|
||||
|
||||
fn initial_mut(&mut self) -> &mut T {
|
||||
@ -120,7 +129,7 @@ where
|
||||
pub fn new(name: &'static str, map: &'static mut [T]) -> Self {
|
||||
let initial = if map.len() > 0 { map[0] } else { T::zero() };
|
||||
Self {
|
||||
map: map,
|
||||
map: SliceMut::Ref(map),
|
||||
initial: initial,
|
||||
name: name,
|
||||
}
|
||||
@ -131,7 +140,7 @@ where
|
||||
unsafe {
|
||||
let initial = if len > 0 { *map_ptr } else { T::zero() };
|
||||
StdMapObserver {
|
||||
map: from_raw_parts_mut(map_ptr, len),
|
||||
map: SliceMut::Ref(from_raw_parts_mut(map_ptr, len)),
|
||||
initial: initial,
|
||||
name: name,
|
||||
}
|
||||
|
@ -5,6 +5,10 @@ use alloc::boxed::Box;
|
||||
use core::any::{Any, TypeId};
|
||||
use core::default::Default;
|
||||
use core::fmt;
|
||||
use core::slice::{Iter, IterMut};
|
||||
use hashbrown::hash_map::{Keys, Values, ValuesMut};
|
||||
|
||||
use crate::AflError;
|
||||
|
||||
pub fn pack_type_id(id: u64) -> TypeId {
|
||||
unsafe { *(&id as *const u64 as *const TypeId) }
|
||||
@ -199,3 +203,241 @@ impl SerdeAnyMap {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct NamedSerdeAnyMap<B>
|
||||
where
|
||||
B: ?Sized + SerdeAny,
|
||||
{
|
||||
map: HashMap<u64, HashMap<u64, Box<B>>>,
|
||||
}
|
||||
|
||||
impl<B> NamedSerdeAnyMap<B>
|
||||
where
|
||||
B: ?Sized + SerdeAny,
|
||||
{
|
||||
pub fn get<T>(&self, name: &'static str) -> Option<&T>
|
||||
where
|
||||
T: Any,
|
||||
{
|
||||
match self.map.get(&unpack_type_id(TypeId::of::<T>())) {
|
||||
None => None,
|
||||
Some(h) => h
|
||||
.get(&xxhash_rust::xxh3::xxh3_64(name.as_bytes()))
|
||||
.map(|x| x.as_any().downcast_ref::<T>().unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn by_typeid(&self, name: &'static str, typeid: &TypeId) -> Option<&B> {
|
||||
match self.map.get(&unpack_type_id(*typeid)) {
|
||||
None => None,
|
||||
Some(h) => h.get(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())).map(|x| x.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut<T>(&mut self, name: &'static str) -> Option<&mut T>
|
||||
where
|
||||
T: Any,
|
||||
{
|
||||
match self.map.get_mut(&unpack_type_id(TypeId::of::<T>())) {
|
||||
None => None,
|
||||
Some(h) => h
|
||||
.get_mut(&xxhash_rust::xxh3::xxh3_64(name.as_bytes()))
|
||||
.map(|x| x.as_any_mut().downcast_mut::<T>().unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn by_typeid_mut(&mut self, name: &'static str, typeid: &TypeId) -> Option<&mut B> {
|
||||
match self.map.get_mut(&unpack_type_id(*typeid)) {
|
||||
None => None,
|
||||
Some(h) => h.get_mut(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())).map(|x| x.as_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all<T>(
|
||||
&self,
|
||||
) -> Option<core::iter::Map<Values<'_, u64, Box<B>>, fn(&Box<B>) -> &T>>
|
||||
where
|
||||
T: Any,
|
||||
{
|
||||
match self.map.get(&unpack_type_id(TypeId::of::<T>())) {
|
||||
None => None,
|
||||
Some(h) => Some(h.values().map(|x| x.as_any().downcast_ref::<T>().unwrap())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_by_typeid(
|
||||
&self,
|
||||
typeid: &TypeId,
|
||||
) -> Option<core::iter::Map<Values<'_, u64, Box<B>>, fn(&Box<B>) -> &B>> {
|
||||
match self.map.get(&unpack_type_id(*typeid)) {
|
||||
None => None,
|
||||
Some(h) => Some(h.values().map(|x| x.as_ref())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_mut<T>(
|
||||
&mut self,
|
||||
) -> Option<core::iter::Map<ValuesMut<'_, u64, Box<B>>, fn(&mut Box<B>) -> &mut T>>
|
||||
where
|
||||
T: Any,
|
||||
{
|
||||
match self.map.get_mut(&unpack_type_id(TypeId::of::<T>())) {
|
||||
None => None,
|
||||
Some(h) => Some(
|
||||
h.values_mut()
|
||||
.map(|x| x.as_any_mut().downcast_mut::<T>().unwrap()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_by_typeid_mut(
|
||||
&mut self,
|
||||
typeid: &TypeId,
|
||||
) -> Option<core::iter::Map<ValuesMut<'_, u64, Box<B>>, fn(&mut Box<B>) -> &mut B>>
|
||||
{
|
||||
match self.map.get_mut(&unpack_type_id(*typeid)) {
|
||||
None => None,
|
||||
Some(h) => Some(h.values_mut().map(|x| x.as_mut())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_typeids(&self) -> core::iter::Map<Keys<'_, u64, HashMap<u64, Box<B>>>, fn(&u64) -> TypeId> {
|
||||
self.map.keys().map(|x| pack_type_id(*x))
|
||||
}
|
||||
|
||||
pub fn for_each(&self, func: fn(&TypeId, &Box<B>) -> Result<(), AflError>) -> Result<(), AflError> {
|
||||
for (id, h) in self.map.iter() {
|
||||
for x in h.values() {
|
||||
func(&pack_type_id(*id), x)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn for_each_mut(&mut self, func: fn(&TypeId, &mut Box<B>) -> Result<(), AflError>) -> Result<(), AflError> {
|
||||
for (id, h) in self.map.iter_mut() {
|
||||
for x in h.values_mut() {
|
||||
func(&pack_type_id(*id), x)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, val: Box<B>, name: &'static str) {
|
||||
let id = unpack_type_id(val.type_id());
|
||||
if !self.map.contains_key(&id) {
|
||||
self.map.insert(id, HashMap::default());
|
||||
}
|
||||
self.map.get_mut(&id).unwrap().insert(xxhash_rust::xxh3::xxh3_64(name.as_bytes()), val);
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
pub fn contains_type<T>(&self) -> bool
|
||||
where
|
||||
T: Any,
|
||||
{
|
||||
self.map.contains_key(&unpack_type_id(TypeId::of::<T>()))
|
||||
}
|
||||
|
||||
pub fn contains<T>(&self, name: &'static str) -> bool
|
||||
where
|
||||
T: Any,
|
||||
{
|
||||
match self.map.get(&unpack_type_id(TypeId::of::<T>())) {
|
||||
None => false,
|
||||
Some(h) => h.contains_key(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Default for NamedSerdeAnyMap<B>
|
||||
where
|
||||
B: ?Sized + SerdeAny,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub enum Ptr<'a, T: 'a + ?Sized> {
|
||||
Ref(&'a T),
|
||||
Owned(Box<T>),
|
||||
}
|
||||
|
||||
impl<'de, 'a, T: 'a + ?Sized> Deserialize<'de> for Ptr<'a, T>
|
||||
where
|
||||
Box<T>: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Deserialize::deserialize(deserializer).map(Ptr::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub enum PtrMut<'a, T: 'a + ?Sized> {
|
||||
Ref(&'a mut T),
|
||||
Owned(Box<T>),
|
||||
}
|
||||
|
||||
impl<'de, 'a, T: 'a + ?Sized> Deserialize<'de> for PtrMut<'a, T>
|
||||
where
|
||||
Box<T>: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Deserialize::deserialize(deserializer).map(PtrMut::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub enum Slice<'a, T: 'a + Sized> {
|
||||
Ref(&'a [T]),
|
||||
Owned(Vec<T>),
|
||||
}
|
||||
|
||||
impl<'de, 'a, T: 'a + Sized> Deserialize<'de> for Slice<'a, T>
|
||||
where
|
||||
Vec<T>: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Deserialize::deserialize(deserializer).map(Slice::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub enum SliceMut<'a, T: 'a + Sized> {
|
||||
Ref(&'a mut [T]),
|
||||
Owned(Vec<T>),
|
||||
}
|
||||
|
||||
impl<'de, 'a, T: 'a + Sized> Deserialize<'de> for SliceMut<'a, T>
|
||||
where
|
||||
Vec<T>: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Deserialize::deserialize(deserializer).map(SliceMut::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user