-
Notifications
You must be signed in to change notification settings - Fork 258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework structured value casting #396
Changes from 10 commits
0459fd0
be90b6e
ef5778b
75211b5
dac6e30
1f59a35
7af0cf1
c00c8aa
690ae53
dd15f5b
256b962
fd2049a
c8a3d37
f9333fb
751ea85
b265bdc
a022108
117b7a8
d0f6561
34d4a49
52ddb2e
84a3a7b
0628fe6
023e7e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#![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") | ||
}) | ||
} | ||
|
||
#[bench] | ||
fn custom_to_value_debug(b: &mut test::Bencher) { | ||
#[derive(Debug)] | ||
struct A; | ||
|
||
b.iter(|| { | ||
Value::from_debug(&A) | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// NOTE: With specialization we could potentially avoid this call using a blanket | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Specialization is possible because we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @hawkw sorry for the random ping but thought you might find this interesting for This is using a static set of type ids sorted at build time and binary searches them for interesting types given a generic There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fascinating, thanks for pinging me! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So... If rust-lang/rust#72437 and rust-lang/rust#72488 are both merged then you’d be able to do this at compile time without specialization :) There’s an example in the |
||
// `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<crate::kv::value::internal::Primitive<'v>> { | ||
// 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::<Option<$ty>>(), | ||
stringify!( | ||
( | ||
std::any::TypeId::of::<Option<$ty>>(), | ||
(|value| unsafe { | ||
debug_assert_eq!(value.type_id(), std::any::TypeId::of::<Option<$ty>>()); | ||
|
||
// 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(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m using this so the
into_primitive
module can tell if it’s in the build script or not. Is there a cfg that exists already for this?