refactor(tvix/eval): simplify NixString representation(s)
Instead of the two different representations (which we don't really use much), use a `Box<str>` (which potentially shaves another 8 bytes off `Value`). NixString values themselves are immutable anyways (which was a guarantee we already had with `SmolStr`), so this doesn't change anything else. Change-Id: I1d8454c056c21ecb0aebc473cfb3ae06cd70dbb6 Reviewed-on: https://cl.tvl.fyi/c/depot/+/8151 Reviewed-by: raitobezarius <tvl@lahfa.xyz> Reviewed-by: flokli <flokli@flokli.de> Tested-by: BuildkiteCI
This commit is contained in:
		
							parent
							
								
									4bbfeaf1cb
								
							
						
					
					
						commit
						1941082cbb
					
				
					 3 changed files with 37 additions and 65 deletions
				
			
		|  | @ -8,16 +8,23 @@ | ||||||
| use std::iter::FromIterator; | use std::iter::FromIterator; | ||||||
| 
 | 
 | ||||||
| use imbl::{ordmap, OrdMap}; | use imbl::{ordmap, OrdMap}; | ||||||
|  | use lazy_static::lazy_static; | ||||||
| use serde::de::{Deserializer, Error, Visitor}; | use serde::de::{Deserializer, Error, Visitor}; | ||||||
| use serde::ser::SerializeMap; | use serde::ser::SerializeMap; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
| use crate::errors::ErrorKind; |  | ||||||
| 
 |  | ||||||
| use super::string::NixString; | use super::string::NixString; | ||||||
| use super::thunk::ThunkSet; | use super::thunk::ThunkSet; | ||||||
| use super::TotalDisplay; | use super::TotalDisplay; | ||||||
| use super::Value; | use super::Value; | ||||||
|  | use crate::errors::ErrorKind; | ||||||
|  | 
 | ||||||
|  | lazy_static! { | ||||||
|  |     static ref NAME_S: NixString = "name".into(); | ||||||
|  |     static ref NAME_REF: &'static NixString = &NAME_S; | ||||||
|  |     static ref VALUE_S: NixString = "value".into(); | ||||||
|  |     static ref VALUE_REF: &'static NixString = &VALUE_S; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests; | mod tests; | ||||||
|  | @ -57,8 +64,8 @@ impl AttrsRep { | ||||||
| 
 | 
 | ||||||
|             AttrsRep::KV { name, value } => { |             AttrsRep::KV { name, value } => { | ||||||
|                 *self = AttrsRep::Im(ordmap! { |                 *self = AttrsRep::Im(ordmap! { | ||||||
|                     NixString::NAME => name.clone(), |                     NAME_S.clone() => name.clone(), | ||||||
|                     NixString::VALUE => value.clone() |                     VALUE_S.clone() => value.clone() | ||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|                 self.map_mut() |                 self.map_mut() | ||||||
|  | @ -230,13 +237,13 @@ impl NixAttrs { | ||||||
|         // Slightly more advanced, but still optimised updates
 |         // Slightly more advanced, but still optimised updates
 | ||||||
|         match (self.0, other.0) { |         match (self.0, other.0) { | ||||||
|             (AttrsRep::Im(mut m), AttrsRep::KV { name, value }) => { |             (AttrsRep::Im(mut m), AttrsRep::KV { name, value }) => { | ||||||
|                 m.insert(NixString::NAME, name); |                 m.insert(NAME_S.clone(), name); | ||||||
|                 m.insert(NixString::VALUE, value); |                 m.insert(VALUE_S.clone(), value); | ||||||
|                 NixAttrs(AttrsRep::Im(m)) |                 NixAttrs(AttrsRep::Im(m)) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             (AttrsRep::KV { name, value }, AttrsRep::Im(mut m)) => { |             (AttrsRep::KV { name, value }, AttrsRep::Im(mut m)) => { | ||||||
|                 match m.entry(NixString::NAME) { |                 match m.entry(NAME_S.clone()) { | ||||||
|                     imbl::ordmap::Entry::Vacant(e) => { |                     imbl::ordmap::Entry::Vacant(e) => { | ||||||
|                         e.insert(name); |                         e.insert(name); | ||||||
|                     } |                     } | ||||||
|  | @ -244,7 +251,7 @@ impl NixAttrs { | ||||||
|                     imbl::ordmap::Entry::Occupied(_) => { /* name from `m` has precedence */ } |                     imbl::ordmap::Entry::Occupied(_) => { /* name from `m` has precedence */ } | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 match m.entry(NixString::VALUE) { |                 match m.entry(VALUE_S.clone()) { | ||||||
|                     imbl::ordmap::Entry::Vacant(e) => { |                     imbl::ordmap::Entry::Vacant(e) => { | ||||||
|                         e.insert(value); |                         e.insert(value); | ||||||
|                     } |                     } | ||||||
|  | @ -318,11 +325,7 @@ impl NixAttrs { | ||||||
|         match self.0 { |         match self.0 { | ||||||
|             AttrsRep::Empty => IntoIter(IntoIterRepr::Empty), |             AttrsRep::Empty => IntoIter(IntoIterRepr::Empty), | ||||||
|             AttrsRep::KV { name, value } => IntoIter(IntoIterRepr::Finite( |             AttrsRep::KV { name, value } => IntoIter(IntoIterRepr::Finite( | ||||||
|                 vec![ |                 vec![(NAME_REF.clone(), name), (VALUE_REF.clone(), value)].into_iter(), | ||||||
|                     (NixString::NAME_REF.clone(), name), |  | ||||||
|                     (NixString::VALUE_REF.clone(), value), |  | ||||||
|                 ] |  | ||||||
|                 .into_iter(), |  | ||||||
|             )), |             )), | ||||||
|             AttrsRep::Im(map) => IntoIter(IntoIterRepr::Im(map.into_iter())), |             AttrsRep::Im(map) => IntoIter(IntoIterRepr::Im(map.into_iter())), | ||||||
|         } |         } | ||||||
|  | @ -411,17 +414,9 @@ impl NixAttrs { | ||||||
| fn attempt_optimise_kv(slice: &mut [Value]) -> Option<NixAttrs> { | fn attempt_optimise_kv(slice: &mut [Value]) -> Option<NixAttrs> { | ||||||
|     let (name_idx, value_idx) = { |     let (name_idx, value_idx) = { | ||||||
|         match (&slice[2], &slice[0]) { |         match (&slice[2], &slice[0]) { | ||||||
|             (Value::String(s1), Value::String(s2)) |             (Value::String(s1), Value::String(s2)) if (*s1 == *NAME_S && *s2 == *VALUE_S) => (3, 1), | ||||||
|                 if (*s1 == NixString::NAME && *s2 == NixString::VALUE) => |  | ||||||
|             { |  | ||||||
|                 (3, 1) |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             (Value::String(s1), Value::String(s2)) |             (Value::String(s1), Value::String(s2)) if (*s1 == *VALUE_S && *s2 == *NAME_S) => (1, 3), | ||||||
|                 if (*s1 == NixString::VALUE && *s2 == NixString::NAME) => |  | ||||||
|             { |  | ||||||
|                 (1, 3) |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             // Technically this branch lets type errors pass,
 |             // Technically this branch lets type errors pass,
 | ||||||
|             // but they will be caught during normal attribute
 |             // but they will be caught during normal attribute
 | ||||||
|  | @ -502,12 +497,12 @@ impl<'a> Iterator for Iter<KeyValue<'a>> { | ||||||
|             KeyValue::KV { name, value, at } => match at { |             KeyValue::KV { name, value, at } => match at { | ||||||
|                 IterKV::Name => { |                 IterKV::Name => { | ||||||
|                     at.next(); |                     at.next(); | ||||||
|                     Some((NixString::NAME_REF, name)) |                     Some((&NAME_REF, name)) | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 IterKV::Value => { |                 IterKV::Value => { | ||||||
|                     at.next(); |                     at.next(); | ||||||
|                     Some((NixString::VALUE_REF, value)) |                     Some((&VALUE_REF, value)) | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 IterKV::Done => None, |                 IterKV::Done => None, | ||||||
|  | @ -542,11 +537,11 @@ impl<'a> Iterator for Keys<'a> { | ||||||
|             KeysInner::Empty => None, |             KeysInner::Empty => None, | ||||||
|             KeysInner::KV(at @ IterKV::Name) => { |             KeysInner::KV(at @ IterKV::Name) => { | ||||||
|                 at.next(); |                 at.next(); | ||||||
|                 Some(NixString::NAME_REF) |                 Some(&NAME_REF) | ||||||
|             } |             } | ||||||
|             KeysInner::KV(at @ IterKV::Value) => { |             KeysInner::KV(at @ IterKV::Value) => { | ||||||
|                 at.next(); |                 at.next(); | ||||||
|                 Some(NixString::VALUE_REF) |                 Some(&VALUE_REF) | ||||||
|             } |             } | ||||||
|             KeysInner::KV(IterKV::Done) => None, |             KeysInner::KV(IterKV::Done) => None, | ||||||
|             KeysInner::Im(m) => m.next(), |             KeysInner::Im(m) => m.next(), | ||||||
|  |  | ||||||
|  | @ -84,10 +84,10 @@ fn test_kv_attrs_iter() { | ||||||
|         .into_iter() |         .into_iter() | ||||||
|         .map(|(k, v)| (k, v)); |         .map(|(k, v)| (k, v)); | ||||||
|     let (k, v) = iter.next().unwrap(); |     let (k, v) = iter.next().unwrap(); | ||||||
|     assert!(k == NixString::NAME_REF); |     assert!(k == *NAME_REF); | ||||||
|     assert!(v.to_str().unwrap() == meaning_val.to_str().unwrap()); |     assert!(v.to_str().unwrap() == meaning_val.to_str().unwrap()); | ||||||
|     let (k, v) = iter.next().unwrap(); |     let (k, v) = iter.next().unwrap(); | ||||||
|     assert!(k == NixString::VALUE_REF); |     assert!(k == *VALUE_REF); | ||||||
|     assert!(v.as_int().unwrap() == forty_two_val.as_int().unwrap()); |     assert!(v.as_int().unwrap() == forty_two_val.as_int().unwrap()); | ||||||
|     assert!(iter.next().is_none()); |     assert!(iter.next().is_none()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,8 @@ | ||||||
| //! This module implements Nix language strings and their different
 | //! This module implements Nix language strings.
 | ||||||
| //! backing implementations.
 | //!
 | ||||||
|  | //! Nix language strings never need to be modified on the language
 | ||||||
|  | //! level, allowing us to shave off some memory overhead and only
 | ||||||
|  | //! paying the cost when creating new strings.
 | ||||||
| use rnix::ast; | use rnix::ast; | ||||||
| use smol_str::SmolStr; | use smol_str::SmolStr; | ||||||
| use std::ffi::OsStr; | use std::ffi::OsStr; | ||||||
|  | @ -11,16 +14,9 @@ use std::{borrow::Cow, fmt::Display, str::Chars}; | ||||||
| use serde::de::{Deserializer, Visitor}; | use serde::de::{Deserializer, Visitor}; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug, Serialize)] |  | ||||||
| #[serde(untagged)] |  | ||||||
| enum StringRepr { |  | ||||||
|     Smol(SmolStr), |  | ||||||
|     Heap(String), |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[repr(transparent)] | #[repr(transparent)] | ||||||
| #[derive(Clone, Debug, Serialize)] | #[derive(Clone, Debug, Serialize)] | ||||||
| pub struct NixString(StringRepr); | pub struct NixString(Box<str>); | ||||||
| 
 | 
 | ||||||
| impl PartialEq for NixString { | impl PartialEq for NixString { | ||||||
|     fn eq(&self, other: &Self) -> bool { |     fn eq(&self, other: &Self) -> bool { | ||||||
|  | @ -44,19 +40,19 @@ impl Ord for NixString { | ||||||
| 
 | 
 | ||||||
| impl From<&str> for NixString { | impl From<&str> for NixString { | ||||||
|     fn from(s: &str) -> Self { |     fn from(s: &str) -> Self { | ||||||
|         NixString(StringRepr::Smol(SmolStr::new(s))) |         NixString(Box::from(s)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl From<String> for NixString { | impl From<String> for NixString { | ||||||
|     fn from(s: String) -> Self { |     fn from(s: String) -> Self { | ||||||
|         NixString(StringRepr::Heap(s)) |         NixString(s.into_boxed_str()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl From<SmolStr> for NixString { | impl From<SmolStr> for NixString { | ||||||
|     fn from(s: SmolStr) -> Self { |     fn from(s: SmolStr) -> Self { | ||||||
|         NixString(StringRepr::Smol(s)) |         NixString(Box::from(s.as_str())) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -109,7 +105,6 @@ impl<'de> Deserialize<'de> for NixString { | ||||||
| mod arbitrary { | mod arbitrary { | ||||||
|     use super::*; |     use super::*; | ||||||
|     use proptest::prelude::{any_with, Arbitrary}; |     use proptest::prelude::{any_with, Arbitrary}; | ||||||
|     use proptest::prop_oneof; |  | ||||||
|     use proptest::strategy::{BoxedStrategy, Strategy}; |     use proptest::strategy::{BoxedStrategy, Strategy}; | ||||||
| 
 | 
 | ||||||
|     impl Arbitrary for NixString { |     impl Arbitrary for NixString { | ||||||
|  | @ -118,29 +113,14 @@ mod arbitrary { | ||||||
|         type Strategy = BoxedStrategy<Self>; |         type Strategy = BoxedStrategy<Self>; | ||||||
| 
 | 
 | ||||||
|         fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { |         fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { | ||||||
|             prop_oneof![ |             any_with::<String>(args).prop_map(Self::from).boxed() | ||||||
|                 // Either generate `StringRepr::Heap`...
 |  | ||||||
|                 any_with::<String>(args).prop_map(Self::from), |  | ||||||
|                 // ...or generate `StringRepr::Smol` (which `impl From<&str> for NixString` returns)
 |  | ||||||
|                 any_with::<String>(args).prop_map(|s| Self::from(s.as_str())), |  | ||||||
|             ] |  | ||||||
|             .boxed() |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl NixString { | impl NixString { | ||||||
|     pub const NAME: Self = NixString(StringRepr::Smol(SmolStr::new_inline("name"))); |  | ||||||
|     pub const NAME_REF: &'static Self = &Self::NAME; |  | ||||||
| 
 |  | ||||||
|     pub const VALUE: Self = NixString(StringRepr::Smol(SmolStr::new_inline("value"))); |  | ||||||
|     pub const VALUE_REF: &'static Self = &Self::VALUE; |  | ||||||
| 
 |  | ||||||
|     pub fn as_str(&self) -> &str { |     pub fn as_str(&self) -> &str { | ||||||
|         match &self.0 { |         &self.0 | ||||||
|             StringRepr::Smol(s) => s.as_str(), |  | ||||||
|             StringRepr::Heap(s) => s, |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Return a displayable representation of the string as an
 |     /// Return a displayable representation of the string as an
 | ||||||
|  | @ -172,14 +152,11 @@ impl NixString { | ||||||
|     pub fn concat(&self, other: &Self) -> Self { |     pub fn concat(&self, other: &Self) -> Self { | ||||||
|         let mut s = self.as_str().to_owned(); |         let mut s = self.as_str().to_owned(); | ||||||
|         s.push_str(other.as_str()); |         s.push_str(other.as_str()); | ||||||
|         NixString(StringRepr::Heap(s)) |         NixString(s.into_boxed_str()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn chars(&self) -> Chars<'_> { |     pub fn chars(&self) -> Chars<'_> { | ||||||
|         match &self.0 { |         self.0.chars() | ||||||
|             StringRepr::Heap(h) => h.chars(), |  | ||||||
|             StringRepr::Smol(s) => s.chars(), |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue