Skip to content

Commit 0063dfc

Browse files
committed
Use Arc<str> instead of String for OptionMap value
First in a series of commits to make this kind of change for values that are part of `State` and will benefit greatly from faster cloning and lower memory footprint. Cloning an Arc is an O(1) operation and the solver has to make many clones of State as it progresses. There is currently a lot of `.to_string()` used on these values but this is expected to change into Arc::clone as other things get similarly converted. Signed-off-by: J Robert Ray <jrray@jrray.org>
1 parent 29af512 commit 0063dfc

File tree

26 files changed

+132
-114
lines changed

26 files changed

+132
-114
lines changed

crates/spk-build/src/build/binary.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,13 @@ where
665665

666666
let mut cmd = cmd.into_std();
667667
cmd.envs(self.environment.drain());
668-
cmd.envs(options.as_ref().to_environment());
668+
cmd.envs(
669+
options
670+
.as_ref()
671+
.to_environment()
672+
.iter()
673+
.map(|(k, v)| (&**k, &**v)),
674+
);
669675
cmd.envs(get_package_build_env(package));
670676
cmd.env("PREFIX", &self.prefix);
671677
// force the base environment to be setup using bash, so that the

crates/spk-build/src/build/binary_test.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,7 @@ async fn test_build_package_options() {
193193
.await
194194
.unwrap()
195195
.option_values();
196-
assert_eq!(
197-
build_options.get(opt_name!("dep")),
198-
Some(&String::from("~1.0.0"))
199-
);
196+
assert_eq!(build_options.get(opt_name!("dep")), Some(&"~1.0.0".into()));
200197
}
201198

202199
#[rstest]

crates/spk-cli/common/src/flags.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ impl Options {
271271
})
272272
.and_then(|(name, value)| Ok((OptName::new(name)?, value)))?;
273273

274-
opts.insert(name.to_owned(), value.to_string());
274+
opts.insert(name.to_owned(), value.into());
275275
}
276276

277277
Ok(opts)
@@ -282,7 +282,7 @@ impl Options {
282282
.get_options()?
283283
.into_iter()
284284
.filter(|(_name, value)| !value.is_empty())
285-
.map(|(name, value)| VarRequest::new_with_value(name, value))
285+
.map(|(name, value)| VarRequest::new_with_value(name, &*value))
286286
.collect())
287287
}
288288
}

crates/spk-cli/common/src/flags_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fn test_option_flags_parsing(#[case] args: &[&str], #[case] expected: &[(&str, &
3030
let actual = options.get_options().unwrap();
3131
let expected: OptionMap = expected
3232
.iter()
33-
.map(|(k, v)| (OptName::new(k).unwrap().to_owned(), v.to_string()))
33+
.map(|(k, v)| (OptName::new(k).unwrap().to_owned(), (*v).into()))
3434
.collect();
3535
assert_eq!(actual, expected);
3636
}

crates/spk-schema/crates/foundation/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ parsedbuf = { path = "../../../parsedbuf" }
2525
relative-path = "1.3"
2626
ring = "0.16.15"
2727
rstest = { workspace = true }
28-
serde = { workspace = true, features = ["derive"] }
28+
serde = { workspace = true, features = ["derive", "rc"] }
2929
serde_yaml = "0.8.17"
3030
spfs = { version = '0.34.6', path = "../../../spfs" }
3131
sys-info = "0.9.0"

crates/spk-schema/crates/foundation/src/option_map/mod.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ pub fn host_options() -> Result<OptionMap> {
7070
};
7171

