Previously, Nix strings were represented as a Box (within Value) pointing to a tuple of an optional context, and another Box pointing to the actual string allocation itself. This is pretty inefficient, both in terms of memory usage (we use 48 whole bytes for a None context!) and in terms of the extra indirection required to get at the actual data. It was necessary, however, because with native Rust DSTs if we had something like `struct NixString(Option<NixContext>, BStr)` we could only pass around *fat* pointers to that value (with the length in the pointer) and that'd make Value need to be bigger (which is a waste of both memory and cache space, since that memory would be unused for all other Values). Instead, this commit implements *manual* allocation of a packed string representation, with the length *in the allocation* as a field past the context. This requires a big old pile of unsafe Rust, but the payoff is clear: hello outpath time: [882.18 ms 897.16 ms 911.23 ms] change: [-15.143% -13.819% -12.500%] (p = 0.00 < 0.05) Performance has improved. Fortunately this change can be localized entirely within value/string.rs, since we were abstracting things out nicely. Change-Id: Ibf56dd16c9c503884f64facbb7f0ac596463efb6 Reviewed-on: https://cl.tvl.fyi/c/depot/+/10852 Tested-by: BuildkiteCI Reviewed-by: raitobezarius <tvl@lahfa.xyz> Autosubmit: aspen <root@gws.fyi>
106 lines
3.1 KiB
Rust
106 lines
3.1 KiB
Rust
//! Support for configurable generation of arbitrary nix values
|
|
|
|
use imbl::proptest::{ord_map, vector};
|
|
use proptest::{prelude::*, strategy::BoxedStrategy};
|
|
use std::ffi::OsString;
|
|
|
|
use super::{attrs::AttrsRep, NixAttrs, NixList, NixString, Value};
|
|
|
|
#[derive(Clone)]
|
|
pub enum Parameters {
|
|
Strategy(BoxedStrategy<Value>),
|
|
Parameters {
|
|
generate_internal_values: bool,
|
|
generate_functions: bool,
|
|
generate_nested: bool,
|
|
},
|
|
}
|
|
|
|
impl Default for Parameters {
|
|
fn default() -> Self {
|
|
Self::Parameters {
|
|
generate_internal_values: false,
|
|
generate_functions: false,
|
|
generate_nested: true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Arbitrary for NixAttrs {
|
|
type Parameters = Parameters;
|
|
type Strategy = BoxedStrategy<Self>;
|
|
|
|
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
|
prop_oneof![
|
|
// Empty attrs representation
|
|
Just(Self(AttrsRep::Empty)),
|
|
// KV representation (name/value pairs)
|
|
(
|
|
any_with::<Value>(args.clone()),
|
|
any_with::<Value>(args.clone())
|
|
)
|
|
.prop_map(|(name, value)| Self(AttrsRep::KV { name, value })),
|
|
// Map representation
|
|
ord_map(NixString::arbitrary(), Value::arbitrary_with(args), 0..100)
|
|
.prop_map(|map| Self(AttrsRep::Im(map)))
|
|
]
|
|
.boxed()
|
|
}
|
|
}
|
|
|
|
impl Arbitrary for NixList {
|
|
type Parameters = <Value as Arbitrary>::Parameters;
|
|
type Strategy = BoxedStrategy<Self>;
|
|
|
|
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
|
vector(<Value as Arbitrary>::arbitrary_with(args), 0..100)
|
|
.prop_map(NixList::from)
|
|
.boxed()
|
|
}
|
|
}
|
|
|
|
impl Arbitrary for Value {
|
|
type Parameters = Parameters;
|
|
type Strategy = BoxedStrategy<Self>;
|
|
|
|
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
|
match args {
|
|
Parameters::Strategy(s) => s,
|
|
Parameters::Parameters {
|
|
generate_internal_values,
|
|
generate_functions,
|
|
generate_nested,
|
|
} => {
|
|
if generate_internal_values || generate_functions {
|
|
todo!("Generating internal values and functions not implemented yet")
|
|
} else if generate_nested {
|
|
non_internal_value().boxed()
|
|
} else {
|
|
leaf_value().boxed()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn leaf_value() -> impl Strategy<Value = Value> {
|
|
use Value::*;
|
|
|
|
prop_oneof![
|
|
Just(Null),
|
|
any::<bool>().prop_map(Bool),
|
|
any::<i64>().prop_map(Integer),
|
|
any::<f64>().prop_map(Float),
|
|
any::<NixString>().prop_map(String),
|
|
any::<OsString>().prop_map(|s| Path(Box::new(s.into()))),
|
|
]
|
|
}
|
|
|
|
fn non_internal_value() -> impl Strategy<Value = Value> {
|
|
leaf_value().prop_recursive(3, 5, 5, |inner| {
|
|
prop_oneof![
|
|
NixAttrs::arbitrary_with(Parameters::Strategy(inner.clone())).prop_map(Value::attrs),
|
|
any_with::<NixList>(Parameters::Strategy(inner)).prop_map(Value::List)
|
|
]
|
|
})
|
|
}
|