refactor: make CompactStr immutable (#2620)

First step towards #2516.

This replaces `compact_str::CompactString` with an immutable interface `CompactStr`.

Currently just implemented as a wrapper around `CompactString` which hides all its mutation methods. A more optimized implementation to follow, which shrinks size of `CompactStr` to 16 bytes by removing the `capacity` field.

The rationale for the change of name is: `CompactString` is like `String` in that it's mutable. `CompactStr` is more like `str` - immutable - so its name mirrors `str`.
This commit is contained in:
overlookmotel 2024-03-06 04:29:32 +00:00 committed by GitHub
parent 0646bf34fa
commit 8001b2f796
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 161 additions and 18 deletions

View file

@ -36,7 +36,7 @@ impl std::ops::Deref for CatchErrorName {
impl Default for CatchErrorNameConfig {
fn default() -> Self {
Self { ignore: vec![], name: CompactStr::new_inline("error") }
Self { ignore: vec![], name: CompactStr::new_const("error") }
}
}

View file

@ -234,5 +234,5 @@ fn base54(n: usize) -> CompactStr {
ret.push(BASE54_CHARS[num % base] as char);
num /= base;
}
CompactStr::new(ret)
CompactStr::new(&ret)
}

View file

@ -5,7 +5,7 @@ use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc};
#[allow(clippy::wildcard_imports)]
use oxc_ast::{ast::*, AstKind, Trivias, TriviasMap, Visit};
use oxc_diagnostics::Error;
use oxc_span::{Atom, CompactStr, SourceType, Span};
use oxc_span::{Atom, SourceType, Span};
use oxc_syntax::{
module_record::{ExportLocalName, ModuleRecord},
operator::AssignmentOperator,
@ -296,11 +296,7 @@ impl<'a> SemanticBuilder<'a> {
pub fn declare_reference(&mut self, reference: Reference) -> ReferenceId {
let reference_name = reference.name().clone();
let reference_id = self.symbols.create_reference(reference);
self.scope.add_unresolved_reference(
self.current_scope_id,
CompactStr::new(reference_name),
reference_id,
);
self.scope.add_unresolved_reference(self.current_scope_id, reference_name, reference_id);
reference_id
}

View file

@ -19,12 +19,13 @@ export type Atom = string;
export type CompactStr = string;
"#;
/// Maximum length for inline string, which can be created with `CompactStr::new_const`.
pub const MAX_INLINE_LEN: usize = 16;
/// An inlinable string for oxc_allocator.
///
/// Use [CompactStr] with [Atom::to_compact_str] or [Atom::into_compact_str] for the
/// lifetimeless form.
///
/// [CompactStr]: crate::CompactStr
#[derive(Clone, Eq)]
pub enum Atom<'a> {
Arena(&'a str),
@ -59,18 +60,18 @@ impl<'a> Atom<'a> {
}
#[inline]
pub fn into_compact_str(self) -> CompactString {
pub fn into_compact_str(self) -> CompactStr {
match self {
Self::Arena(s) => CompactString::new(s),
Self::Compact(s) => s,
Self::Arena(s) => CompactStr::new(s),
Self::Compact(s) => CompactStr::from(s),
}
}
#[inline]
pub fn to_compact_str(&self) -> CompactString {
pub fn to_compact_str(&self) -> CompactStr {
match &self {
Self::Arena(s) => CompactString::new(s),
Self::Compact(s) => s.clone(),
Self::Arena(s) => CompactStr::new(s),
Self::Compact(s) => CompactStr::from(s.clone()),
}
}
}
@ -145,3 +146,150 @@ impl<'a> fmt::Display for Atom<'a> {
fmt::Display::fmt(self.as_str(), f)
}
}
/// Lifetimeless version of `Atom<'_>` which owns its own string data allocation.
///
/// `CompactStr` is immutable. Use `CompactStr::into_string` for a mutable `String`.
///
/// Currently implemented as just a wrapper around `compact_str::CompactString`,
/// but will be reduced in size with a custom implementation later.
#[derive(Clone, Eq)]
pub struct CompactStr(CompactString);
impl CompactStr {
/// Create a new `CompactStr`.
///
/// If `&str` is `'static` and no more than `MAX_INLINE_LEN` bytes,
/// prefer `CompactStr::new_const` which creates the `CompactStr` at compile time.
///
/// # Examples
/// ```
/// let s = CompactStr::new("long string which can't use new_const for");
/// ```
#[inline]
pub fn new(s: &str) -> Self {
Self(CompactString::new(s))
}
/// Create a `CompactStr` at compile time.
///
/// String must be no longer than `MAX_INLINE_LEN` bytes.
///
/// Prefer this over `CompactStr::new` or `CompactStr::from` where string
/// is `'static` and not longer than `MAX_INLINE_LEN` bytes.
///
/// # Panics
/// Panics if string is longer than `MAX_INLINE_LEN` bytes.
///
/// # Examples
/// ```
/// const S: CompactStr = CompactStr::new_const("short");
/// ```
#[inline]
pub const fn new_const(s: &'static str) -> Self {
assert!(s.len() <= MAX_INLINE_LEN);
Self(CompactString::new_inline(s))
}
/// Get string content as a `&str` slice.
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
/// Convert a `CompactStr` into a `String`.
#[inline]
pub fn into_string(self) -> String {
self.0.into_string()
}
/// Get length of `CompactStr`.
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
/// Check if a `CompactStr` is empty (0 length).
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl From<&str> for CompactStr {
fn from(s: &str) -> Self {
Self(CompactString::from(s))
}
}
impl From<String> for CompactStr {
fn from(s: String) -> Self {
Self(CompactString::from(s))
}
}
impl From<CompactString> for CompactStr {
fn from(s: CompactString) -> Self {
Self(s)
}
}
impl Deref for CompactStr {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl AsRef<str> for CompactStr {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for CompactStr {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<T: AsRef<str>> PartialEq<T> for CompactStr {
fn eq(&self, other: &T) -> bool {
self.as_str() == other.as_ref()
}
}
impl PartialEq<CompactStr> for &str {
fn eq(&self, other: &CompactStr) -> bool {
*self == other.as_str()
}
}
impl hash::Hash for CompactStr {
fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
self.as_str().hash(hasher);
}
}
impl fmt::Debug for CompactStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}
impl fmt::Display for CompactStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
#[cfg(feature = "serde")]
impl Serialize for CompactStr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}

View file

@ -7,8 +7,7 @@ mod source_type;
mod span;
pub use crate::{
atom::Atom,
atom::{Atom, CompactStr, MAX_INLINE_LEN as ATOM_MAX_INLINE_LEN},
source_type::{Language, LanguageVariant, ModuleKind, SourceType, VALID_EXTENSIONS},
span::{GetSpan, Span, SPAN},
};
pub use compact_str::CompactString as CompactStr;