From 0459fd02ed4609efde3554f4e7cbb04c2ed42b0e Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 22 May 2020 20:59:17 +1000 Subject: [PATCH 01/23] capture primitives in from_debug and from_display calls This commit also removes some unsafe that we don't need --- src/kv/value/fill.rs | 4 +- src/kv/value/internal/cast.rs | 132 +++++++++++++++------------------- src/kv/value/internal/fmt.rs | 29 +++++++- src/kv/value/internal/mod.rs | 61 +++------------- 4 files changed, 97 insertions(+), 129 deletions(-) diff --git a/src/kv/value/fill.rs b/src/kv/value/fill.rs index 9642132f4..222b78fd0 100644 --- a/src/kv/value/fill.rs +++ b/src/kv/value/fill.rs @@ -2,7 +2,7 @@ use std::fmt; -use super::internal::{Erased, Inner, Visitor}; +use super::internal::{Inner, Visitor}; use super::{Error, Value}; impl<'v> Value<'v> { @@ -12,7 +12,7 @@ impl<'v> Value<'v> { T: Fill + 'static, { Value { - inner: Inner::Fill(unsafe { Erased::new_unchecked::(value) }), + inner: Inner::Fill(value), } } } diff --git a/src/kv/value/internal/cast.rs b/src/kv/value/internal/cast.rs index d2aa86e63..ec227407f 100644 --- a/src/kv/value/internal/cast.rs +++ b/src/kv/value/internal/cast.rs @@ -4,10 +4,10 @@ //! but may end up executing arbitrary caller code if the value is complex. //! They will also attempt to downcast erased types into a primitive where possible. -use std::any::TypeId; +use std::any::Any; use std::fmt; -use super::{Erased, Inner, Primitive, Visitor}; +use super::{Inner, Primitive, Visitor}; use crate::kv::value::{Error, Value}; impl<'v> Value<'v> { @@ -168,6 +168,54 @@ impl<'v> Value<'v> { } } +// NOTE: With specialization we could potentially avoid this call using a blanket +// `ToPrimitive` trait that defaults to `None` except for these specific types +pub(super) fn into_primitive<'v>(value: &'v (dyn Any + 'static)) -> Option> { + macro_rules! type_ids { + ($($value:ident : $ty:ty => $cast:expr,)*) => {{ + $( + if let Some($value) = $value.downcast_ref::<$ty>() { + return Some(Primitive::from($cast)); + } + )* + + $( + if let Some($value) = $value.downcast_ref::>() { + return Some(if let Some($value) = $value { + Primitive::from($cast) + } else { + Primitive::None + }); + } + )* + + None + }}; + } + + type_ids![ + value: usize => *value as u64, + value: u8 => *value as u64, + value: u16 => *value as u64, + value: u32 => *value as u64, + value: u64 => *value, + + value: isize => *value as i64, + value: i8 => *value as i64, + value: i16 => *value as i64, + value: i32 => *value as i64, + value: i64 => *value, + + value: f32 => *value as f64, + value: f64 => *value, + + value: char => *value, + value: bool => *value, + + value: &str => *value, + ] +} + impl<'v> Inner<'v> { /// Cast the inner value to another type. fn cast(self) -> Cast<'v> { @@ -203,8 +251,8 @@ impl<'v> Inner<'v> { Ok(()) } - fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> { - self.0 = Cast::Primitive(Primitive::Str(v)); + #[cfg(feature = "std")] + fn str(&mut self, _: &str) -> Result<(), Error> { Ok(()) } @@ -213,9 +261,8 @@ impl<'v> Inner<'v> { Ok(()) } - #[cfg(feature = "std")] - fn str(&mut self, v: &str) -> Result<(), Error> { - self.0 = Cast::String(v.into()); + fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> { + self.0 = Cast::Primitive(Primitive::Str(v)); Ok(()) } @@ -231,28 +278,18 @@ impl<'v> Inner<'v> { } } - // Try downcast an erased value first - // It also lets us avoid the Visitor infrastructure for simple primitives - let primitive = match self { - Inner::Primitive(value) => Some(value), - Inner::Fill(value) => value.downcast_primitive(), - Inner::Debug(value) => value.downcast_primitive(), - Inner::Display(value) => value.downcast_primitive(), - - #[cfg(feature = "sval")] - Inner::Sval(value) => value.downcast_primitive(), - }; - - primitive.map(Cast::Primitive).unwrap_or_else(|| { + if let Inner::Primitive(value) = self { + Cast::Primitive(value) + } else { // If the erased value isn't a primitive then we visit it let mut cast = CastVisitor(Cast::Primitive(Primitive::None)); let _ = self.visit(&mut cast); cast.0 - }) + } } } -pub(super) enum Cast<'v> { +enum Cast<'v> { Primitive(Primitive<'v>), #[cfg(feature = "std")] String(String), @@ -321,57 +358,6 @@ impl<'v> Primitive<'v> { } } -impl<'v, T: ?Sized + 'static> Erased<'v, T> { - // NOTE: This function is a perfect candidate for memoization - // The outcome could be stored in a `Cell` - fn downcast_primitive(self) -> Option> { - macro_rules! type_ids { - ($($value:ident : $ty:ty => $cast:expr,)*) => {{ - struct TypeIds; - - impl TypeIds { - fn downcast_primitive<'v, T: ?Sized>(&self, value: Erased<'v, T>) -> Option> { - $( - if TypeId::of::<$ty>() == value.type_id { - let $value = unsafe { value.downcast_unchecked::<$ty>() }; - return Some(Primitive::from($cast)); - } - )* - - None - } - } - - TypeIds - }}; - } - - let type_ids = type_ids![ - value: usize => *value as u64, - value: u8 => *value as u64, - value: u16 => *value as u64, - value: u32 => *value as u64, - value: u64 => *value, - - value: isize => *value as i64, - value: i8 => *value as i64, - value: i16 => *value as i64, - value: i32 => *value as i64, - value: i64 => *value, - - value: f32 => *value as f64, - value: f64 => *value, - - value: char => *value, - value: bool => *value, - - value: &str => *value, - ]; - - type_ids.downcast_primitive(self) - } -} - #[cfg(feature = "std")] mod std_support { use super::*; diff --git a/src/kv/value/internal/fmt.rs b/src/kv/value/internal/fmt.rs index f8b92a3a1..ccd51bcc2 100644 --- a/src/kv/value/internal/fmt.rs +++ b/src/kv/value/internal/fmt.rs @@ -5,7 +5,7 @@ use std::fmt; -use super::{Erased, Inner, Visitor}; +use super::{Inner, Visitor, cast}; use crate::kv; use crate::kv::value::{Error, Slot}; @@ -15,8 +15,15 @@ impl<'v> kv::Value<'v> { where T: fmt::Debug + 'static, { + // If the value is a primitive type, then cast it here, avoiding needing to erase its value + // This makes `Value`s produced by `from_debug` more useful, which is worthwhile since the + // trait appears in the standard library so is widely implemented kv::Value { - inner: Inner::Debug(unsafe { Erased::new_unchecked::(value) }), + inner: if let Some(primitive) = cast::into_primitive(value) { + Inner::Primitive(primitive) + } else { + Inner::Debug(value) + }, } } @@ -25,8 +32,15 @@ impl<'v> kv::Value<'v> { where T: fmt::Display + 'static, { + // If the value is a primitive type, then cast it here, avoiding needing to erase its value + // This makes `Value`s produced by `from_display` more useful, which is worthwhile since the + // trait appears in the standard library so is widely implemented kv::Value { - inner: Inner::Display(unsafe { Erased::new_unchecked::(value) }), + inner: if let Some(primitive) = cast::into_primitive(value) { + Inner::Primitive(primitive) + } else { + Inner::Display(value) + }, } } } @@ -125,6 +139,7 @@ impl<'a, 'b: 'a, 'v> Visitor<'v> for FmtVisitor<'a, 'b> { #[cfg(test)] mod tests { use super::*; + use kv::value::test::Token; #[test] fn fmt_cast() { @@ -142,4 +157,12 @@ mod tests { .expect("invalid value") ); } + + #[test] + fn fmt_capture() { + assert_eq!(kv::Value::from_debug(&1u16).to_token(), Token::U64(1)); + assert_eq!(kv::Value::from_display(&1u16).to_token(), Token::U64(1)); + + assert_eq!(kv::Value::from_debug(&Some(1u16)).to_token(), Token::U64(1)); + } } diff --git a/src/kv/value/internal/mod.rs b/src/kv/value/internal/mod.rs index 429f0db98..9b8913898 100644 --- a/src/kv/value/internal/mod.rs +++ b/src/kv/value/internal/mod.rs @@ -3,8 +3,6 @@ //! This implementation isn't intended to be public. It may need to change //! for optimizations or to support new external serialization frameworks. -use std::any::TypeId; - use super::{Error, Fill, Slot}; pub(super) mod cast; @@ -18,27 +16,29 @@ pub(super) enum Inner<'v> { /// A simple primitive value that can be copied without allocating. Primitive(Primitive<'v>), /// A value that can be filled. - Fill(Erased<'v, dyn Fill + 'static>), + Fill(&'v (dyn Fill + 'static)), /// A debuggable value. - Debug(Erased<'v, dyn fmt::Debug + 'static>), + Debug(&'v (dyn fmt::Debug + 'static)), /// A displayable value. - Display(Erased<'v, dyn fmt::Display + 'static>), + Display(&'v (dyn fmt::Display + 'static)), #[cfg(feature = "kv_unstable_sval")] /// A structured value from `sval`. - Sval(Erased<'v, dyn sval::Value + 'static>), + Sval(&'v (dyn sval::Value + 'static)), } impl<'v> Inner<'v> { pub(super) fn visit(self, visitor: &mut dyn Visitor<'v>) -> Result<(), Error> { match self { Inner::Primitive(value) => value.visit(visitor), - Inner::Fill(value) => value.get().fill(&mut Slot::new(visitor)), - Inner::Debug(value) => visitor.debug(value.get()), - Inner::Display(value) => visitor.display(value.get()), + + Inner::Debug(value) => visitor.debug(value), + Inner::Display(value) => visitor.display(value), + + Inner::Fill(value) => value.fill(&mut Slot::new(visitor)), #[cfg(feature = "kv_unstable_sval")] - Inner::Sval(value) => visitor.sval(value.get()), + Inner::Sval(value) => visitor.sval(value), } } } @@ -138,44 +138,3 @@ impl<'v> From> for Primitive<'v> { Primitive::Fmt(v) } } - -/// A downcastable dynamic type. -pub(super) struct Erased<'v, T: ?Sized> { - type_id: TypeId, - inner: &'v T, -} - -impl<'v, T: ?Sized> Clone for Erased<'v, T> { - fn clone(&self) -> Self { - Erased { - type_id: self.type_id, - inner: self.inner, - } - } -} - -impl<'v, T: ?Sized> Copy for Erased<'v, T> {} - -impl<'v, T: ?Sized> Erased<'v, T> { - // SAFETY: `U: Unsize` and the underlying value `T` must not change - // We could add a safe variant of this method with the `Unsize` trait - pub(super) unsafe fn new_unchecked(inner: &'v T) -> Self - where - U: 'static, - T: 'static, - { - Erased { - type_id: TypeId::of::(), - inner, - } - } - - pub(super) fn get(self) -> &'v T { - self.inner - } - - // SAFETY: The underlying type of `T` is `U` - pub(super) unsafe fn downcast_unchecked(self) -> &'v U { - &*(self.inner as *const T as *const U) - } -} From be90b6e317568e0e0952ffdfedaceccfb81c1996 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 22 May 2020 21:56:48 +1000 Subject: [PATCH 02/23] experiment with different strategies for lookups --- benches/value.rs | 28 +++++++++++++++ src/kv/value/internal/cast.rs | 65 ++++++++++++++++++++++++++++++++++- src/lib.rs | 2 ++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 benches/value.rs diff --git a/benches/value.rs b/benches/value.rs new file mode 100644 index 000000000..b70a2152d --- /dev/null +++ b/benches/value.rs @@ -0,0 +1,28 @@ +#![cfg(feature = "kv_unstable")] +#![feature(test)] + +extern crate test; +extern crate log; + +use log::kv::Value; + +#[bench] +fn u8_to_value(b: &mut test::Bencher) { + b.iter(|| { + Value::from(1u8) + }) +} + +#[bench] +fn u8_to_value_debug(b: &mut test::Bencher) { + b.iter(|| { + Value::from_debug(&1u8) + }) +} + +#[bench] +fn str_to_value_debug(b: &mut test::Bencher) { + b.iter(|| { + Value::from_debug(&"a string") + }) +} \ No newline at end of file diff --git a/src/kv/value/internal/cast.rs b/src/kv/value/internal/cast.rs index ec227407f..7d1d86ba8 100644 --- a/src/kv/value/internal/cast.rs +++ b/src/kv/value/internal/cast.rs @@ -4,7 +4,7 @@ //! but may end up executing arbitrary caller code if the value is complex. //! They will also attempt to downcast erased types into a primitive where possible. -use std::any::Any; +use std::any::{TypeId, Any}; use std::fmt; use super::{Inner, Primitive, Visitor}; @@ -168,8 +168,10 @@ impl<'v> Value<'v> { } } +/* // NOTE: With specialization we could potentially avoid this call using a blanket // `ToPrimitive` trait that defaults to `None` except for these specific types +// It won't work with `&str` though in the `min_specialization` case pub(super) fn into_primitive<'v>(value: &'v (dyn Any + 'static)) -> Option> { macro_rules! type_ids { ($($value:ident : $ty:ty => $cast:expr,)*) => {{ @@ -215,6 +217,67 @@ pub(super) fn into_primitive<'v>(value: &'v (dyn Any + 'static)) -> Option *value, ] } +*/ + +pub(super) fn into_primitive<'v>(value: &'v (dyn Any + 'static)) -> Option> { + unsafe fn downcast_unchecked<'a, T: 'static>(value: &'a (dyn Any + 'static)) -> &'a T { + &*(value as *const dyn Any as *const T) + } + + macro_rules! type_ids { + ($($value:ident : $ty:ty => $cast:expr,)*) => { + [ + $( + (TypeId::of::<$ty>(), |value: &(dyn Any + 'static)| { + let $value = unsafe { downcast_unchecked::<$ty>(value) }; + Primitive::from($cast) + }), + )* + + $( + (TypeId::of::>(), |value: &(dyn Any + 'static)| { + let value = unsafe { downcast_unchecked::>(value) }; + if let Some($value) = value { + Primitive::from($cast) + } else { + Primitive::None + } + }), + )* + ] + }; + } + + let type_ids: &mut [(TypeId, for<'a> fn(&'a (dyn Any + 'static)) -> Primitive<'a>)] = &mut type_ids![ + value: usize => *value as u64, + value: u8 => *value as u64, + value: u16 => *value as u64, + value: u32 => *value as u64, + value: u64 => *value, + + value: isize => *value as i64, + value: i8 => *value as i64, + value: i16 => *value as i64, + value: i32 => *value as i64, + value: i64 => *value, + + value: f32 => *value as f64, + value: f64 => *value, + + value: char => *value, + value: bool => *value, + + value: &str => *value, + ]; + + type_ids.sort_unstable_by_key(|&(k, _)| k); + + if let Ok(i) = type_ids.binary_search_by_key(&value.type_id(), |&(k, _)| k) { + Some((type_ids[i].1)(value)) + } else { + None + } +} impl<'v> Inner<'v> { /// Cast the inner value to another type. diff --git a/src/lib.rs b/src/lib.rs index fceda8044..fd60a3924 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -276,6 +276,8 @@ #![cfg_attr(rustbuild, feature(staged_api, rustc_private))] #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] +#![feature(const_type_id)] + #[cfg(all(not(feature = "std"), not(test)))] extern crate core as std; From ef5778b1ca0d5918920e412cc581e49201b2162d Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 23 May 2020 02:16:52 +1000 Subject: [PATCH 03/23] wire up build-time type id sorting --- benches/value.rs | 12 +- build.rs | 9 ++ src/kv/value/impls.rs | 66 +++++----- src/kv/value/internal/cast.rs | 113 ++---------------- src/kv/value/internal/mod.rs | 54 +++++++++ src/kv/value/internal/sorted_type_ids.expr.rs | 89 ++++++++++++++ src/lib.rs | 1 + 7 files changed, 207 insertions(+), 137 deletions(-) create mode 100644 src/kv/value/internal/sorted_type_ids.expr.rs diff --git a/benches/value.rs b/benches/value.rs index b70a2152d..c416be231 100644 --- a/benches/value.rs +++ b/benches/value.rs @@ -25,4 +25,14 @@ fn str_to_value_debug(b: &mut test::Bencher) { b.iter(|| { Value::from_debug(&"a string") }) -} \ No newline at end of file +} + +#[bench] +fn custom_to_value_debug(b: &mut test::Bencher) { + #[derive(Debug)] + struct A; + + b.iter(|| { + Value::from_debug(&A); + }) +} diff --git a/build.rs b/build.rs index 6717bf0f7..d0e213827 100644 --- a/build.rs +++ b/build.rs @@ -2,6 +2,8 @@ //! atomics and sets `cfg` flags accordingly. use std::env; +use std::fs; +use std::path::Path; fn main() { let target = env::var("TARGET").unwrap(); @@ -10,5 +12,12 @@ fn main() { println!("cargo:rustc-cfg=atomic_cas"); } + #[cfg(feature = "kv_unstable")] + { + let path = Path::new(&env::var_os("OUT_DIR").unwrap()).join("sorted_type_ids.expr.rs"); + + fs::write(path, include!("src/kv/value/internal/sorted_type_ids.expr.rs")).unwrap(); + } + println!("cargo:rerun-if-changed=build.rs"); } diff --git a/src/kv/value/impls.rs b/src/kv/value/impls.rs index 23e8b1bed..862b29c5f 100644 --- a/src/kv/value/impls.rs +++ b/src/kv/value/impls.rs @@ -7,24 +7,6 @@ use std::fmt; use super::{Primitive, ToValue, Value}; -macro_rules! impl_into_owned { - ($($into_ty:ty => $convert:ident,)*) => { - $( - impl ToValue for $into_ty { - fn to_value(&self) -> Value { - Value::from(*self) - } - } - - impl<'v> From<$into_ty> for Value<'v> { - fn from(value: $into_ty) -> Self { - Value::from_primitive(value as $convert) - } - } - )* - }; -} - impl<'v> ToValue for &'v str { fn to_value(&self) -> Value { Value::from(*self) @@ -67,24 +49,42 @@ where } } -impl_into_owned! [ - usize => u64, - u8 => u64, - u16 => u64, - u32 => u64, - u64 => u64, +macro_rules! impl_to_value_primitive { + ($($into_ty:ty,)*) => { + $( + impl ToValue for $into_ty { + fn to_value(&self) -> Value { + Value::from(*self) + } + } + + impl<'v> From<$into_ty> for Value<'v> { + fn from(value: $into_ty) -> Self { + Value::from_primitive(value) + } + } + )* + }; +} + +impl_to_value_primitive! [ + usize, + u8, + u16, + u32, + u64, - isize => i64, - i8 => i64, - i16 => i64, - i32 => i64, - i64 => i64, + isize, + i8, + i16, + i32, + i64, - f32 => f64, - f64 => f64, + f32, + f64, - char => char, - bool => bool, + char, + bool, ]; #[cfg(feature = "std")] diff --git a/src/kv/value/internal/cast.rs b/src/kv/value/internal/cast.rs index 7d1d86ba8..4aec47221 100644 --- a/src/kv/value/internal/cast.rs +++ b/src/kv/value/internal/cast.rs @@ -4,7 +4,7 @@ //! but may end up executing arbitrary caller code if the value is complex. //! They will also attempt to downcast erased types into a primitive where possible. -use std::any::{TypeId, Any}; +use std::any::{Any, TypeId}; use std::fmt; use super::{Inner, Primitive, Visitor}; @@ -168,112 +168,19 @@ impl<'v> Value<'v> { } } -/* // NOTE: With specialization we could potentially avoid this call using a blanket // `ToPrimitive` trait that defaults to `None` except for these specific types // It won't work with `&str` though in the `min_specialization` case pub(super) fn into_primitive<'v>(value: &'v (dyn Any + 'static)) -> Option> { - macro_rules! type_ids { - ($($value:ident : $ty:ty => $cast:expr,)*) => {{ - $( - if let Some($value) = $value.downcast_ref::<$ty>() { - return Some(Primitive::from($cast)); - } - )* - - $( - if let Some($value) = $value.downcast_ref::>() { - return Some(if let Some($value) = $value { - Primitive::from($cast) - } else { - Primitive::None - }); - } - )* - - None - }}; - } - - type_ids![ - value: usize => *value as u64, - value: u8 => *value as u64, - value: u16 => *value as u64, - value: u32 => *value as u64, - value: u64 => *value, - - value: isize => *value as i64, - value: i8 => *value as i64, - value: i16 => *value as i64, - value: i32 => *value as i64, - value: i64 => *value, - - value: f32 => *value as f64, - value: f64 => *value, - - value: char => *value, - value: bool => *value, - - value: &str => *value, - ] -} -*/ - -pub(super) fn into_primitive<'v>(value: &'v (dyn Any + 'static)) -> Option> { - unsafe fn downcast_unchecked<'a, T: 'static>(value: &'a (dyn Any + 'static)) -> &'a T { - &*(value as *const dyn Any as *const T) - } - - macro_rules! type_ids { - ($($value:ident : $ty:ty => $cast:expr,)*) => { - [ - $( - (TypeId::of::<$ty>(), |value: &(dyn Any + 'static)| { - let $value = unsafe { downcast_unchecked::<$ty>(value) }; - Primitive::from($cast) - }), - )* - - $( - (TypeId::of::>(), |value: &(dyn Any + 'static)| { - let value = unsafe { downcast_unchecked::>(value) }; - if let Some($value) = value { - Primitive::from($cast) - } else { - Primitive::None - } - }), - )* - ] - }; - } - - let type_ids: &mut [(TypeId, for<'a> fn(&'a (dyn Any + 'static)) -> Primitive<'a>)] = &mut type_ids![ - value: usize => *value as u64, - value: u8 => *value as u64, - value: u16 => *value as u64, - value: u32 => *value as u64, - value: u64 => *value, - - value: isize => *value as i64, - value: i8 => *value as i64, - value: i16 => *value as i64, - value: i32 => *value as i64, - value: i64 => *value, - - value: f32 => *value as f64, - value: f64 => *value, - - value: char => *value, - value: bool => *value, - - value: &str => *value, - ]; - - type_ids.sort_unstable_by_key(|&(k, _)| k); - - if let Ok(i) = type_ids.binary_search_by_key(&value.type_id(), |&(k, _)| k) { - Some((type_ids[i].1)(value)) + // The set of type ids that map to primitives are generated at build-time + // by the contents of `sorted_type_ids.expr`. These type ids are pre-sorted + // so that they can be searched efficiently. See the `sorted_type_ids.expr.rs` + // file for the set of types that appear in this list + static TYPE_IDS: [(TypeId, for<'a> fn(&'a (dyn Any + 'static)) -> Primitive<'a>); 30] = include!(concat!(env!("OUT_DIR"), "/sorted_type_ids.expr.rs")); + + debug_assert!(TYPE_IDS.is_sorted_by_key(|&(k, _)| k)); + if let Ok(i) = TYPE_IDS.binary_search_by_key(&value.type_id(), |&(k, _)| k) { + Some((TYPE_IDS[i].1)(value)) } else { None } diff --git a/src/kv/value/internal/mod.rs b/src/kv/value/internal/mod.rs index 9b8913898..fe916b5c7 100644 --- a/src/kv/value/internal/mod.rs +++ b/src/kv/value/internal/mod.rs @@ -97,18 +97,72 @@ impl<'v> Primitive<'v> { } } +impl<'v> From for Primitive<'v> { + fn from(v: u8) -> Self { + Primitive::Unsigned(v as u64) + } +} + +impl<'v> From for Primitive<'v> { + fn from(v: u16) -> Self { + Primitive::Unsigned(v as u64) + } +} + +impl<'v> From for Primitive<'v> { + fn from(v: u32) -> Self { + Primitive::Unsigned(v as u64) + } +} + impl<'v> From for Primitive<'v> { fn from(v: u64) -> Self { Primitive::Unsigned(v) } } +impl<'v> From for Primitive<'v> { + fn from(v: usize) -> Self { + Primitive::Unsigned(v as u64) + } +} + +impl<'v> From for Primitive<'v> { + fn from(v: i8) -> Self { + Primitive::Signed(v as i64) + } +} + +impl<'v> From for Primitive<'v> { + fn from(v: i16) -> Self { + Primitive::Signed(v as i64) + } +} + +impl<'v> From for Primitive<'v> { + fn from(v: i32) -> Self { + Primitive::Signed(v as i64) + } +} + impl<'v> From for Primitive<'v> { fn from(v: i64) -> Self { Primitive::Signed(v) } } +impl<'v> From for Primitive<'v> { + fn from(v: isize) -> Self { + Primitive::Signed(v as i64) + } +} + +impl<'v> From for Primitive<'v> { + fn from(v: f32) -> Self { + Primitive::Float(v as f64) + } +} + impl<'v> From for Primitive<'v> { fn from(v: f64) -> Self { Primitive::Float(v) diff --git a/src/kv/value/internal/sorted_type_ids.expr.rs b/src/kv/value/internal/sorted_type_ids.expr.rs new file mode 100644 index 000000000..2912cb6cc --- /dev/null +++ b/src/kv/value/internal/sorted_type_ids.expr.rs @@ -0,0 +1,89 @@ +/* +An expression fragment for a stringified set of type id to primitive mappings. + +This file is used by `build.rs` to generate a pre-sorted list of type ids. +*/ + +{ + macro_rules! type_ids { + ($($ty:ty,)*) => { + [ + $( + ( + std::any::TypeId::of::<$ty>(), + stringify!( + ( + std::any::TypeId::of::<$ty>(), + (|value| unsafe { + debug_assert_eq!(value.type_id(), std::any::TypeId::of::<$ty>()); + + // SAFETY: We verify the value is $ty before casting + let value = *(value as *const dyn std::any::Any as *const $ty); + value.into() + }) as for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a> + ) + ) + ), + )* + $( + ( + std::any::TypeId::of::>(), + stringify!( + ( + std::any::TypeId::of::>(), + (|value| unsafe { + debug_assert_eq!(value.type_id(), std::any::TypeId::of::>()); + + // SAFETY: We verify the value is Option<$ty> before casting + let value = *(value as *const dyn std::any::Any as *const Option<$ty>); + if let Some(value) = value { + value.into() + } else { + Primitive::None + } + }) as for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a> + ) + ) + ), + )* + ] + }; + } + + let mut type_ids = type_ids![ + usize, + u8, + u16, + u32, + u64, + + isize, + i8, + i16, + i32, + i64, + + f32, + f64, + + char, + bool, + + &str, + ]; + + type_ids.sort_by_key(|&(k, _)| k); + + let mut ordered_type_ids_expr = String::new(); + + ordered_type_ids_expr.push('['); + + for (_, v) in &type_ids { + ordered_type_ids_expr.push_str(v); + ordered_type_ids_expr.push(','); + } + + ordered_type_ids_expr.push(']'); + + ordered_type_ids_expr +} diff --git a/src/lib.rs b/src/lib.rs index fd60a3924..00952d1c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -276,6 +276,7 @@ #![cfg_attr(rustbuild, feature(staged_api, rustc_private))] #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] +#![feature(is_sorted)] #![feature(const_type_id)] #[cfg(all(not(feature = "std"), not(test)))] From 75211b592edb19b357177348714393106705f504 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 23 May 2020 02:51:58 +1000 Subject: [PATCH 04/23] refactor type id generation to encapsulate details --- build.rs | 12 +- src/kv/value/internal/cast/into_primitive.rs | 109 ++++++++++++++++++ .../value/internal/{cast.rs => cast/mod.rs} | 23 +--- 3 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 src/kv/value/internal/cast/into_primitive.rs rename src/kv/value/internal/{cast.rs => cast/mod.rs} (92%) diff --git a/build.rs b/build.rs index d0e213827..ee2fac44a 100644 --- a/build.rs +++ b/build.rs @@ -2,8 +2,10 @@ //! atomics and sets `cfg` flags accordingly. use std::env; -use std::fs; -use std::path::Path; + +#[cfg(feature = "kv_unstable")] +#[path = "src/kv/value/internal/cast/into_primitive.rs"] +mod into_primitive; fn main() { let target = env::var("TARGET").unwrap(); @@ -13,11 +15,9 @@ fn main() { } #[cfg(feature = "kv_unstable")] - { - let path = Path::new(&env::var_os("OUT_DIR").unwrap()).join("sorted_type_ids.expr.rs"); + into_primitive::generate(); - fs::write(path, include!("src/kv/value/internal/sorted_type_ids.expr.rs")).unwrap(); - } + println!("cargo:rustc-cfg=src_build"); println!("cargo:rerun-if-changed=build.rs"); } diff --git a/src/kv/value/internal/cast/into_primitive.rs b/src/kv/value/internal/cast/into_primitive.rs new file mode 100644 index 000000000..ab12bcd4e --- /dev/null +++ b/src/kv/value/internal/cast/into_primitive.rs @@ -0,0 +1,109 @@ +// NOTE: With specialization we could potentially avoid this call using a blanket +// `ToPrimitive` trait that defaults to `None` except for these specific types +// It won't work with `&str` though in the `min_specialization` case +#[cfg(src_build)] +pub(in kv::value) fn into_primitive<'v>(value: &'v (dyn std::any::Any + 'static)) -> Option> { + // The set of type ids that map to primitives are generated at build-time + // by the contents of `sorted_type_ids.expr`. These type ids are pre-sorted + // so that they can be searched efficiently. See the `sorted_type_ids.expr.rs` + // file for the set of types that appear in this list + static TYPE_IDS: [(std::any::TypeId, for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a>); 30] = include!(concat!(env!("OUT_DIR"), "/into_primitive.rs")); + + debug_assert!(TYPE_IDS.is_sorted_by_key(|&(k, _)| k)); + if let Ok(i) = TYPE_IDS.binary_search_by_key(&value.type_id(), |&(k, _)| k) { + Some((TYPE_IDS[i].1)(value)) + } else { + None + } +} + +// When the `src_build` config is not set then we're in the build script +// This function will generate an expression fragment used by `into_primitive` +#[cfg(not(src_build))] +pub fn generate() { + use std::path::Path; + use std::{fs, env}; + + macro_rules! type_ids { + ($($ty:ty,)*) => { + [ + $( + ( + std::any::TypeId::of::<$ty>(), + stringify!( + ( + std::any::TypeId::of::<$ty>(), + (|value| unsafe { + debug_assert_eq!(value.type_id(), std::any::TypeId::of::<$ty>()); + + // SAFETY: We verify the value is $ty before casting + let value = *(value as *const dyn std::any::Any as *const $ty); + value.into() + }) as for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a> + ) + ) + ), + )* + $( + ( + std::any::TypeId::of::>(), + stringify!( + ( + std::any::TypeId::of::>(), + (|value| unsafe { + debug_assert_eq!(value.type_id(), std::any::TypeId::of::>()); + + // SAFETY: We verify the value is Option<$ty> before casting + let value = *(value as *const dyn std::any::Any as *const Option<$ty>); + if let Some(value) = value { + value.into() + } else { + crate::kv::value::internal::Primitive::None + } + }) as for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a> + ) + ) + ), + )* + ] + }; + } + + let mut type_ids = type_ids![ + usize, + u8, + u16, + u32, + u64, + + isize, + i8, + i16, + i32, + i64, + + f32, + f64, + + char, + bool, + + &str, + ]; + + type_ids.sort_by_key(|&(k, _)| k); + + let mut ordered_type_ids_expr = String::new(); + + ordered_type_ids_expr.push('['); + + for (_, v) in &type_ids { + ordered_type_ids_expr.push_str(v); + ordered_type_ids_expr.push(','); + } + + ordered_type_ids_expr.push(']'); + + let path = Path::new(&env::var_os("OUT_DIR").unwrap()).join("into_primitive.rs"); + fs::write(path, ordered_type_ids_expr).unwrap(); +} diff --git a/src/kv/value/internal/cast.rs b/src/kv/value/internal/cast/mod.rs similarity index 92% rename from src/kv/value/internal/cast.rs rename to src/kv/value/internal/cast/mod.rs index 4aec47221..ede54176d 100644 --- a/src/kv/value/internal/cast.rs +++ b/src/kv/value/internal/cast/mod.rs @@ -4,12 +4,15 @@ //! but may end up executing arbitrary caller code if the value is complex. //! They will also attempt to downcast erased types into a primitive where possible. -use std::any::{Any, TypeId}; use std::fmt; use super::{Inner, Primitive, Visitor}; use crate::kv::value::{Error, Value}; +mod into_primitive; + +pub(super) use self::into_primitive::into_primitive; + impl<'v> Value<'v> { /// Try get a `usize` from this value. /// @@ -168,24 +171,6 @@ impl<'v> Value<'v> { } } -// NOTE: With specialization we could potentially avoid this call using a blanket -// `ToPrimitive` trait that defaults to `None` except for these specific types -// It won't work with `&str` though in the `min_specialization` case -pub(super) fn into_primitive<'v>(value: &'v (dyn Any + 'static)) -> Option> { - // The set of type ids that map to primitives are generated at build-time - // by the contents of `sorted_type_ids.expr`. These type ids are pre-sorted - // so that they can be searched efficiently. See the `sorted_type_ids.expr.rs` - // file for the set of types that appear in this list - static TYPE_IDS: [(TypeId, for<'a> fn(&'a (dyn Any + 'static)) -> Primitive<'a>); 30] = include!(concat!(env!("OUT_DIR"), "/sorted_type_ids.expr.rs")); - - debug_assert!(TYPE_IDS.is_sorted_by_key(|&(k, _)| k)); - if let Ok(i) = TYPE_IDS.binary_search_by_key(&value.type_id(), |&(k, _)| k) { - Some((TYPE_IDS[i].1)(value)) - } else { - None - } -} - impl<'v> Inner<'v> { /// Cast the inner value to another type. fn cast(self) -> Cast<'v> { From dac6e30e32481a24808e77b94a3dcbff922ffe61 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 23 May 2020 03:03:09 +1000 Subject: [PATCH 05/23] remove dead code --- src/kv/value/internal/sorted_type_ids.expr.rs | 89 ------------------- 1 file changed, 89 deletions(-) delete mode 100644 src/kv/value/internal/sorted_type_ids.expr.rs diff --git a/src/kv/value/internal/sorted_type_ids.expr.rs b/src/kv/value/internal/sorted_type_ids.expr.rs deleted file mode 100644 index 2912cb6cc..000000000 --- a/src/kv/value/internal/sorted_type_ids.expr.rs +++ /dev/null @@ -1,89 +0,0 @@ -/* -An expression fragment for a stringified set of type id to primitive mappings. - -This file is used by `build.rs` to generate a pre-sorted list of type ids. -*/ - -{ - macro_rules! type_ids { - ($($ty:ty,)*) => { - [ - $( - ( - std::any::TypeId::of::<$ty>(), - stringify!( - ( - std::any::TypeId::of::<$ty>(), - (|value| unsafe { - debug_assert_eq!(value.type_id(), std::any::TypeId::of::<$ty>()); - - // SAFETY: We verify the value is $ty before casting - let value = *(value as *const dyn std::any::Any as *const $ty); - value.into() - }) as for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a> - ) - ) - ), - )* - $( - ( - std::any::TypeId::of::>(), - stringify!( - ( - std::any::TypeId::of::>(), - (|value| unsafe { - debug_assert_eq!(value.type_id(), std::any::TypeId::of::>()); - - // SAFETY: We verify the value is Option<$ty> before casting - let value = *(value as *const dyn std::any::Any as *const Option<$ty>); - if let Some(value) = value { - value.into() - } else { - Primitive::None - } - }) as for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a> - ) - ) - ), - )* - ] - }; - } - - let mut type_ids = type_ids![ - usize, - u8, - u16, - u32, - u64, - - isize, - i8, - i16, - i32, - i64, - - f32, - f64, - - char, - bool, - - &str, - ]; - - type_ids.sort_by_key(|&(k, _)| k); - - let mut ordered_type_ids_expr = String::new(); - - ordered_type_ids_expr.push('['); - - for (_, v) in &type_ids { - ordered_type_ids_expr.push_str(v); - ordered_type_ids_expr.push(','); - } - - ordered_type_ids_expr.push(']'); - - ordered_type_ids_expr -} From 1f59a35b95926028e9cfd28cd2a713b36639fafe Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 26 May 2020 15:08:57 +1000 Subject: [PATCH 06/23] refactor casting a bit and fix sval --- src/kv/value/internal/cast/mod.rs | 8 ++++++-- src/kv/value/internal/fmt.rs | 34 +++++++++++++++++-------------- src/kv/value/internal/sval.rs | 21 ++++++++++++------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/kv/value/internal/cast/mod.rs b/src/kv/value/internal/cast/mod.rs index ede54176d..f0a1b9760 100644 --- a/src/kv/value/internal/cast/mod.rs +++ b/src/kv/value/internal/cast/mod.rs @@ -11,7 +11,11 @@ use crate::kv::value::{Error, Value}; mod into_primitive; -pub(super) use self::into_primitive::into_primitive; +pub(super) fn try_from_primitive<'v, T: 'static>(value: &'v T) -> Option> { + into_primitive::into_primitive(value).map(|primitive| Value { + inner: Inner::Primitive(primitive) + }) +} impl<'v> Value<'v> { /// Try get a `usize` from this value. @@ -244,7 +248,7 @@ impl<'v> Inner<'v> { } } -enum Cast<'v> { +pub(super) enum Cast<'v> { Primitive(Primitive<'v>), #[cfg(feature = "std")] String(String), diff --git a/src/kv/value/internal/fmt.rs b/src/kv/value/internal/fmt.rs index ccd51bcc2..bc7891a63 100644 --- a/src/kv/value/internal/fmt.rs +++ b/src/kv/value/internal/fmt.rs @@ -7,7 +7,7 @@ use std::fmt; use super::{Inner, Visitor, cast}; use crate::kv; -use crate::kv::value::{Error, Slot}; +use crate::kv::value::{Error, Slot, ToValue}; impl<'v> kv::Value<'v> { /// Get a value from a debuggable type. @@ -18,13 +18,7 @@ impl<'v> kv::Value<'v> { // If the value is a primitive type, then cast it here, avoiding needing to erase its value // This makes `Value`s produced by `from_debug` more useful, which is worthwhile since the // trait appears in the standard library so is widely implemented - kv::Value { - inner: if let Some(primitive) = cast::into_primitive(value) { - Inner::Primitive(primitive) - } else { - Inner::Debug(value) - }, - } + cast::try_from_primitive(value).unwrap_or(kv::Value { inner: Inner::Debug(value) }) } /// Get a value from a displayable type. @@ -35,13 +29,7 @@ impl<'v> kv::Value<'v> { // If the value is a primitive type, then cast it here, avoiding needing to erase its value // This makes `Value`s produced by `from_display` more useful, which is worthwhile since the // trait appears in the standard library so is widely implemented - kv::Value { - inner: if let Some(primitive) = cast::into_primitive(value) { - Inner::Primitive(primitive) - } else { - Inner::Display(value) - }, - } + cast::try_from_primitive(value).unwrap_or(kv::Value { inner: Inner::Display(value) }) } } @@ -93,6 +81,22 @@ impl<'v> fmt::Display for kv::Value<'v> { } } +impl ToValue for dyn fmt::Debug { + fn to_value(&self) -> kv::Value { + kv::Value { + inner: Inner::Debug(self) + } + } +} + +impl ToValue for dyn fmt::Display { + fn to_value(&self) -> kv::Value { + kv::Value { + inner: Inner::Display(self) + } + } +} + struct FmtVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>); impl<'a, 'b: 'a, 'v> Visitor<'v> for FmtVisitor<'a, 'b> { diff --git a/src/kv/value/internal/sval.rs b/src/kv/value/internal/sval.rs index 12d184290..3c6ab2853 100644 --- a/src/kv/value/internal/sval.rs +++ b/src/kv/value/internal/sval.rs @@ -7,10 +7,10 @@ extern crate sval; use std::fmt; -use super::cast::Cast; -use super::{Erased, Inner, Primitive, Visitor}; +use super::cast::{self, Cast}; +use super::{Inner, Primitive, Visitor}; use crate::kv; -use crate::kv::value::{Error, Slot}; +use crate::kv::value::{Error, Slot, ToValue}; impl<'v> kv::Value<'v> { /// Get a value from a structured type. @@ -18,9 +18,8 @@ impl<'v> kv::Value<'v> { where T: sval::Value + 'static, { - kv::Value { - inner: Inner::Sval(unsafe { Erased::new_unchecked::(value) }), - } + // If the value is a primitive type, then cast it here, avoiding needing to erase its value + cast::try_from_primitive(value).unwrap_or(kv::Value { inner: Inner::Sval(value) }) } } @@ -48,6 +47,14 @@ impl<'v> sval::Value for kv::Value<'v> { } } +impl ToValue for dyn sval::Value { + fn to_value(&self) -> kv::Value { + kv::Value { + inner: Inner::Sval(self) + } + } +} + pub(in kv::value) use self::sval::Value; pub(super) fn fmt(f: &mut fmt::Formatter, v: &dyn sval::Value) -> Result<(), Error> { @@ -156,7 +163,7 @@ mod tests { #[test] fn test_from_sval() { - assert_eq!(kv::Value::from_sval(&42u64).to_token(), Token::Sval); + assert_eq!(kv::Value::from_sval(&42u64).to_token(), Token::U64(42)); } #[test] From 7af0cf16785baf151d7e7bb71440154756dcd1e4 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 26 May 2020 15:18:38 +1000 Subject: [PATCH 07/23] shift some docs --- src/kv/value/internal/cast/mod.rs | 4 ++++ src/kv/value/internal/fmt.rs | 6 ------ src/kv/value/internal/sval.rs | 1 - 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/kv/value/internal/cast/mod.rs b/src/kv/value/internal/cast/mod.rs index f0a1b9760..762bd1b9f 100644 --- a/src/kv/value/internal/cast/mod.rs +++ b/src/kv/value/internal/cast/mod.rs @@ -11,6 +11,10 @@ use crate::kv::value::{Error, Value}; mod into_primitive; +/// Attempt to capture a primitive from some generic value. +/// +/// If the value is a primitive type, then cast it here, avoiding needing to erase its value +/// This makes `Value`s produced by `Value::from_*` more useful pub(super) fn try_from_primitive<'v, T: 'static>(value: &'v T) -> Option> { into_primitive::into_primitive(value).map(|primitive| Value { inner: Inner::Primitive(primitive) diff --git a/src/kv/value/internal/fmt.rs b/src/kv/value/internal/fmt.rs index bc7891a63..497721aa6 100644 --- a/src/kv/value/internal/fmt.rs +++ b/src/kv/value/internal/fmt.rs @@ -15,9 +15,6 @@ impl<'v> kv::Value<'v> { where T: fmt::Debug + 'static, { - // If the value is a primitive type, then cast it here, avoiding needing to erase its value - // This makes `Value`s produced by `from_debug` more useful, which is worthwhile since the - // trait appears in the standard library so is widely implemented cast::try_from_primitive(value).unwrap_or(kv::Value { inner: Inner::Debug(value) }) } @@ -26,9 +23,6 @@ impl<'v> kv::Value<'v> { where T: fmt::Display + 'static, { - // If the value is a primitive type, then cast it here, avoiding needing to erase its value - // This makes `Value`s produced by `from_display` more useful, which is worthwhile since the - // trait appears in the standard library so is widely implemented cast::try_from_primitive(value).unwrap_or(kv::Value { inner: Inner::Display(value) }) } } diff --git a/src/kv/value/internal/sval.rs b/src/kv/value/internal/sval.rs index 3c6ab2853..14316580d 100644 --- a/src/kv/value/internal/sval.rs +++ b/src/kv/value/internal/sval.rs @@ -18,7 +18,6 @@ impl<'v> kv::Value<'v> { where T: sval::Value + 'static, { - // If the value is a primitive type, then cast it here, avoiding needing to erase its value cast::try_from_primitive(value).unwrap_or(kv::Value { inner: Inner::Sval(value) }) } } From c00c8aabd884df3d5e5ff5fc35ffaf10a7a8a8c1 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 26 May 2020 15:26:24 +1000 Subject: [PATCH 08/23] explicit From impls for dyn traits --- src/key_values/source.rs | 393 ++++++++++++++++++++++++++++++++++ src/kv/value/internal/fmt.rs | 20 +- src/kv/value/internal/sval.rs | 8 +- 3 files changed, 416 insertions(+), 5 deletions(-) create mode 100644 src/key_values/source.rs diff --git a/src/key_values/source.rs b/src/key_values/source.rs new file mode 100644 index 000000000..61f5fe19f --- /dev/null +++ b/src/key_values/source.rs @@ -0,0 +1,393 @@ +//! Sources of structured key-value pairs. + +use std::fmt; +use std::borrow::Borrow; +use std::marker::PhantomData; + +use super::value; + +use super::private::{value, value_owned}; + +#[doc(inline)] +pub use super::private::{Key, ToValue, Value, ValueOwned}; + +#[doc(inline)] +pub use super::Error; + +use std::borrow::ToOwned; + +// TODO: Would it be safe to remove the `ToOwned` bounds in `no_std`? +// TODO: We might need to make some methods private so it can't be +// implemented +impl ToValue for T +where + T: value::Value + Send + Sync + ToOwned, + T::Owned: value::Value + Send + Sync + 'static, +{ + fn to_value(&self) -> Value { + value(self) + } + + fn to_owned(&self) -> ValueOwned { + value_owned(self.to_owned()) + } +} + +impl<'a> ToValue for &'a dyn ToValue { + fn to_value(&self) -> Value { + (**self).to_value() + } + + fn to_owned(&self) -> ValueOwned { + (**self).to_owned() + } +} + +fn ensure_to_value<'a>() { + fn is_to_value() {} + + is_to_value::<&'a dyn ToValue>(); + is_to_value::<&'a str>(); +} + +/// A source for key value pairs that can be serialized. +pub trait Source { + /// Serialize the key value pairs. + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; + + /// Erase this `Source` so it can be used without + /// requiring generic type parameters. + fn erase(&self) -> ErasedSource + where + Self: Sized, + { + ErasedSource::erased(self) + } + + /// Find the value for a given key. + /// + /// If the key is present multiple times, this method will + /// return the *last* value for the given key. + /// + /// The default implementation will scan all key-value pairs. + /// Implementors are encouraged provide a more efficient version + /// if they can. Standard collections like `BTreeMap` and `HashMap` + /// will do an indexed lookup instead of a scan. + fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> + where + Q: Borrow, + { + struct Get<'k, 'v>(Key<'k>, Option>); + + impl<'k, 'kvs> Visitor<'kvs> for Get<'k, 'kvs> { + fn visit_pair(&mut self, k: Key<'kvs>, v: Value<'kvs>) -> Result<(), Error> { + if k == self.0 { + self.1 = Some(v); + } + + Ok(()) + } + } + + let mut visitor = Get(Key::new(&key), None); + let _ = self.visit(&mut visitor); + + visitor.1 + } + + /// An adapter to borrow self. + fn by_ref(&self) -> &Self { + self + } + + /// Chain two `Source`s together. + fn chain(self, other: KVS) -> Chained + where + Self: Sized, + { + Chained(self, other) + } + + /// Apply a function to each key-value pair. + fn try_for_each(self, f: F) -> Result<(), Error> + where + Self: Sized, + F: FnMut(Key, Value) -> Result<(), E>, + E: Into, + { + struct ForEach(F, PhantomData); + + impl<'kvs, F, E> Visitor<'kvs> for ForEach + where + F: FnMut(Key, Value) -> Result<(), E>, + E: Into, + { + fn visit_pair(&mut self, k: Key<'kvs>, v: Value<'kvs>) -> Result<(), Error> { + (self.0)(k, v).map_err(Into::into) + } + } + + self.visit(&mut ForEach(f, Default::default())) + } + + /// Serialize the key-value pairs as a map. + #[cfg(feature = "structured_serde")] + fn serialize_as_map(self) -> SerializeAsMap + where + Self: Sized, + { + SerializeAsMap(self) + } + + /// Serialize the key-value pairs as a sequence. + #[cfg(feature = "structured_serde")] + fn serialize_as_seq(self) -> SerializeAsSeq + where + Self: Sized, + { + SerializeAsSeq(self) + } +} + +impl<'a, T: ?Sized> Source for &'a T +where + T: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + (*self).visit(visitor) + } + + fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> + where + Q: Borrow, + { + (*self).get(key) + } +} + +/// A visitor for key value pairs. +/// +/// The lifetime of the keys and values is captured by the `'kvs` type. +pub trait Visitor<'kvs> { + /// Visit a key value pair. + fn visit_pair(&mut self, k: Key<'kvs>, v: Value<'kvs>) -> Result<(), Error>; +} + +impl<'a, 'kvs, T: ?Sized> Visitor<'kvs> for &'a mut T +where + T: Visitor<'kvs>, +{ + fn visit_pair(&mut self, k: Key<'kvs>, v: Value<'kvs>) -> Result<(), Error> { + (*self).visit_pair(k, v) + } +} + +/// A chain of two `Source`s. +#[derive(Debug)] +pub struct Chained(A, B); + +impl Source for Chained +where + A: Source, + B: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + self.0.visit(visitor)?; + self.1.visit(visitor)?; + + Ok(()) + } +} + +/// Serialize the key-value pairs as a map. +#[derive(Debug)] +#[cfg(feature = "structured_serde")] +pub struct SerializeAsMap(KVS); + +/// Serialize the key-value pairs as a sequence. +#[derive(Debug)] +#[cfg(feature = "structured_serde")] +pub struct SerializeAsSeq(KVS); + +impl Source for (K, V) +where + K: Borrow, + V: ToValue, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> + { + visitor.visit_pair(Key::new(&self.0), self.1.to_value()) + } +} + +impl Source for [KVS] where KVS: Source { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + for kv in self { + kv.visit(visitor)?; + } + + Ok(()) + } +} + +/// A key value source on a `Record`. +#[derive(Clone)] +pub struct ErasedSource<'a>(&'a dyn ErasedSourceBridge); + +impl<'a> ErasedSource<'a> { + pub fn erased(kvs: &'a impl Source) -> Self { + ErasedSource(kvs) + } + + pub fn empty() -> Self { + ErasedSource(&(&[] as &[(&str, ValueOwned)])) + } +} + +impl<'a> fmt::Debug for ErasedSource<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Source").finish() + } +} + +impl<'a> Source for ErasedSource<'a> { + fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { + self.0.erased_visit(visitor) + } + + fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> + where + Q: Borrow, + { + self.0.erased_get(key.borrow()) + } +} + +/// A trait that erases a `Source` so it can be stored +/// in a `Record` without requiring any generic parameters. +trait ErasedSourceBridge { + fn erased_visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; + fn erased_get<'kvs>(&'kvs self, key: &str) -> Option>; +} + +impl ErasedSourceBridge for KVS +where + KVS: Source + ?Sized, +{ + fn erased_visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + self.visit(visitor) + } + + fn erased_get<'kvs>(&'kvs self, key: &str) -> Option> { + self.get(key) + } +} + +#[cfg(feature = "structured_serde")] +mod serde_support { + use super::*; + + use serde::ser::{Serialize, Serializer, SerializeMap, SerializeSeq}; + + impl Serialize for SerializeAsMap + where + KVS: Source, + { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(None)?; + + self.0 + .by_ref() + .try_for_each(|k, v| map.serialize_entry(&k, &v)) + .map_err(Error::into_serde)?; + + map.end() + } + } + + impl Serialize for SerializeAsSeq + where + KVS: Source, + { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(None)?; + + self.0 + .by_ref() + .try_for_each(|k, v| seq.serialize_element(&(&k, &v))) + .map_err(Error::into_serde)?; + + seq.end() + } + } +} + +#[cfg(feature = "structured_serde")] +pub use self::serde_support::*; + +#[cfg(feature = "std")] +mod std_support { + use super::*; + + use std::hash::Hash; + use std::collections::{HashMap, BTreeMap}; + + impl Source for Vec where KVS: Source { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + self.as_slice().visit(visitor) + } + } + + impl Source for BTreeMap + where + K: Borrow + Ord, + V: ToValue, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> + { + for (k, v) in self { + visitor.visit_pair(Key::new(k), v.to_value())?; + } + + Ok(()) + } + + fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> + where + Q: Borrow, + { + BTreeMap::get(self, key.borrow()).map(|v| v.to_value()) + } + } + + impl Source for HashMap + where + K: Borrow + Eq + Hash, + V: ToValue, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> + { + for (k, v) in self { + visitor.visit_pair(Key::new(k), v.to_value())?; + } + + Ok(()) + } + + fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> + where + Q: Borrow, + { + HashMap::get(self, key.borrow()).map(|v| v.to_value()) + } + } +} + +#[cfg(feature = "std")] +pub use self::std_support::*; diff --git a/src/kv/value/internal/fmt.rs b/src/kv/value/internal/fmt.rs index 497721aa6..e1d74fb70 100644 --- a/src/kv/value/internal/fmt.rs +++ b/src/kv/value/internal/fmt.rs @@ -77,16 +77,28 @@ impl<'v> fmt::Display for kv::Value<'v> { impl ToValue for dyn fmt::Debug { fn to_value(&self) -> kv::Value { - kv::Value { - inner: Inner::Debug(self) - } + kv::Value::from(self) } } impl ToValue for dyn fmt::Display { fn to_value(&self) -> kv::Value { + kv::Value::from(self) + } +} + +impl<'v> From<&'v (dyn fmt::Debug + 'static)> for kv::Value<'v> { + fn from(value: &'v (dyn fmt::Debug + 'static)) -> kv::Value<'v> { + kv::Value { + inner: Inner::Debug(value) + } + } +} + +impl<'v> From<&'v (dyn fmt::Display + 'static)> for kv::Value<'v> { + fn from(value: &'v (dyn fmt::Display + 'static)) -> kv::Value<'v> { kv::Value { - inner: Inner::Display(self) + inner: Inner::Display(value) } } } diff --git a/src/kv/value/internal/sval.rs b/src/kv/value/internal/sval.rs index 14316580d..86b88de06 100644 --- a/src/kv/value/internal/sval.rs +++ b/src/kv/value/internal/sval.rs @@ -48,8 +48,14 @@ impl<'v> sval::Value for kv::Value<'v> { impl ToValue for dyn sval::Value { fn to_value(&self) -> kv::Value { + kv::Value::from(self) + } +} + +impl<'v> From<&'v (dyn sval::Value + 'static)> for kv::Value<'v> { + fn from(value: &'v (dyn sval::Value + 'static)) -> kv::Value<'v> { kv::Value { - inner: Inner::Sval(self) + inner: Inner::Sval(value) } } } From 690ae5307d6e04f77e52a99ff99cd70ef12c2835 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 26 May 2020 19:51:35 +1000 Subject: [PATCH 09/23] remove junk file --- src/key_values/source.rs | 393 --------------------------------------- 1 file changed, 393 deletions(-) delete mode 100644 src/key_values/source.rs diff --git a/src/key_values/source.rs b/src/key_values/source.rs deleted file mode 100644 index 61f5fe19f..000000000 --- a/src/key_values/source.rs +++ /dev/null @@ -1,393 +0,0 @@ -//! Sources of structured key-value pairs. - -use std::fmt; -use std::borrow::Borrow; -use std::marker::PhantomData; - -use super::value; - -use super::private::{value, value_owned}; - -#[doc(inline)] -pub use super::private::{Key, ToValue, Value, ValueOwned}; - -#[doc(inline)] -pub use super::Error; - -use std::borrow::ToOwned; - -// TODO: Would it be safe to remove the `ToOwned` bounds in `no_std`? -// TODO: We might need to make some methods private so it can't be -// implemented -impl ToValue for T -where - T: value::Value + Send + Sync + ToOwned, - T::Owned: value::Value + Send + Sync + 'static, -{ - fn to_value(&self) -> Value { - value(self) - } - - fn to_owned(&self) -> ValueOwned { - value_owned(self.to_owned()) - } -} - -impl<'a> ToValue for &'a dyn ToValue { - fn to_value(&self) -> Value { - (**self).to_value() - } - - fn to_owned(&self) -> ValueOwned { - (**self).to_owned() - } -} - -fn ensure_to_value<'a>() { - fn is_to_value() {} - - is_to_value::<&'a dyn ToValue>(); - is_to_value::<&'a str>(); -} - -/// A source for key value pairs that can be serialized. -pub trait Source { - /// Serialize the key value pairs. - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; - - /// Erase this `Source` so it can be used without - /// requiring generic type parameters. - fn erase(&self) -> ErasedSource - where - Self: Sized, - { - ErasedSource::erased(self) - } - - /// Find the value for a given key. - /// - /// If the key is present multiple times, this method will - /// return the *last* value for the given key. - /// - /// The default implementation will scan all key-value pairs. - /// Implementors are encouraged provide a more efficient version - /// if they can. Standard collections like `BTreeMap` and `HashMap` - /// will do an indexed lookup instead of a scan. - fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> - where - Q: Borrow, - { - struct Get<'k, 'v>(Key<'k>, Option>); - - impl<'k, 'kvs> Visitor<'kvs> for Get<'k, 'kvs> { - fn visit_pair(&mut self, k: Key<'kvs>, v: Value<'kvs>) -> Result<(), Error> { - if k == self.0 { - self.1 = Some(v); - } - - Ok(()) - } - } - - let mut visitor = Get(Key::new(&key), None); - let _ = self.visit(&mut visitor); - - visitor.1 - } - - /// An adapter to borrow self. - fn by_ref(&self) -> &Self { - self - } - - /// Chain two `Source`s together. - fn chain(self, other: KVS) -> Chained - where - Self: Sized, - { - Chained(self, other) - } - - /// Apply a function to each key-value pair. - fn try_for_each(self, f: F) -> Result<(), Error> - where - Self: Sized, - F: FnMut(Key, Value) -> Result<(), E>, - E: Into, - { - struct ForEach(F, PhantomData); - - impl<'kvs, F, E> Visitor<'kvs> for ForEach - where - F: FnMut(Key, Value) -> Result<(), E>, - E: Into, - { - fn visit_pair(&mut self, k: Key<'kvs>, v: Value<'kvs>) -> Result<(), Error> { - (self.0)(k, v).map_err(Into::into) - } - } - - self.visit(&mut ForEach(f, Default::default())) - } - - /// Serialize the key-value pairs as a map. - #[cfg(feature = "structured_serde")] - fn serialize_as_map(self) -> SerializeAsMap - where - Self: Sized, - { - SerializeAsMap(self) - } - - /// Serialize the key-value pairs as a sequence. - #[cfg(feature = "structured_serde")] - fn serialize_as_seq(self) -> SerializeAsSeq - where - Self: Sized, - { - SerializeAsSeq(self) - } -} - -impl<'a, T: ?Sized> Source for &'a T -where - T: Source, -{ - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - (*self).visit(visitor) - } - - fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> - where - Q: Borrow, - { - (*self).get(key) - } -} - -/// A visitor for key value pairs. -/// -/// The lifetime of the keys and values is captured by the `'kvs` type. -pub trait Visitor<'kvs> { - /// Visit a key value pair. - fn visit_pair(&mut self, k: Key<'kvs>, v: Value<'kvs>) -> Result<(), Error>; -} - -impl<'a, 'kvs, T: ?Sized> Visitor<'kvs> for &'a mut T -where - T: Visitor<'kvs>, -{ - fn visit_pair(&mut self, k: Key<'kvs>, v: Value<'kvs>) -> Result<(), Error> { - (*self).visit_pair(k, v) - } -} - -/// A chain of two `Source`s. -#[derive(Debug)] -pub struct Chained(A, B); - -impl Source for Chained -where - A: Source, - B: Source, -{ - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - self.0.visit(visitor)?; - self.1.visit(visitor)?; - - Ok(()) - } -} - -/// Serialize the key-value pairs as a map. -#[derive(Debug)] -#[cfg(feature = "structured_serde")] -pub struct SerializeAsMap(KVS); - -/// Serialize the key-value pairs as a sequence. -#[derive(Debug)] -#[cfg(feature = "structured_serde")] -pub struct SerializeAsSeq(KVS); - -impl Source for (K, V) -where - K: Borrow, - V: ToValue, -{ - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> - { - visitor.visit_pair(Key::new(&self.0), self.1.to_value()) - } -} - -impl Source for [KVS] where KVS: Source { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - for kv in self { - kv.visit(visitor)?; - } - - Ok(()) - } -} - -/// A key value source on a `Record`. -#[derive(Clone)] -pub struct ErasedSource<'a>(&'a dyn ErasedSourceBridge); - -impl<'a> ErasedSource<'a> { - pub fn erased(kvs: &'a impl Source) -> Self { - ErasedSource(kvs) - } - - pub fn empty() -> Self { - ErasedSource(&(&[] as &[(&str, ValueOwned)])) - } -} - -impl<'a> fmt::Debug for ErasedSource<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Source").finish() - } -} - -impl<'a> Source for ErasedSource<'a> { - fn visit<'kvs>(&'kvs self, visitor: &mut Visitor<'kvs>) -> Result<(), Error> { - self.0.erased_visit(visitor) - } - - fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> - where - Q: Borrow, - { - self.0.erased_get(key.borrow()) - } -} - -/// A trait that erases a `Source` so it can be stored -/// in a `Record` without requiring any generic parameters. -trait ErasedSourceBridge { - fn erased_visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; - fn erased_get<'kvs>(&'kvs self, key: &str) -> Option>; -} - -impl ErasedSourceBridge for KVS -where - KVS: Source + ?Sized, -{ - fn erased_visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - self.visit(visitor) - } - - fn erased_get<'kvs>(&'kvs self, key: &str) -> Option> { - self.get(key) - } -} - -#[cfg(feature = "structured_serde")] -mod serde_support { - use super::*; - - use serde::ser::{Serialize, Serializer, SerializeMap, SerializeSeq}; - - impl Serialize for SerializeAsMap - where - KVS: Source, - { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut map = serializer.serialize_map(None)?; - - self.0 - .by_ref() - .try_for_each(|k, v| map.serialize_entry(&k, &v)) - .map_err(Error::into_serde)?; - - map.end() - } - } - - impl Serialize for SerializeAsSeq - where - KVS: Source, - { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(None)?; - - self.0 - .by_ref() - .try_for_each(|k, v| seq.serialize_element(&(&k, &v))) - .map_err(Error::into_serde)?; - - seq.end() - } - } -} - -#[cfg(feature = "structured_serde")] -pub use self::serde_support::*; - -#[cfg(feature = "std")] -mod std_support { - use super::*; - - use std::hash::Hash; - use std::collections::{HashMap, BTreeMap}; - - impl Source for Vec where KVS: Source { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { - self.as_slice().visit(visitor) - } - } - - impl Source for BTreeMap - where - K: Borrow + Ord, - V: ToValue, - { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> - { - for (k, v) in self { - visitor.visit_pair(Key::new(k), v.to_value())?; - } - - Ok(()) - } - - fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> - where - Q: Borrow, - { - BTreeMap::get(self, key.borrow()).map(|v| v.to_value()) - } - } - - impl Source for HashMap - where - K: Borrow + Eq + Hash, - V: ToValue, - { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> - { - for (k, v) in self { - visitor.visit_pair(Key::new(k), v.to_value())?; - } - - Ok(()) - } - - fn get<'kvs, Q>(&'kvs self, key: Q) -> Option> - where - Q: Borrow, - { - HashMap::get(self, key.borrow()).map(|v| v.to_value()) - } - } -} - -#[cfg(feature = "std")] -pub use self::std_support::*; From dd15f5bc9457f908abcf04b5ced5f2863f63200c Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 26 May 2020 19:57:19 +1000 Subject: [PATCH 10/23] remove an invalid ; --- benches/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/value.rs b/benches/value.rs index c416be231..3c7d22512 100644 --- a/benches/value.rs +++ b/benches/value.rs @@ -33,6 +33,6 @@ fn custom_to_value_debug(b: &mut test::Bencher) { struct A; b.iter(|| { - Value::from_debug(&A); + Value::from_debug(&A) }) } From 256b9621ac3520b16d361c014f417e4c73685705 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Wed, 27 May 2020 02:31:21 +1000 Subject: [PATCH 11/23] const works out the same as static --- src/kv/value/internal/cast/into_primitive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kv/value/internal/cast/into_primitive.rs b/src/kv/value/internal/cast/into_primitive.rs index ab12bcd4e..bb842e207 100644 --- a/src/kv/value/internal/cast/into_primitive.rs +++ b/src/kv/value/internal/cast/into_primitive.rs @@ -7,7 +7,7 @@ pub(in kv::value) fn into_primitive<'v>(value: &'v (dyn std::any::Any + 'static) // by the contents of `sorted_type_ids.expr`. These type ids are pre-sorted // so that they can be searched efficiently. See the `sorted_type_ids.expr.rs` // file for the set of types that appear in this list - static TYPE_IDS: [(std::any::TypeId, for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a>); 30] = include!(concat!(env!("OUT_DIR"), "/into_primitive.rs")); + const TYPE_IDS: [(std::any::TypeId, for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a>); 30] = include!(concat!(env!("OUT_DIR"), "/into_primitive.rs")); debug_assert!(TYPE_IDS.is_sorted_by_key(|&(k, _)| k)); if let Ok(i) = TYPE_IDS.binary_search_by_key(&value.type_id(), |&(k, _)| k) { From fd2049a0e9a4ed1aa51b8fa6de64be6baf0c2d19 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Wed, 3 Jun 2020 22:12:44 +1000 Subject: [PATCH 12/23] remove 'static bounds where unneeded --- src/kv/value/internal/fmt.rs | 12 ++++++------ src/kv/value/internal/mod.rs | 8 ++++---- src/kv/value/internal/sval.rs | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/kv/value/internal/fmt.rs b/src/kv/value/internal/fmt.rs index e1d74fb70..0a1a8c5c5 100644 --- a/src/kv/value/internal/fmt.rs +++ b/src/kv/value/internal/fmt.rs @@ -75,28 +75,28 @@ impl<'v> fmt::Display for kv::Value<'v> { } } -impl ToValue for dyn fmt::Debug { +impl<'v> ToValue for dyn fmt::Debug + 'v { fn to_value(&self) -> kv::Value { kv::Value::from(self) } } -impl ToValue for dyn fmt::Display { +impl<'v> ToValue for dyn fmt::Display + 'v { fn to_value(&self) -> kv::Value { kv::Value::from(self) } } -impl<'v> From<&'v (dyn fmt::Debug + 'static)> for kv::Value<'v> { - fn from(value: &'v (dyn fmt::Debug + 'static)) -> kv::Value<'v> { +impl<'v> From<&'v (dyn fmt::Debug)> for kv::Value<'v> { + fn from(value: &'v (dyn fmt::Debug)) -> kv::Value<'v> { kv::Value { inner: Inner::Debug(value) } } } -impl<'v> From<&'v (dyn fmt::Display + 'static)> for kv::Value<'v> { - fn from(value: &'v (dyn fmt::Display + 'static)) -> kv::Value<'v> { +impl<'v> From<&'v (dyn fmt::Display)> for kv::Value<'v> { + fn from(value: &'v (dyn fmt::Display)) -> kv::Value<'v> { kv::Value { inner: Inner::Display(value) } diff --git a/src/kv/value/internal/mod.rs b/src/kv/value/internal/mod.rs index fe916b5c7..be743ebe2 100644 --- a/src/kv/value/internal/mod.rs +++ b/src/kv/value/internal/mod.rs @@ -16,15 +16,15 @@ pub(super) enum Inner<'v> { /// A simple primitive value that can be copied without allocating. Primitive(Primitive<'v>), /// A value that can be filled. - Fill(&'v (dyn Fill + 'static)), + Fill(&'v (dyn Fill)), /// A debuggable value. - Debug(&'v (dyn fmt::Debug + 'static)), + Debug(&'v (dyn fmt::Debug)), /// A displayable value. - Display(&'v (dyn fmt::Display + 'static)), + Display(&'v (dyn fmt::Display)), #[cfg(feature = "kv_unstable_sval")] /// A structured value from `sval`. - Sval(&'v (dyn sval::Value + 'static)), + Sval(&'v (dyn sval::Value)), } impl<'v> Inner<'v> { diff --git a/src/kv/value/internal/sval.rs b/src/kv/value/internal/sval.rs index 86b88de06..77048e16b 100644 --- a/src/kv/value/internal/sval.rs +++ b/src/kv/value/internal/sval.rs @@ -46,14 +46,14 @@ impl<'v> sval::Value for kv::Value<'v> { } } -impl ToValue for dyn sval::Value { +impl<'v> ToValue for dyn sval::Value + 'v { fn to_value(&self) -> kv::Value { kv::Value::from(self) } } -impl<'v> From<&'v (dyn sval::Value + 'static)> for kv::Value<'v> { - fn from(value: &'v (dyn sval::Value + 'static)) -> kv::Value<'v> { +impl<'v> From<&'v (dyn sval::Value)> for kv::Value<'v> { + fn from(value: &'v (dyn sval::Value)) -> kv::Value<'v> { kv::Value { inner: Inner::Sval(value) } From c8a3d37c80e9a8099797e592d985786e09e4f061 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Wed, 3 Jun 2020 22:50:09 +1000 Subject: [PATCH 13/23] add const version --- Cargo.toml | 1 + src/kv/value/internal/cast/into_primitive.rs | 54 +++++++++++++++++++- src/lib.rs | 2 +- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab8a3b57a..48a96141a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ std = [] # this will have a tighter MSRV before stabilization kv_unstable = [] kv_unstable_sval = ["kv_unstable", "sval/fmt"] +kv_unstable_const_primitive = [] [dependencies] cfg-if = "0.1.2" diff --git a/src/kv/value/internal/cast/into_primitive.rs b/src/kv/value/internal/cast/into_primitive.rs index bb842e207..f02cce7e1 100644 --- a/src/kv/value/internal/cast/into_primitive.rs +++ b/src/kv/value/internal/cast/into_primitive.rs @@ -1,7 +1,57 @@ +#[cfg(all(src_build, feature = "kv_unstable_const_primitive"))] +pub(super) fn into_primitive<'v, T: 'static>(value: &'v T) -> Option> { + use std::any::TypeId; + + use crate::kv::value::internal::Primitive; + + trait ToPrimitive where Self: 'static { + const CALL: fn(&Self) -> Option = { + const U8: TypeId = TypeId::of::(); + const U16: TypeId = TypeId::of::(); + const U32: TypeId = TypeId::of::(); + const U64: TypeId = TypeId::of::(); + + const I8: TypeId = TypeId::of::(); + const I16: TypeId = TypeId::of::(); + const I32: TypeId = TypeId::of::(); + const I64: TypeId = TypeId::of::(); + + const STR: TypeId = TypeId::of::<&'static str>(); + + match TypeId::of::() { + U8 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u8) })), + U16 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u16) })), + U32 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u32) })), + U64 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u64) })), + + I8 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i8) })), + I16 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i16) })), + I32 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i32) })), + I64 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i64) })), + + STR => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const &'static str) })), + + _ => |_| None, + } + }; + + fn to_primitive(&self) -> Option { + (Self::CALL)(self) + } + } + + impl ToPrimitive for T { } + + value.to_primitive() +} + +#[cfg(all(not(src_build), feature = "kv_unstable_const_primitive"))] +pub fn generate() { } + // NOTE: With specialization we could potentially avoid this call using a blanket // `ToPrimitive` trait that defaults to `None` except for these specific types // It won't work with `&str` though in the `min_specialization` case -#[cfg(src_build)] +#[cfg(all(src_build, not(feature = "kv_unstable_const_primitive")))] pub(in kv::value) fn into_primitive<'v>(value: &'v (dyn std::any::Any + 'static)) -> Option> { // The set of type ids that map to primitives are generated at build-time // by the contents of `sorted_type_ids.expr`. These type ids are pre-sorted @@ -19,7 +69,7 @@ pub(in kv::value) fn into_primitive<'v>(value: &'v (dyn std::any::Any + 'static) // When the `src_build` config is not set then we're in the build script // This function will generate an expression fragment used by `into_primitive` -#[cfg(not(src_build))] +#[cfg(all(not(src_build), not(feature = "kv_unstable_const_primitive")))] pub fn generate() { use std::path::Path; use std::{fs, env}; diff --git a/src/lib.rs b/src/lib.rs index 00952d1c5..2401ff964 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -277,7 +277,7 @@ #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] #![feature(is_sorted)] -#![feature(const_type_id)] +#![feature(const_type_id, const_if_match)] #[cfg(all(not(feature = "std"), not(test)))] extern crate core as std; From f9333fbacd51c8363cc3c98d94f01888baeea2c9 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 4 Jun 2020 00:56:18 +1000 Subject: [PATCH 14/23] add specialization-based conversion --- Cargo.toml | 1 + src/kv/value/internal/cast/into_primitive.rs | 105 +++++++++++++++++-- src/kv/value/internal/mod.rs | 16 +++ src/lib.rs | 2 +- 4 files changed, 116 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 48a96141a..d219a80d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ std = [] kv_unstable = [] kv_unstable_sval = ["kv_unstable", "sval/fmt"] kv_unstable_const_primitive = [] +kv_unstable_spec_primitive = [] [dependencies] cfg-if = "0.1.2" diff --git a/src/kv/value/internal/cast/into_primitive.rs b/src/kv/value/internal/cast/into_primitive.rs index f02cce7e1..bff919c8a 100644 --- a/src/kv/value/internal/cast/into_primitive.rs +++ b/src/kv/value/internal/cast/into_primitive.rs @@ -1,3 +1,6 @@ +// Use consts to match a type with a conversion fn +// Pros: fast, will work on stable soon (possibly 1.45.0) +// Cons: requires a `'static` bound #[cfg(all(src_build, feature = "kv_unstable_const_primitive"))] pub(super) fn into_primitive<'v, T: 'static>(value: &'v T) -> Option> { use std::any::TypeId; @@ -48,10 +51,98 @@ pub(super) fn into_primitive<'v, T: 'static>(value: &'v T) -> Option(value: &'v T) -> Option> { + use std::any::TypeId; + + use crate::kv::value::internal::Primitive; + + trait ToPrimitive { + fn to_primitive(&self) -> Option; + } + + impl ToPrimitive for T { + default fn to_primitive(&self) -> Option { + None + } + } + + impl ToPrimitive for u8 { + #[inline] + fn to_primitive(&self) -> Option { + Some(Primitive::from(*self)) + } + } + + impl ToPrimitive for u16 { + #[inline] + fn to_primitive(&self) -> Option { + Some(Primitive::from(*self)) + } + } + + impl ToPrimitive for u32 { + #[inline] + fn to_primitive(&self) -> Option { + Some(Primitive::from(*self)) + } + } + + impl ToPrimitive for u64 { + #[inline] + fn to_primitive(&self) -> Option { + Some(Primitive::from(*self)) + } + } + + impl ToPrimitive for i8 { + #[inline] + fn to_primitive(&self) -> Option { + Some(Primitive::from(*self)) + } + } + + impl ToPrimitive for i16 { + #[inline] + fn to_primitive(&self) -> Option { + Some(Primitive::from(*self)) + } + } + + impl ToPrimitive for i32 { + #[inline] + fn to_primitive(&self) -> Option { + Some(Primitive::from(*self)) + } + } + + impl ToPrimitive for i64 { + #[inline] + fn to_primitive(&self) -> Option { + Some(Primitive::from(*self)) + } + } + + impl ToPrimitive for str { + #[inline] + fn to_primitive(&self) -> Option { + Some(Primitive::from(self)) + } + } + + value.to_primitive() +} + +#[cfg(all(not(src_build), feature = "kv_unstable_spec_primitive"))] +pub fn generate() { } + +// Use a build-time generated set of type ids to match a type with a conversion fn +// Pros: works on stable +// Cons: not 'free', complicates build script, requires `'static` bound +#[cfg(all(src_build, not(any(feature = "kv_unstable_const_primitive", feature = "kv_unstable_spec_primitive"))))] pub(in kv::value) fn into_primitive<'v>(value: &'v (dyn std::any::Any + 'static)) -> Option> { // The set of type ids that map to primitives are generated at build-time // by the contents of `sorted_type_ids.expr`. These type ids are pre-sorted @@ -69,7 +160,7 @@ pub(in kv::value) fn into_primitive<'v>(value: &'v (dyn std::any::Any + 'static) // When the `src_build` config is not set then we're in the build script // This function will generate an expression fragment used by `into_primitive` -#[cfg(all(not(src_build), not(feature = "kv_unstable_const_primitive")))] +#[cfg(all(not(src_build), not(any(feature = "kv_unstable_const_primitive", feature = "kv_unstable_spec_primitive"))))] pub fn generate() { use std::path::Path; use std::{fs, env}; @@ -88,7 +179,7 @@ pub fn generate() { // SAFETY: We verify the value is $ty before casting let value = *(value as *const dyn std::any::Any as *const $ty); - value.into() + crate::kv::value::internal::Primitive::from(value) }) as for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a> ) ) @@ -106,7 +197,7 @@ pub fn generate() { // SAFETY: We verify the value is Option<$ty> before casting let value = *(value as *const dyn std::any::Any as *const Option<$ty>); if let Some(value) = value { - value.into() + crate::kv::value::internal::Primitive::from(value) } else { crate::kv::value::internal::Primitive::None } diff --git a/src/kv/value/internal/mod.rs b/src/kv/value/internal/mod.rs index be743ebe2..4acbe683c 100644 --- a/src/kv/value/internal/mod.rs +++ b/src/kv/value/internal/mod.rs @@ -98,96 +98,112 @@ impl<'v> Primitive<'v> { } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: u8) -> Self { Primitive::Unsigned(v as u64) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: u16) -> Self { Primitive::Unsigned(v as u64) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: u32) -> Self { Primitive::Unsigned(v as u64) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: u64) -> Self { Primitive::Unsigned(v) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: usize) -> Self { Primitive::Unsigned(v as u64) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: i8) -> Self { Primitive::Signed(v as i64) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: i16) -> Self { Primitive::Signed(v as i64) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: i32) -> Self { Primitive::Signed(v as i64) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: i64) -> Self { Primitive::Signed(v) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: isize) -> Self { Primitive::Signed(v as i64) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: f32) -> Self { Primitive::Float(v as f64) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: f64) -> Self { Primitive::Float(v) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: bool) -> Self { Primitive::Bool(v) } } impl<'v> From for Primitive<'v> { + #[inline] fn from(v: char) -> Self { Primitive::Char(v) } } impl<'v> From<&'v str> for Primitive<'v> { + #[inline] fn from(v: &'v str) -> Self { Primitive::Str(v) } } impl<'v> From> for Primitive<'v> { + #[inline] fn from(v: fmt::Arguments<'v>) -> Self { Primitive::Fmt(v) } diff --git a/src/lib.rs b/src/lib.rs index 2401ff964..bc3e06b2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -277,7 +277,7 @@ #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] #![feature(is_sorted)] -#![feature(const_type_id, const_if_match)] +#![feature(const_type_id, const_if_match, min_specialization)] #[cfg(all(not(feature = "std"), not(test)))] extern crate core as std; From 751ea850db136fb49519f41f60e39e295d378603 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 4 Jun 2020 03:01:32 +1000 Subject: [PATCH 15/23] add some notes about Value construction --- build.rs | 6 +- src/kv/value/internal/cast/mod.rs | 4 +- .../cast/{into_primitive.rs => primitive.rs} | 6 +- src/kv/value/mod.rs | 86 +++++++++++++++++++ 4 files changed, 94 insertions(+), 8 deletions(-) rename src/kv/value/internal/cast/{into_primitive.rs => primitive.rs} (95%) diff --git a/build.rs b/build.rs index ee2fac44a..b73f4caa1 100644 --- a/build.rs +++ b/build.rs @@ -4,8 +4,8 @@ use std::env; #[cfg(feature = "kv_unstable")] -#[path = "src/kv/value/internal/cast/into_primitive.rs"] -mod into_primitive; +#[path = "src/kv/value/internal/cast/primitive.rs"] +mod primitive; fn main() { let target = env::var("TARGET").unwrap(); @@ -15,7 +15,7 @@ fn main() { } #[cfg(feature = "kv_unstable")] - into_primitive::generate(); + primitive::generate(); println!("cargo:rustc-cfg=src_build"); diff --git a/src/kv/value/internal/cast/mod.rs b/src/kv/value/internal/cast/mod.rs index 762bd1b9f..650e9bc8b 100644 --- a/src/kv/value/internal/cast/mod.rs +++ b/src/kv/value/internal/cast/mod.rs @@ -9,14 +9,14 @@ use std::fmt; use super::{Inner, Primitive, Visitor}; use crate::kv::value::{Error, Value}; -mod into_primitive; +mod primitive; /// Attempt to capture a primitive from some generic value. /// /// If the value is a primitive type, then cast it here, avoiding needing to erase its value /// This makes `Value`s produced by `Value::from_*` more useful pub(super) fn try_from_primitive<'v, T: 'static>(value: &'v T) -> Option> { - into_primitive::into_primitive(value).map(|primitive| Value { + primitive::from_any(value).map(|primitive| Value { inner: Inner::Primitive(primitive) }) } diff --git a/src/kv/value/internal/cast/into_primitive.rs b/src/kv/value/internal/cast/primitive.rs similarity index 95% rename from src/kv/value/internal/cast/into_primitive.rs rename to src/kv/value/internal/cast/primitive.rs index bff919c8a..aaa35fc7a 100644 --- a/src/kv/value/internal/cast/into_primitive.rs +++ b/src/kv/value/internal/cast/primitive.rs @@ -2,7 +2,7 @@ // Pros: fast, will work on stable soon (possibly 1.45.0) // Cons: requires a `'static` bound #[cfg(all(src_build, feature = "kv_unstable_const_primitive"))] -pub(super) fn into_primitive<'v, T: 'static>(value: &'v T) -> Option> { +pub(super) fn from_any<'v, T: 'static>(value: &'v T) -> Option> { use std::any::TypeId; use crate::kv::value::internal::Primitive; @@ -55,7 +55,7 @@ pub fn generate() { } // Pros: fast, doesn't require `'static` bound // Cons: might not stabilize for a long time, doesn't work with `&str` #[cfg(all(src_build, feature = "kv_unstable_spec_primitive"))] -pub(super) fn into_primitive<'v, T>(value: &'v T) -> Option> { +pub(super) fn from_any<'v, T>(value: &'v T) -> Option> { use std::any::TypeId; use crate::kv::value::internal::Primitive; @@ -143,7 +143,7 @@ pub fn generate() { } // Pros: works on stable // Cons: not 'free', complicates build script, requires `'static` bound #[cfg(all(src_build, not(any(feature = "kv_unstable_const_primitive", feature = "kv_unstable_spec_primitive"))))] -pub(in kv::value) fn into_primitive<'v>(value: &'v (dyn std::any::Any + 'static)) -> Option> { +pub(in kv::value) fn from_any<'v>(value: &'v (dyn std::any::Any + 'static)) -> Option> { // The set of type ids that map to primitives are generated at build-time // by the contents of `sorted_type_ids.expr`. These type ids are pre-sorted // so that they can be searched efficiently. See the `sorted_type_ids.expr.rs` diff --git a/src/kv/value/mod.rs b/src/kv/value/mod.rs index 422adb0b5..6f8601d23 100644 --- a/src/kv/value/mod.rs +++ b/src/kv/value/mod.rs @@ -34,6 +34,92 @@ impl<'v> ToValue for Value<'v> { } /// A value in a structured key-value pair. +/// +/// # Capturing values +/// +/// There are a few ways to capture a value: +/// +/// - Using the `Value::from_*` methods. +/// - Using the `ToValue` trait. +/// - Using the standard `From` trait. +/// - Using the `Fill` API. +/// +/// ## Using the `Value::from_*` methods +/// +/// `Value` offers a few constructor methods that capture values of different kinds. +/// These methods typically require a `T: 'static`. +/// +/// ``` +/// let value = Value::from_debug(&42i32); +/// +/// assert_eq!(Some(42), value.to_i32()); +/// ``` +/// +/// ## Using the `ToValue` trait +/// +/// The `ToValue` trait can be used to capture values generically. +/// It's the bound used by `Source`. +/// +/// ``` +/// let value = 42i32.to_value(); +/// +/// assert_eq!(Some(42), value.to_i32()); +/// ``` +/// +/// ``` +/// let value = (&42i32 as &dyn Debug).to_value(); +/// +/// assert_eq!(None, value.to_i32()); +/// ``` +/// +/// ## Using the standard `From` trait +/// +/// Standard types that implement `ToValue` also implement `From`. +/// +/// ``` +/// let value = Value::from(42i32); +/// +/// assert_eq!(Some(42), value.to_i32()); +/// ``` +/// +/// ``` +/// let value = Value::from(&42i32 as &dyn Debug); +/// +/// assert_eq!(None, value.to_i32()); +/// ``` +/// +/// ## Using the `Fill` API +/// +/// The `Fill` trait is a way to bridge APIs that may not be directly +/// compatible with other constructor methods. +/// +/// ``` +/// struct FillSigned; +/// +/// impl Fill for FillSigned { +/// fn fill(&self, slot: &mut Slot) -> Result<(), Error> { +/// slot.fill_any(42i32) +/// } +/// } +/// +/// let value = Value::from(&FillSigned); +/// +/// assert_eq!(Some(42), value.to_i32()); +/// ``` +/// +/// ``` +/// struct FillDebug; +/// +/// impl Fill for FillDebug { +/// fn fill(&self, slot: &mut Slot) -> Result<(), Error> { +/// slot.fill_debug(&42i32 as &dyn Debug) +/// } +/// } +/// +/// let value = Value::from(&FillDebug); +/// +/// assert_eq!(None, value.to_i32()); +/// ``` pub struct Value<'v> { inner: Inner<'v>, } From b265bdcc7f16f6f31ed9fcb86edbfeb9ee4a6aef Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 4 Jun 2020 03:09:32 +1000 Subject: [PATCH 16/23] add a from_any method to avoid trait imports --- src/kv/value/mod.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/kv/value/mod.rs b/src/kv/value/mod.rs index 6f8601d23..888a5311a 100644 --- a/src/kv/value/mod.rs +++ b/src/kv/value/mod.rs @@ -50,6 +50,12 @@ impl<'v> ToValue for Value<'v> { /// These methods typically require a `T: 'static`. /// /// ``` +/// let value = Value::from_any(&42i32); +/// +/// assert_eq!(Some(42), value.to_i32()); +/// ``` +/// +/// ``` /// let value = Value::from_debug(&42i32); /// /// assert_eq!(Some(42), value.to_i32()); @@ -125,6 +131,14 @@ pub struct Value<'v> { } impl<'v> Value<'v> { + /// Get a value from a type implementing `ToValue`. + pub fn from_any(value: &'v T) -> Self + where + T: ToValue, + { + value.to_value() + } + /// Get a value from an internal primitive. fn from_primitive(value: T) -> Self where From a0221080b77d80d1d63dce85f117a9d1bc8aea3a Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 4 Jun 2020 20:41:06 +1000 Subject: [PATCH 17/23] remove implied Sized bound --- src/kv/value/internal/cast/mod.rs | 3 ++- src/kv/value/internal/cast/primitive.rs | 10 +++++----- src/kv/value/internal/sval.rs | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/kv/value/internal/cast/mod.rs b/src/kv/value/internal/cast/mod.rs index 650e9bc8b..b125224d8 100644 --- a/src/kv/value/internal/cast/mod.rs +++ b/src/kv/value/internal/cast/mod.rs @@ -215,7 +215,8 @@ impl<'v> Inner<'v> { } #[cfg(feature = "std")] - fn str(&mut self, _: &str) -> Result<(), Error> { + fn str(&mut self, s: &str) -> Result<(), Error> { + self.0 = Cast::String(s.to_owned()); Ok(()) } diff --git a/src/kv/value/internal/cast/primitive.rs b/src/kv/value/internal/cast/primitive.rs index aaa35fc7a..1e7d3f33d 100644 --- a/src/kv/value/internal/cast/primitive.rs +++ b/src/kv/value/internal/cast/primitive.rs @@ -2,7 +2,7 @@ // Pros: fast, will work on stable soon (possibly 1.45.0) // Cons: requires a `'static` bound #[cfg(all(src_build, feature = "kv_unstable_const_primitive"))] -pub(super) fn from_any<'v, T: 'static>(value: &'v T) -> Option> { +pub(super) fn from_any<'v, T: ?Sized + 'static>(value: &'v T) -> Option> { use std::any::TypeId; use crate::kv::value::internal::Primitive; @@ -43,7 +43,7 @@ pub(super) fn from_any<'v, T: 'static>(value: &'v T) -> Option ToPrimitive for T { } + impl ToPrimitive for T { } value.to_primitive() } @@ -55,7 +55,7 @@ pub fn generate() { } // Pros: fast, doesn't require `'static` bound // Cons: might not stabilize for a long time, doesn't work with `&str` #[cfg(all(src_build, feature = "kv_unstable_spec_primitive"))] -pub(super) fn from_any<'v, T>(value: &'v T) -> Option> { +pub(super) fn from_any<'v, T: ?Sized>(value: &'v T) -> Option> { use std::any::TypeId; use crate::kv::value::internal::Primitive; @@ -63,8 +63,8 @@ pub(super) fn from_any<'v, T>(value: &'v T) -> Option Option; } - - impl ToPrimitive for T { + + impl ToPrimitive for T { default fn to_primitive(&self) -> Option { None } diff --git a/src/kv/value/internal/sval.rs b/src/kv/value/internal/sval.rs index 77048e16b..befb36e76 100644 --- a/src/kv/value/internal/sval.rs +++ b/src/kv/value/internal/sval.rs @@ -203,4 +203,19 @@ mod tests { .expect("invalid value") ); } + + #[cfg(feature = "std")] + mod std_support { + use super::*; + + #[test] + fn sval_cast() { + assert_eq!( + "a string", + kv::Value::from_sval(&"a string".to_owned()) + .to_str() + .expect("invalid value") + ); + } + } } From 117b7a88c83dc32ac3f2e4f960bb671cd6deb5ca Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Wed, 1 Jul 2020 10:28:23 +1000 Subject: [PATCH 18/23] tidy up type id checking --- Cargo.toml | 2 - build.rs | 56 +++++++++- src/kv/value/internal/cast/primitive.rs | 130 ++++-------------------- src/lib.rs | 4 +- 4 files changed, 74 insertions(+), 118 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d219a80d2..ab8a3b57a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,8 +47,6 @@ std = [] # this will have a tighter MSRV before stabilization kv_unstable = [] kv_unstable_sval = ["kv_unstable", "sval/fmt"] -kv_unstable_const_primitive = [] -kv_unstable_spec_primitive = [] [dependencies] cfg-if = "0.1.2" diff --git a/build.rs b/build.rs index b73f4caa1..af6b12bb1 100644 --- a/build.rs +++ b/build.rs @@ -2,22 +2,72 @@ //! atomics and sets `cfg` flags accordingly. use std::env; +use std::process::Command; +use std::str::{self, FromStr}; #[cfg(feature = "kv_unstable")] #[path = "src/kv/value/internal/cast/primitive.rs"] mod primitive; fn main() { - let target = env::var("TARGET").unwrap(); + let minor = match rustc_minor_version() { + Some(minor) => minor, + None => return, + }; + let target = match rustc_target() { + Some(target) => target, + None => return + }; + + // If the target isn't thumbv6 then we can use atomic CAS if !target.starts_with("thumbv6") { println!("cargo:rustc-cfg=atomic_cas"); } + // If the Rust version is at least 1.46.0 then we can use type ids at compile time + if minor >= 46 { + println!("cargo:rustc-cfg=const_type_id"); + } + + // Generate sorted type id lookup #[cfg(feature = "kv_unstable")] primitive::generate(); - println!("cargo:rustc-cfg=src_build"); - + println!("cargo:rustc-cfg=srcbuild"); println!("cargo:rerun-if-changed=build.rs"); } + +fn rustc_target() -> Option { + env::var("TARGET").ok() +} + +// From the `serde` build script +fn rustc_minor_version() -> Option { + let rustc = match env::var_os("RUSTC") { + Some(rustc) => rustc, + None => return None, + }; + + let output = match Command::new(rustc).arg("--version").output() { + Ok(output) => output, + Err(_) => return None, + }; + + let version = match str::from_utf8(&output.stdout) { + Ok(version) => version, + Err(_) => return None, + }; + + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + + let next = match pieces.next() { + Some(next) => next, + None => return None, + }; + + u32::from_str(next).ok() +} diff --git a/src/kv/value/internal/cast/primitive.rs b/src/kv/value/internal/cast/primitive.rs index 1e7d3f33d..9271c2905 100644 --- a/src/kv/value/internal/cast/primitive.rs +++ b/src/kv/value/internal/cast/primitive.rs @@ -1,166 +1,74 @@ // Use consts to match a type with a conversion fn -// Pros: fast, will work on stable soon (possibly 1.45.0) -// Cons: requires a `'static` bound -#[cfg(all(src_build, feature = "kv_unstable_const_primitive"))] +#[cfg(all(srcbuild, const_type_id))] pub(super) fn from_any<'v, T: ?Sized + 'static>(value: &'v T) -> Option> { use std::any::TypeId; use crate::kv::value::internal::Primitive; - + trait ToPrimitive where Self: 'static { const CALL: fn(&Self) -> Option = { const U8: TypeId = TypeId::of::(); const U16: TypeId = TypeId::of::(); const U32: TypeId = TypeId::of::(); const U64: TypeId = TypeId::of::(); - + const I8: TypeId = TypeId::of::(); const I16: TypeId = TypeId::of::(); const I32: TypeId = TypeId::of::(); const I64: TypeId = TypeId::of::(); - + const STR: TypeId = TypeId::of::<&'static str>(); - + match TypeId::of::() { U8 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u8) })), U16 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u16) })), U32 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u32) })), U64 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u64) })), - + I8 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i8) })), I16 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i16) })), I32 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i32) })), I64 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i64) })), - + STR => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const &'static str) })), - + _ => |_| None, } }; - - fn to_primitive(&self) -> Option { - (Self::CALL)(self) - } - } - - impl ToPrimitive for T { } - - value.to_primitive() -} - -#[cfg(all(not(src_build), feature = "kv_unstable_const_primitive"))] -pub fn generate() { } - -// Use specialization to match a type with a conversion fn -// Pros: fast, doesn't require `'static` bound -// Cons: might not stabilize for a long time, doesn't work with `&str` -#[cfg(all(src_build, feature = "kv_unstable_spec_primitive"))] -pub(super) fn from_any<'v, T: ?Sized>(value: &'v T) -> Option> { - use std::any::TypeId; - - use crate::kv::value::internal::Primitive; - - trait ToPrimitive { - fn to_primitive(&self) -> Option; - } - - impl ToPrimitive for T { - default fn to_primitive(&self) -> Option { - None - } - } - - impl ToPrimitive for u8 { - #[inline] - fn to_primitive(&self) -> Option { - Some(Primitive::from(*self)) - } - } - impl ToPrimitive for u16 { - #[inline] fn to_primitive(&self) -> Option { - Some(Primitive::from(*self)) - } - } - - impl ToPrimitive for u32 { - #[inline] - fn to_primitive(&self) -> Option { - Some(Primitive::from(*self)) - } - } - - impl ToPrimitive for u64 { - #[inline] - fn to_primitive(&self) -> Option { - Some(Primitive::from(*self)) - } - } - - impl ToPrimitive for i8 { - #[inline] - fn to_primitive(&self) -> Option { - Some(Primitive::from(*self)) - } - } - - impl ToPrimitive for i16 { - #[inline] - fn to_primitive(&self) -> Option { - Some(Primitive::from(*self)) - } - } - - impl ToPrimitive for i32 { - #[inline] - fn to_primitive(&self) -> Option { - Some(Primitive::from(*self)) - } - } - - impl ToPrimitive for i64 { - #[inline] - fn to_primitive(&self) -> Option { - Some(Primitive::from(*self)) + (Self::CALL)(self) } } - impl ToPrimitive for str { - #[inline] - fn to_primitive(&self) -> Option { - Some(Primitive::from(self)) - } - } + impl ToPrimitive for T { } value.to_primitive() } -#[cfg(all(not(src_build), feature = "kv_unstable_spec_primitive"))] +#[cfg(all(not(src_build), const_type_id))] +#[allow(dead_code)] pub fn generate() { } // Use a build-time generated set of type ids to match a type with a conversion fn -// Pros: works on stable -// Cons: not 'free', complicates build script, requires `'static` bound -#[cfg(all(src_build, not(any(feature = "kv_unstable_const_primitive", feature = "kv_unstable_spec_primitive"))))] -pub(in kv::value) fn from_any<'v>(value: &'v (dyn std::any::Any + 'static)) -> Option> { +#[cfg(all(srcbuild, not(const_type_id)))] +pub(super) fn from_any<'v>(value: &'v (dyn std::any::Any + 'static)) -> Option> { // The set of type ids that map to primitives are generated at build-time // by the contents of `sorted_type_ids.expr`. These type ids are pre-sorted // so that they can be searched efficiently. See the `sorted_type_ids.expr.rs` // file for the set of types that appear in this list - const TYPE_IDS: [(std::any::TypeId, for<'a> fn(&'a (dyn std::any::Any + 'static)) -> crate::kv::value::internal::Primitive<'a>); 30] = include!(concat!(env!("OUT_DIR"), "/into_primitive.rs")); + let type_ids = include!(concat!(env!("OUT_DIR"), "/into_primitive.rs")); - debug_assert!(TYPE_IDS.is_sorted_by_key(|&(k, _)| k)); - if let Ok(i) = TYPE_IDS.binary_search_by_key(&value.type_id(), |&(k, _)| k) { - Some((TYPE_IDS[i].1)(value)) + if let Ok(i) = type_ids.binary_search_by_key(&value.type_id(), |&(k, _)| k) { + Some((type_ids[i].1)(value)) } else { None } } // When the `src_build` config is not set then we're in the build script -// This function will generate an expression fragment used by `into_primitive` -#[cfg(all(not(src_build), not(any(feature = "kv_unstable_const_primitive", feature = "kv_unstable_spec_primitive"))))] +#[cfg(all(not(srcbuild), not(const_type_id)))] +#[allow(dead_code)] pub fn generate() { use std::path::Path; use std::{fs, env}; diff --git a/src/lib.rs b/src/lib.rs index bc3e06b2d..e1cd5b8ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -276,8 +276,8 @@ #![cfg_attr(rustbuild, feature(staged_api, rustc_private))] #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] -#![feature(is_sorted)] -#![feature(const_type_id, const_if_match, min_specialization)] +// TODO: Remove once /~https://github.com/rust-lang/rust/pull/72488 is merged +#![cfg_attr(const_type_id, feature(const_type_id))] #[cfg(all(not(feature = "std"), not(test)))] extern crate core as std; From d0f6561d06655782b6df05fc75d18857446035ec Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Wed, 1 Jul 2020 11:18:15 +1000 Subject: [PATCH 19/23] flesh out const eval based conversions --- src/kv/value/fill.rs | 2 +- src/kv/value/internal/cast/primitive.rs | 119 ++++++++++++++++-------- src/kv/value/internal/fmt.rs | 57 +++++++++--- src/kv/value/internal/sval.rs | 35 +++++-- 4 files changed, 149 insertions(+), 64 deletions(-) diff --git a/src/kv/value/fill.rs b/src/kv/value/fill.rs index 222b78fd0..9197d4c5a 100644 --- a/src/kv/value/fill.rs +++ b/src/kv/value/fill.rs @@ -9,7 +9,7 @@ impl<'v> Value<'v> { /// Get a value from a fillable slot. pub fn from_fill(value: &'v T) -> Self where - T: Fill + 'static, + T: Fill, { Value { inner: Inner::Fill(value), diff --git a/src/kv/value/internal/cast/primitive.rs b/src/kv/value/internal/cast/primitive.rs index 9271c2905..991a70046 100644 --- a/src/kv/value/internal/cast/primitive.rs +++ b/src/kv/value/internal/cast/primitive.rs @@ -1,58 +1,98 @@ +/* +This module generates code to try efficiently convert some arbitrary `T: 'static` into +a `Primitive`. It's used by both the `build.rs` script and by the source itself. There +are currently two implementations here: + +- When the compiler version is less than `1.46.0` we check type ids at runtime. This +means generating a pre-sorted list of type ids at compile time using the `build.rs` +and matching them at runtime. +- When the compiler version is at least `1.46.0` we use const evaluation to check type ids +at compile time. There's no generated code from `build.rs` involved. + +In the future when `min_specialization` is stabilized we could use it instead and avoid needing +the `'static` bound altogether. +*/ + // Use consts to match a type with a conversion fn #[cfg(all(srcbuild, const_type_id))] -pub(super) fn from_any<'v, T: ?Sized + 'static>(value: &'v T) -> Option> { +pub(super) fn from_any<'v, T: ?Sized + 'static>( + value: &'v T, +) -> Option> { use std::any::TypeId; use crate::kv::value::internal::Primitive; - trait ToPrimitive where Self: 'static { - const CALL: fn(&Self) -> Option = { - const U8: TypeId = TypeId::of::(); - const U16: TypeId = TypeId::of::(); - const U32: TypeId = TypeId::of::(); - const U64: TypeId = TypeId::of::(); - - const I8: TypeId = TypeId::of::(); - const I16: TypeId = TypeId::of::(); - const I32: TypeId = TypeId::of::(); - const I64: TypeId = TypeId::of::(); - - const STR: TypeId = TypeId::of::<&'static str>(); - - match TypeId::of::() { - U8 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u8) })), - U16 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u16) })), - U32 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u32) })), - U64 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const u64) })), - - I8 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i8) })), - I16 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i16) })), - I32 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i32) })), - I64 => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const i64) })), - - STR => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const &'static str) })), - - _ => |_| None, + macro_rules! to_primitive { + ($($ty:ty : ($const_ident:ident, $option_ident:ident),)*) => { + trait ToPrimitive + where + Self: 'static, + { + const CALL: fn(&Self) -> Option = { + $( + const $const_ident: TypeId = TypeId::of::<$ty>(); + const $option_ident: TypeId = TypeId::of::>(); + );* + + match TypeId::of::() { + $( + $const_ident => |v| Some(Primitive::from(unsafe { *(v as *const Self as *const $ty) })), + $option_ident => |v| Some({ + let v = unsafe { *(v as *const Self as *const Option<$ty>) }; + match v { + Some(v) => Primitive::from(v), + None => Primitive::None, + } + }), + )* + + _ => |_| None, + } + }; + + fn to_primitive(&self) -> Option { + (Self::CALL)(self) + } } - }; - fn to_primitive(&self) -> Option { - (Self::CALL)(self) + impl ToPrimitive for T {} } } - impl ToPrimitive for T { } + // NOTE: The types here *must* match the ones used below when `const_type_id` is not available + to_primitive![ + usize: (USIZE, OPTION_USIZE), + u8: (U8, OPTION_U8), + u16: (U16, OPTION_U16), + u32: (U32, OPTION_U32), + u64: (U64, OPTION_U64), + + isize: (ISIZE, OPTION_ISIZE), + i8: (I8, OPTION_I8), + i16: (I16, OPTION_I16), + i32: (I32, OPTION_I32), + i64: (I64, OPTION_I64), + + f32: (F32, OPTION_F32), + f64: (F64, OPTION_F64), + + char: (CHAR, OPTION_CHAR), + bool: (BOOL, OPTION_BOOL), + &'static str: (STR, OPTION_STR), + ]; value.to_primitive() } #[cfg(all(not(src_build), const_type_id))] #[allow(dead_code)] -pub fn generate() { } +pub fn generate() {} // Use a build-time generated set of type ids to match a type with a conversion fn #[cfg(all(srcbuild, not(const_type_id)))] -pub(super) fn from_any<'v>(value: &'v (dyn std::any::Any + 'static)) -> Option> { +pub(super) fn from_any<'v>( + value: &'v (dyn std::any::Any + 'static), +) -> Option> { // The set of type ids that map to primitives are generated at build-time // by the contents of `sorted_type_ids.expr`. These type ids are pre-sorted // so that they can be searched efficiently. See the `sorted_type_ids.expr.rs` @@ -71,7 +111,7 @@ pub(super) fn from_any<'v>(value: &'v (dyn std::any::Any + 'static)) -> Option { @@ -118,26 +158,23 @@ pub fn generate() { }; } + // NOTE: The types here *must* match the ones used above when `const_type_id` is available let mut type_ids = type_ids![ usize, u8, u16, u32, u64, - isize, i8, i16, i32, i64, - f32, f64, - char, bool, - - &str, + &'static str, ]; type_ids.sort_by_key(|&(k, _)| k); diff --git a/src/kv/value/internal/fmt.rs b/src/kv/value/internal/fmt.rs index 0a1a8c5c5..ce77d0232 100644 --- a/src/kv/value/internal/fmt.rs +++ b/src/kv/value/internal/fmt.rs @@ -5,25 +5,55 @@ use std::fmt; -use super::{Inner, Visitor, cast}; +use super::{cast, Inner, Visitor}; use crate::kv; use crate::kv::value::{Error, Slot, ToValue}; impl<'v> kv::Value<'v> { /// Get a value from a debuggable type. - pub fn from_debug(value: &'v T) -> Self + /// + /// This method will attempt to capture the given value as a well-known primitive + /// before resorting to using its `Debug` implementation. + pub fn capture_debug(value: &'v T) -> Self where T: fmt::Debug + 'static, { - cast::try_from_primitive(value).unwrap_or(kv::Value { inner: Inner::Debug(value) }) + cast::try_from_primitive(value).unwrap_or(kv::Value { + inner: Inner::Debug(value), + }) + } + + /// Get a value from a debuggable type. + pub fn from_debug(value: &'v T) -> Self + where + T: fmt::Debug, + { + kv::Value { + inner: Inner::Debug(value), + } } /// Get a value from a displayable type. - pub fn from_display(value: &'v T) -> Self + /// + /// This method will attempt to capture the given value as a well-known primitive + /// before resorting to using its `Display` implementation. + pub fn capture_display(value: &'v T) -> Self where T: fmt::Display + 'static, { - cast::try_from_primitive(value).unwrap_or(kv::Value { inner: Inner::Display(value) }) + cast::try_from_primitive(value).unwrap_or(kv::Value { + inner: Inner::Display(value), + }) + } + + /// Get a value from a displayable type. + pub fn from_display(value: &'v T) -> Self + where + T: fmt::Display, + { + kv::Value { + inner: Inner::Display(value), + } } } @@ -90,7 +120,7 @@ impl<'v> ToValue for dyn fmt::Display + 'v { impl<'v> From<&'v (dyn fmt::Debug)> for kv::Value<'v> { fn from(value: &'v (dyn fmt::Debug)) -> kv::Value<'v> { kv::Value { - inner: Inner::Debug(value) + inner: Inner::Debug(value), } } } @@ -98,7 +128,7 @@ impl<'v> From<&'v (dyn fmt::Debug)> for kv::Value<'v> { impl<'v> From<&'v (dyn fmt::Display)> for kv::Value<'v> { fn from(value: &'v (dyn fmt::Display)) -> kv::Value<'v> { kv::Value { - inner: Inner::Display(value) + inner: Inner::Display(value), } } } @@ -155,14 +185,14 @@ mod tests { fn fmt_cast() { assert_eq!( 42u32, - kv::Value::from_debug(&42u64) + kv::Value::capture_debug(&42u64) .to_u32() .expect("invalid value") ); assert_eq!( "a string", - kv::Value::from_display(&"a string") + kv::Value::capture_display(&"a string") .to_borrowed_str() .expect("invalid value") ); @@ -170,9 +200,12 @@ mod tests { #[test] fn fmt_capture() { - assert_eq!(kv::Value::from_debug(&1u16).to_token(), Token::U64(1)); - assert_eq!(kv::Value::from_display(&1u16).to_token(), Token::U64(1)); + assert_eq!(kv::Value::capture_debug(&1u16).to_token(), Token::U64(1)); + assert_eq!(kv::Value::capture_display(&1u16).to_token(), Token::U64(1)); - assert_eq!(kv::Value::from_debug(&Some(1u16)).to_token(), Token::U64(1)); + assert_eq!( + kv::Value::capture_debug(&Some(1u16)).to_token(), + Token::U64(1) + ); } } diff --git a/src/kv/value/internal/sval.rs b/src/kv/value/internal/sval.rs index befb36e76..fa3add7a7 100644 --- a/src/kv/value/internal/sval.rs +++ b/src/kv/value/internal/sval.rs @@ -14,11 +14,26 @@ use crate::kv::value::{Error, Slot, ToValue}; impl<'v> kv::Value<'v> { /// Get a value from a structured type. - pub fn from_sval(value: &'v T) -> Self + /// + /// This method will attempt to capture the given value as a well-known primitive + /// before resorting to using its `Value` implementation. + pub fn capture_sval(value: &'v T) -> Self where T: sval::Value + 'static, { - cast::try_from_primitive(value).unwrap_or(kv::Value { inner: Inner::Sval(value) }) + cast::try_from_primitive(value).unwrap_or(kv::Value { + inner: Inner::Sval(value), + }) + } + + /// Get a value from a structured type. + pub fn from_sval(value: &'v T) -> Self + where + T: sval::Value, + { + kv::Value { + inner: Inner::Sval(value), + } } } @@ -55,7 +70,7 @@ impl<'v> ToValue for dyn sval::Value + 'v { impl<'v> From<&'v (dyn sval::Value)> for kv::Value<'v> { fn from(value: &'v (dyn sval::Value)) -> kv::Value<'v> { kv::Value { - inner: Inner::Sval(value) + inner: Inner::Sval(value), } } } @@ -110,7 +125,7 @@ pub(super) fn cast<'v>(v: &dyn sval::Value) -> Cast<'v> { } impl Error { - fn from_sval(_: sval::value::Error) -> Self { + fn capture_sval(_: sval::value::Error) -> Self { Error::msg("`sval` serialization failed") } @@ -167,8 +182,8 @@ mod tests { use kv::value::test::Token; #[test] - fn test_from_sval() { - assert_eq!(kv::Value::from_sval(&42u64).to_token(), Token::U64(42)); + fn test_capture_sval() { + assert_eq!(kv::Value::capture_sval(&42u64).to_token(), Token::U64(42)); } #[test] @@ -183,14 +198,14 @@ mod tests { fn sval_cast() { assert_eq!( 42u32, - kv::Value::from_sval(&42u64) + kv::Value::capture_sval(&42u64) .to_u32() .expect("invalid value") ); assert_eq!( "a string", - kv::Value::from_sval(&"a string") + kv::Value::capture_sval(&"a string") .to_borrowed_str() .expect("invalid value") ); @@ -198,7 +213,7 @@ mod tests { #[cfg(feature = "std")] assert_eq!( "a string", - kv::Value::from_sval(&"a string") + kv::Value::capture_sval(&"a string") .to_str() .expect("invalid value") ); @@ -212,7 +227,7 @@ mod tests { fn sval_cast() { assert_eq!( "a string", - kv::Value::from_sval(&"a string".to_owned()) + kv::Value::capture_sval(&"a string".to_owned()) .to_str() .expect("invalid value") ); From 52ddb2eca5a9d2fa2e9930eadab83cf520c17805 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Wed, 1 Jul 2020 11:36:53 +1000 Subject: [PATCH 20/23] get doc tests to pass --- src/kv/value/internal/sval.rs | 2 +- src/kv/value/mod.rs | 98 ++++++++++++++++++++++------------- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/src/kv/value/internal/sval.rs b/src/kv/value/internal/sval.rs index 91541af85..252efd152 100644 --- a/src/kv/value/internal/sval.rs +++ b/src/kv/value/internal/sval.rs @@ -167,7 +167,7 @@ pub(super) fn cast<'v>(v: &dyn sval::Value) -> Cast<'v> { } impl Error { - fn capture_sval(_: sval::value::Error) -> Self { + fn from_sval(_: sval::value::Error) -> Self { Error::msg("`sval` serialization failed") } diff --git a/src/kv/value/mod.rs b/src/kv/value/mod.rs index 888a5311a..8d8282624 100644 --- a/src/kv/value/mod.rs +++ b/src/kv/value/mod.rs @@ -34,96 +34,120 @@ impl<'v> ToValue for Value<'v> { } /// A value in a structured key-value pair. -/// +/// /// # Capturing values -/// +/// /// There are a few ways to capture a value: -/// +/// +/// - Using the `Value::capture_*` methods. /// - Using the `Value::from_*` methods. /// - Using the `ToValue` trait. /// - Using the standard `From` trait. /// - Using the `Fill` API. -/// -/// ## Using the `Value::from_*` methods -/// +/// +/// ## Using the `Value::capture_*` methods +/// /// `Value` offers a few constructor methods that capture values of different kinds. -/// These methods typically require a `T: 'static`. -/// +/// These methods require a `T: 'static` to support downcasting. +/// /// ``` -/// let value = Value::from_any(&42i32); -/// +/// use log::kv::Value; +/// +/// let value = Value::capture_debug(&42i32); +/// /// assert_eq!(Some(42), value.to_i32()); /// ``` -/// +/// +/// ## Using the `Value::from_*` methods +/// +/// `Value` offers a few constructor methods that capture values of different kinds. +/// These methods don't require `T: 'static`, but can't support downcasting. +/// /// ``` +/// use log::kv::Value; +/// /// let value = Value::from_debug(&42i32); -/// -/// assert_eq!(Some(42), value.to_i32()); +/// +/// assert_eq!(None, value.to_i32()); /// ``` -/// +/// /// ## Using the `ToValue` trait -/// +/// /// The `ToValue` trait can be used to capture values generically. /// It's the bound used by `Source`. -/// +/// /// ``` +/// # use log::kv::ToValue; /// let value = 42i32.to_value(); -/// +/// /// assert_eq!(Some(42), value.to_i32()); /// ``` -/// +/// /// ``` +/// # use std::fmt::Debug; +/// use log::kv::ToValue; +/// /// let value = (&42i32 as &dyn Debug).to_value(); -/// +/// /// assert_eq!(None, value.to_i32()); /// ``` -/// +/// /// ## Using the standard `From` trait -/// +/// /// Standard types that implement `ToValue` also implement `From`. -/// +/// /// ``` +/// use log::kv::Value; +/// /// let value = Value::from(42i32); -/// +/// /// assert_eq!(Some(42), value.to_i32()); /// ``` -/// +/// /// ``` +/// # use std::fmt::Debug; +/// use log::kv::Value; +/// /// let value = Value::from(&42i32 as &dyn Debug); -/// +/// /// assert_eq!(None, value.to_i32()); /// ``` -/// +/// /// ## Using the `Fill` API -/// +/// /// The `Fill` trait is a way to bridge APIs that may not be directly /// compatible with other constructor methods. -/// +/// /// ``` +/// use log::kv::value::{Value, Slot, Fill, Error}; +/// /// struct FillSigned; -/// +/// /// impl Fill for FillSigned { /// fn fill(&self, slot: &mut Slot) -> Result<(), Error> { /// slot.fill_any(42i32) /// } /// } -/// -/// let value = Value::from(&FillSigned); -/// +/// +/// let value = Value::from_fill(&FillSigned); +/// /// assert_eq!(Some(42), value.to_i32()); /// ``` -/// +/// /// ``` +/// # use std::fmt::Debug; +/// use log::kv::value::{Value, Slot, Fill, Error}; +/// /// struct FillDebug; -/// +/// /// impl Fill for FillDebug { /// fn fill(&self, slot: &mut Slot) -> Result<(), Error> { /// slot.fill_debug(&42i32 as &dyn Debug) /// } /// } -/// -/// let value = Value::from(&FillDebug); -/// +/// +/// let value = Value::from_fill(&FillDebug); +/// /// assert_eq!(None, value.to_i32()); /// ``` pub struct Value<'v> { From 84a3a7b04472f185402b240285bfc2b29aa300b0 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Wed, 1 Jul 2020 11:38:17 +1000 Subject: [PATCH 21/23] run fmt --- benches/value.rs | 18 +++++------------- build.rs | 2 +- src/kv/value/impls.rs | 20 +------------------- src/kv/value/internal/cast/mod.rs | 2 +- src/kv/value/internal/fmt.rs | 1 - src/lib.rs | 1 - 6 files changed, 8 insertions(+), 36 deletions(-) diff --git a/benches/value.rs b/benches/value.rs index 3c7d22512..4e6960e80 100644 --- a/benches/value.rs +++ b/benches/value.rs @@ -1,30 +1,24 @@ #![cfg(feature = "kv_unstable")] #![feature(test)] -extern crate test; extern crate log; +extern crate test; use log::kv::Value; #[bench] fn u8_to_value(b: &mut test::Bencher) { - b.iter(|| { - Value::from(1u8) - }) + b.iter(|| Value::from(1u8)) } #[bench] fn u8_to_value_debug(b: &mut test::Bencher) { - b.iter(|| { - Value::from_debug(&1u8) - }) + b.iter(|| Value::from_debug(&1u8)) } #[bench] fn str_to_value_debug(b: &mut test::Bencher) { - b.iter(|| { - Value::from_debug(&"a string") - }) + b.iter(|| Value::from_debug(&"a string")) } #[bench] @@ -32,7 +26,5 @@ fn custom_to_value_debug(b: &mut test::Bencher) { #[derive(Debug)] struct A; - b.iter(|| { - Value::from_debug(&A) - }) + b.iter(|| Value::from_debug(&A)) } diff --git a/build.rs b/build.rs index af6b12bb1..7d3eadb78 100644 --- a/build.rs +++ b/build.rs @@ -17,7 +17,7 @@ fn main() { let target = match rustc_target() { Some(target) => target, - None => return + None => return, }; // If the target isn't thumbv6 then we can use atomic CAS diff --git a/src/kv/value/impls.rs b/src/kv/value/impls.rs index 1b58d9aef..506128bf2 100644 --- a/src/kv/value/impls.rs +++ b/src/kv/value/impls.rs @@ -67,25 +67,7 @@ macro_rules! impl_to_value_primitive { }; } -impl_to_value_primitive! [ - usize, - u8, - u16, - u32, - u64, - - isize, - i8, - i16, - i32, - i64, - - f32, - f64, - - char, - bool, -]; +impl_to_value_primitive![usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32, f64, char, bool,]; #[cfg(feature = "std")] mod std_support { diff --git a/src/kv/value/internal/cast/mod.rs b/src/kv/value/internal/cast/mod.rs index b125224d8..ec6a22379 100644 --- a/src/kv/value/internal/cast/mod.rs +++ b/src/kv/value/internal/cast/mod.rs @@ -17,7 +17,7 @@ mod primitive; /// This makes `Value`s produced by `Value::from_*` more useful pub(super) fn try_from_primitive<'v, T: 'static>(value: &'v T) -> Option> { primitive::from_any(value).map(|primitive| Value { - inner: Inner::Primitive(primitive) + inner: Inner::Primitive(primitive), }) } diff --git a/src/kv/value/internal/fmt.rs b/src/kv/value/internal/fmt.rs index b456ca0b4..bde703c20 100644 --- a/src/kv/value/internal/fmt.rs +++ b/src/kv/value/internal/fmt.rs @@ -227,7 +227,6 @@ impl<'v> fmt::Display for kv::Value<'v> { } } - impl<'v> ToValue for dyn fmt::Debug + 'v { fn to_value(&self) -> kv::Value { kv::Value::from(self) diff --git a/src/lib.rs b/src/lib.rs index 7bc4075cd..0aad768e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -275,7 +275,6 @@ // an unstable crate #![cfg_attr(rustbuild, feature(staged_api, rustc_private))] #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] - // TODO: Remove once /~https://github.com/rust-lang/rust/pull/72488 is merged #![cfg_attr(const_type_id, feature(const_type_id))] From 0628fe6101fc821bb3c03ef2eab8001bce4748c8 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 30 Jul 2020 15:33:22 +1000 Subject: [PATCH 22/23] remove unneeded unstable attribute --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0aad768e5..4e5c6c447 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -275,8 +275,6 @@ // an unstable crate #![cfg_attr(rustbuild, feature(staged_api, rustc_private))] #![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))] -// TODO: Remove once /~https://github.com/rust-lang/rust/pull/72488 is merged -#![cfg_attr(const_type_id, feature(const_type_id))] #[cfg(all(not(feature = "std"), not(test)))] extern crate core as std; From 023e7e52af83b16bd38dd25b76aac2b7adcc59c5 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 30 Jul 2020 15:34:53 +1000 Subject: [PATCH 23/23] const type ids needs 1.47 --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 7d3eadb78..40d16129b 100644 --- a/build.rs +++ b/build.rs @@ -26,7 +26,7 @@ fn main() { } // If the Rust version is at least 1.46.0 then we can use type ids at compile time - if minor >= 46 { + if minor >= 47 { println!("cargo:rustc-cfg=const_type_id"); }