Compare commits

...

6 Commits

Author SHA1 Message Date
Meghan Denny
4e711ff26a fix the args order here 2024-09-06 23:26:01 -07:00
Meghan Denny
25f998f024 bring back this error handling 2024-09-06 23:25:50 -07:00
Meghan Denny
bfd22b81c4 make this not a magic number 2024-09-06 20:31:44 -07:00
Meghan Denny
6d9554c220 add emulation of these internal fields 2024-09-06 20:31:33 -07:00
Meghan Denny
18660fad49 align internal name of zlib.Base to be ZlibBase like node 2024-09-06 19:32:56 -07:00
Meghan Denny
8a432bd1f2 fix memory leak when passing strings to zlib.crc32 2024-09-06 19:31:57 -07:00
7 changed files with 248 additions and 42 deletions

View File

@@ -55,14 +55,25 @@ pub const BrotliReaderArrayList = struct {
state: State = State.Uninitialized,
total_out: usize = 0,
total_in: usize = 0,
flushOp: BrotliEncoder.Operation,
finishFlushOp: BrotliEncoder.Operation,
fullFlushOp: BrotliEncoder.Operation,
pub usingnamespace bun.New(BrotliReaderArrayList);
pub fn newWithOptions(input: []const u8, list: *std.ArrayListUnmanaged(u8), allocator: std.mem.Allocator, options: DecoderOptions) !*BrotliReaderArrayList {
return BrotliReaderArrayList.new(try initWithOptions(input, list, allocator, options));
return BrotliReaderArrayList.new(try initWithOptions(input, list, allocator, options, .process, .finish, .flush));
}
pub fn initWithOptions(input: []const u8, list: *std.ArrayListUnmanaged(u8), allocator: std.mem.Allocator, options: DecoderOptions) !BrotliReaderArrayList {
pub fn initWithOptions(
input: []const u8,
list: *std.ArrayListUnmanaged(u8),
allocator: std.mem.Allocator,
options: DecoderOptions,
flushOp: BrotliEncoder.Operation,
finishFlushOp: BrotliEncoder.Operation,
fullFlushOp: BrotliEncoder.Operation,
) !BrotliReaderArrayList {
if (!BrotliDecoder.initializeBrotli()) {
return error.BrotliFailedToLoad;
}
@@ -81,6 +92,9 @@ pub const BrotliReaderArrayList = struct {
.list = list.*,
.list_allocator = allocator,
.brotli = brotli,
.flushOp = flushOp,
.finishFlushOp = finishFlushOp,
.fullFlushOp = fullFlushOp,
};
}

View File

@@ -37,6 +37,7 @@ pub const BrotliEncoder = struct {
pub usingnamespace JSC.Codegen.JSBrotliEncoder;
stream: brotli.BrotliCompressionStream,
chunkSize: c_uint,
maxOutputLength: usize,
freelist: FreeList = .{},
@@ -81,7 +82,7 @@ pub const BrotliEncoder = struct {
const callback = arguments[2];
const mode = arguments[3].to(u8);
_ = globalThis.checkMinOrGetDefault(opts, "chunkSize", u32, 64, 1024 * 16) orelse return .zero;
const chunkSize = globalThis.checkMinOrGetDefault(opts, "chunkSize", u32, 64, 1024 * 16) orelse return .zero;
const maxOutputLength = globalThis.checkMinOrGetDefaultU64(opts, "maxOutputLength", usize, 0, std.math.maxInt(u52)) orelse return .zero;
const flush = globalThis.checkRangesOrGetDefault(opts, "flush", u8, 0, 3, 0) orelse return .zero;
const finishFlush = globalThis.checkRangesOrGetDefault(opts, "finishFlush", u8, 0, 3, 2) orelse return .zero;
@@ -93,6 +94,7 @@ pub const BrotliEncoder = struct {
globalThis.throw("Failed to create BrotliEncoder", .{});
return .zero;
},
.chunkSize = chunkSize,
.maxOutputLength = maxOutputLength,
.mode = mode,
});
@@ -407,6 +409,31 @@ pub const BrotliEncoder = struct {
_ = callframe;
return .undefined;
}
pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.chunkSize);
}
pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.flushOp);
}
pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.finishFlushOp);
}
pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.fullFlushOp);
}
pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.maxOutputLength);
}
};
pub const BrotliDecoder = struct {
@@ -415,6 +442,7 @@ pub const BrotliDecoder = struct {
globalThis: *JSC.JSGlobalObject,
stream: brotli.BrotliReaderArrayList,
chunkSize: c_uint,
maxOutputLength: usize,
mode: u8,
@@ -465,7 +493,7 @@ pub const BrotliDecoder = struct {
const callback = arguments[2];
const mode = arguments[3].to(u8);
_ = globalThis.checkMinOrGetDefault(opts, "chunkSize", u32, 64, 1024 * 16) orelse return .zero;
const chunkSize = globalThis.checkMinOrGetDefault(opts, "chunkSize", u32, 64, 1024 * 16) orelse return .zero;
const maxOutputLength = globalThis.checkMinOrGetDefaultU64(opts, "maxOutputLength", usize, 0, std.math.maxInt(u52)) orelse return .zero;
const flush = globalThis.checkRangesOrGetDefault(opts, "flush", u8, 0, 6, 0) orelse return .zero;
const finishFlush = globalThis.checkRangesOrGetDefault(opts, "finishFlush", u8, 0, 6, 2) orelse return .zero;
@@ -474,16 +502,14 @@ pub const BrotliDecoder = struct {
var this: *BrotliDecoder = BrotliDecoder.new(.{
.globalThis = globalThis,
.stream = undefined, // &this.output needs to be a stable pointer
.chunkSize = chunkSize,
.maxOutputLength = maxOutputLength,
.mode = mode,
});
this.stream = brotli.BrotliReaderArrayList.initWithOptions("", &this.output, bun.default_allocator, .{}) catch {
this.stream = brotli.BrotliReaderArrayList.initWithOptions("", &this.output, bun.default_allocator, .{}, @enumFromInt(flush), @enumFromInt(finishFlush), @enumFromInt(fullFlush)) catch {
globalThis.throw("Failed to create BrotliDecoder", .{});
return .zero;
};
_ = flush;
_ = finishFlush;
_ = fullFlush;
if (opts.get(globalThis, "params")) |params| {
inline for (std.meta.fields(bun.brotli.c.BrotliDecoderParameter)) |f| {
@@ -765,4 +791,29 @@ pub const BrotliDecoder = struct {
_ = callframe;
return .undefined;
}
pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.chunkSize);
}
pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.flushOp);
}
pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.finishFlushOp);
}
pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.fullFlushOp);
}
pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.maxOutputLength);
}
};