7272
if let Some(id) = info.id {
73-
opts.insert(OptName::distro().to_owned(), id.clone());
73+
opts.insert(OptName::distro().to_owned(), id.clone().into());
7474
match OptNameBuf::try_from(id) {
7575
Ok(id) => {
7676
if let Some(version_id) = info.version_id {
77-
opts.insert(id, version_id);
77+
opts.insert(id, version_id.into());
7878
}
7979
}
8080
Err(err) => {
@@ -90,11 +90,11 @@ pub fn host_options() -> Result<OptionMap> {
9090
#[derive(Default, Clone, Hash, PartialEq, Eq, Serialize, Ord, PartialOrd)]
9191
#[serde(transparent)]
9292
pub struct OptionMap {
93-
options: BTreeMap<OptNameBuf, String>,
93+
options: BTreeMap<OptNameBuf, Arc<str>>,
9494
}
9595

9696
impl std::ops::Deref for OptionMap {
97-
type Target = BTreeMap<OptNameBuf, String>;
97+
type Target = BTreeMap<OptNameBuf, Arc<str>>;
9898

9999
fn deref(&self) -> &Self::Target {
100100
&self.options
@@ -107,16 +107,16 @@ impl std::ops::DerefMut for OptionMap {
107107
}
108108
}
109109

110-
impl From<&Arc<BTreeMap<OptNameBuf, String>>> for OptionMap {
111-
fn from(hm: &Arc<BTreeMap<OptNameBuf, String>>) -> Self {
110+
impl From<&Arc<BTreeMap<OptNameBuf, Arc<str>>>> for OptionMap {
111+
fn from(hm: &Arc<BTreeMap<OptNameBuf, Arc<str>>>) -> Self {
112112
Self {
113113
options: (**hm).clone(),
114114
}
115115
}
116116
}
117117

118-
impl FromIterator<(OptNameBuf, String)> for OptionMap {
119-
fn from_iter<T: IntoIterator<Item = (OptNameBuf, String)>>(iter: T) -> Self {
118+
impl FromIterator<(OptNameBuf, Arc<str>)> for OptionMap {
119+
fn from_iter<T: IntoIterator<Item = (OptNameBuf, Arc<str>)>>(iter: T) -> Self {
120120
Self {
121121
options: BTreeMap::from_iter(iter),
122122
}
@@ -137,8 +137,8 @@ impl std::fmt::Display for OptionMap {
137137
}
138138

139139
impl IntoIterator for OptionMap {
140-
type IntoIter = std::collections::btree_map::IntoIter<OptNameBuf, String>;
141-
type Item = (OptNameBuf, String);
140+
type IntoIter = std::collections::btree_map::IntoIter<OptNameBuf, Arc<str>>;
141+
type Item = (OptNameBuf, Arc<str>);
142142

143143
fn into_iter(self) -> Self::IntoIter {
144144
self.options.into_iter()
@@ -147,16 +147,16 @@ impl IntoIterator for OptionMap {
147147

148148
impl OptionMap {
149149
/// Return the data of these options as environment variables.
150-
pub fn to_environment(&self) -> HashMap<String, String> {
150+
pub fn to_environment(&self) -> HashMap<Arc<str>, Arc<str>> {
151151
let mut out = HashMap::default();
152152
for (name, value) in self.iter() {
153153
let var_name = format!("SPK_OPT_{name}");
154-
out.insert(var_name, value.into());
154+
out.insert(var_name.into(), Arc::clone(value));
155155
}
156156
out
157157
}
158158

159-
fn items(&self) -> Vec<(OptNameBuf, String)> {
159+
fn items(&self) -> Vec<(OptNameBuf, Arc<str>)> {
160160
self.options
161161
.iter()
162162
.map(|(k, v)| (k.to_owned(), v.to_owned()))
@@ -278,7 +278,7 @@ impl<'de> Deserialize<'de> for OptionMap {
278278
{
279279
let mut options = OptionMap::default();
280280
while let Some((name, value)) = map.next_entry::<OptNameBuf, Stringified>()? {
281-
options.insert(name, value.0);
281+
options.insert(name, value.0.into());
282282
}
283283
Ok(options)
284284
}

crates/spk-schema/crates/foundation/src/option_map/option_map_test.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,10 @@ fn test_package_options() {
2929
fn test_option_map_deserialize_scalar() {
3030
let opts: OptionMap =
3131
serde_yaml::from_str("{one: one, two: 2, three: false, four: 4.4}").unwrap();
32-
assert_eq!(
33-
opts.options.get(opt_name!("one")).map(String::to_owned),
34-
Some("one".to_string())
35-
);
36-
assert_eq!(
37-
opts.options.get(opt_name!("two")).map(String::to_owned),
38-
Some("2".to_string())
39-
);
40-
assert_eq!(
41-
opts.options.get(opt_name!("three")).map(String::to_owned),
42-
Some("false".to_string())
43-
);
44-
assert_eq!(
45-
opts.options.get(opt_name!("four")).map(String::to_owned),
46-
Some("4.4".to_string())
47-
);
32+
assert_eq!(opts.options.get(opt_name!("one")), Some(&"one".into()));
33+
assert_eq!(opts.options.get(opt_name!("two")), Some(&"2".into()));
34+
assert_eq!(opts.options.get(opt_name!("three")), Some(&"false".into()));
35+
assert_eq!(opts.options.get(opt_name!("four")), Some(&"4.4".into()));
4836
}
4937

5038
#[rstest]

crates/spk-schema/src/option.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ impl Opt {
9898
}
9999
}
100100

101-
pub fn validate(&self, value: Option<&str>) -> Compatibility {
101+
pub fn validate<S>(&self, value: Option<S>) -> Compatibility
102+
where
103+
S: AsRef<str>,
104+
{
102105
match self {
103106
Self::Pkg(opt) => opt.validate(value),
104107
Self::Var(opt) => opt.validate(value),
@@ -118,7 +121,10 @@ impl Opt {
118121
/// Return the current value of this option, if set.
119122
///
120123
/// Given is only returned if the option is not currently set to something else.
121-
pub fn get_value(&self, given: Option<&str>) -> String {
124+
pub fn get_value<S>(&self, given: Option<S>) -> String
125+
where
126+
S: Copy + ToString,
127+
{
122128
let value = match self {
123129
Self::Pkg(opt) => opt.get_value(given),
124130
Self::Var(opt) => opt.get_value(given),
@@ -378,7 +384,10 @@ impl VarOpt {
378384
})
379385
}
380386

381-
pub fn get_value(&self, given: Option<&str>) -> Option<String> {
387+
pub fn get_value<S>(&self, given: Option<S>) -> Option<String>
388+
where
389+
S: ToString,
390+
{
382391
if let Some(v) = &self.value {
383392
if !v.is_empty() {
384393
return Some(v.clone());
@@ -404,12 +413,15 @@ impl VarOpt {
404413
Ok(())
405414
}
406415

407-
pub fn validate(&self, value: Option<&str>) -> Compatibility {
416+
pub fn validate<S>(&self, value: Option<S>) -> Compatibility
417+
where
418+
S: AsRef<str>,
419+
{
408420
if value.is_none() && self.value.is_some() {
409421
return self.validate(self.value.as_deref());
410422
}
411423
let assigned = self.value.as_deref();
412-
match (value, assigned) {
424+
match (value.as_ref().map(AsRef::<str>::as_ref), assigned) {
413425
(None, Some(_)) => Compatibility::Compatible,
414426
(Some(value), Some(assigned)) => {
415427
if value == assigned {
@@ -496,7 +508,10 @@ impl PkgOpt {
496508
})
497509
}
498510

499-
pub fn get_value(&self, given: Option<&str>) -> Option<String> {
511+
pub fn get_value<S>(&self, given: Option<S>) -> Option<String>
512+
where
513+
S: ToString,
514+
{
500515
if let Some(v) = &self.value {
501516
Some(v.clone())
502517
} else if let Some(v) = given {
@@ -533,8 +548,14 @@ impl PkgOpt {
533548
Ok(())
534549
}
535550

536-
pub fn validate(&self, value: Option<&str>) -> Compatibility {
537-
let value = value.unwrap_or_default();
551+
pub fn validate<S>(&self, value: Option<S>) -> Compatibility
552+
where
553+
S: AsRef<str>,
554+
{
555+
let value = value
556+
.as_ref()
557+
.map(AsRef::<str>::as_ref)
558+
.unwrap_or_else(|| "");
538559

539560
// skip any default that might exist since
540561
// that does not represent a definitive range

crates/spk-schema/src/option_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn test_var_opt_validation(#[case] spec: &str, #[case] value: &str, #[case] expe
4444
#[case("{static: static, var: my-var/default}", Some("static"))] // static supersedes default
4545
fn test_var_opt_parse_value(#[case] spec: &str, #[case] expected: Option<&str>) {
4646
let opt = Opt::from_yaml(spec).unwrap().into_var().unwrap();
47-
let actual = opt.get_value(None);
47+
let actual = opt.get_value::<&str>(None);
4848
assert_eq!(actual.as_deref(), expected);
4949
}
5050

crates/spk-schema/src/package.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,7 @@ pub trait Package:
6969
let mut must_exist = given_options.package_options_without_global(self.name());
7070
let given_options = given_options.package_options(self.name());
7171
for option in self.options().iter() {
72-
let value = given_options
73-
.get(option.full_name().without_namespace())
74-
.map(String::as_str);
72+
let value = given_options.get(option.full_name().without_namespace());
7573
let compat = option.validate(value);
7674
if !compat.is_ok() {
7775
return Compatibility::incompatible(format!(

0 commit comments

Comments
 (0)