diff --git a/examples/js_dsl/mod.test.ts b/examples/js_dsl/mod.test.ts index 42db759..031fb20 100644 --- a/examples/js_dsl/mod.test.ts +++ b/examples/js_dsl/mod.test.ts @@ -278,6 +278,35 @@ describe("mixed DSL + N-API", () => { expect(obj).toEqual({ x: 10 }); }); + it("reports the active Node version", () => { + expect(mod.nodeVersion()).toEqual(process.versions.node); + expect(mod.nodeRelease()).toEqual(process.release.name); + }); + + it("returns a TypedArray's backing ArrayBuffer and range", () => { + const backing = new ArrayBuffer(16); + const view = new Uint16Array(backing, 4, 3); + + expect(mod.typedArrayInfoMatches(view, backing, 3, 4)).toBe(true); + }); + + it("rejects unsupported TypedArray element types", () => { + const Float16ArrayCtor = Reflect.get(globalThis, "Float16Array"); + if (typeof Float16ArrayCtor !== "function") return; + const view = Reflect.construct(Float16ArrayCtor, [3]) as Uint16Array; + + expect(() => + mod.typedArrayInfoMatches(view, view.buffer, 3, 0), + ).toThrow("UnsupportedTypedarrayType"); + }); + + it("returns a DataView's backing ArrayBuffer and range", () => { + const backing = new ArrayBuffer(16); + const view = new DataView(backing, 4, 6); + + expect(mod.dataViewInfoMatches(view, backing, 6, 4)).toBe(true); + }); + it("randomBytes16 uses js.io() to produce a Uint8Array", () => { const bytes = mod.randomBytes16(); expect(bytes).toBeInstanceOf(Uint8Array); diff --git a/examples/js_dsl/mod.zig b/examples/js_dsl/mod.zig index 99f5438..ec9a076 100644 --- a/examples/js_dsl/mod.zig +++ b/examples/js_dsl/mod.zig @@ -334,6 +334,52 @@ pub fn makeObject(key: String, value: Number) !Value { return .{ .val = obj }; } +pub fn nodeVersion() !String { + const semantic_version = (try js.env().getNodeVersion()).toSemanticVersion(); + var buffer: [64]u8 = undefined; + const version = std.fmt.bufPrint( + &buffer, + "{d}.{d}.{d}", + .{ semantic_version.major, semantic_version.minor, semantic_version.patch }, + ) catch return error.FormatError; + return String.from(version); +} + +pub fn nodeRelease() !String { + const release = (try js.env().getNodeVersion()).getRelease(); + return String.from(std.mem.span(release)); +} + +pub fn typedArrayInfoMatches( + value: Value, + expected_arraybuffer: Value, + expected_length: Number, + expected_byte_offset: Number, +) !Boolean { + const info = try value.toValue().getTypedarrayInfo(); + const length: usize = @intCast(expected_length.assertU32()); + const byte_offset: usize = @intCast(expected_byte_offset.assertU32()); + if (info.length != length) return Boolean.from(false); + if (info.byte_offset != byte_offset) return Boolean.from(false); + if (info.data.len != length * info.array_type.elementSize()) return Boolean.from(false); + return Boolean.from(try info.arraybuffer.strictEquals(expected_arraybuffer.toValue())); +} + +pub fn dataViewInfoMatches( + value: Value, + expected_arraybuffer: Value, + expected_byte_length: Number, + expected_byte_offset: Number, +) !Boolean { + const info = try value.toValue().getDataviewInfo(); + const byte_length: usize = @intCast(expected_byte_length.assertU32()); + const byte_offset: usize = @intCast(expected_byte_offset.assertU32()); + if (info.byte_length != byte_length) return Boolean.from(false); + if (info.byte_offset != byte_offset) return Boolean.from(false); + if (info.data.len != byte_length) return Boolean.from(false); + return Boolean.from(try info.arraybuffer.strictEquals(expected_arraybuffer.toValue())); +} + /// Generate 16 random bytes using the DSL-managed shared std.Io instance. pub fn randomBytes16() Uint8Array { var bytes: [16]u8 = undefined; diff --git a/src/Env.zig b/src/Env.zig index 4735d80..029f6e7 100644 --- a/src/Env.zig +++ b/src/Env.zig @@ -889,11 +889,11 @@ pub fn asyncInit(self: Env, async_resource: Value, async_resource_name: Value) N /// https://nodejs.org/api/n-api.html#napi_get_node_version pub fn getNodeVersion(self: Env) NapiError!NodeVersion { - var version: c.napi_node_version = undefined; + var version: [*c]const c.napi_node_version = undefined; try status.check( - c.napi_get_node_version(self.env, @ptrCast(&version)), + c.napi_get_node_version(self.env, &version), ); - return NodeVersion{ .version = version }; + return NodeVersion{ .version = version.* }; } /// https://nodejs.org/api/n-api.html#napi_get_version diff --git a/src/Value.zig b/src/Value.zig index bd3b206..a6cae2a 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const c = @import("c.zig").c; const status = @import("status.zig"); const NapiError = @import("status.zig").NapiError; @@ -142,15 +143,40 @@ pub const TypedarrayInfo = struct { byte_offset: usize, }; +pub const TypedarrayInfoError = NapiError || error{ + UnsupportedTypedarrayType, +}; + /// https://nodejs.org/api/n-api.html#napi_get_typedarray_info -pub fn getTypedarrayInfo(self: Value) NapiError!TypedarrayInfo { - var info: TypedarrayInfo = undefined; +pub fn getTypedarrayInfo(self: Value) TypedarrayInfoError!TypedarrayInfo { + var array_type_raw: c.napi_typedarray_type = undefined; + var length: usize = undefined; var data: [*]u8 = undefined; - try status.check( - c.napi_get_typedarray_info(self.env, self.value, @ptrCast(&info.array_type), &info.length, @ptrCast(&data), @ptrCast(&info.arraybuffer), &info.byte_offset), - ); - info.data = data[0 .. info.length * info.array_type.elementSize()]; - return info; + var arraybuffer: c.napi_value = undefined; + var byte_offset: usize = undefined; + try status.check( + c.napi_get_typedarray_info( + self.env, + self.value, + &array_type_raw, + &length, + @ptrCast(&data), + &arraybuffer, + &byte_offset, + ), + ); + const array_type = std.enums.fromInt(TypedarrayType, array_type_raw) orelse + return error.UnsupportedTypedarrayType; + return .{ + .array_type = array_type, + .length = length, + .data = data[0 .. length * array_type.elementSize()], + .arraybuffer = .{ + .env = self.env, + .value = arraybuffer, + }, + .byte_offset = byte_offset, + }; } pub const DataViewInfo = struct { @@ -162,13 +188,29 @@ pub const DataViewInfo = struct { /// https://nodejs.org/api/n-api.html#napi_get_dataview_info pub fn getDataviewInfo(self: Value) NapiError!DataViewInfo { - var info: DataViewInfo = undefined; + var byte_length: usize = undefined; var data: [*]u8 = undefined; - try status.check( - c.napi_get_dataview_info(self.env, self.value, &info.byte_length, @ptrCast(&data), @ptrCast(&info.arraybuffer), &info.byte_offset), + var arraybuffer: c.napi_value = undefined; + var byte_offset: usize = undefined; + try status.check( + c.napi_get_dataview_info( + self.env, + self.value, + &byte_length, + @ptrCast(&data), + &arraybuffer, + &byte_offset, + ), ); - info.data = data[0..info.byte_length]; - return info; + return .{ + .byte_length = byte_length, + .data = data[0..byte_length], + .arraybuffer = .{ + .env = self.env, + .value = arraybuffer, + }, + .byte_offset = byte_offset, + }; } /// https://nodejs.org/api/n-api.html#napi_get_date_value