View File

@@ -439,6 +439,31 @@ pub const ZlibEncoder = struct {
return .undefined;
}
pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.chunkSize);
}
pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.flush);
}
pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.finishFlush);
}
pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.fullFlush);
}
pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.maxOutputLength);
}
};
pub const ZlibDecoder = struct {
@@ -871,6 +896,31 @@ pub const ZlibDecoder = struct {
_ = callframe;
return .undefined;
}
pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.chunkSize);
}
pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.flush);
}
pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.finishFlush);
}
pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.fullFlush);
}
pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.maxOutputLength);
}
};
const Options = struct {
@@ -991,7 +1041,8 @@ fn handleTransformSyncStreamError(err: anyerror, globalThis: *JSC.JSGlobalObject
}
fn runCallback(callback: JSC.JSValue, globalObject: *JSC.JSGlobalObject, thisValue: JSC.JSValue, arguments: []const JSC.JSValue) ?void {
_ = callback.call(globalObject, thisValue, arguments);
const result = callback.call(globalObject, thisValue, arguments);
if (result.toError()) |_| return null;
if (globalObject.hasException()) return null;
return;
}

View File

@@ -37,6 +37,21 @@ export default [
fn: "close",
length: 0,
},
chunkSize: {
getter: "getChunkSize",
},
flush: {
getter: "getFlush",
},
finishFlush: {
getter: "getFinishFlush",
},
fullFlush: {
getter: "getFullFlush",
},
maxOutputLength: {
getter: "getMaxOutputLength",
},
},
}),
define({
@@ -76,6 +91,21 @@ export default [
fn: "close",
length: 0,
},
chunkSize: {
getter: "getChunkSize",
},
flush: {
getter: "getFlush",
},
finishFlush: {
getter: "getFinishFlush",
},
fullFlush: {
getter: "getFullFlush",
},
maxOutputLength: {
getter: "getMaxOutputLength",
},
},
}),
define({
@@ -128,6 +158,21 @@ export default [
fn: "params",
length: 3,
},
chunkSize: {
getter: "getChunkSize",
},
flush: {
getter: "getFlush",
},
finishFlush: {
getter: "getFinishFlush",
},
fullFlush: {
getter: "getFullFlush",
},
maxOutputLength: {
getter: "getMaxOutputLength",
},
},
}),
define({
@@ -181,6 +226,21 @@ export default [
fn: "params",
length: 3,
},
chunkSize: {
getter: "getChunkSize",
},
flush: {
getter: "getFlush",
},
finishFlush: {
getter: "getFinishFlush",
},
fullFlush: {
getter: "getFullFlush",
},
maxOutputLength: {
getter: "getMaxOutputLength",
},
},
}),
];

