Skip to content

Commit 39ef911

Browse files
committed
refactor(napi/parser, allocator): raw transfer: store buffer size and align as consts (#12275)
Similar to #12268. Instead of hard-coding the size and alignment of raw transfer buffers in multiple places, codegen add these to generated files, and everywhere else import the values from those files. This is more robust.
1 parent c68b607 commit 39ef911

File tree

12 files changed

+119
-44
lines changed

12 files changed

+119
-44
lines changed

.github/generated/ast_changes_watch_list.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
src:
55
- '.github/generated/ast_changes_watch_list.yml'
6+
- 'crates/oxc_allocator/src/generated/fixed_size_constants.rs'
67
- 'crates/oxc_ast/src/ast/comment.rs'
78
- 'crates/oxc_ast/src/ast/js.rs'
89
- 'crates/oxc_ast/src/ast/jsx.rs'
@@ -68,6 +69,7 @@ src:
6869
- 'napi/parser/generated/lazy/walk.js'
6970
- 'napi/parser/src/generated/assert_layouts.rs'
7071
- 'napi/parser/src/generated/derive_estree.rs'
72+
- 'napi/parser/src/generated/raw_transfer_constants.rs'
7173
- 'napi/parser/src/raw_transfer_types.rs'
7274
- 'npm/oxc-types/types.d.ts'
7375
- 'tasks/ast_tools/src/**'

crates/oxc_allocator/src/fixed_size.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ use std::{
55
ptr::NonNull,
66
};
77

8-
use crate::Allocator;
8+
use crate::{
9+
Allocator,
10+
fixed_size_constants::{BUFFER_ALIGN, BUFFER_SIZE},
11+
};
912

1013
const TWO_GIB: usize = 1 << 31;
11-
const FOUR_GIB: usize = 1 << 32;
1214

1315
// What we ideally want is an allocation 2 GiB in size, aligned on 4 GiB.
1416
// But system allocator on Mac OS refuses allocations with 4 GiB alignment.
@@ -25,10 +27,8 @@ const FOUR_GIB: usize = 1 << 32;
2527
// Could just use that built-in workaround, rather than implementing our own, or allocate a 6 GiB chunk
2628
// with alignment 16, to skip Rust's built-in workaround.
2729
// Note: Rust's workaround will likely commit a whole page of memory, just to store the real pointer.
28-
const ALLOC_SIZE: usize = FOUR_GIB;
30+
const ALLOC_SIZE: usize = BUFFER_SIZE + TWO_GIB;
2931
const ALLOC_ALIGN: usize = TWO_GIB;
30-
const CHUNK_SIZE: usize = TWO_GIB;
31-
pub const CHUNK_ALIGN: usize = FOUR_GIB;
3232

3333
const ALLOC_LAYOUT: Layout = match Layout::from_size_align(ALLOC_SIZE, ALLOC_ALIGN) {
3434
Ok(layout) => layout,
@@ -71,12 +71,12 @@ impl FixedSizeAllocator {
7171
alloc_ptr.add(offset)
7272
};
7373

74-
debug_assert!(chunk_ptr.as_ptr() as usize % CHUNK_ALIGN == 0);
74+
debug_assert!(chunk_ptr.as_ptr() as usize % BUFFER_ALIGN == 0);
7575

76-
// SAFETY: Memory region starting at `chunk_ptr` with `CHUNK_SIZE` bytes is within
76+
// SAFETY: Memory region starting at `chunk_ptr` with `BUFFER_SIZE` bytes is within
7777
// the allocation we just made.
7878
// `chunk_ptr` has high alignment (4 GiB). `size` is large and a high power of 2 (2 GiB).
79-
let allocator = unsafe { Allocator::from_raw_parts(chunk_ptr, CHUNK_SIZE) };
79+
let allocator = unsafe { Allocator::from_raw_parts(chunk_ptr, BUFFER_SIZE) };
8080

8181
// Store pointer to original allocation, so it can be used to deallocate in `drop`
8282
Self { allocator: ManuallyDrop::new(allocator), alloc_ptr }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Auto-generated code, DO NOT EDIT DIRECTLY!
2+
// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`.
3+
4+
#![expect(clippy::unreadable_literal)]
5+
6+
pub const BUFFER_SIZE: usize = 2147483648;
7+
pub const BUFFER_ALIGN: usize = 4294967296;

crates/oxc_allocator/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,20 @@ pub use pool::{AllocatorGuard, AllocatorPool};
6565
pub use string_builder::StringBuilder;
6666
pub use take_in::{Dummy, TakeIn};
6767
pub use vec::Vec;
68+
69+
mod generated {
70+
#[cfg(all(
71+
feature = "fixed_size",
72+
not(feature = "disable_fixed_size"),
73+
target_pointer_width = "64",
74+
target_endian = "little"
75+
))]
76+
pub mod fixed_size_constants;
77+
}
78+
#[cfg(all(
79+
feature = "fixed_size",
80+
not(feature = "disable_fixed_size"),
81+
target_pointer_width = "64",
82+
target_endian = "little"
83+
))]
84+
use generated::fixed_size_constants;

