Copy-on-write for record values (#12305)

# Description
This adds a `SharedCow` type as a transparent copy-on-write pointer that
clones to unique on mutate.

As an initial test, the `Record` within `Value::Record` is shared.

There are some pretty big wins for performance. I'll post benchmark
results in a comment. The biggest winner is nested access, as that would
have cloned the records for each cell path follow before and it doesn't
have to anymore.

The reusability of the `SharedCow` type is nice and I think it could be
used to clean up the previous work I did with `Arc` in `EngineState`.
It's meant to be a mostly transparent clone-on-write that just clones on
`.to_mut()` or `.into_owned()` if there are actually multiple
references, but avoids cloning if the reference is unique.

# User-Facing Changes
- `Value::Record` field is a different type (plugin authors)

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting
- [ ] use for `EngineState`
- [ ] use for `Value::List`
This commit is contained in:
Devyn Cairns
2024-04-13 18:42:03 -07:00
committed by GitHub
parent b508d1028c
commit 2ae9ad8676
52 changed files with 328 additions and 222 deletions

View File

@ -125,6 +125,7 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
.collect::<Vec<_>>()
.join(separator),
Value::Record { val, .. } => val
.into_owned()
.into_iter()
.map(|(x, y)| format!("{}: {}", x, local_into_string(y, ", ", config)))
.collect::<Vec<_>>()

View File

@ -325,8 +325,8 @@ impl Job {
// alternatives like {tag: a attributes: {} content: []}, {tag: a attribbutes: null
// content: null}, {tag: a}. See to_xml_entry for more
let attrs = match attrs {
Value::Record { val, .. } => val,
Value::Nothing { .. } => Box::new(Record::new()),
Value::Record { val, .. } => val.into_owned(),
Value::Nothing { .. } => Record::new(),
_ => {
return Err(ShellError::CantConvert {
to_type: "XML".into(),
@ -350,7 +350,7 @@ impl Job {
}
};
self.write_tag(entry_span, tag, tag_span, *attrs, content)
self.write_tag(entry_span, tag, tag_span, attrs, content)
}
}