View File

@@ -4382,6 +4382,9 @@ pub const JSValue = enum(JSValueReprInt) {
extern fn JSBuffer__bufferFromPointerAndLengthAndDeinit(*JSGlobalObject, [*]u8, usize, ?*anyopaque, JSC.C.JSTypedArrayBytesDeallocator) JSValue;
pub fn jsNumberWithType(comptime Number: type, number: Number) JSValue {
if (@typeInfo(Number) == .Enum) {
return jsNumberWithType(@typeInfo(Number).Enum.tag_type, @intFromEnum(number));
}
return switch (comptime Number) {
JSValue => number,
u0 => jsNumberFromInt32(0),

View File

@@ -17,7 +17,7 @@ pub const createZlibDecoder = bun.JSC.API.ZlibDecoder.create;
pub fn crc32(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
const arguments = callframe.arguments(2).ptr;
const data: []const u8 = blk: {
const data: ZigString.Slice = blk: {
const data: JSC.JSValue = arguments[0];
var exceptionref: JSC.C.JSValueRef = null;
@@ -25,9 +25,9 @@ pub fn crc32(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callcon
return globalThis.throwInvalidArgumentTypeValue("data", "string or an instance of Buffer, TypedArray, or DataView", .undefined);
}
if (data.isString()) {
break :blk data.asString().toSlice(globalThis, bun.default_allocator).slice();
break :blk data.asString().toSlice(globalThis, bun.default_allocator);
}
const buffer = JSC.Buffer.fromJS(globalThis, data, &exceptionref) orelse {
const buffer: JSC.Buffer = JSC.Buffer.fromJS(globalThis, data, &exceptionref) orelse {
const ty_str = data.jsTypeString(globalThis).toSlice(globalThis, bun.default_allocator);
defer ty_str.deinit();
globalThis.ERR_INVALID_ARG_TYPE("The \"data\" property must be an instance of Buffer, TypedArray, DataView, or ArrayBuffer. Received {s}", .{ty_str.slice()}).throw();
@@ -37,8 +37,9 @@ pub fn crc32(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callcon
globalThis.throwValue(JSC.JSValue.c(ptr));
return .zero;
}
break :blk buffer.slice();
break :blk ZigString.Slice.fromUTF8NeverFree(buffer.slice());
};
defer data.deinit();
const value: u32 = blk: {
const value: JSC.JSValue = arguments[1];
@@ -64,5 +65,6 @@ pub fn crc32(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callcon
};
// crc32 returns a u64 but the data will always be within a u32 range so the outer @intCast is always safe.
return JSC.JSValue.jsNumber(@as(u32, @intCast(bun.zlib.crc32(value, data.ptr, @intCast(data.len)))));
const slice_u8 = data.slice();
return JSC.JSValue.jsNumber(@as(u32, @intCast(bun.zlib.crc32(value, slice_u8.ptr, @intCast(slice_u8.len)))));
}

View File

@@ -29,7 +29,7 @@ const kFlushBuffers: Buffer[] = [];
//
function Base(method, options) {
function ZlibBase(options, method) {
if (options == null) options = {};
if ($isObject(options)) {
options.maxOutputLength ??= maxOutputLengthDefault;
@@ -45,8 +45,8 @@ function Base(method, options) {
this[kHandle] = private_constructor(options, {}, null, method);
stream.Transform.$call(this, options);
}
Base.prototype = Object.create(stream.Transform.prototype);
ObjectDefineProperty(Base.prototype, "_handle", {
ZlibBase.prototype = Object.create(stream.Transform.prototype);
ObjectDefineProperty(ZlibBase.prototype, "_handle", {
get: function () {
return this[kHandle];
},
@@ -54,25 +54,50 @@ ObjectDefineProperty(Base.prototype, "_handle", {
//noop
},
});
ObjectDefineProperty(Base.prototype, "bytesWritten", {
ObjectDefineProperty(ZlibBase.prototype, "bytesWritten", {
get: function () {
return this[kHandle].bytesWritten;
},
});
ObjectDefineProperty(Base.prototype, "bytesRead", {
ObjectDefineProperty(ZlibBase.prototype, "bytesRead", {
get: function () {
return this[kHandle].bytesRead;
},
});
ObjectDefineProperty(Base.prototype, "_closed", {
ObjectDefineProperty(ZlibBase.prototype, "_closed", {
get: function () {
return this[kHandle].closed;
},
});
Base.prototype.flush = function (kind, callback) {
ObjectDefineProperty(ZlibBase.prototype, "_chunkSize", {
get: function () {
return this[kHandle].chunkSize;
},
});
ObjectDefineProperty(ZlibBase.prototype, "_defaultFlushFlag", {
get: function () {
return this[kHandle].flush;
},
});
ObjectDefineProperty(ZlibBase.prototype, "_finishFlushFlag", {
get: function () {
return this[kHandle].finishFlush;
},
});
ObjectDefineProperty(ZlibBase.prototype, "_defaultFullFlushFlag", {
get: function () {
return this[kHandle].fullFlush;
},
});
ObjectDefineProperty(ZlibBase.prototype, "_maxOutputLength", {
get: function () {
return this[kHandle].maxOutputLength;
},
});
ZlibBase.prototype.flush = function (kind, callback) {
if (typeof kind === "function" || (kind === undefined && !callback)) {
callback = kind;
kind = 3;
kind = this._defaultFullFlushFlag;
}
if (this.writableFinished) {
if (callback) process.nextTick(callback);
@@ -82,32 +107,32 @@ Base.prototype.flush = function (kind, callback) {
this.write(kFlushBuffers[kind], "", callback);
}
};
Base.prototype.reset = function () {
ZlibBase.prototype.reset = function () {
assert(this[kHandle], "zlib binding closed");
return this[kHandle].reset();
};
Base.prototype.close = function (callback) {
ZlibBase.prototype.close = function (callback) {
if (callback) stream.finished(this, callback);
this.destroy();
};
Base.prototype._transform = function _transform(chunk, encoding, callback) {
ZlibBase.prototype._transform = function _transform(chunk, encoding, callback) {
try {
callback(undefined, this[kHandle].transformSync(chunk, encoding, false));
} catch (err) {
callback(err, undefined);
}
};
Base.prototype._flush = function _flush(callback) {
ZlibBase.prototype._flush = function _flush(callback) {
try {
callback(undefined, this[kHandle].transformSync("", undefined, true));
} catch (err) {
callback(err, undefined);
}
};
Base.prototype._final = function (callback) {
ZlibBase.prototype._final = function (callback) {
callback();
};
Base.prototype._processChunk = function (chunk, flushFlag, cb) {
ZlibBase.prototype._processChunk = function (chunk, flushFlag, cb) {
// _processChunk() is left for backwards compatibility
if (typeof cb === "function") processChunk(this, chunk, flushFlag, cb);
else return processChunkSync(this, chunk, flushFlag);
@@ -124,10 +149,10 @@ function processChunk(self, chunk, flushFlag, cb) {
//
function Zlib(method, options) {
Base.$call(this, method, options);
function Zlib(options, method) {
ZlibBase.$call(this, options, method);
}
Zlib.prototype = Object.create(Base.prototype);
Zlib.prototype = Object.create(ZlibBase.prototype);
ObjectDefineProperty(Zlib.prototype, "_level", {
get: function () {
return this[kHandle].level;
@@ -154,23 +179,23 @@ Zlib.prototype._transform = function _transform(chunk, encoding, callback) {
function BrotliCompress(opts) {
if (!(this instanceof BrotliCompress)) return new BrotliCompress(opts);
Base.$call(this, BROTLI_ENCODE, opts);
ZlibBase.$call(this, opts, BROTLI_ENCODE);
}
BrotliCompress.prototype = Object.create(Base.prototype);
BrotliCompress.prototype = Object.create(ZlibBase.prototype);
//
function BrotliDecompress(opts) {
if (!(this instanceof BrotliDecompress)) return new BrotliDecompress(opts);
Base.$call(this, BROTLI_DECODE, opts);
ZlibBase.$call(this, opts, BROTLI_DECODE);
}
BrotliDecompress.prototype = Object.create(Base.prototype);
BrotliDecompress.prototype = Object.create(ZlibBase.prototype);
//
function Deflate(opts) {
if (!(this instanceof Deflate)) return new Deflate(opts);
Zlib.$call(this, DEFLATE, opts);
Zlib.$call(this, opts, DEFLATE);
}
Deflate.prototype = Object.create(Zlib.prototype);
@@ -178,7 +203,7 @@ Deflate.prototype = Object.create(Zlib.prototype);
function Inflate(opts) {
if (!(this instanceof Inflate)) return new Inflate(opts);
Zlib.$call(this, INFLATE, opts);
Zlib.$call(this, opts, INFLATE);
}
Inflate.prototype = Object.create(Zlib.prototype);
@@ -186,7 +211,7 @@ Inflate.prototype = Object.create(Zlib.prototype);
function DeflateRaw(opts) {
if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts);
Zlib.$call(this, DEFLATERAW, opts);
Zlib.$call(this, opts, DEFLATERAW);
}
DeflateRaw.prototype = Object.create(Zlib.prototype);
@@ -194,7 +219,7 @@ DeflateRaw.prototype = Object.create(Zlib.prototype);
function InflateRaw(opts) {
if (!(this instanceof InflateRaw)) return new InflateRaw(opts);
Zlib.$call(this, INFLATERAW, opts);
Zlib.$call(this, opts, INFLATERAW);
}
InflateRaw.prototype = Object.create(Zlib.prototype);
@@ -202,7 +227,7 @@ InflateRaw.prototype = Object.create(Zlib.prototype);
function Gzip(opts) {
if (!(this instanceof Gzip)) return new Gzip(opts);
Zlib.$call(this, GZIP, opts);
Zlib.$call(this, opts, GZIP);
}
Gzip.prototype = Object.create(Zlib.prototype);
@@ -210,7 +235,7 @@ Gzip.prototype = Object.create(Zlib.prototype);
function Gunzip(opts) {
if (!(this instanceof Gunzip)) return new Gunzip(opts);
Zlib.$call(this, GUNZIP, opts);
Zlib.$call(this, opts, GUNZIP);
}
Gunzip.prototype = Object.create(Zlib.prototype);
@@ -218,7 +243,7 @@ Gunzip.prototype = Object.create(Zlib.prototype);
function Unzip(opts) {
if (!(this instanceof Unzip)) return new Unzip(opts);
Zlib.$call(this, UNZIP, opts);
Zlib.$call(this, opts, UNZIP);
}
Unzip.prototype = Object.create(Zlib.prototype);