Skip to content

Commit 803a23b

Browse files
authored
Rework structured value casting (#396)
Also adds a const-fn based mechanism for pulling concrete values out of generic ones
1 parent ca54ac7 commit 803a23b

File tree

10 files changed

+667
-190
lines changed

10 files changed

+667
-190
lines changed

benches/value.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![cfg(feature = "kv_unstable")]
2+
#![feature(test)]
3+
4+
extern crate log;
5+
extern crate test;
6+
7+
use log::kv::Value;
8+
9+
#[bench]
10+
fn u8_to_value(b: &mut test::Bencher) {
11+
b.iter(|| Value::from(1u8))
12+
}
13+
14+
#[bench]
15+
fn u8_to_value_debug(b: &mut test::Bencher) {
16+
b.iter(|| Value::from_debug(&1u8))
17+
}
18+
19+
#[bench]
20+
fn str_to_value_debug(b: &mut test::Bencher) {
21+
b.iter(|| Value::from_debug(&"a string"))
22+
}
23+
24+
#[bench]
25+
fn custom_to_value_debug(b: &mut test::Bencher) {
26+
#[derive(Debug)]
27+
struct A;
28+
29+
b.iter(|| Value::from_debug(&A))
30+
}

build.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,72 @@
22
//! atomics and sets `cfg` flags accordingly.
33
44
use std::env;
5+
use std::process::Command;
6+
use std::str::{self, FromStr};
7+
8+
#[cfg(feature = "kv_unstable")]
9+
#[path = "src/kv/value/internal/cast/primitive.rs"]
10+
mod primitive;
511

612
fn main() {
7-
let target = env::var("TARGET").unwrap();
13+
let minor = match rustc_minor_version() {
14+
Some(minor) => minor,
15+
None => return,
16+
};
17+
18+
let target = match rustc_target() {
19+
Some(target) => target,
20+
None => return,
21+
};
822

23+
// If the target isn't thumbv6 then we can use atomic CAS
924
if !target.starts_with("thumbv6") {
1025
println!("cargo:rustc-cfg=atomic_cas");
1126
}
1227

28+
// If the Rust version is at least 1.46.0 then we can use type ids at compile time
29+
if minor >= 47 {
30+
println!("cargo:rustc-cfg=const_type_id");
31+
}
32+
33+
// Generate sorted type id lookup
34+
#[cfg(feature = "kv_unstable")]
35+
primitive::generate();
36+
37+
println!("cargo:rustc-cfg=srcbuild");
1338
println!("cargo:rerun-if-changed=build.rs");
1439
}
40+
41+
fn rustc_target() -> Option<String> {
42+
env::var("TARGET").ok()
43+
}
44+
45+
// From the `serde` build script
46+
fn rustc_minor_version() -> Option<u32> {
47+
let rustc = match env::var_os("RUSTC") {
48+
Some(rustc) => rustc,
49+
None => return None,
50+
};
51+
52+
let output = match Command::new(rustc).arg("--version").output() {
53+
Ok(output) => output,
54+
Err(_) => return None,
55+
};
56+
57+
let version = match str::from_utf8(&output.stdout) {
58+
Ok(version) => version,
59+
Err(_) => return None,
60+
};
61+
62+
let mut pieces = version.split('.');
63+
if pieces.next() != Some("rustc 1") {
64+
return None;
65+
}
66+
67+
let next = match pieces.next() {
68+
Some(next) => next,
69+
None => return None,
70+
};
71+
72+
u32::from_str(next).ok()
73+
}

src/kv/value/fill.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
33
use std::fmt;
44

5-
use super::internal::{Erased, Inner, Visitor};
5+
use super::internal::{Inner, Visitor};
66
use super::{Error, Value};
77

88
impl<'v> Value<'v> {
99
/// Get a value from a fillable slot.
1010
pub fn from_fill<T>(value: &'v T) -> Self
1111
where
12-
T: Fill + 'static,
12+
T: Fill,
1313
{
1414
Value {
15-
inner: Inner::Fill(unsafe { Erased::new_unchecked::<T>(value) }),
15+
inner: Inner::Fill(value),
1616
}
1717
}
1818
}

src/kv/value/impls.rs

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,6 @@ use std::fmt;
77

88
use super::{Primitive, ToValue, Value};
99

10-
macro_rules! impl_into_owned {
11-
($($into_ty:ty => $convert:ident,)*) => {
12-
$(
13-
impl ToValue for $into_ty {
14-
fn to_value(&self) -> Value {
15-
Value::from(*self)
16-
}
17-
}
18-
19-
impl<'v> From<$into_ty> for Value<'v> {
20-
fn from(value: $into_ty) -> Self {
21-
Value::from_primitive(value as $convert)
22-
}
23-
}
24-
)*
25-
};
26-
}
27-
2810
impl<'v> ToValue for &'v str {
2911
fn to_value(&self) -> Value {
3012
Value::from(*self)
@@ -67,25 +49,25 @@ where
6749
}
6850
}
6951

70-
impl_into_owned! [
71-
usize => u64,
72-
u8 => u64,
73-
u16 => u64,
74-
u32 => u64,
75-
u64 => u64,
76-
77-
isize => i64,
78-
i8 => i64,
79-
i16 => i64,
80-
i32 => i64,
81-
i64 => i64,
82-
83-
f32 => f64,
84-
f64 => f64,
85-
86-
char => char,
87-
bool => bool,
88-
];
52+
macro_rules! impl_to_value_primitive {
53+
($($into_ty:ty,)*) => {
54+
$(
55+
impl ToValue for $into_ty {
56+
fn to_value(&self) -> Value {
57+
Value::from(*self)
58+
}
59+
}
60+
61+
impl<'v> From<$into_ty> for Value<'v> {
62+
fn from(value: $into_ty) -> Self {
63+
Value::from_primitive(value)
64+
}
65+
}
66+
)*
67+
};
68+
}
69+
70+
impl_to_value_primitive![usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32, f64, char, bool,];
8971

9072
#[cfg(feature = "std")]
9173
mod std_support {

src/kv/value/internal/cast.rs renamed to src/kv/value/internal/cast/mod.rs

Lines changed: 22 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,23 @@
44
//! but may end up executing arbitrary caller code if the value is complex.
55
//! They will also attempt to downcast erased types into a primitive where possible.
66
7-
use std::any::TypeId;
87
use std::fmt;
98

10-
use super::{Erased, Inner, Primitive, Visitor};
9+
use super::{Inner, Primitive, Visitor};
1110
use crate::kv::value::{Error, Value};
1211

12+
mod primitive;
13+
14+
/// Attempt to capture a primitive from some generic value.
15+
///
16+
/// If the value is a primitive type, then cast it here, avoiding needing to erase its value
17+
/// This makes `Value`s produced by `Value::from_*` more useful
18+
pub(super) fn try_from_primitive<'v, T: 'static>(value: &'v T) -> Option<Value<'v>> {
19+
primitive::from_any(value).map(|primitive| Value {
20+
inner: Inner::Primitive(primitive),
21+
})
22+
}
23+
1324
impl<'v> Value<'v> {
1425
/// Try get a `usize` from this value.
1526
///
@@ -203,8 +214,9 @@ impl<'v> Inner<'v> {
203214
Ok(())
204215
}
205216

206-
fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
207-
self.0 = Cast::Primitive(Primitive::Str(v));
217+
#[cfg(feature = "std")]
218+
fn str(&mut self, s: &str) -> Result<(), Error> {
219+
self.0 = Cast::String(s.to_owned());
208220
Ok(())
209221
}
210222

@@ -213,9 +225,8 @@ impl<'v> Inner<'v> {
213225
Ok(())
214226
}
215227

216-
#[cfg(feature = "std")]
217-
fn str(&mut self, v: &str) -> Result<(), Error> {
218-
self.0 = Cast::String(v.into());
228+
fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
229+
self.0 = Cast::Primitive(Primitive::Str(v));
219230
Ok(())
220231
}
221232

@@ -231,24 +242,14 @@ impl<'v> Inner<'v> {
231242
}
232243
}
233244

234-
// Try downcast an erased value first
235-
// It also lets us avoid the Visitor infrastructure for simple primitives
236-
let primitive = match self {
237-
Inner::Primitive(value) => Some(value),
238-
Inner::Fill(value) => value.downcast_primitive(),
239-
Inner::Debug(value) => value.downcast_primitive(),
240-
Inner::Display(value) => value.downcast_primitive(),
241-
242-
#[cfg(feature = "sval")]
243-
Inner::Sval(value) => value.downcast_primitive(),
244-
};
245-
246-
primitive.map(Cast::Primitive).unwrap_or_else(|| {
245+
if let Inner::Primitive(value) = self {
246+
Cast::Primitive(value)
247+
} else {
247248
// If the erased value isn't a primitive then we visit it
248249
let mut cast = CastVisitor(Cast::Primitive(Primitive::None));
249250
let _ = self.visit(&mut cast);
250251
cast.0
251-
})
252+
}
252253
}
253254
}
254255

@@ -321,57 +322,6 @@ impl<'v> Primitive<'v> {
321322
}
322323
}
323324

324-
impl<'v, T: ?Sized + 'static> Erased<'v, T> {
325-
// NOTE: This function is a perfect candidate for memoization
326-
// The outcome could be stored in a `Cell<Primitive>`
327-
fn downcast_primitive(self) -> Option<Primitive<'v>> {
328-
macro_rules! type_ids {
329-
($($value:ident : $ty:ty => $cast:expr,)*) => {{
330-
struct TypeIds;
331-
332-
impl TypeIds {
333-
fn downcast_primitive<'v, T: ?Sized>(&self, value: Erased<'v, T>) -> Option<Primitive<'v>> {
334-
$(
335-
if TypeId::of::<$ty>() == value.type_id {
336-
let $value = unsafe { value.downcast_unchecked::<$ty>() };
337-
return Some(Primitive::from($cast));
338-
}
339-
)*
340-
341-
None
342-
}
343-
}
344-
345-
TypeIds
346-
}};
347-
}
348-
349-
let type_ids = type_ids![
350-
value: usize => *value as u64,
351-
value: u8 => *value as u64,
352-
value: u16 => *value as u64,
353-
value: u32 => *value as u64,
354-
value: u64 => *value,
355-
356-
value: isize => *value as i64,
357-
value: i8 => *value as i64,
358-
value: i16 => *value as i64,
359-
value: i32 => *value as i64,
360-
value: i64 => *value,
361-
362-
value: f32 => *value as f64,
363-
value: f64 => *value,
364-
365-
value: char => *value,
366-
value: bool => *value,
367-
368-
value: &str => *value,
369-
];
370-
371-
type_ids.downcast_primitive(self)
372-
}
373-
}
374-
375325
#[cfg(feature = "std")]
376326
mod std_support {
377327
use super::*;

0 commit comments

Comments
 (0)