mirror of
https://github.com/oven-sh/bun
synced 2026-02-17 06:12:08 +00:00
Compare commits
1 Commits
codex/upda
...
jarred/mor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bda25b6b2d |
108
src/brotli.zig
108
src/brotli.zig
@@ -1,7 +1,12 @@
|
||||
const bun = @import("root").bun;
|
||||
const std = @import("std");
|
||||
const c = @import("./deps/brotli_decoder.zig");
|
||||
const c = struct {
|
||||
pub usingnamespace @import("./deps/brotli_decoder.zig");
|
||||
pub usingnamespace @import("./deps/brotli_encoder.zig");
|
||||
};
|
||||
|
||||
const BrotliDecoder = c.BrotliDecoder;
|
||||
const BrotliEncoder = c.BrotliEncoder;
|
||||
|
||||
const mimalloc = bun.Mimalloc;
|
||||
|
||||
@@ -15,7 +20,7 @@ const BrotliAllocator = struct {
|
||||
return mimalloc.mi_malloc(len) orelse unreachable;
|
||||
}
|
||||
|
||||
pub fn free(_: ?*anyopaque, data: *anyopaque) callconv(.C) void {
|
||||
pub fn free(_: ?*anyopaque, data: ?*anyopaque) callconv(.C) void {
|
||||
if (comptime bun.is_heap_breakdown_enabled) {
|
||||
const zone = bun.HeapBreakdown.malloc_zone_t.get(BrotliAllocator);
|
||||
zone.malloc_zone_free(data);
|
||||
@@ -26,7 +31,7 @@ const BrotliAllocator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Options = struct {
|
||||
pub const DecoderOptions = struct {
|
||||
pub const Params = std.enums.EnumFieldStruct(c.BrotliDecoderParameter, bool, false);
|
||||
|
||||
params: Params = Params{
|
||||
@@ -54,7 +59,7 @@ pub const BrotliReaderArrayList = struct {
|
||||
|
||||
pub usingnamespace bun.New(BrotliReaderArrayList);
|
||||
|
||||
pub fn initWithOptions(input: []const u8, list: *std.ArrayListUnmanaged(u8), allocator: std.mem.Allocator, options: Options) !*BrotliReaderArrayList {
|
||||
pub fn initWithOptions(input: []const u8, list: *std.ArrayListUnmanaged(u8), allocator: std.mem.Allocator, options: DecoderOptions) !*BrotliReaderArrayList {
|
||||
if (!BrotliDecoder.initializeBrotli()) {
|
||||
return error.BrotliFailedToLoad;
|
||||
}
|
||||
@@ -163,3 +168,98 @@ pub const BrotliReaderArrayList = struct {
|
||||
this.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
pub const BrotliCompressionStream = struct {
|
||||
pub const State = enum {
|
||||
Inflating,
|
||||
End,
|
||||
Error,
|
||||
};
|
||||
|
||||
brotli: *BrotliEncoder,
|
||||
state: State = State.Inflating,
|
||||
total_out: usize = 0,
|
||||
total_in: usize = 0,
|
||||
|
||||
pub fn init() !BrotliCompressionStream {
|
||||
const instance = BrotliEncoder.createInstance(&BrotliAllocator.alloc, &BrotliAllocator.free, null) orelse return error.BrotliFailedToCreateInstance;
|
||||
|
||||
return BrotliCompressionStream{
|
||||
.brotli = instance,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writeChunk(this: *BrotliCompressionStream, input: []const u8, last: bool) ![]const u8 {
|
||||
const result = this.brotli.compressStream(if (last) BrotliEncoder.Operation.finish else .process, input);
|
||||
|
||||
if (!result.success) {
|
||||
this.state = .Error;
|
||||
return error.BrotliCompressionError;
|
||||
}
|
||||
|
||||
return result.output;
|
||||
}
|
||||
|
||||
pub fn write(this: *BrotliCompressionStream, input: []const u8, last: bool) ![]const u8 {
|
||||
if (this.state == .End or this.state == .Error) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return this.writeChunk(input, last);
|
||||
}
|
||||
|
||||
pub fn end(this: *BrotliCompressionStream) ![]const u8 {
|
||||
defer this.state = .End;
|
||||
|
||||
return try this.write("", true);
|
||||
}
|
||||
|
||||
pub fn deinit(this: *BrotliCompressionStream) void {
|
||||
this.brotli.destroyInstance();
|
||||
}
|
||||
|
||||
fn NewWriter(comptime InputWriter: type) type {
|
||||
return struct {
|
||||
compressor: *BrotliCompressionStream,
|
||||
input_writer: InputWriter,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(compressor: *BrotliCompressionStream, input_writer: InputWriter) Self {
|
||||
return Self{
|
||||
.compressor = compressor,
|
||||
.input_writer = input_writer,
|
||||
};
|
||||
}
|
||||
|
||||
pub const WriteError = error{BrotliCompressionError} || InputWriter.Error;
|
||||
|
||||
pub fn write(self: Self, to_compress: []const u8) WriteError!usize {
|
||||
const decompressed = try self.compressor.write(to_compress, false);
|
||||
try self.input_writer.writeAll(decompressed);
|
||||
return to_compress.len;
|
||||
}
|
||||
|
||||
pub fn end(self: Self) !usize {
|
||||
const decompressed = try self.compressor.end();
|
||||
try self.input_writer.writeAll(decompressed);
|
||||
}
|
||||
|
||||
pub const Writer = std.io.Writer(@This(), WriteError, Self.write);
|
||||
|
||||
pub fn writer(self: Self) Writer {
|
||||
return Writer{
|
||||
.context = self,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writerContext(this: *BrotliCompressionStream, writable: anytype) NewWriter(@TypeOf(writable)) {
|
||||
return NewWriter(@TypeOf(writable)).init(this, writable);
|
||||
}
|
||||
|
||||
pub fn writer(this: *BrotliCompressionStream, writable: anytype) NewWriter(@TypeOf(writable)).Writer {
|
||||
return this.writerContext(writable).writer();
|
||||
}
|
||||
};
|
||||
|
||||
63
src/bun.js/api/brotli.classes.ts
Normal file
63
src/bun.js/api/brotli.classes.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { define } from "../../codegen/class-definitions";
|
||||
|
||||
export default [
|
||||
define({
|
||||
name: "BrotliEncoder",
|
||||
construct: true,
|
||||
noConstructor: true,
|
||||
finalize: true,
|
||||
configurable: false,
|
||||
hasPendingActivity: true,
|
||||
klass: {},
|
||||
JSType: "0b11101110",
|
||||
values: ["callback"],
|
||||
proto: {
|
||||
encode: {
|
||||
fn: "encode",
|
||||
length: 2,
|
||||
},
|
||||
encodeSync: {
|
||||
fn: "encodeSync",
|
||||
length: 2,
|
||||
},
|
||||
end: {
|
||||
fn: "end",
|
||||
length: 2,
|
||||
},
|
||||
endSync: {
|
||||
fn: "endSync",
|
||||
length: 2,
|
||||
},
|
||||
},
|
||||
}),
|
||||
define({
|
||||
name: "BrotliDecoder",
|
||||
construct: true,
|
||||
noConstructor: true,
|
||||
finalize: true,
|
||||
configurable: false,
|
||||
hasPendingActivity: true,
|
||||
klass: {},
|
||||
JSType: "0b11101110",
|
||||
values: ["callback"],
|
||||
|
||||
proto: {
|
||||
decode: {
|
||||
fn: "decode",
|
||||
length: 2,
|
||||
},
|
||||
decodeSync: {
|
||||
fn: "decodeSync",
|
||||
length: 2,
|
||||
},
|
||||
end: {
|
||||
fn: "end",
|
||||
length: 2,
|
||||
},
|
||||
endSync: {
|
||||
fn: "endSync",
|
||||
length: 2,
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
354
src/bun.js/api/brotli.zig
Normal file
354
src/bun.js/api/brotli.zig
Normal file
@@ -0,0 +1,354 @@
|
||||
const bun = @import("root").bun;
|
||||
const JSC = bun.JSC;
|
||||
const std = @import("std");
|
||||
const brotli = bun.brotli;
|
||||
|
||||
const Queue = std.fifo.LinearFifo(JSC.Node.BlobOrStringOrBuffer, .Dynamic);
|
||||
|
||||
fn ConcurrentByteProcessor(comptime Processor: type) type {
|
||||
_ = Processor; // autofix
|
||||
return struct {};
|
||||
}
|
||||
|
||||
pub const BrotliEncoder = struct {
|
||||
pub usingnamespace bun.NewRefCounted(@This(), deinit);
|
||||
pub usingnamespace JSC.Codegen.JSBrotliEncoder;
|
||||
|
||||
stream: brotli.BrotliCompressionStream,
|
||||
|
||||
freelist: Queue = Queue.init(bun.default_allocator),
|
||||
freelist_write_lock: bun.Lock = bun.Lock.init(),
|
||||
|
||||
globalObject: *JSC.JSGlobalObject,
|
||||
|
||||
input: Queue = Queue.init(bun.default_allocator),
|
||||
input_lock: bun.Lock = bun.Lock.init(),
|
||||
|
||||
has_called_end: bool = false,
|
||||
callback_value: JSC.Strong = .{},
|
||||
|
||||
output: std.ArrayListUnmanaged(u8) = .{},
|
||||
output_lock: bun.Lock = bun.Lock.init(),
|
||||
|
||||
has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
|
||||
pending_encode_job_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
|
||||
ref_count: u32 = 1,
|
||||
write_failed: bool = false,
|
||||
poll_ref: bun.Async.KeepAlive = bun.Async.KeepAlive{},
|
||||
|
||||
pub fn hasPendingActivity(this: *BrotliEncoder) callconv(.C) bool {
|
||||
return this.has_pending_activity.load(.Monotonic) > 0;
|
||||
}
|
||||
|
||||
pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*BrotliEncoder {
|
||||
globalThis.throw("BrotliEncoder is not constructable", .{});
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn create(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
const arguments = callframe.arguments(3).slice();
|
||||
|
||||
if (arguments.len < 3) {
|
||||
globalThis.throwNotEnoughArguments("BrotliEncoder", 3, arguments.len);
|
||||
return .zero;
|
||||
}
|
||||
|
||||
const callback = arguments[2];
|
||||
|
||||
if (!callback.isCallable(globalThis.vm())) {
|
||||
globalThis.throwInvalidArguments("BrotliEncoder callback is not callable", .{});
|
||||
return .zero;
|
||||
}
|
||||
|
||||
var this: *BrotliEncoder = BrotliEncoder.new(.{
|
||||
.globalObject = globalThis,
|
||||
.stream = brotli.BrotliCompressionStream.init() catch {
|
||||
globalThis.throw("Failed to create BrotliEncoder", .{});
|
||||
return .zero;
|
||||
},
|
||||
});
|
||||
|
||||
const out = this.toJS(globalThis);
|
||||
@This().callbackSetCached(out, globalThis, callback);
|
||||
this.callback_value.set(globalThis, callback);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
pub fn finalize(this: *BrotliEncoder) callconv(.C) void {
|
||||
this.deref();
|
||||
}
|
||||
|
||||
pub fn deinit(this: *BrotliEncoder) void {
|
||||
this.callback_value.deinit();
|
||||
this.drainFreelist();
|
||||
this.output.deinit(bun.default_allocator);
|
||||
this.stream.deinit();
|
||||
this.input.deinit();
|
||||
}
|
||||
|
||||
fn drainFreelist(this: *BrotliEncoder) void {
|
||||
this.freelist_write_lock.lock();
|
||||
defer this.freelist_write_lock.unlock();
|
||||
const to_free = this.freelist.readableSlice(0);
|
||||
for (to_free) |*input| {
|
||||
input.deinit();
|
||||
}
|
||||
this.freelist.discard(to_free.len);
|
||||
}
|
||||
|
||||
pub fn runFromJSThread(this: *BrotliEncoder) void {
|
||||
this.poll_ref.unref(this.globalObject.bunVM());
|
||||
|
||||
defer {
|
||||
this.deref();
|
||||
}
|
||||
this.drainFreelist();
|
||||
|
||||
const value = brk: {
|
||||
this.output_lock.lock();
|
||||
defer this.output_lock.unlock();
|
||||
|
||||
if (this.output.items.len == 0)
|
||||
return;
|
||||
|
||||
if (this.output.items.len > 16 * 1024) {
|
||||
defer this.output.items = &.{};
|
||||
break :brk JSC.JSValue.createBuffer(this.globalObject, this.output.items, bun.default_allocator);
|
||||
} else {
|
||||
defer this.output.clearRetainingCapacity();
|
||||
break :brk JSC.ArrayBuffer.createBuffer(this.globalObject, this.output.items);
|
||||
}
|
||||
};
|
||||
|
||||
const result = this.callback_value.get().?.call(this.globalObject, &.{
|
||||
if (this.write_failed)
|
||||
this.globalObject.createErrorInstance("BrotliError", .{})
|
||||
else
|
||||
JSC.JSValue.null,
|
||||
value,
|
||||
});
|
||||
|
||||
if (result.toError()) |err| {
|
||||
this.globalObject.bunVM().runErrorHandler(err, null);
|
||||
}
|
||||
}
|
||||
|
||||
// We can only run one encode job at a time
|
||||
// But we don't have an idea of a serial dispatch queue
|
||||
// So instead, we let you enqueue as many times as you want
|
||||
// and if one is already running, we just don't do anything
|
||||
const EncodeJob = struct {
|
||||
task: JSC.WorkPoolTask = .{ .callback = &runTask },
|
||||
encoder: *BrotliEncoder,
|
||||
|
||||
pub usingnamespace bun.New(@This());
|
||||
|
||||
pub fn run(this: *EncodeJob) void {
|
||||
defer {
|
||||
_ = this.encoder.has_pending_activity.fetchSub(1, .Monotonic);
|
||||
this.encoder.deref();
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
var any = false;
|
||||
|
||||
if (this.encoder.pending_encode_job_count.fetchAdd(1, .Monotonic) == 0) {
|
||||
var is_last = false;
|
||||
while (true) {
|
||||
const pending: []bun.JSC.Node.BlobOrStringOrBuffer = brk: {
|
||||
this.encoder.input_lock.lock();
|
||||
defer this.encoder.input_lock.unlock();
|
||||
is_last = this.encoder.has_called_end;
|
||||
const readable = this.encoder.input.readableSlice(0);
|
||||
const out = bun.default_allocator.dupe(std.meta.Child(@TypeOf(readable)), readable) catch bun.outOfMemory();
|
||||
this.encoder.input.discard(readable.len);
|
||||
break :brk out;
|
||||
};
|
||||
defer bun.default_allocator.free(pending);
|
||||
const Writer = struct {
|
||||
encoder: *BrotliEncoder,
|
||||
|
||||
pub const Error = error{OutOfMemory};
|
||||
pub fn writeAll(writer: @This(), chunk: []const u8) Error!void {
|
||||
writer.encoder.output_lock.lock();
|
||||
defer writer.encoder.output_lock.unlock();
|
||||
|
||||
try writer.encoder.output.appendSlice(bun.default_allocator, chunk);
|
||||
}
|
||||
};
|
||||
|
||||
defer {
|
||||
this.encoder.freelist_write_lock.lock();
|
||||
this.encoder.freelist.write(pending) catch unreachable;
|
||||
this.encoder.freelist_write_lock.unlock();
|
||||
}
|
||||
for (pending) |input| {
|
||||
var writer = this.encoder.stream.writer(Writer{ .encoder = this.encoder });
|
||||
writer.writeAll(input.slice()) catch {
|
||||
_ = this.encoder.pending_encode_job_count.fetchSub(1, .Monotonic);
|
||||
this.encoder.write_failed = true;
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
any = any or pending.len > 0;
|
||||
|
||||
if (this.encoder.pending_encode_job_count.fetchSub(1, .Monotonic) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_last and any) {
|
||||
var output = &this.encoder.output;
|
||||
this.encoder.output_lock.lock();
|
||||
defer {
|
||||
this.encoder.output_lock.unlock();
|
||||
}
|
||||
|
||||
output.appendSlice(bun.default_allocator, this.encoder.stream.end() catch {
|
||||
_ = this.encoder.pending_encode_job_count.fetchSub(1, .Monotonic);
|
||||
this.encoder.write_failed = true;
|
||||
return;
|
||||
}) catch {
|
||||
_ = this.encoder.pending_encode_job_count.fetchSub(1, .Monotonic);
|
||||
this.encoder.write_failed = true;
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (any) {
|
||||
var vm = this.encoder.globalObject.bunVMConcurrently();
|
||||
this.encoder.ref();
|
||||
this.encoder.poll_ref.refConcurrently(vm);
|
||||
vm.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this.encoder)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn runTask(this: *JSC.WorkPoolTask) void {
|
||||
var job: *EncodeJob = @fieldParentPtr(EncodeJob, "task", this);
|
||||
job.run();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn encode(this: *BrotliEncoder, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
const arguments = callframe.arguments(3);
|
||||
|
||||
if (arguments.len < 2) {
|
||||
globalObject.throwNotEnoughArguments("BrotliEncoder.encode", 2, arguments.len);
|
||||
return .zero;
|
||||
}
|
||||
|
||||
if (this.has_called_end) {
|
||||
globalObject.throw("BrotliEncoder.encode called after BrotliEncoder.end", .{});
|
||||
return .zero;
|
||||
}
|
||||
|
||||
const input = callframe.argument(0);
|
||||
const optional_encoding = callframe.argument(1);
|
||||
const is_last = callframe.argument(2).toBoolean();
|
||||
|
||||
const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalObject, bun.default_allocator, input, optional_encoding, true) orelse {
|
||||
globalObject.throwInvalidArgumentType("BrotliEncoder.encode", "input", "Blob, String, or Buffer");
|
||||
return .zero;
|
||||
};
|
||||
|
||||
_ = this.has_pending_activity.fetchAdd(1, .Monotonic);
|
||||
if (is_last)
|
||||
this.has_called_end = true;
|
||||
|
||||
var task = EncodeJob.new(.{
|
||||
.encoder = this,
|
||||
});
|
||||
|
||||
{
|
||||
this.input_lock.lock();
|
||||
defer this.input_lock.unlock();
|
||||
|
||||
this.input.writeItem(input_to_queue) catch unreachable;
|
||||
}
|
||||
this.ref();
|
||||
JSC.WorkPool.schedule(&task.task);
|
||||
|
||||
return .undefined;
|
||||
}
|
||||
pub fn encodeSync(this: *BrotliEncoder, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
_ = this;
|
||||
_ = globalObject;
|
||||
_ = callframe;
|
||||
|
||||
return .zero;
|
||||
}
|
||||
pub fn end(this: *BrotliEncoder, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
_ = this;
|
||||
_ = globalObject;
|
||||
_ = callframe;
|
||||
|
||||
return .zero;
|
||||
}
|
||||
pub fn endSync(this: *BrotliEncoder, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
_ = this;
|
||||
_ = globalObject;
|
||||
_ = callframe;
|
||||
|
||||
return .zero;
|
||||
}
|
||||
};
|
||||
|
||||
pub const BrotliDecoder = struct {
|
||||
pub usingnamespace bun.NewRefCounted(@This(), deinit);
|
||||
pub usingnamespace JSC.Codegen.JSBrotliDecoder;
|
||||
|
||||
has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
|
||||
ref_count: u32 = 1,
|
||||
|
||||
pub fn hasPendingActivity(this: *BrotliDecoder) callconv(.C) bool {
|
||||
return this.has_pending_activity.load(.Monotonic) > 0;
|
||||
}
|
||||
|
||||
pub fn deinit(this: *BrotliDecoder) void {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*BrotliDecoder {
|
||||
globalThis.throw("Crypto is not constructable", .{});
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn finalize(this: *BrotliDecoder) callconv(.C) void {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
pub fn decode(this: *BrotliDecoder, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
_ = this;
|
||||
_ = globalObject;
|
||||
_ = callframe;
|
||||
|
||||
return .zero;
|
||||
}
|
||||
pub fn decodeSync(this: *BrotliDecoder, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
_ = this;
|
||||
_ = globalObject;
|
||||
_ = callframe;
|
||||
|
||||
return .zero;
|
||||
}
|
||||
pub fn end(this: *BrotliDecoder, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
_ = this;
|
||||
_ = globalObject;
|
||||
_ = callframe;
|
||||
|
||||
return .zero;
|
||||
}
|
||||
pub fn endSync(this: *BrotliDecoder, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
_ = this;
|
||||
_ = globalObject;
|
||||
_ = callframe;
|
||||
|
||||
return .zero;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn exportAll() void {
|
||||
@export(BrotliEncoder.create, .{ .name = "BrotliEncoder__createFromJS" });
|
||||
}
|
||||
@@ -4916,5 +4916,6 @@ comptime {
|
||||
_ = Crypto.JSPasswordObject.JSPasswordObject__create;
|
||||
BunObject.exportAll();
|
||||
@export(InternalTestingAPIs.BunInternalFunction__syntaxHighlighter, .{ .name = "BunInternalFunction__syntaxHighlighter" });
|
||||
@import("./brotli.zig").exportAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1701,7 +1701,7 @@ JSC_DEFINE_HOST_FUNCTION(jsReceiveMessageOnPort, (JSGlobalObject * lexicalGlobal
|
||||
}
|
||||
|
||||
extern "C" EncodedJSValue BunInternalFunction__syntaxHighlighter(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame);
|
||||
|
||||
extern "C" EncodedJSValue BrotliEncoder__createFromJS(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame);
|
||||
// we're trying out a new way to do this lazy loading
|
||||
// this is $lazy() in js code
|
||||
JSC_DEFINE_HOST_FUNCTION(functionLazyLoad,
|
||||
@@ -1894,6 +1894,15 @@ JSC_DEFINE_HOST_FUNCTION(functionLazyLoad,
|
||||
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getUnpackedSettings"_s)), JSC::JSFunction::create(vm, globalObject, 1, "getUnpackedSettings"_s, BUN__HTTP2__getUnpackedSettings, ImplementationVisibility::Public, NoIntrinsic), 0);
|
||||
return JSValue::encode(obj);
|
||||
}
|
||||
|
||||
if (string == "internal/zlib"_s) {
|
||||
auto* obj = constructEmptyObject(globalObject);
|
||||
|
||||
obj->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "createBrotliEncoder"_s), 1, BrotliEncoder__createFromJS, ImplementationVisibility::Public, NoIntrinsic, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
|
||||
|
||||
return JSValue::encode(obj);
|
||||
}
|
||||
|
||||
if (string == "internal/tls"_s) {
|
||||
auto* obj = constructEmptyObject(globalObject);
|
||||
|
||||
|
||||
@@ -843,9 +843,7 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2,
|
||||
}
|
||||
|
||||
return (
|
||||
left->sanitizedNameString(globalObject) == right->sanitizedNameString(globalObject) &&
|
||||
left->sanitizedMessageString(globalObject) == right->sanitizedMessageString(globalObject)
|
||||
);
|
||||
left->sanitizedNameString(globalObject) == right->sanitizedNameString(globalObject) && left->sanitizedMessageString(globalObject) == right->sanitizedMessageString(globalObject));
|
||||
}
|
||||
}
|
||||
case Int8ArrayType:
|
||||
@@ -2473,8 +2471,8 @@ JSC__JSValue JSC__JSValue__fromEntries(JSC__JSGlobalObject* globalObject, ZigStr
|
||||
return JSC::JSValue::encode(object);
|
||||
}
|
||||
|
||||
|
||||
JSC__JSValue JSC__JSValue__keys(JSC__JSGlobalObject* globalObject, JSC__JSValue objectValue) {
|
||||
JSC__JSValue JSC__JSValue__keys(JSC__JSGlobalObject* globalObject, JSC__JSValue objectValue)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
@@ -2482,16 +2480,15 @@ JSC__JSValue JSC__JSValue__keys(JSC__JSGlobalObject* globalObject, JSC__JSValue
|
||||
JSC::JSObject* object = JSC::JSValue::decode(objectValue).toObject(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude)));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude)));
|
||||
}
|
||||
|
||||
bool JSC__JSValue__hasOwnProperty(JSC__JSValue jsValue, JSC__JSGlobalObject* globalObject, ZigString key) {
|
||||
bool JSC__JSValue__hasOwnProperty(JSC__JSValue jsValue, JSC__JSGlobalObject* globalObject, ZigString key)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
|
||||
JSC::JSValue value = JSC::JSValue::decode(jsValue);
|
||||
return value.toObject(globalObject)->hasOwnProperty(globalObject, JSC::PropertyName(JSC::Identifier::fromString(vm, Zig::toString(key))));
|
||||
|
||||
}
|
||||
|
||||
bool JSC__JSValue__asArrayBuffer_(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1,
|
||||
@@ -3413,7 +3410,7 @@ JSC__JSValue JSC__JSValue__fromTimevalNoTruncate(JSC__JSGlobalObject* globalObje
|
||||
auto big_nsec = JSC::JSBigInt::createFrom(globalObject, nsec);
|
||||
auto big_sec = JSC::JSBigInt::createFrom(globalObject, sec);
|
||||
auto big_1e6 = JSC::JSBigInt::createFrom(globalObject, 1e6);
|
||||
auto sec_as_nsec = JSC::JSBigInt::multiply(globalObject, big_1e6, big_sec);
|
||||
auto sec_as_nsec = JSC::JSBigInt::multiply(globalObject, big_1e6, big_sec);
|
||||
ASSERT(sec_as_nsec.isHeapBigInt());
|
||||
auto* big_sec_as_nsec = sec_as_nsec.asHeapBigInt();
|
||||
ASSERT(big_sec_as_nsec);
|
||||
|
||||
@@ -64,4 +64,7 @@ pub const Classes = struct {
|
||||
pub const Crypto = JSC.WebCore.Crypto;
|
||||
pub const FFI = JSC.FFI;
|
||||
pub const H2FrameParser = JSC.API.H2FrameParser;
|
||||
|
||||
pub const BrotliEncoder = JSC.API.BrotliEncoder;
|
||||
pub const BrotliDecoder = JSC.API.BrotliDecoder;
|
||||
};
|
||||
|
||||
@@ -337,6 +337,8 @@ const Lchown = JSC.Node.Async.lchown;
|
||||
const Unlink = JSC.Node.Async.unlink;
|
||||
const WaitPidResultTask = JSC.Subprocess.WaiterThread.WaitPidResultTask;
|
||||
const TimerReference = JSC.BunTimer.Timeout.TimerReference;
|
||||
const BrotliDecoder = JSC.API.BrotliDecoder;
|
||||
const BrotliEncoder = JSC.API.BrotliEncoder;
|
||||
// Task.get(ReadFileTask) -> ?ReadFileTask
|
||||
pub const Task = TaggedPointerUnion(.{
|
||||
FetchTasklet,
|
||||
@@ -397,6 +399,7 @@ pub const Task = TaggedPointerUnion(.{
|
||||
Unlink,
|
||||
WaitPidResultTask,
|
||||
TimerReference,
|
||||
BrotliEncoder,
|
||||
});
|
||||
const UnboundedQueue = @import("./unbounded_queue.zig").UnboundedQueue;
|
||||
pub const ConcurrentTask = struct {
|
||||
@@ -934,6 +937,10 @@ pub const EventLoop = struct {
|
||||
var any: *TimerReference = task.get(TimerReference).?;
|
||||
any.runFromJSThread();
|
||||
},
|
||||
@field(Task.Tag, typeBaseName(@typeName(BrotliEncoder))) => {
|
||||
var any: *BrotliEncoder = task.get(BrotliEncoder).?;
|
||||
any.runFromJSThread();
|
||||
},
|
||||
|
||||
else => if (Environment.allow_assert) {
|
||||
bun.Output.prettyln("\nUnexpected tag: {s}\n", .{@tagName(task.tag())});
|
||||
|
||||
@@ -205,7 +205,7 @@ pub const BlobOrStringOrBuffer = union(enum) {
|
||||
return .{ .string_or_buffer = StringOrBuffer.fromJS(global, allocator, value) orelse return null };
|
||||
}
|
||||
|
||||
pub fn fromJSWithEncodingValue(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding_value: JSC.JSValue) ?BlobOrStringOrBuffer {
|
||||
pub fn fromJSWithEncodingValueMaybeAsync(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding_value: JSC.JSValue, is_async: bool) ?BlobOrStringOrBuffer {
|
||||
if (value.as(JSC.WebCore.Blob)) |blob| {
|
||||
if (blob.store) |store| {
|
||||
store.ref();
|
||||
@@ -214,7 +214,11 @@ pub const BlobOrStringOrBuffer = union(enum) {
|
||||
return .{ .blob = blob.* };
|
||||
}
|
||||
|
||||
return .{ .string_or_buffer = StringOrBuffer.fromJSWithEncodingValue(global, allocator, value, encoding_value) orelse return null };
|
||||
return .{ .string_or_buffer = StringOrBuffer.fromJSWithEncodingValueMaybeAsync(global, allocator, value, encoding_value, is_async) orelse return null };
|
||||
}
|
||||
|
||||
pub fn fromJSWithEncodingValue(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding_value: JSC.JSValue) ?BlobOrStringOrBuffer {
|
||||
return fromJSWithEncodingValueMaybeAsync(global, allocator, value, encoding_value, false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -397,6 +401,16 @@ pub const StringOrBuffer = union(enum) {
|
||||
|
||||
return fromJSWithEncoding(global, allocator, value, encoding);
|
||||
}
|
||||
|
||||
pub fn fromJSWithEncodingValueMaybeAsync(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding_value: JSC.JSValue, maybe_async: bool) ?StringOrBuffer {
|
||||
const encoding: Encoding = brk: {
|
||||
if (!encoding_value.isCell())
|
||||
break :brk .utf8;
|
||||
break :brk Encoding.fromJS(encoding_value, global) orelse .utf8;
|
||||
};
|
||||
|
||||
return fromJSWithEncodingMaybeAsync(global, allocator, value, encoding, maybe_async);
|
||||
}
|
||||
};
|
||||
|
||||
pub const ErrorCode = @import("./nodejs_error_code.zig").Code;
|
||||
|
||||
141
src/deps/brotli_encoder.zig
Normal file
141
src/deps/brotli_encoder.zig
Normal file
@@ -0,0 +1,141 @@
|
||||
const bun = @import("root").bun;
|
||||
const std = @import("std");
|
||||
|
||||
pub const brotli_alloc_func = ?*const fn (?*anyopaque, usize) callconv(.C) ?*anyopaque;
|
||||
pub const brotli_free_func = ?*const fn (?*anyopaque, ?*anyopaque) callconv(.C) void;
|
||||
pub const struct_BrotliSharedDictionaryStruct = opaque {};
|
||||
pub const BrotliSharedDictionary = struct_BrotliSharedDictionaryStruct;
|
||||
pub const BROTLI_SHARED_DICTIONARY_RAW: c_int = 0;
|
||||
pub const BROTLI_SHARED_DICTIONARY_SERIALIZED: c_int = 1;
|
||||
pub const enum_BrotliSharedDictionaryType = c_uint;
|
||||
pub const BrotliSharedDictionaryType = enum_BrotliSharedDictionaryType;
|
||||
extern fn BrotliSharedDictionaryCreateInstance(alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) ?*BrotliSharedDictionary;
|
||||
extern fn BrotliSharedDictionaryDestroyInstance(dict: ?*BrotliSharedDictionary) void;
|
||||
extern fn BrotliSharedDictionaryAttach(dict: ?*BrotliSharedDictionary, @"type": BrotliSharedDictionaryType, data_size: usize, data: [*c]const u8) c_int;
|
||||
pub const BROTLI_MODE_GENERIC: c_int = 0;
|
||||
pub const BROTLI_MODE_TEXT: c_int = 1;
|
||||
pub const BROTLI_MODE_FONT: c_int = 2;
|
||||
pub const BrotliEncoderMode = enum(c_uint) {
|
||||
generic = 0,
|
||||
text = 1,
|
||||
font = 2,
|
||||
};
|
||||
pub const BROTLI_OPERATION_PROCESS: c_int = 0;
|
||||
pub const BROTLI_OPERATION_FLUSH: c_int = 1;
|
||||
pub const BROTLI_OPERATION_FINISH: c_int = 2;
|
||||
pub const BROTLI_OPERATION_EMIT_METADATA: c_int = 3;
|
||||
|
||||
pub const BROTLI_PARAM_MODE: c_int = 0;
|
||||
pub const BROTLI_PARAM_QUALITY: c_int = 1;
|
||||
pub const BROTLI_PARAM_LGWIN: c_int = 2;
|
||||
pub const BROTLI_PARAM_LGBLOCK: c_int = 3;
|
||||
pub const BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: c_int = 4;
|
||||
pub const BROTLI_PARAM_SIZE_HINT: c_int = 5;
|
||||
pub const BROTLI_PARAM_LARGE_WINDOW: c_int = 6;
|
||||
pub const BROTLI_PARAM_NPOSTFIX: c_int = 7;
|
||||
pub const BROTLI_PARAM_NDIRECT: c_int = 8;
|
||||
pub const BROTLI_PARAM_STREAM_OFFSET: c_int = 9;
|
||||
pub const BrotliEncoderParameter = enum(c_uint) {
|
||||
mode = 0,
|
||||
quality = 1,
|
||||
lgwin = 2,
|
||||
lgblock = 3,
|
||||
disable_literal_context_modeling = 4,
|
||||
size_hint = 5,
|
||||
large_window = 6,
|
||||
npostfix = 7,
|
||||
ndirect = 8,
|
||||
stream_offset = 9,
|
||||
};
|
||||
pub const BrotliEncoder = opaque {
|
||||
pub const Operation = enum(c_uint) {
|
||||
process = 0,
|
||||
flush = 1,
|
||||
finish = 2,
|
||||
emit_metadata = 3,
|
||||
};
|
||||
|
||||
extern fn BrotliEncoderSetParameter(state: *BrotliEncoder, param: BrotliEncoderParameter, value: u32) c_int;
|
||||
extern fn BrotliEncoderCreateInstance(alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) *BrotliEncoder;
|
||||
extern fn BrotliEncoderDestroyInstance(state: *BrotliEncoder) void;
|
||||
pub const struct_BrotliEncoderPreparedDictionaryStruct = opaque {};
|
||||
pub const BrotliEncoderPreparedDictionary = struct_BrotliEncoderPreparedDictionaryStruct;
|
||||
extern fn BrotliEncoderPrepareDictionary(@"type": BrotliSharedDictionaryType, data_size: usize, data: [*c]const u8, quality: c_int, alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) *BrotliEncoderPreparedDictionary;
|
||||
extern fn BrotliEncoderDestroyPreparedDictionary(dictionary: *BrotliEncoderPreparedDictionary) void;
|
||||
extern fn BrotliEncoderAttachPreparedDictionary(state: *BrotliEncoder, dictionary: ?*const BrotliEncoderPreparedDictionary) c_int;
|
||||
extern fn BrotliEncoderMaxCompressedSize(input_size: usize) usize;
|
||||
extern fn BrotliEncoderCompress(quality: c_int, lgwin: c_int, mode: BrotliEncoderMode, input_size: usize, input_buffer: [*]const u8, encoded_size: *usize, encoded_buffer: [*]u8) c_int;
|
||||
extern fn BrotliEncoderCompressStream(state: *BrotliEncoder, op: Operation, available_in: *usize, next_in: *?[*]const u8, available_out: *usize, next_out: ?[*]u8, total_out: ?*usize) c_int;
|
||||
extern fn BrotliEncoderIsFinished(state: *BrotliEncoder) c_int;
|
||||
extern fn BrotliEncoderHasMoreOutput(state: *BrotliEncoder) c_int;
|
||||
extern fn BrotliEncoderTakeOutput(state: *BrotliEncoder, size: *usize) ?[*]const u8;
|
||||
extern fn BrotliEncoderEstimatePeakMemoryUsage(quality: c_int, lgwin: c_int, input_size: usize) usize;
|
||||
extern fn BrotliEncoderGetPreparedDictionarySize(dictionary: ?*const BrotliEncoderPreparedDictionary) usize;
|
||||
extern fn BrotliEncoderVersion() u32;
|
||||
|
||||
pub fn createInstance(alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) callconv(.C) ?*BrotliEncoder {
|
||||
return BrotliEncoderCreateInstance(alloc_func, free_func, @"opaque");
|
||||
}
|
||||
|
||||
pub fn destroyInstance(state: *BrotliEncoder) callconv(.C) void {
|
||||
return BrotliEncoderDestroyInstance(state);
|
||||
}
|
||||
|
||||
pub fn hasMoreOutput(state: *BrotliEncoder) callconv(.C) bool {
|
||||
return BrotliEncoderHasMoreOutput(state) > 0;
|
||||
}
|
||||
|
||||
pub fn takeOutput(state: *BrotliEncoder) []const u8 {
|
||||
var size: usize = 0;
|
||||
if (BrotliEncoderTakeOutput(state, &size)) |ptr| {
|
||||
return ptr[0..size];
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
pub const CompressionResult = struct {
|
||||
success: bool = false,
|
||||
has_more: bool = false,
|
||||
output: []const u8 = "",
|
||||
};
|
||||
|
||||
// https://github.com/google/brotli/blob/2ad58d8603294f5ee33d23bb725e0e6a17c1de50/go/cbrotli/writer.go#L23-L40
|
||||
pub fn compressStream(state: *BrotliEncoder, op: Operation, data: []const u8) CompressionResult {
|
||||
var available_in = data.len;
|
||||
var next_in: ?[*]const u8 = data.ptr;
|
||||
|
||||
var available_out: usize = 0;
|
||||
|
||||
var result = CompressionResult{};
|
||||
|
||||
result.success = BrotliEncoderCompressStream(state, op, &available_in, &next_in, &available_out, null, null) > 0;
|
||||
|
||||
if (result.success) {
|
||||
result.output = takeOutput(state);
|
||||
}
|
||||
|
||||
result.has_more = BrotliEncoderHasMoreOutput(state) > 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn setParameter(state: *BrotliEncoder, param: BrotliEncoderParameter, value: u32) bool {
|
||||
return BrotliEncoderSetParameter(state, param, value) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH = 4;
|
||||
pub const SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH = 31;
|
||||
pub const SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS = 64;
|
||||
pub const SHARED_BROTLI_MAX_COMPOUND_DICTS = 15;
|
||||
pub const BROTLI_MIN_WINDOW_BITS = 10;
|
||||
pub const BROTLI_MAX_WINDOW_BITS = 24;
|
||||
pub const BROTLI_LARGE_MAX_WINDOW_BITS = 30;
|
||||
pub const BROTLI_MIN_INPUT_BLOCK_BITS = 16;
|
||||
pub const BROTLI_MAX_INPUT_BLOCK_BITS = 24;
|
||||
pub const BROTLI_MIN_QUALITY = 0;
|
||||
pub const BROTLI_MAX_QUALITY = 11;
|
||||
pub const BROTLI_DEFAULT_QUALITY = 11;
|
||||
pub const BROTLI_DEFAULT_WINDOW = 22;
|
||||
pub const BROTLI_DEFAULT_MODE = BROTLI_MODE_GENERIC;
|
||||
@@ -8,6 +8,8 @@ const BufferModule = require("node:buffer");
|
||||
const StreamModule = require("node:stream");
|
||||
const Util = require("node:util");
|
||||
|
||||
const { createBrotliEncoder } = $lazy("internal/zlib");
|
||||
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __commonJS = (cb, mod) =>
|
||||
function __require() {
|
||||
@@ -4067,21 +4069,15 @@ var require_lib = __commonJS({
|
||||
return zlibBufferSync(new InflateRaw(opts), buffer);
|
||||
};
|
||||
|
||||
// not implemented, stubs
|
||||
for (const method of [
|
||||
"BrotliCompress",
|
||||
"BrotliDecompress",
|
||||
"brotliCompress",
|
||||
"brotliCompressSync",
|
||||
"brotliDecompress",
|
||||
"brotliDecompressSync",
|
||||
"createBrotliCompress",
|
||||
"createBrotliDecompress",
|
||||
]) {
|
||||
exports[method] = function (buffer, opts, callback) {
|
||||
throw new Error(`zlib.${method} is not implemented`);
|
||||
};
|
||||
}
|
||||
exports.brotliCompress = function (buffer, opts, callback) {
|
||||
if (typeof opts === "function") {
|
||||
callback = opts;
|
||||
opts = {};
|
||||
}
|
||||
|
||||
const encoder = createBrotliEncoder(opts, {}, callback);
|
||||
encoder.encode(buffer, undefined, true);
|
||||
};
|
||||
|
||||
function zlibBuffer(engine, buffer, callback) {
|
||||
var buffers = [];
|
||||
|
||||
@@ -47,6 +47,8 @@ pub const API = struct {
|
||||
pub const TLSSocket = @import("./bun.js/api/bun/socket.zig").TLSSocket;
|
||||
pub const Listener = @import("./bun.js/api/bun/socket.zig").Listener;
|
||||
pub const H2FrameParser = @import("./bun.js/api/bun/h2_frame_parser.zig").H2FrameParser;
|
||||
pub const BrotliEncoder = @import("./bun.js/api/brotli.zig").BrotliEncoder;
|
||||
pub const BrotliDecoder = @import("./bun.js/api/brotli.zig").BrotliDecoder;
|
||||
};
|
||||
pub const DNS = @import("./bun.js/api/bun/dns_resolver.zig");
|
||||
pub const FFI = @import("./bun.js/api/ffi.zig").FFI;
|
||||
|
||||
Reference in New Issue
Block a user