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

@@ -201,13 +201,13 @@ impl Value {
// the `2`.
if let Value::Record { val, .. } = self {
val.retain_mut(|key, value| {
val.to_mut().retain_mut(|key, value| {
let span = value.span();
match key {
// Grouped options
"ls" => {
if let Value::Record { val, .. } = value {
val.retain_mut(|key2, value| {
val.to_mut().retain_mut(|key2, value| {
let span = value.span();
match key2 {
"use_ls_colors" => {
@@ -236,7 +236,7 @@ impl Value {
}
"rm" => {
if let Value::Record { val, .. } = value {
val.retain_mut(|key2, value| {
val.to_mut().retain_mut(|key2, value| {
let span = value.span();
match key2 {
"always_trash" => {
@@ -263,7 +263,7 @@ impl Value {
"history" => {
let history = &mut config.history;
if let Value::Record { val, .. } = value {
val.retain_mut(|key2, value| {
val.to_mut().retain_mut(|key2, value| {
let span = value.span();
match key2 {
"isolation" => {
@@ -305,7 +305,7 @@ impl Value {
}
"completions" => {
if let Value::Record { val, .. } = value {
val.retain_mut(|key2, value| {
val.to_mut().retain_mut(|key2, value| {
let span = value.span();
match key2 {
"quick" => {
@@ -326,7 +326,7 @@ impl Value {
}
"external" => {
if let Value::Record { val, .. } = value {
val.retain_mut(|key3, value|
val.to_mut().retain_mut(|key3, value|
{
let span = value.span();
match key3 {
@@ -393,7 +393,7 @@ impl Value {
}
"cursor_shape" => {
if let Value::Record { val, .. } = value {
val.retain_mut(|key2, value| {
val.to_mut().retain_mut(|key2, value| {
let span = value.span();
let config_point = match key2 {
"vi_insert" => &mut config.cursor_shape_vi_insert,
@@ -426,7 +426,7 @@ impl Value {
}
"table" => {
if let Value::Record { val, .. } = value {
val.retain_mut(|key2, value| {
val.to_mut().retain_mut(|key2, value| {
let span = value.span();
match key2 {
"mode" => {
@@ -451,7 +451,7 @@ impl Value {
}
Value::Record { val, .. } => {
let mut invalid = false;
val.retain(|key3, value| {
val.to_mut().retain(|key3, value| {
match key3 {
"left" => {
match value.as_int() {
@@ -546,7 +546,7 @@ impl Value {
}
"filesize" => {
if let Value::Record { val, .. } = value {
val.retain_mut(|key2, value| {
val.to_mut().retain_mut(|key2, value| {
let span = value.span();
match key2 {
"metric" => {
@@ -719,7 +719,7 @@ impl Value {
},
"datetime_format" => {
if let Value::Record { val, .. } = value {
val.retain_mut(|key2, value|
val.to_mut().retain_mut(|key2, value|
{
let span = value.span();
match key2 {

View File

@@ -39,7 +39,7 @@ impl PluginGcConfigs {
self.plugins = HashMap::new();
}
val.retain_mut(|key, value| {
val.to_mut().retain_mut(|key, value| {
let span = value.span();
match key {
"default" => {
@@ -88,7 +88,7 @@ fn process_plugins(
// Remove any plugin configs that aren't in the value
plugins.retain(|key, _| val.contains(key));
val.retain_mut(|key, value| {
val.to_mut().retain_mut(|key, value| {
if matches!(value, Value::Record { .. }) {
plugins.entry(key.to_owned()).or_default().process(
&join_path(path, &[key]),
@@ -150,7 +150,7 @@ impl PluginGcConfig {
self.stop_after = PluginGcConfig::default().stop_after;
}
val.retain_mut(|key, value| {
val.to_mut().retain_mut(|key, value| {
let span = value.span();
match key {
"enabled" => process_bool_config(value, errors, &mut self.enabled),