pub const wyhash = hashWrap(std.hash.Wyhash); pub const adler32 = hashWrap(std.hash.Adler32); pub const crc32 = hashWrap(std.hash.Crc32); pub const cityHash32 = hashWrap(std.hash.CityHash32); pub const cityHash64 = hashWrap(std.hash.CityHash64); pub const xxHash32 = hashWrap(struct { pub fn hash(seed: u32, bytes: []const u8) u32 { // sidestep .hash taking in anytype breaking ArgTuple // downstream by forcing a type signature on the input return std.hash.XxHash32.hash(seed, bytes); } }); pub const xxHash64 = hashWrap(struct { pub fn hash(seed: u32, bytes: []const u8) u64 { // sidestep .hash taking in anytype breaking ArgTuple // downstream by forcing a type signature on the input return std.hash.XxHash64.hash(seed, bytes); } }); pub const xxHash3 = hashWrap(struct { pub fn hash(seed: u32, bytes: []const u8) u64 { // sidestep .hash taking in anytype breaking ArgTuple // downstream by forcing a type signature on the input return std.hash.XxHash3.hash(seed, bytes); } }); pub const murmur32v2 = hashWrap(std.hash.murmur.Murmur2_32); pub const murmur32v3 = hashWrap(std.hash.murmur.Murmur3_32); pub const murmur64v2 = hashWrap(std.hash.murmur.Murmur2_64); pub fn create(globalThis: *JSC.JSGlobalObject) JSC.JSValue { const function = JSC.createCallback(globalThis, ZigString.static("hash"), 1, wyhash); const fns = comptime .{ "wyhash", "adler32", "crc32", "cityHash32", "cityHash64", "xxHash32", "xxHash64", "xxHash3", "murmur32v2", "murmur32v3", "murmur64v2", }; inline for (fns) |name| { const value = JSC.createCallback( globalThis, ZigString.static(name), 1, @field(HashObject, name), ); function.put(globalThis, comptime ZigString.static(name), value); } return function; } fn hashWrap(comptime Hasher_: anytype) JSC.JSHostFnZig { return struct { const Hasher = Hasher_; pub fn hash(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { const arguments = callframe.arguments_old(2).slice(); var args = JSC.CallFrame.ArgumentsSlice.init(globalThis.bunVM(), arguments); defer args.deinit(); var input: []const u8 = ""; var input_slice = ZigString.Slice.empty; defer input_slice.deinit(); if (args.nextEat()) |arg| { if (arg.as(JSC.WebCore.Blob)) |blob| { // TODO: files input = blob.sharedView(); } else { switch (arg.jsTypeLoose()) { .ArrayBuffer, .Int8Array, .Uint8Array, .Uint8ClampedArray, .Int16Array, .Uint16Array, .Int32Array, .Uint32Array, .Float16Array, .Float32Array, .Float64Array, .BigInt64Array, .BigUint64Array, .DataView, => { var array_buffer = arg.asArrayBuffer(globalThis) orelse { return globalThis.throwInvalidArguments("ArrayBuffer conversion error", .{}); }; input = array_buffer.byteSlice(); }, else => { input_slice = try arg.toSlice(globalThis, bun.default_allocator); input = input_slice.slice(); }, } } } // std.hash has inconsistent interfaces // const Function = if (@hasDecl(Hasher, "hashWithSeed")) Hasher.hashWithSeed else Hasher.hash; var function_args: std.meta.ArgsTuple(@TypeOf(Function)) = undefined; if (comptime std.meta.fields(std.meta.ArgsTuple(@TypeOf(Function))).len == 1) { return JSC.JSValue.jsNumber(Function(input)); } else { var seed: u64 = 0; if (args.nextEat()) |arg| { if (arg.isNumber() or arg.isBigInt()) { seed = arg.toUInt64NoTruncate(); } } if (comptime bun.trait.isNumber(@TypeOf(function_args[0]))) { function_args[0] = @as(@TypeOf(function_args[0]), @truncate(seed)); function_args[1] = input; } else { function_args[0] = input; function_args[1] = @as(@TypeOf(function_args[1]), @truncate(seed)); } const value = @call(.auto, Function, function_args); if (@TypeOf(value) == u32) { return JSC.JSValue.jsNumber(@as(u32, @bitCast(value))); } return JSC.JSValue.fromUInt64NoTruncate(globalThis, value); } } }.hash; } const HashObject = @This(); const JSC = bun.JSC; const JSValue = JSC.JSValue; const JSGlobalObject = JSC.JSGlobalObject; const JSObject = JSC.JSObject; const std = @import("std"); const bun = @import("bun"); const ZigString = JSC.ZigString;