Skip to content

Commit

Permalink
WIP: zlib one-shot methods
Browse files Browse the repository at this point in the history
  • Loading branch information
npaun committed Aug 20, 2024
1 parent 066a1a2 commit b414709
Show file tree
Hide file tree
Showing 6 changed files with 744 additions and 86 deletions.
91 changes: 83 additions & 8 deletions src/node/internal/internal_zlib.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,83 @@
import { default as zlibUtil } from 'node-internal:zlib';
import { Buffer } from 'node-internal:internal_buffer';
import { validateUint32 } from 'node-internal:validators';
import { isArrayBufferView } from 'node-internal:internal_types';
import { ERR_INVALID_ARG_TYPE } from 'node-internal:internal_errors';
import { ZlibOptions } from 'node:zlib';

const { ZLIB_OS_CODE } = zlibUtil;

type CompressCallback = (error: Error | null, result: Buffer | null) => void;

function crc32(data: ArrayBufferView | string, value: number = 0): number {
if (typeof data === 'string') {
data = Buffer.from(data);
} else if (!isArrayBufferView(data)) {
throw new ERR_INVALID_ARG_TYPE('data', 'ArrayBufferView', typeof data);
}
validateUint32(value, 'value');
return zlibUtil.crc32(data, value);
}

function inflateSync(
data: ArrayBufferView | string,
options: ZlibOptions = {}
): Buffer {
return Buffer.from(
zlibUtil.inflateSync(data, options, zlibUtil.CONST_INFLATE)
);
}

function deflateSync(
data: ArrayBufferView | string,
options: ZlibOptions = {}
): Buffer {
return Buffer.from(
zlibUtil.deflateSync(data, options, zlibUtil.CONST_DEFLATE)
);
}

function gunzipSync(
data: ArrayBufferView | string,
options: ZlibOptions = {}
): Buffer {
return Buffer.from(
zlibUtil.inflateSync(data, options, zlibUtil.CONST_GUNZIP)
);
}

function gzipSync(
data: ArrayBufferView | string,
options: ZlibOptions = {}
): Buffer {
return Buffer.from(zlibUtil.deflateSync(data, options, zlibUtil.CONST_GZIP));
}

function inflateRawSync(
data: ArrayBufferView | string,
options: ZlibOptions = {}
): Buffer {
return Buffer.from(
zlibUtil.inflateSync(data, options, zlibUtil.CONST_INFLATERAW)
);
}

function deflateRawSync(
data: ArrayBufferView | string,
options: ZlibOptions = {}
): Buffer {
return Buffer.from(
zlibUtil.deflateSync(data, options, zlibUtil.CONST_DEFLATERAW)
);
}

function deflate(
data: ArrayBufferView | string,
options: ZlibOptions = {},
callback: CompressCallback
): void {
zlibUtil.deflateWithCb(
data,
options,
zlibUtil.CONST_DEFLATE,
(error, result) =>
callback(error ? Error(error) : null, result ? Buffer.from(result) : null)
);
}

const constPrefix = 'CONST_';
const constants = {};

Expand All @@ -35,4 +99,15 @@ Object.defineProperties(
)
);

export { crc32, constants };
export {
crc32,
constants,
inflateSync,
deflateSync,
deflate,
gunzipSync,
gzipSync,
inflateRawSync,
deflateRawSync,
ZLIB_OS_CODE,
};
27 changes: 26 additions & 1 deletion src/node/internal/zlib.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
export function crc32(data: ArrayBufferView, value: number): number;
import type { ZlibOptions } from 'node:zlib';

type CompressCallback = (
error: string | null,
result: ArrayBuffer | null
) => void;

export function crc32(data: ArrayBufferView | string, value: number): number;
export function inflateSync(
data: ArrayBufferView | string,
options: ZlibOptions,
mode: number
): ArrayBuffer;
export function deflateSync(
data: ArrayBufferView | string,
options: ZlibOptions,
mode: number
): ArrayBuffer;
export function deflateWithCb(
data: ArrayBufferView | string,
options: ZlibOptions,
mode: number,
cb: CompressCallback
): void;

// zlib.constants (part of the API contract for node:zlib)
export const CONST_Z_NO_FLUSH: number;
Expand Down Expand Up @@ -52,6 +75,8 @@ export const CONST_Z_MIN_LEVEL: number;
export const CONST_Z_MAX_LEVEL: number;
export const CONST_Z_DEFAULT_LEVEL: number;

export const ZLIB_OS_CODE: number;

export const CONST_BROTLI_OPERATION_PROCESS: number;
export const CONST_BROTLI_OPERATION_FLUSH: number;
export const CONST_BROTLI_OPERATION_FINISH: number;
Expand Down
34 changes: 32 additions & 2 deletions src/node/zlib.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
import { crc32, constants } from 'node-internal:internal_zlib';
import {
crc32,
inflateSync,
deflateSync,
deflate,
gunzipSync,
gzipSync,
inflateRawSync,
deflateRawSync,
constants,
ZLIB_OS_CODE,
} from 'node-internal:internal_zlib';