crates/oxc_allocator/src/pool.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,7 @@ mod wrapper {
117117
target_endian = "little"
118118
))]
119119
mod wrapper {
120-
use crate::{
121-
Allocator,
122-
fixed_size::{CHUNK_ALIGN, FixedSizeAllocator},
123-
};
120+
use crate::{Allocator, fixed_size::FixedSizeAllocator, fixed_size_constants::BUFFER_ALIGN};
124121

125122
/// Structure which wraps an [`Allocator`] with fixed size of 2 GiB, and aligned on 4 GiB.
126123
///
@@ -144,12 +141,12 @@ mod wrapper {
144141
self.0.reset();
145142

146143
// Set data pointer back to start.
147-
// SAFETY: Fixed-size allocators have data pointer originally aligned on `CHUNK_ALIGN`,
148-
// and size less than `CHUNK_ALIGN`. So we can restore original data pointer by rounding down
149-
// to next multiple of `CHUNK_ALIGN`.
144+
// SAFETY: Fixed-size allocators have data pointer originally aligned on `BUFFER_ALIGN`,
145+
// and size less than `BUFFER_ALIGN`. So we can restore original data pointer by rounding down
146+
// to next multiple of `BUFFER_ALIGN`.
150147
unsafe {
151148
let data_ptr = self.0.data_ptr();
152-
let offset = data_ptr.as_ptr() as usize % CHUNK_ALIGN;
149+
let offset = data_ptr.as_ptr() as usize % BUFFER_ALIGN;
153150
let data_ptr = data_ptr.sub(offset);
154151
self.0.set_data_ptr(data_ptr);
155152
}

napi/parser/generated/constants.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
// Auto-generated code, DO NOT EDIT DIRECTLY!
22
// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`.
33

4-
const DATA_POINTER_POS_32 = 536870908,
4+
const BUFFER_SIZE = 2147483648,
5+
BUFFER_ALIGN = 4294967296,
6+
DATA_POINTER_POS_32 = 536870908,
57
IS_TS_FLAG_POS = 2147483636,
68
PROGRAM_OFFSET = 0;
79

8-
module.exports = { DATA_POINTER_POS_32, IS_TS_FLAG_POS, PROGRAM_OFFSET };
10+
module.exports = {
11+
BUFFER_SIZE,
12+
BUFFER_ALIGN,
13+
DATA_POINTER_POS_32,
14+
IS_TS_FLAG_POS,
15+
PROGRAM_OFFSET,
16+
};

napi/parser/raw-transfer/common.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const {
77
parseAsyncRaw: parseAsyncRawBinding,
88
getBufferOffset,
99
} = require('../bindings.js');
10-
const { IS_TS_FLAG_POS } = require('../generated/constants.js');
10+
const { BUFFER_SIZE, BUFFER_ALIGN, IS_TS_FLAG_POS } = require('../generated/constants.js');
1111

1212
module.exports = {
1313
parseSyncRawImpl,
@@ -141,9 +141,8 @@ async function parseAsyncRawImpl(filename, sourceText, options, convert) {
141141
return data;
142142
}
143143

144-
const ONE_GIB = 1 << 30,
145-
TWO_GIB = ONE_GIB * 2,
146-
SIX_GIB = ONE_GIB * 6;
144+
const ARRAY_BUFFER_SIZE = BUFFER_SIZE + BUFFER_ALIGN;
145+
const ONE_GIB = 1 << 30;
147146

148147
// We keep a cache of buffers for raw transfer, so we can reuse them as much as possible.
149148
//
@@ -277,10 +276,10 @@ function clearBuffersCache() {
277276
* @returns {Uint8Array} - Buffer
278277
*/
279278
function createBuffer() {
280-
const arrayBuffer = new ArrayBuffer(SIX_GIB);
279+
const arrayBuffer = new ArrayBuffer(ARRAY_BUFFER_SIZE);
281280
const offset = getBufferOffset(new Uint8Array(arrayBuffer));
282-
const buffer = new Uint8Array(arrayBuffer, offset, TWO_GIB);
283-
buffer.uint32 = new Uint32Array(arrayBuffer, offset, TWO_GIB / 4);
284-
buffer.float64 = new Float64Array(arrayBuffer, offset, TWO_GIB / 8);
281+
const buffer = new Uint8Array(arrayBuffer, offset, BUFFER_SIZE);
282+
buffer.uint32 = new Uint32Array(arrayBuffer, offset, BUFFER_SIZE / 4);
283+
buffer.float64 = new Float64Array(arrayBuffer, offset, BUFFER_SIZE / 8);
285284
return buffer;
286285
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Auto-generated code, DO NOT EDIT DIRECTLY!
2+
// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`.
3+
4+
#![expect(clippy::unreadable_literal)]
5+
6+
pub const BUFFER_SIZE: usize = 2147483648;
7+
pub const BUFFER_ALIGN: usize = 4294967296;

napi/parser/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ mod generated {
4949
// Note: We intentionally don't import `generated/derive_estree.rs`. It's not needed.
5050
#[cfg(debug_assertions)]
5151
pub mod assert_layouts;
52+
#[cfg(all(target_pointer_width = "64", target_endian = "little"))]
53+
pub mod raw_transfer_constants;
5254
}
55+
#[cfg(all(target_pointer_width = "64", target_endian = "little"))]
56+
use generated::raw_transfer_constants;
5357

5458
#[derive(Clone, Copy, PartialEq, Eq)]
5559
enum AstType {

napi/parser/src/raw_transfer.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use oxc_napi::get_source_type;
1919

2020
use crate::{
2121
AstType, ParserOptions, get_ast_type, parse,
22+
raw_transfer_constants::{BUFFER_ALIGN, BUFFER_SIZE},
2223
raw_transfer_types::{EcmaScriptModule, Error, RawTransferData},
2324
};
2425

@@ -35,22 +36,19 @@ use crate::{
3536
// 2. JS bitwise operators work only on signed 32-bit integers, with 32nd bit as sign bit.
3637
// So avoiding the 32nd bit being set enables using `>>` bitshift operator, which may be cheaper
3738
// than `>>>`, without offsets being interpreted as negative.
38-
const TWO_GIB: usize = 1 << 31;
39-
const FOUR_GIB: usize = 1 << 32;
4039

41-
const BUFFER_SIZE: usize = TWO_GIB;
42-
const BUFFER_ALIGN: usize = FOUR_GIB;
4340
const BUMP_ALIGN: usize = 16;
4441

45-
/// Get offset within a `Uint8Array` which is aligned on 4 GiB.
42+
/// Get offset within a `Uint8Array` which is aligned on `BUFFER_ALIGN`.
4643
///
4744
/// Does not check that the offset is within bounds of `buffer`.
48-
/// To ensure it always is, provide a `Uint8Array` of at least 4 GiB size.
45+
/// To ensure it always is, provide a `Uint8Array` of at least `BUFFER_SIZE` bytes.
4946
#[napi(skip_typescript)]
5047
pub fn get_buffer_offset(buffer: Uint8Array) -> u32 {
5148
let buffer = &*buffer;
52-
let buffer_addr32 = buffer.as_ptr() as u32;
53-
0u32.wrapping_sub(buffer_addr32)
49+
let offset = BUFFER_ALIGN - (buffer.as_ptr() as usize % BUFFER_ALIGN);
50+
#[expect(clippy::cast_possible_truncation)]
51+
return offset as u32;
5452
}
5553

5654
/// Parse AST into provided `Uint8Array` buffer, synchronously.

0 commit comments

Comments
 (0)