Skip to content
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

Merged
merged 24 commits into from
Aug 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions benches/value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![cfg(feature = "kv_unstable")]
#![feature(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))
}

#[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))
}
61 changes: 60 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +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 >= 47 {
println!("cargo:rustc-cfg=const_type_id");
}

// Generate sorted type id lookup
#[cfg(feature = "kv_unstable")]
primitive::generate();

println!("cargo:rustc-cfg=srcbuild");
println!("cargo:rerun-if-changed=build.rs");
}

fn rustc_target() -> Option<String> {
env::var("TARGET").ok()
}

// From the `serde` build script
fn rustc_minor_version() -> Option<u32> {
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()
}
6 changes: 3 additions & 3 deletions src/kv/value/fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

use std::fmt;

use super::internal::{Erased, Inner, Visitor};
use super::internal::{Inner, Visitor};
use super::{Error, Value};

impl<'v> Value<'v> {
/// Get a value from a fillable slot.
pub fn from_fill<T>(value: &'v T) -> Self
where
T: Fill + 'static,
T: Fill,
{
Value {
inner: Inner::Fill(unsafe { Erased::new_unchecked::<T>(value) }),
inner: Inner::Fill(value),
}
}
}
Expand Down
56 changes: 19 additions & 37 deletions src/kv/value/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -67,25 +49,25 @@ where
}
}

impl_into_owned! [
usize => u64,
u8 => u64,
u16 => u64,
u32 => u64,
u64 => u64,

isize => i64,
i8 => i64,
i16 => i64,
i32 => i64,
i64 => i64,

f32 => f64,
f64 => f64,

char => char,
bool => bool,
];
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, i8, i16, i32, i64, f32, f64, char, bool,];

#[cfg(feature = "std")]
mod std_support {
Expand Down
94 changes: 22 additions & 72 deletions src/kv/value/internal/cast.rs → src/kv/value/internal/cast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,23 @@
//! 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::fmt;

use super::{Erased, Inner, Primitive, Visitor};
use super::{Inner, Primitive, Visitor};
use crate::kv::value::{Error, Value};

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<Value<'v>> {
primitive::from_any(value).map(|primitive| Value {
inner: Inner::Primitive(primitive),
})
}

impl<'v> Value<'v> {
/// Try get a `usize` from this value.
///
Expand Down Expand Up @@ -203,8 +214,9 @@ 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, s: &str) -> Result<(), Error> {
self.0 = Cast::String(s.to_owned());
Ok(())
}

Expand All @@ -213,9 +225,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(())
}

Expand All @@ -231,24 +242,14 @@ 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
})
}
}
}

Expand Down Expand Up @@ -321,57 +322,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<Primitive>`
fn downcast_primitive(self) -> Option<Primitive<'v>> {
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<Primitive<'v>> {
$(
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::*;
Expand Down
Loading