Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat(allocator): add String::from_strs_array_in (#9329)
This method is similar to in [Vec::from_array_in](https://github.com/oxc-project/oxc/blob/5acc6ec3e9b51b3c6649409759e5039b6bdce8eb/crates/oxc_allocator/src/vec.rs#L140-L167), this aims to solve the problem that we want to construct an `ArenaString` with the given `&str`s where from different variables.

For example:

https://github.com/oxc-project/oxc/blob/36a90a61e85bd132040dc9a562efc12e5ae59673/crates/oxc_transformer/src/common/helper_loader.rs#L309-L318

This can refactored to

```diff
- let mut source = ArenaString::with_capacity_in(len, ctx.ast.allocator);
- source.push_str(&self.module_name);
- source.push_str("/helpers/");
- source.push_str(helper_name);
+ ArenaString::from_array_in([&self.module_name, "/helpers/", helper_name], ctx.ast.allocator);
```
  • Loading branch information
Dunqing committed Mar 7, 2025
commit 8b51a75111a81024b1a13ec5219260ba62b18874
76 changes: 76 additions & 0 deletions crates/oxc_allocator/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::{
hash::{Hash, Hasher},
mem::ManuallyDrop,
ops::{Deref, DerefMut},
ptr,
};

use bumpalo::collections::String as BumpaloString;
Expand Down Expand Up @@ -116,6 +117,51 @@ impl<'alloc> String<'alloc> {
}
}

/// Create a new [`String`] from a fixed-size &str array, allocated in the given `allocator`.
///
/// # Examples
/// ```
/// use oxc_allocator::{Allocator, String};
///
/// let allocator = Allocator::default();
/// let string = String::from_strs_array_in(["hello", "world"], &allocator);
/// ```
///
/// # Panics
///
/// Panics if the sum of length of all strings exceeds `usize::MAX`.
#[inline]
pub fn from_strs_array_in<const N: usize>(
strings: [&str; N],
allocator: &'alloc Allocator,
) -> String<'alloc> {
let len = strings.iter().fold(0usize, |l, s| l.checked_add(s.len()).unwrap());

let mut ptr = Vec::with_capacity_in(len, allocator);

let mut dst = ptr.as_mut_ptr();
for str in strings {
let len = str.len();

// SAFETY:
// `src` is obtained from a `&str`
// `dst` is obtained from a properly allocated `Vec<u8>` with sufficient
// capacity to hold all strings.
// Both `src` and `dst` are properly aligned for `u8`.
// No overlapping, because we've copying from a `&str` to a newly allocated buffer.
unsafe {
let src = str.as_ptr();
ptr::copy_nonoverlapping(src, dst, len);
dst = dst.add(len);
}
}

// SAFETY:
// `ptr` was allocated with correct size for `[&str, len]`.
// `len` is the sum of lengths of all strings.
unsafe { String::from_raw_parts_in(ptr.as_mut_ptr(), len, len, allocator) }
}

/// Creates a new [`String`] from a length, capacity, and pointer.
///
/// # SAFETY
Expand Down Expand Up @@ -250,3 +296,33 @@ impl Hash for String<'_> {
self.as_str().hash(hasher);
}
}

#[cfg(test)]
mod test {

use crate::Allocator;
use crate::String;

#[test]
fn string_from_array() {
let hello = "hello";
let world = std::string::String::from("world");
let allocator = Allocator::default();
let string = String::from_strs_array_in([hello, &world, "!"], &allocator);
assert_eq!(string, "helloworld!");
}

#[test]
fn string_from_empty_array() {
let allocator = Allocator::default();
let string = String::from_strs_array_in([], &allocator);
assert_eq!(string, "");
}

#[test]
fn string_from_maybe_empty_str_array() {
let allocator = Allocator::default();
let string = String::from_strs_array_in(["", "hello", ""], &allocator);
assert_eq!(string, "hello");
}
}
Loading