Use arena from ArenaPool for Blob (and File)

This commit is contained in:
Karl Seguin
2026-03-11 09:21:54 +08:00
parent 487ee18358
commit ff26b0d5a4
3 changed files with 56 additions and 54 deletions

View File

@@ -247,16 +247,15 @@ fn eventInit(arena: Allocator, typ: String, value: anytype) !Event {
}; };
} }
pub fn blob(self: *Factory, child: anytype) !*@TypeOf(child) { pub fn blob(_: *const Factory, arena: Allocator, child: anytype) !*@TypeOf(child) {
const allocator = self._slab.allocator();
// Special case: Blob has slice and mime fields, so we need manual setup // Special case: Blob has slice and mime fields, so we need manual setup
const chain = try PrototypeChain( const chain = try PrototypeChain(
&.{ Blob, @TypeOf(child) }, &.{ Blob, @TypeOf(child) },
).allocate(allocator); ).allocate(arena);
const blob_ptr = chain.get(0); const blob_ptr = chain.get(0);
blob_ptr.* = .{ blob_ptr.* = .{
._arena = arena,
._type = unionInit(Blob.Type, chain.get(1)), ._type = unionInit(Blob.Type, chain.get(1)),
._slice = "", ._slice = "",
._mime = "", ._mime = "",

View File

@@ -21,8 +21,12 @@ const Writer = std.Io.Writer;
const js = @import("../js/js.zig"); const js = @import("../js/js.zig");
const Page = @import("../Page.zig"); const Page = @import("../Page.zig");
const Session = @import("../Session.zig");
const Mime = @import("../Mime.zig"); const Mime = @import("../Mime.zig");
const Allocator = std.mem.Allocator;
/// https://w3c.github.io/FileAPI/#blob-section /// https://w3c.github.io/FileAPI/#blob-section
/// https://developer.mozilla.org/en-US/docs/Web/API/Blob /// https://developer.mozilla.org/en-US/docs/Web/API/Blob
const Blob = @This(); const Blob = @This();
@@ -31,6 +35,8 @@ pub const _prototype_root = true;
_type: Type, _type: Type,
_arena: Allocator,
/// Immutable slice of blob. /// Immutable slice of blob.
/// Note that another blob may hold a pointer/slice to this, /// Note that another blob may hold a pointer/slice to this,
/// so its better to leave the deallocation of it to arena allocator. /// so its better to leave the deallocation of it to arena allocator.
@@ -69,6 +75,9 @@ pub fn initWithMimeValidation(
validate_mime: bool, validate_mime: bool,
page: *Page, page: *Page,
) !*Blob { ) !*Blob {
const arena = try page.getArena(.{ .debug = "Blob" });
errdefer page.releaseArena(arena);
const options: InitOptions = maybe_options orelse .{}; const options: InitOptions = maybe_options orelse .{};
const mime: []const u8 = blk: { const mime: []const u8 = blk: {
@@ -77,7 +86,7 @@ pub fn initWithMimeValidation(
break :blk ""; break :blk "";
} }
const buf = try page.arena.dupe(u8, t); const buf = try arena.dupe(u8, t);
if (validate_mime) { if (validate_mime) {
// Full MIME parsing per MIME sniff spec (for Content-Type headers) // Full MIME parsing per MIME sniff spec (for Content-Type headers)
@@ -99,7 +108,7 @@ pub fn initWithMimeValidation(
const data = blk: { const data = blk: {
if (maybe_blob_parts) |blob_parts| { if (maybe_blob_parts) |blob_parts| {
var w: Writer.Allocating = .init(page.arena); var w: Writer.Allocating = .init(arena);
const use_native_endings = std.mem.eql(u8, options.endings, "native"); const use_native_endings = std.mem.eql(u8, options.endings, "native");
try writeBlobParts(&w.writer, blob_parts, use_native_endings); try writeBlobParts(&w.writer, blob_parts, use_native_endings);
@@ -109,11 +118,19 @@ pub fn initWithMimeValidation(
break :blk ""; break :blk "";
}; };
return page._factory.create(Blob{ const self = try arena.create(Blob);
self.* = .{
._arena = arena,
._type = .generic, ._type = .generic,
._slice = data, ._slice = data,
._mime = mime, ._mime = mime,
}); };
return self;
}
pub fn deinit(self: *Blob, shutdown: bool, session: *Session) void {
_ = shutdown;
session.releaseArena(self._arena);
} }
const largest_vector = @max(std.simd.suggestVectorLength(u8) orelse 1, 8); const largest_vector = @max(std.simd.suggestVectorLength(u8) orelse 1, 8);
@@ -264,57 +281,31 @@ pub fn bytes(self: *const Blob, page: *Page) !js.Promise {
/// from a subset of the blob on which it's called. /// from a subset of the blob on which it's called.
pub fn slice( pub fn slice(
self: *const Blob, self: *const Blob,
maybe_start: ?i32, start_: ?i32,
maybe_end: ?i32, end_: ?i32,
maybe_content_type: ?[]const u8, content_type_: ?[]const u8,
page: *Page, page: *Page,
) !*Blob { ) !*Blob {
const mime: []const u8 = blk: {
if (maybe_content_type) |content_type| {
if (content_type.len == 0) {
break :blk "";
}
break :blk try page.dupeString(content_type);
}
break :blk "";
};
const data = self._slice; const data = self._slice;
if (maybe_start) |_start| {
const start = blk: {
if (_start < 0) {
break :blk data.len -| @abs(_start);
}
break :blk @min(data.len, @as(u31, @intCast(_start))); const start = blk: {
const requested_start = start_ orelse break :blk 0;
if (requested_start < 0) {
break :blk data.len -| @abs(requested_start);
}
break :blk @min(data.len, @as(u31, @intCast(requested_start)));
}; };
const end: usize = blk: { const end: usize = blk: {
if (maybe_end) |_end| { const requested_end = end_ orelse break :blk data.len;
if (_end < 0) { if (requested_end < 0) {
break :blk @max(start, data.len -| @abs(_end)); break :blk @max(start, data.len -| @abs(requested_end));
} }
break :blk @min(data.len, @max(start, @as(u31, @intCast(_end)))); break :blk @min(data.len, @max(start, @as(u31, @intCast(requested_end))));
}
break :blk data.len;
}; };
return page._factory.create(Blob{ return Blob.init(&.{data[start..end]}, .{ .type = content_type_ orelse "" }, page);
._type = .generic,
._slice = data[start..end],
._mime = mime,
});
}
return page._factory.create(Blob{
._type = .generic,
._slice = data,
._mime = mime,
});
} }
/// Returns the size of the Blob in bytes. /// Returns the size of the Blob in bytes.
@@ -334,6 +325,8 @@ pub const JsApi = struct {
pub const name = "Blob"; pub const name = "Blob";
pub const prototype_chain = bridge.prototypeChain(); pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined; pub var class_id: bridge.ClassId = undefined;
pub const weak = true;
pub const finalizer = bridge.finalizer(Blob.deinit);
}; };
pub const constructor = bridge.constructor(Blob.init, .{}); pub const constructor = bridge.constructor(Blob.init, .{});

View File

@@ -18,9 +18,11 @@
const std = @import("std"); const std = @import("std");
const Page = @import("../Page.zig");
const Blob = @import("Blob.zig");
const js = @import("../js/js.zig"); const js = @import("../js/js.zig");
const Page = @import("../Page.zig");
const Session = @import("../Session.zig");
const Blob = @import("Blob.zig");
const File = @This(); const File = @This();
@@ -29,7 +31,13 @@ _proto: *Blob,
// TODO: Implement File API. // TODO: Implement File API.
pub fn init(page: *Page) !*File { pub fn init(page: *Page) !*File {
return page._factory.blob(File{ ._proto = undefined }); const arena = try page.getArena(.{ .debug = "File" });
errdefer page.releaseArena(arena);
return page._factory.blob(arena, File{ ._proto = undefined });
}
pub fn deinit(self: *File, shutdown: bool, session: *Session) void {
self._proto.deinit(shutdown, session);
} }
pub const JsApi = struct { pub const JsApi = struct {
@@ -39,6 +47,8 @@ pub const JsApi = struct {
pub const name = "File"; pub const name = "File";
pub const prototype_chain = bridge.prototypeChain(); pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined; pub var class_id: bridge.ClassId = undefined;
pub const weak = true;
pub const finalizer = bridge.finalizer(File.deinit);
}; };
pub const constructor = bridge.constructor(File.init, .{}); pub const constructor = bridge.constructor(File.init, .{});