diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index de9c4849..ca4e83d4 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -1045,6 +1045,12 @@ pub const FinalizerCallback = struct { } }; +/// Creates a new typed array. Memory is owned by JS context. +/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Typed_arrays +pub fn createTypedArray(self: *Context, comptime array_type: js.ArrayType, size: usize) js.ArrayBufferRef(array_type) { + return .init(self.isolate, size); +} + // == Profiler == pub fn startCpuProfiler(self: *Context) void { if (comptime !IS_DEBUG) { diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index 78ec45b0..281449a9 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -306,6 +306,14 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts) js.Value => return value, js.Exception => return .{ .local = self, .handle = isolate.throwException(value.handle) }, + js.ArrayBufferRef(.Int8), js.ArrayBufferRef(.Uint8), js.ArrayBufferRef(.Uint8Clamped), + js.ArrayBufferRef(.Int16), js.ArrayBufferRef(.Uint16), + js.ArrayBufferRef(.Int32), js.ArrayBufferRef(.Uint32), + js.ArrayBufferRef(.Float16), js.ArrayBufferRef(.Float32), js.ArrayBufferRef(.Float64), + => { + return .{ .local = self, .handle = value.internal }; + }, + inline js.Function, js.Object, diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index 16f553a5..d76d2e7e 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -77,14 +77,69 @@ pub const ArrayBuffer = struct { } }; -/// `ref` indicates bytes are not copied by `simpleZigValueToJs`; -/// instead, `values` references an already allocated memory. Note that -/// this variant assumes memory is (de)allocated by an arena allocator. -/// -/// `copy` behaves the same as `TypedArray(T)`. -pub fn Uint8ClampedArray(comptime state: enum(u1) { ref, copy }) type { - return struct { - values: if (state == .ref) []u8 else []const u8, +pub const ArrayType = enum(u8) { + Int8, + Uint8, + Uint8Clamped, + Int16, + Uint16, + Int32, + Uint32, + Float16, + Float32, + Float64, +}; + +pub fn ArrayBufferRef(comptime kind: ArrayType) type { + return extern struct { + const Self = @This(); + + const BackingInt = switch (kind) { + .Int8 => i8, + .Uint8, .Uint8Clamped => u8, + .Int16 => i16, + .Uint16 => u16, + .Int32 => i32, + .Uint32 => u32, + .Float16 => f16, + .Float32 => f32, + .Float64 => f64, + }; + + internal: *const v8.Value, + + pub fn init(isolate: Isolate, size: usize) Self { + const bits = switch (@typeInfo(BackingInt)) { + .int => |n| n.bits, + .float => |f| f.bits, + else => unreachable, + }; + + var array_buffer: *const v8.ArrayBuffer = undefined; + if (size == 0) { + array_buffer = v8.v8__ArrayBuffer__New(isolate.handle, 0).?; + } else { + const buffer_len = size * bits / 8; + const backing_store = v8.v8__ArrayBuffer__NewBackingStore(isolate.handle, buffer_len).?; + const backing_store_ptr = v8.v8__BackingStore__TO_SHARED_PTR(backing_store); + array_buffer = v8.v8__ArrayBuffer__New2(isolate.handle, &backing_store_ptr).?; + } + + const internal: *const v8.Value = switch (comptime kind) { + .Int8 => @ptrCast(v8.v8__Int8Array__New(array_buffer, 0, size).?), + .Uint8 => @ptrCast(v8.v8__Uint8Array__New(array_buffer, 0, size).?), + .Uint8Clamped => @ptrCast(v8.v8__Uint8ClampedArray__New(array_buffer, 0, size).?), + .Int16 => @ptrCast(v8.v8__Int16Array__New(array_buffer, 0, size).?), + .Uint16 => @ptrCast(v8.v8__Uint16Array__New(array_buffer, 0, size).?), + .Int32 => @ptrCast(v8.v8__Int32Array__New(array_buffer, 0, size).?), + .Uint32 => @ptrCast(v8.v8__Uint32Array__New(array_buffer, 0, size).?), + .Float16 => @ptrCast(v8.v8__Float16Array__New(array_buffer, 0, size).?), + .Float32 => @ptrCast(v8.v8__Float32Array__New(array_buffer, 0, size).?), + .Float64 => @ptrCast(v8.v8__Float64Array__New(array_buffer, 0, size).?), + }; + + return .{ .internal = internal }; + } }; } @@ -205,40 +260,6 @@ pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool, // but this can never be valid. @compileError("Invalid TypeArray type: " ++ @typeName(value_type)); }, - Uint8ClampedArray(.ref) => { - const values = value.values; - const len = values.len; - var array_buffer: *const v8.ArrayBuffer = undefined; - if (len == 0) { - array_buffer = v8.v8__ArrayBuffer__New(isolate.handle, 0).?; - } else { - // `deleter` cannot be null. - const empty_deleter = struct { - fn deleter(_: ?*anyopaque, _: usize, _: ?*anyopaque) callconv(.c) void {} - }.deleter; - const backing_store = v8.v8__ArrayBuffer__NewBackingStore2(values.ptr, len, empty_deleter, null); - const backing_store_ptr = v8.v8__BackingStore__TO_SHARED_PTR(backing_store); - // Attach store to array buffer. - array_buffer = v8.v8__ArrayBuffer__New2(isolate.handle, &backing_store_ptr).?; - } - return @ptrCast(v8.v8__Uint8ClampedArray__New(array_buffer, 0, len)); - }, - Uint8ClampedArray(.copy) => { - const values = value.values; - const len = values.len; - var array_buffer: *const v8.ArrayBuffer = undefined; - if (len == 0) { - array_buffer = v8.v8__ArrayBuffer__New(isolate.handle, 0).?; - } else { - const backing_store = v8.v8__ArrayBuffer__NewBackingStore(isolate.handle, len); - const data: [*]u8 = @ptrCast(@alignCast(v8.v8__BackingStore__Data(backing_store))); - @memcpy(data[0..len], @as([]const u8, @ptrCast(values))[0..len]); - const backing_store_ptr = v8.v8__BackingStore__TO_SHARED_PTR(backing_store); - // Attach store to array buffer. - array_buffer = v8.v8__ArrayBuffer__New2(isolate.handle, &backing_store_ptr).?; - } - return @ptrCast(v8.v8__Uint8ClampedArray__New(array_buffer, 0, len)); - }, inline String, BigInt, Integer, Number, Value, Object => return value.handle, else => {}, }