Skip to content

Commit a006f3b

Browse files
committed
feat(data_structures): add boxed_slice! and boxed_array! macros
1 parent 4577b71 commit a006f3b

File tree

6 files changed

+69
-12
lines changed

6 files changed

+69
-12
lines changed

crates/oxc_data_structures/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ ropey = { workspace = true, optional = true }
2626

2727
[features]
2828
default = []
29-
all = ["assert_unchecked", "code_buffer", "inline_string", "rope", "slice_iter", "stack"]
29+
all = ["assert_unchecked", "box_macros", "code_buffer", "inline_string", "rope", "slice_iter", "stack"]
3030
assert_unchecked = []
31+
box_macros = []
3132
code_buffer = ["assert_unchecked"]
3233
inline_string = ["assert_unchecked"]
3334
rope = ["dep:ropey"]

crates/oxc_data_structures/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This crate provides specialized data structures and utilities that are used thro
1313
- **Inline strings**: Memory-efficient string storage for short strings
1414
- **Slice iterators**: Enhanced iteration capabilities for slices
1515
- **Rope data structure**: Efficient text manipulation for large documents
16+
- **Box macros**: Macros for creating boxed arrays / slices (similar to `vec!` macro)
1617

1718
## Architecture
1819

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//! Macros for creating boxed slices and arrays.
2+
3+
// TODO: We might be able to make these more performant by using `MaybeUninit`.
4+
5+
/// Macro to create a boxed slice (`Box<[T]>`).
6+
///
7+
/// Similar to standard library's `vec!` macro.
8+
///
9+
/// If length is a const (known at compile time), you would likely be better off using a boxed array instead
10+
/// (`boxed_array!` macro).
11+
///
12+
/// # Example
13+
/// ```
14+
/// # fn get_length_somehow() -> usize { 5 }
15+
///
16+
/// use oxc_data_structures::box_macros::boxed_slice;
17+
///
18+
/// let len = get_length_somehow();
19+
/// // Creates a `Box<[u64]>`
20+
/// let boxed = boxed_slice![0u64; len];
21+
/// ```
22+
#[macro_export]
23+
macro_rules! boxed_slice {
24+
($value:expr; $len:expr) => {
25+
::std::vec![$value; $len].into_boxed_slice()
26+
};
27+
}
28+
29+
pub use boxed_slice;
30+
31+
/// Macro to create a boxed array (`Box<[T; N]>`).
32+
///
33+
/// Similar to standard library's `vec!` macro.
34+
///
35+
/// `$len` must be a const expression.
36+
///
37+
/// # Example
38+
/// ```
39+
/// use oxc_data_structures::box_macros::boxed_array;
40+
///
41+
/// const LENGTH: usize = 5;
42+
/// // Creates a `Box<[u64; LENGTH]>`
43+
/// let boxed = boxed_array![0u64; LENGTH];
44+
/// ```
45+
#[macro_export]
46+
macro_rules! boxed_array {
47+
($value:expr; $len:expr) => {{
48+
// Make sure `$len` is const
49+
const LEN: usize = $len;
50+
let boxed_slice = ::std::vec![$value; LEN].into_boxed_slice();
51+
// `.ok()` is to support types which are not `Debug`
52+
#[allow(clippy::allow_attributes)]
53+
#[allow(clippy::missing_panics_doc, reason = "infallible")]
54+
::std::boxed::Box::<[_; LEN]>::try_from(boxed_slice).ok().unwrap()
55+
}};
56+
}
57+
58+
pub use boxed_array;

crates/oxc_data_structures/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
#[cfg(feature = "assert_unchecked")]
66
mod assert_unchecked;
77

8+
#[cfg(feature = "box_macros")]
9+
pub mod box_macros;
10+
811
#[cfg(feature = "code_buffer")]
912
pub mod code_buffer;
1013

crates/oxc_linter/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ oxc_ast_macros = { workspace = true, optional = true }
3434
oxc_ast_visit = { workspace = true }
3535
oxc_cfg = { workspace = true }
3636
oxc_codegen = { workspace = true }
37-
oxc_data_structures = { workspace = true, optional = true }
37+
oxc_data_structures = { workspace = true, features = ["box_macros"] }
3838
oxc_diagnostics = { workspace = true }
3939
oxc_ecmascript = { workspace = true }
4040
oxc_index = { workspace = true, features = ["serde"] }

crates/oxc_linter/src/lib.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{path::Path, rc::Rc};
55

66
use oxc_allocator::Allocator;
77
use oxc_ast::ast_kind::AST_TYPE_MAX;
8+
use oxc_data_structures::box_macros::boxed_array;
89
use oxc_semantic::AstNode;
910

1011
#[cfg(all(feature = "oxlint2", not(feature = "disable_oxlint2")))]
@@ -169,23 +170,16 @@ impl Linter {
169170
//
170171
// See https://github.com/oxc-project/oxc/pull/6600 for more context.
171172
if semantic.nodes().len() > 200_000 {
172-
const AST_TYPES_LEN: usize = AST_TYPE_MAX as usize + 1;
173-
174173
// Collect rules into a Vec so that we can iterate over the rules multiple times
175174
let rules = rules.collect::<Vec<_>>();
176175

177176
// TODO: It seems like there is probably a more intelligent way to preallocate space here. This will
178177
// likely incur quite a few unnecessary reallocs currently. We theoretically could compute this at
179178
// compile-time since we know all of the rules and their AST node type information ahead of time.
180179
//
181-
// Convert to boxed array to help compiler see that indexing into it with an `AstType`
182-
// cannot go out of bounds, and remove bounds checks. The `unwrap` is infallible and should be optimized out.
183-
let rules_by_ast_type = vec![Vec::new(); AST_TYPES_LEN];
184-
#[expect(clippy::missing_panics_doc, reason = "infallible")]
185-
let mut rules_by_ast_type =
186-
Box::<[_; AST_TYPES_LEN]>::try_from(rules_by_ast_type.into_boxed_slice())
187-
.ok()
188-
.unwrap();
180+
// Use boxed array to help compiler see that indexing into it with an `AstType`
181+
// cannot go out of bounds, and remove bounds checks.
182+
let mut rules_by_ast_type = boxed_array![Vec::new(); AST_TYPE_MAX as usize + 1];
189183
// TODO: Compute needed capacity. This is a slight overestimate as not 100% of rules will need to run on all
190184
// node types, but it at least guarantees we won't need to realloc.
191185
let mut rules_any_ast_type = Vec::with_capacity(rules.len());

0 commit comments

Comments
 (0)