mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
429 lines
18 KiB
Zig
429 lines
18 KiB
Zig
const owned = @This();
|
|
|
|
/// 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.
|
|
pub fn Owned(comptime Pointer: type) type {
|
|
return OwnedIn(Pointer, bun.DefaultAllocator);
|
|
}
|
|
|
|
/// An owned pointer or slice allocated using any `std.mem.Allocator`.
|
|
///
|
|
/// This type is an alias of `OwnedIn(Pointer, std.mem.Allocator)`, and thus stores the
|
|
/// `std.mem.Allocator` at runtime.
|
|
pub fn Dynamic(comptime Pointer: type) type {
|
|
return OwnedIn(Pointer, std.mem.Allocator);
|
|
}
|
|
|
|
/// 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.
|
|
pub fn OwnedIn(comptime Pointer: type, comptime Allocator: type) type {
|
|
return struct {
|
|
const Self = @This();
|
|
const info = PointerInfo.parse(Pointer, .{});
|
|
const NonOptionalPointer = info.NonOptionalPointer;
|
|
const Child = info.Child;
|
|
const ConstPointer = AddConst(Pointer);
|
|
|
|
#pointer: Pointer,
|
|
#allocator: Allocator,
|
|
|
|
/// An unmanaged version of this owned pointer. This type doesn't store the allocator and
|
|
/// is the same size as a raw pointer.
|
|
///
|
|
/// If `Allocator` is a zero-sized type, there is no advantage to using this type. Just
|
|
/// use a normal owned pointer, which has no overhead in this case.
|
|
pub const Unmanaged = owned.Unmanaged(Pointer, Allocator);
|
|
|
|
/// Allocates a new owned pointer with a default-initialized `Allocator`.
|
|
pub const alloc = switch (info.kind()) {
|
|
.single => struct {
|
|
pub fn alloc(value: Child) AllocError!Self {
|
|
return .allocIn(value, bun.memory.initDefault(Allocator));
|
|
}
|
|
},
|
|
.slice => struct {
|
|
/// Note: this creates *shallow* copies of `elem`.
|
|
pub fn alloc(count: usize, elem: Child) AllocError!Self {
|
|
return .allocIn(count, elem, bun.memory.initDefault(Allocator));
|
|
}
|
|
},
|
|
}.alloc;
|
|
|
|
/// Allocates a new owned pointer with the given allocator.
|
|
pub const allocIn = switch (info.kind()) {
|
|
.single => struct {
|
|
pub fn allocIn(value: Child, allocator_: Allocator) AllocError!Self {
|
|
const data = try bun.memory.create(
|
|
Child,
|
|
bun.allocators.asStd(allocator_),
|
|
value,
|
|
);
|
|
return .{
|
|
.#pointer = data,
|
|
.#allocator = allocator_,
|
|
};
|
|
}
|
|
},
|
|
.slice => struct {
|
|
/// Note: this creates *shallow* copies of `elem`.
|
|
pub fn allocIn(count: usize, elem: Child, allocator_: Allocator) AllocError!Self {
|
|
const data = try bun.allocators.asStd(allocator_).alloc(Child, count);
|
|
@memset(data, elem);
|
|
return .{
|
|
.#pointer = data,
|
|
.#allocator = allocator_,
|
|
};
|
|
}
|
|
},
|
|
}.allocIn;
|
|
|
|
/// Allocates an owned pointer for a single item, and calls `bun.outOfMemory` if allocation
|
|
/// fails.
|
|
///
|
|
/// It must be possible to default-initialize `Allocator`.
|
|
pub const new = if (info.kind() == .single) struct {
|
|
pub fn new(value: Child) Self {
|
|
return bun.handleOom(Self.alloc(value));
|
|
}
|
|
}.new;
|
|
|
|
/// Creates an owned pointer by allocating memory and performing a shallow copy of `data`.
|
|
///
|
|
/// It must be possible to default-initialize `Allocator`.
|
|
pub fn allocDupe(data: ConstPointer) AllocError!Self {
|
|
return .allocDupeIn(data, bun.memory.initDefault(Allocator));
|
|
}
|
|
|
|
/// Creates an owned pointer by allocating memory with the given allocator and performing
|
|
/// a shallow copy of `data`.
|
|
pub fn allocDupeIn(data: ConstPointer, allocator_: Allocator) AllocError!Self {
|
|
const unwrapped = if (comptime info.isOptional())
|
|
data orelse return .initNull()
|
|
else
|
|
data;
|
|
return switch (comptime info.kind()) {
|
|
.single => .allocIn(unwrapped.*, allocator_),
|
|
.slice => .{
|
|
.#pointer = try bun.allocators.asStd(allocator_).dupe(Child, unwrapped),
|
|
.#allocator = allocator_,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Creates an owned pointer from a raw pointer.
|
|
///
|
|
/// Requirements:
|
|
///
|
|
/// * It must be permissible to free `data` with a new instance of `Allocator` created
|
|
/// with `bun.memory.initDefault(Allocator)`.
|
|
/// * `data` must not be freed for the life of the owned pointer.
|
|
///
|
|
/// NOTE: If `Allocator` is the default allocator, and `Pointer` is a single-item pointer,
|
|
/// `data` must have been allocated with `bun.new`, `bun.tryNew`, or `bun.memory.create`,
|
|
/// NOT `bun.default_allocator.create`. If `data` came from an owned pointer, this
|
|
/// requirement is satisfied.
|
|
///
|
|
/// `Allocator` is the default allocator if `Allocator.allocator` returns
|
|
/// `bun.default_allocator` when called on a default-initialized `Allocator` (created with
|
|
/// `bun.memory.initDefault`). Most notably, this is true for `bun.DefaultAllocator`.
|
|
pub fn fromRaw(data: Pointer) Self {
|
|
return .fromRawIn(data, bun.memory.initDefault(Allocator));
|
|
}
|
|
|
|
/// Creates an owned pointer from a raw pointer and allocator.
|
|
///
|
|
/// Requirements:
|
|
///
|
|
/// * It must be permissible to free `data` with `allocator`.
|
|
/// * `data` must not be freed for the life of the owned pointer.
|
|
///
|
|
/// NOTE: If `allocator` is the default allocator, and `Pointer` is a single-item pointer,
|
|
/// `data` must have been allocated with `bun.new`, `bun.tryNew`, or `bun.memory.create`,
|
|
/// NOT `bun.default_allocator.create`. If `data` came from `intoRaw` on another owned
|
|
/// pointer, this requirement is satisfied.
|
|
///
|
|
/// `allocator` is the default allocator if either of the following is true:
|
|
/// * `allocator` is `bun.default_allocator`
|
|
/// * `allocator.allocator()` returns `bun.default_allocator`
|
|
pub fn fromRawIn(data: Pointer, allocator_: Allocator) Self {
|
|
return .{
|
|
.#pointer = data,
|
|
// Code shouldn't rely on null pointers having a specific allocator, since
|
|
// `initNull` necessarily sets this field to undefined.
|
|
.#allocator = if ((comptime info.isOptional()) and data == null)
|
|
undefined
|
|
else
|
|
allocator_,
|
|
};
|
|
}
|
|
|
|
/// Calls `deinit` on the underlying data (pointer target or slice elements) and then
|
|
/// frees the memory.
|
|
///
|
|
/// `deinit` is also called on the allocator.
|
|
///
|
|
/// This method invalidates `self`.
|
|
pub fn deinit(self: *Self) void {
|
|
self.deinitImpl(.deep);
|
|
}
|
|
|
|
/// Frees the memory without calling `deinit` on the underlying data. `deinit` is still
|
|
/// called on the allocator.
|
|
///
|
|
/// This method invalidates `self`.
|
|
pub fn deinitShallow(self: *Self) void {
|
|
self.deinitImpl(.shallow);
|
|
}
|
|
|
|
/// Returns the inner pointer or slice.
|
|
pub fn get(self: Self) Pointer {
|
|
return self.#pointer;
|
|
}
|
|
|
|
/// Converts an owned pointer into a raw pointer. This releases ownership of the pointer.
|
|
///
|
|
/// This method calls `deinit` on the allocator. If you need to retain access to the
|
|
/// allocator, use `intoRawWithAllocator`.
|
|
///
|
|
/// NOTE: If the current allocator is the default allocator, and `Pointer` is a single-item
|
|
/// pointer, the pointer must be freed with `bun.destroy` or `bun.memory.destroy`, NOT
|
|
/// `bun.default_allocator.destroy`. Or it can be turned back into an owned pointer.
|
|
///
|
|
/// This method invalidates `self`.
|
|
pub fn intoRaw(self: *Self) Pointer {
|
|
defer self.* = undefined;
|
|
if ((comptime !info.isOptional()) or self.#pointer != null) {
|
|
bun.memory.deinit(&self.#allocator);
|
|
}
|
|
return self.#pointer;
|
|
}
|
|
|
|
const PointerAndAllocator = if (info.isOptional())
|
|
?struct { NonOptionalPointer, Allocator }
|
|
else
|
|
struct { Pointer, Allocator };
|
|
|
|
/// Converts an owned pointer into a raw pointer and allocator, releasing ownership of the
|
|
/// pointer.
|
|
///
|
|
/// NOTE: If the current allocator is the default allocator, and `Pointer` is a single-item
|
|
/// pointer, the pointer must be freed with `bun.destroy` or `bun.memory.destroy`, NOT
|
|
/// `bun.default_allocator.destroy`. Or it can be turned back into an owned pointer.
|
|
///
|
|
/// This method invalidates `self`.
|
|
pub fn intoRawWithAllocator(self: *Self) PointerAndAllocator {
|
|
defer self.* = undefined;
|
|
const data = if (comptime info.isOptional())
|
|
self.#pointer orelse return null
|
|
else
|
|
self.#pointer;
|
|
return .{ data, self.#allocator };
|
|
}
|
|
|
|
/// Returns a null owned pointer. This function is provided only if `Pointer` is an
|
|
/// optional type.
|
|
///
|
|
/// It is permitted, but not required, to call `deinit` on the returned value.
|
|
pub const initNull = if (info.isOptional()) struct {
|
|
pub fn initNull() Self {
|
|
return .{
|
|
.#pointer = null,
|
|
.#allocator = undefined,
|
|
};
|
|
}
|
|
}.initNull;
|
|
|
|
/// Converts an `Owned(?T)` into an `?Owned(T)`.
|
|
///
|
|
/// This method sets `self` to null. It is therefore permitted, but not required, to call
|
|
/// `deinit` on `self`.
|
|
///
|
|
/// This method is provided only if `Pointer` is an optional type.
|
|
pub const take = if (info.isOptional()) struct {
|
|
const OwnedNonOptional = OwnedIn(NonOptionalPointer, Allocator);
|
|
|
|
pub fn take(self: *Self) ?OwnedNonOptional {
|
|
defer self.* = .initNull();
|
|
return .{
|
|
.#pointer = self.#pointer orelse return null,
|
|
.#allocator = self.#allocator,
|
|
};
|
|
}
|
|
}.take;
|
|
|
|
/// Like `deinit`, but sets `self` to null instead of invalidating it.
|
|
///
|
|
/// This method is provided only if `Pointer` is an optional type.
|
|
pub const reset = if (info.isOptional()) struct {
|
|
pub fn reset(self: *Self) void {
|
|
defer self.* = .initNull();
|
|
self.deinit();
|
|
}
|
|
}.reset;
|
|
|
|
/// Converts an `Owned(T)` into a non-null `Owned(?T)`.
|
|
///
|
|
/// This method invalidates `self`.
|
|
pub const toOptional = if (!info.isOptional()) struct {
|
|
const OwnedOptional = OwnedIn(?Pointer, Allocator);
|
|
|
|
pub fn toOptional(self: *Self) OwnedOptional {
|
|
defer self.* = undefined;
|
|
return .{
|
|
.#pointer = self.#pointer,
|
|
.#allocator = self.#allocator,
|
|
};
|
|
}
|
|
}.toOptional;
|
|
|
|
/// Converts this owned pointer into an unmanaged variant that doesn't store the allocator.
|
|
///
|
|
/// There is no reason to use this method if `Allocator` is a zero-sized type, as a normal
|
|
/// owned pointer has no overhead in this case.
|
|
///
|
|
/// This method invalidates `self`.
|
|
pub fn toUnmanaged(self: *Self) Self.Unmanaged {
|
|
defer self.* = undefined;
|
|
return .{
|
|
.#pointer = self.#pointer,
|
|
};
|
|
}
|
|
|
|
/// Converts an owned pointer that uses a fixed type of allocator into a dynamic one
|
|
/// that uses any `std.mem.Allocator`.
|
|
///
|
|
/// It must be possible to use the `std.mem.Allocator` returned by `Allocator.allocator`
|
|
/// even after deinitializing the `Allocator`. As a safety check, this method will not
|
|
/// compile if `Allocator.Borrowed` exists and is a different type from `Allocator`, as
|
|
/// this likely indicates a scenario where this invariant will not hold.
|
|
///
|
|
/// There is no reason to use this method if `Allocator` is already `std.mem.Allocator`.
|
|
///
|
|
/// This method invalidates `self`.
|
|
pub fn toDynamic(self: *Self) owned.Dynamic(Pointer) {
|
|
if (comptime @hasDecl(Allocator, "Borrowed") and Allocator.Borrowed != Allocator) {
|
|
// If this allocator can be borrowed as a different type, it's likely that the
|
|
// `std.mem.Allocator` returned by `Allocator.allocator` won't be valid after the
|
|
// `Allocator` is dropped.
|
|
@compileError("allocator won't live long enough");
|
|
}
|
|
|
|
defer self.* = undefined;
|
|
const data = if (comptime info.isOptional())
|
|
self.#pointer orelse return .initNull()
|
|
else
|
|
self.#pointer;
|
|
defer bun.memory.deinit(&self.#allocator);
|
|
return .fromRawIn(data, self.getStdAllocator());
|
|
}
|
|
|
|
const MaybeAllocator = if (info.isOptional())
|
|
?bun.allocators.Borrowed(Allocator)
|
|
else
|
|
bun.allocators.Borrowed(Allocator);
|
|
|
|
/// Returns a borrowed version of the allocator.
|
|
///
|
|
/// Not all allocators have a separate borrowed type; in this case, the allocator is
|
|
/// returned as-is. For example, if `Allocator` is `std.mem.Allocator`, this method also
|
|
/// returns `std.mem.Allocator`.
|
|
pub fn allocator(self: Self) MaybeAllocator {
|
|
return if ((comptime info.isOptional()) and self.#pointer == null)
|
|
null
|
|
else
|
|
bun.allocators.borrow(self.#allocator);
|
|
}
|
|
|
|
fn getStdAllocator(self: Self) std.mem.Allocator {
|
|
return bun.allocators.asStd(self.#allocator);
|
|
}
|
|
|
|
fn deinitImpl(self: *Self, comptime mode: enum { deep, shallow }) void {
|
|
defer self.* = undefined;
|
|
const data = if (comptime info.isOptional())
|
|
self.#pointer orelse return
|
|
else
|
|
self.#pointer;
|
|
if (comptime mode == .deep) {
|
|
bun.memory.deinit(data);
|
|
}
|
|
switch (comptime info.kind()) {
|
|
.single => bun.memory.destroy(self.getStdAllocator(), data),
|
|
.slice => self.getStdAllocator().free(data),
|
|
}
|
|
bun.memory.deinit(&self.#allocator);
|
|
}
|
|
};
|
|
}
|
|
|
|
/// An unmanaged version of `OwnedIn(Pointer, Allocator)` that doesn't store the allocator.
|
|
///
|
|
/// If `Allocator` is a zero-sized type, there is no benefit to using this type. Just use a
|
|
/// normal owned pointer, which has no overhead in this case.
|
|
///
|
|
/// This type is accessible as `OwnedIn(Pointer, Allocator).Unmanaged`.
|
|
fn Unmanaged(comptime Pointer: type, comptime Allocator: type) type {
|
|
return struct {
|
|
const Self = @This();
|
|
const info = PointerInfo.parse(Pointer, .{});
|
|
|
|
#pointer: Pointer,
|
|
|
|
const Managed = OwnedIn(Pointer, Allocator);
|
|
|
|
/// Converts this unmanaged owned pointer back into a managed version.
|
|
///
|
|
/// `allocator` must be the allocator that was used to allocate the pointer.
|
|
///
|
|
/// This method invalidates `self`.
|
|
pub fn toManaged(self: *Self, allocator: Allocator) Managed {
|
|
defer self.* = undefined;
|
|
const data = if (comptime info.isOptional())
|
|
self.#pointer orelse return .initNull()
|
|
else
|
|
self.#pointer;
|
|
return .fromRawIn(data, allocator);
|
|
}
|
|
|
|
/// Deinitializes the pointer or slice. See `Owned.deinit` for more information.
|
|
///
|
|
/// `allocator` must be the allocator that was used to allocate the pointer.
|
|
///
|
|
/// This method invalidates `self`.
|
|
pub fn deinit(self: *Self, allocator: Allocator) void {
|
|
var managed = self.toManaged(allocator);
|
|
managed.deinit();
|
|
}
|
|
|
|
/// Returns the inner pointer or slice.
|
|
pub fn get(self: Self) Pointer {
|
|
return self.#pointer;
|
|
}
|
|
};
|
|
}
|
|
|
|
const bun = @import("bun");
|
|
const std = @import("std");
|
|
const AllocError = std.mem.Allocator.Error;
|
|
|
|
const meta = @import("./meta.zig");
|
|
const AddConst = meta.AddConst;
|
|
const PointerInfo = meta.PointerInfo;
|