1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
//! Owned pointer abstractions.
//!
//! PORT NOTE: The Zig `Owned(comptime Pointer: type)` is a single type-returning function that
//! dispatches on `@typeInfo(Pointer)` (single-item vs slice, optional vs non-optional). Rust has
//! no `@typeInfo`, so the four shapes become four distinct std types per the crate map:
//!
//! Zig Rust
//! ─────────────────────── ──────────────────────────
//! Owned(*T) Box<T>
//! Owned([]T) Box<[T]> (or Vec<T> if it grows)
//! Owned(?*T) Option<Box<T>>
//! Owned(?[]T) Option<Box<[T]>>
//! OwnedIn(P, Allocator) Box<T> — allocator param deleted (global mimalloc)
//! Dynamic(P) Box<T> — std.mem.Allocator field deleted
//! Unmanaged Box<T> — managed/unmanaged split disappears
//!
//! Callers should use the std types directly (PORTING.md §Pointers). This file exists so
//! `bun_ptr::owned::*` resolves and so the Zig API surface has a 1:1 diffable mapping comment.
/// An owned pointer or slice that was allocated using the default allocator.
///
/// This type is a wrapper around a pointer or slice of type `Pointer` that was allocated using
/// `bun.default_allocator`. Calling `deinit` on this type first calls `deinit` on the underlying
/// data, and then frees the memory.
///
/// `Pointer` can be a single-item pointer, a slice, or an optional version of either of those;
/// e.g., `Owned(*u8)`, `Owned([]u8)`, `Owned(?*u8)`, or `Owned(?[]u8)`.
///
/// This type is an alias of `OwnedIn(Pointer, bun.DefaultAllocator)`, and thus has no overhead
/// because `bun.DefaultAllocator` is a zero-sized type.
///
/// PORT NOTE: in Rust this is `Box<T>` / `Box<[T]>` / `Option<Box<_>>`. The alias below covers
/// only the `*T` (single, non-optional) case, which is the overwhelmingly common one. Slice and
/// optional callers use `Box<[T]>` / `Option<Box<T>>` directly.
pub type Owned<T> = ;
// TODO(port): Zig `Owned` accepts a *pointer type* (`*T`, `[]T`, `?*T`, `?[]T`) and branches on
// kind via @typeInfo. Rust generics cannot inspect "is T a slice / is T optional", so a single
// alias cannot cover all four. Audit call sites; they should already be `Box<T>` /
// `Box<[T]>` / `Option<Box<_>>` per PORTING.md §Pointers and LIFETIMES.tsv.
/// `std.mem.Allocator` param/field is deleted entirely outside AST crates. `Dynamic` collapses
/// to `Box<T>`.
pub type Dynamic<T> = ;
// TODO(port): if any caller genuinely needs a runtime-chosen allocator (e.g. arena vs heap at
// runtime), that caller is in an AST crate and should use `bumpalo::boxed::Box<'bump, T>` or a
// bespoke enum — not this type. Audit call sites if one appears.
/// An owned pointer or slice, allocated using an instance of `Allocator`.
///
/// `Allocator` must be one of the following:
///
/// * `std.mem.Allocator`
/// * A type with a method named `allocator` that takes no parameters (except `self`) and returns
/// an instance of `std.mem.Allocator`.
///
/// If `Allocator` is a zero-sized type, the owned pointer has no overhead compared to a raw
/// pointer.
///
/// PORT NOTE: the `Allocator` type parameter is dropped — global mimalloc. See module doc.
pub type OwnedIn<T /*, Allocator */> = ;
// TODO(port): nightly `allocator_api` (`Box<T, A>`) would be the literal translation, but
// PORTING.md forbids it (delete allocator params). Keeping the alias single-param.
// ──────────────────────────────────────────────────────────────────────────────────────────────
// The block below mirrors the body of `fn OwnedIn(...) type { return struct { ... } }` so that
// reviewers can diff method-by-method against owned.zig. Each Zig method is mapped to
// its `Box<T>` / `Box<[T]>` / `Option<Box<_>>` equivalent.
// ──────────────────────────────────────────────────────────────────────────────────────────────
// const Self = @This();
// const info = PointerInfo.parse(Pointer, .{}); → no @typeInfo; shape is encoded in the
// choice of std type at the call site
// const NonOptionalPointer = info.NonOptionalPointer; → `T` in `Option<Box<T>>`
// const Child = info.Child; → `T` in `Box<T>` / element of `Box<[T]>`
// const ConstPointer = AddConst(Pointer); → `&T` / `&[T]`
// #pointer: Pointer, → the Box itself (Box<T> IS the pointer)
// #allocator: Allocator, → deleted (global mimalloc; PORTING.md §Allocators)
// pub const Unmanaged = owned.Unmanaged(Pointer, Allocator);
// → managed/unmanaged split disappears (no allocator field to elide). `Box<T>` is already
// "unmanaged" in the Zig sense (no per-value allocator storage).
// ── alloc ────────────────────────────────────────────────────────────────────────────────────
// Allocates a new owned pointer with a default-initialized `Allocator`.
//
// .single: alloc(value: Child) AllocError!Self
// → Box::new(value) (infallible — aborts on OOM, same as bun.handleOom)
// → Box::try_new(value) // TODO(port): nightly; use if AllocError must propagate
//
// .slice: alloc(count: usize, elem: Child) AllocError!Self (shallow copies of `elem`)
// → vec![elem; count].into_boxed_slice() where Child: Clone
//
// PORT NOTE: Zig returns `AllocError!Self`; Rust `Box::new` aborts on OOM. PORTING.md says
// `bun.handleOom(expr)` → `expr`, so the fallible form is not needed at most call sites.
// ── allocIn ──────────────────────────────────────────────────────────────────────────────────
// Allocates a new owned pointer with the given allocator.
//
// .single: allocIn(value, allocator) → Box::new(value) (allocator param deleted)
// .slice: allocIn(count, elem, allocator)
// → vec![elem; count].into_boxed_slice() (allocator param deleted)
//
// The Zig body does `bun.memory.create` / `allocator.alloc` + `@memset`. In Rust the vec! macro
// handles both allocation and fill.
// ── new ──────────────────────────────────────────────────────────────────────────────────────
// Allocates an owned pointer for a single item, and calls `bun.outOfMemory` if allocation fails.
// new(value: Child) Self
// → Box::new(value)
// ── allocDupe ────────────────────────────────────────────────────────────────────────────────
// Creates an owned pointer by allocating memory and performing a shallow copy of `data`.
// allocDupe(data: ConstPointer) AllocError!Self
// .single: → Box::new(data.clone()) // or Box::new(*data) if Copy
// .slice: → Box::<[T]>::from(data) // == data.to_vec().into_boxed_slice()
// optional: → data.map(|d| Box::<[T]>::from(d))
// ── allocDupeIn ──────────────────────────────────────────────────────────────────────────────
// allocDupeIn(data, allocator) → same as allocDupe; allocator param deleted.
// ── fromRaw ──────────────────────────────────────────────────────────────────────────────────
// Creates an owned pointer from a raw pointer.
// fromRaw(data: Pointer) Self
// .single: → unsafe { bun_core::heap::take(data) }
// .slice: → unsafe { bun_core::heap::take(core::ptr::slice_from_raw_parts_mut(ptr, len)) }
// or, when `data` came from `Vec::into_raw_parts`:
// unsafe { Vec::from_raw_parts(ptr, len, cap) }.into_boxed_slice()
// optional: → if data.is_null() { None } else { Some(unsafe { bun_core::heap::take(data) }) }
//
// PORT NOTE: the Zig doc's caveat about `bun.new` vs `bun.default_allocator.create` is the
// typed-mimalloc-heap distinction; in Rust both paths go through the same `#[global_allocator]`,
// so the caveat does not apply.
// ── fromRawIn ────────────────────────────────────────────────────────────────────────────────
// fromRawIn(data, allocator) → same as fromRaw; allocator param deleted.
// Zig sets `#allocator = undefined` when optional+null — irrelevant in Rust (no field).
// ── deinit ───────────────────────────────────────────────────────────────────────────────────
// Calls `deinit` on the underlying data (pointer target or slice elements) and then frees.
// deinit(self: *Self) void
// → drop(boxed) (implicit at scope exit; see PORTING.md §Idiom)
// `deinit` on the allocator → no-op (no allocator field).
// ── deinitShallow ────────────────────────────────────────────────────────────────────────────
// Frees the memory without calling `deinit` on the underlying data.
// deinitShallow(self: *Self) void
// → let _ = bun_core::heap::into_raw(ManuallyDrop::into_inner(/* ... */));
// PORT NOTE: "free the box allocation but don't drop T" is unusual in Rust. The two real uses:
// (a) T has no Drop → plain `drop(boxed)` is already shallow.
// (b) caller moved the payload out first → use `*boxed` to move out, then `drop(boxed)`.
// If a literal "dealloc without dropping" is needed:
// unsafe {
// let raw = bun_core::heap::into_raw(boxed);
// core::ptr::drop_in_place(raw as *mut ManuallyDrop<T>); // no-op
// alloc::alloc::dealloc(raw.cast(), Layout::new::<T>());
// }
// // TODO(port): audit callers of deinitShallow; likely all fall under (a) or (b).
// ── get ──────────────────────────────────────────────────────────────────────────────────────
// Returns the inner pointer or slice.
// get(self: Self) Pointer
// .single: → &*boxed / &mut *boxed (Deref/DerefMut)
// .slice: → &boxed[..] / &mut boxed[..]
// optional: → opt.as_deref() / opt.as_deref_mut()
// ── intoRaw ──────────────────────────────────────────────────────────────────────────────────
// Converts an owned pointer into a raw pointer, releasing ownership.
// intoRaw(self: *Self) Pointer
// .single: → bun_core::heap::into_raw(boxed)
// .slice: → bun_core::heap::into_raw(boxed) (yields *mut [T]; use .as_mut_ptr()/.len())
// optional: → opt.map(bun_core::heap::into_raw).unwrap_or(core::ptr::null_mut())
// `bun.memory.deinit(&self.#allocator)` → no-op.
// ── PointerAndAllocator / intoRawWithAllocator ───────────────────────────────────────────────
// intoRawWithAllocator(self: *Self) (Pointer, Allocator) | ?(NonOptionalPointer, Allocator)
// → bun_core::heap::into_raw(boxed) (allocator dropped from tuple)
// // TODO(port): if any caller actually inspects the returned allocator, it needs rethinking.
// ── initNull ─────────────────────────────────────────────────────────────────────────────────
// Returns a null owned pointer (only when `Pointer` is optional).
// initNull() Self
// → None::<Box<T>>
// ── take ─────────────────────────────────────────────────────────────────────────────────────
// Converts an `Owned(?T)` into an `?Owned(T)`, leaving `self` null.
// take(self: *Self) ?OwnedNonOptional
// → opt.take() (Option::take — identical semantics)
// ── reset ────────────────────────────────────────────────────────────────────────────────────
// Like `deinit`, but sets `self` to null instead of invalidating it.
// reset(self: *Self) void
// → *opt = None; (drops the old Box, leaves None)
// ── toOptional ───────────────────────────────────────────────────────────────────────────────
// Converts an `Owned(T)` into a non-null `Owned(?T)`.
// toOptional(self: *Self) OwnedOptional
// → Some(boxed)
// ── toUnmanaged ──────────────────────────────────────────────────────────────────────────────
// Converts to an unmanaged variant that doesn't store the allocator.
// toUnmanaged(self: *Self) Self.Unmanaged
// → boxed (identity; no allocator field to drop)
// ── toDynamic ────────────────────────────────────────────────────────────────────────────────
// Converts a fixed-allocator owned pointer into one storing `std.mem.Allocator`.
// toDynamic(self: *Self) Dynamic(Pointer)
// → boxed (identity; allocator type erased → deleted)
// The `@hasDecl(Allocator, "Borrowed")` compile-time check has no Rust analogue and is
// unnecessary once the allocator param is gone.
// ── allocator ────────────────────────────────────────────────────────────────────────────────
// Returns a borrowed version of the allocator.
// allocator(self: Self) MaybeAllocator
// → () (no allocator stored)
// // TODO(port): callers should be deleted along with the allocator threading.
// ── getStdAllocator (private) ────────────────────────────────────────────────────────────────
// → deleted.
// ── deinitImpl (private) ─────────────────────────────────────────────────────────────────────
// deinitImpl(self, comptime mode: enum { deep, shallow })
// .deep → drop(boxed)
// .shallow → see deinitShallow above
// The `info.kind()` switch (`bun.memory.destroy` vs `allocator.free`) is subsumed by Box's
// Drop impl, which knows its own Layout.
// ──────────────────────────────────────────────────────────────────────────────────────────────
// fn Unmanaged(comptime Pointer: type, comptime Allocator: type) type
// ──────────────────────────────────────────────────────────────────────────────────────────────
pub type Unmanaged<T /*, Allocator */> = ;
// #pointer: Pointer, → the Box itself
// const Managed = OwnedIn(Pointer, Allocator); → Box<T>
// ── toManaged ────────────────────────────────────────────────────────────────────────────────
// toManaged(self: *Self, allocator: Allocator) Managed
// → boxed (identity; allocator param deleted)
// ── deinit ───────────────────────────────────────────────────────────────────────────────────
// deinit(self: *Self, allocator: Allocator) void
// → drop(boxed) (allocator param deleted)
// ── get ──────────────────────────────────────────────────────────────────────────────────────
// get(self: Self) Pointer
// → &*boxed / &boxed[..]
// ──────────────────────────────────────────────────────────────────────────────────────────────
// Convenience free functions for the slice / optional shapes that the `Owned<T> = Box<T>` alias
// cannot express. These provide a landing spot if a generic helper is wanted; otherwise
// callers use the std forms inline.
// ──────────────────────────────────────────────────────────────────────────────────────────────
/// `Owned([]T).allocDupe(data)` → `Box::<[T]>::from(data)`
///
/// Shallow-copies `data` into a freshly heap-allocated boxed slice.
/// For empty input this returns a zero-length `Box` with a dangling
/// pointer and **no allocation** — identical to `Box::<[T]>::default()`,
/// so callers MUST NOT add their own `is_empty()` guard (that pattern is
/// a Zig-port artifact where the static `""` must not be freed — irrelevant
/// in Rust where empty boxed slices are non-allocating).
/// `Owned(*T).fromRaw(ptr)` → `bun_core::heap::take(ptr)`
///
/// # Safety
/// `data` must have been produced by `bun_core::heap::into_raw`/`alloc` (or
/// equivalently allocated via the global allocator with the layout of `T`)
/// and must not be freed elsewhere for the life of the returned `Box`.
pub unsafe
/// `Owned(*T).intoRaw()` → `bun_core::heap::into_raw(boxed)`
// Suppress unused-import warnings until the unused helpers are pruned.
// ported from: src/ptr/owned.zig