export { crc32, constants };
export {
crc32,
inflateSync,
deflateSync,
deflate,
gunzipSync,
gzipSync,
inflateRawSync,
deflateRawSync,
constants,
ZLIB_OS_CODE,
};

export default {
crc32,
inflateSync,
deflateSync,
deflate,
gunzipSync,
gzipSync,
inflateRawSync,
deflateRawSync,
constants,
ZLIB_OS_CODE,
};
161 changes: 154 additions & 7 deletions src/workerd/api/node/tests/zlib-nodejs-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { strictEqual, throws, deepStrictEqual } from 'node:assert';
import { Buffer } from 'node:buffer';
import { crc32, constants } from 'node:zlib';
import {
crc32,
constants,
inflateSync,
deflateSync,
deflate,
gzipSync,
deflateRawSync,
ZLIB_OS_CODE,
} from 'node:zlib';
import process from 'node:process';

// The following test data comes from
// https://github.com/zlib-ng/zlib-ng/blob/5401b24/test/test_crc32.cc
Expand Down Expand Up @@ -234,12 +244,9 @@ export const crc32Test = {
}

[undefined, null, true, 1, () => {}, {}].forEach((invalid) => {
throws(
() => {
crc32(invalid);
},
{ code: 'ERR_INVALID_ARG_TYPE' }
);
throws(() => {
crc32(invalid);
}, TypeError("Failed to execute 'crc32' on 'ZlibUtil': parameter 1 is not of type 'string or ArrayBuffer or ArrayBufferView'."));
});

[null, true, () => {}, {}].forEach((invalid) => {
Expand Down Expand Up @@ -366,3 +373,143 @@ export const constantsTest = {
]);
},
};

const BIG_DATA = 'horse'.repeat(50_000) + 'cow'.repeat(49_000);

export const inflateSyncTest = {
test() {
strictEqual(inflateSync(deflateSync(BIG_DATA)).toString(), BIG_DATA);
throws(
() => inflateSync('garbage data'),
Error('Z_DATA_ERROR: incorrect header check')
);

strictEqual(
inflateSync(Buffer.from('OE9LyixKUUiCEQAmfgUk', 'base64'), {
windowBits: 11,
level: 4,
}).toString(),
'bird bird bird'
);
},
};

export const deflateSyncTest = {
test() {
throws(
() => deflateSync('hello world', { windowBits: 9000 }),
RangeError('Value 9000 is out of range [8..15]')
);
throws(
() => deflateSync('hello world', { strategy: 400 }),
TypeError('Value is not a member of the enum')
);
throws(
() => deflateSync(BIG_DATA, { maxOutputLength: 32 }).toString('base64'),
RangeError('Memory limit exceeded')
);

strictEqual(
deflateSync('bird bird bird', { windowBits: 11, level: 4 }).toString(
'base64'
),
'OE9LyixKUUiCEQAmfgUk'
);
strictEqual(
deflateSync(Buffer.from('bird bird bird'), {
windowBits: 11,
level: 4,
}).toString('base64'),
'OE9LyixKUUiCEQAmfgUk'
);
console.log(process.platform);

deepStrictEqual(
gzipSync('water, water, everywhere, nor any drop to drink'),
Buffer.from([
0x1f,
0x8b,
0x8,
0x0,
0x0,
0x0,
0x0,
0x0,
0x0,
ZLIB_OS_CODE,
0x2b,
0x4f,
0x2c,
0x49,
0x2d,
0xd2,
0x51,
0x28,
0x87,
0x50,
0xa9,
0x65,
0xa9,
0x45,
0x95,
0xe5,
0x19,
0xa9,
0x45,
0xa9,
0x3a,
0xa,
0x79,
0xf9,
0x45,
0xa,
0x89,
0x79,
0x95,
0xa,
0x29,
0x45,
0xf9,
0x5,
0xa,
0x25,
0xf9,
0x40,
0x3a,
0x33,
0x2f,
0x1b,
0x0,
0xaa,
0x92,
0xf2,
0x3c,
0x2f,
0x0,
0x0,
0x0,
])
);
strictEqual(
deflateRawSync('as idle as a painted ship upon a painted ocean').toString(
'base64'
),
'SyxWyEzJSVVILFZIVChIzMwrSU1RKM7ILFAoLcjPQxLLT05NzAMA'
);
},
};

export const deflateTest = {
test() {
deflate('bird bird bird', { windowBits: 11, level: 4 }, (_, result) => {
strictEqual(result.toString('base64'), 'OE9LyixKUUiCEQAmfgUk');
});

deflate('garbage data', { level: -9000 }, (error, _) => {
strictEqual(
error.message,
'RangeError: Value -9000 is out of range [-1..9]'
);
});
},
};
Loading

0 comments on commit b414709

Please sign in to comment.