Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions res/gz-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

async function readableStreamToBuffer(stream) {
const reader = stream.getReader();
const chunks = [];

try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) {
chunks.push(value);
}
}
} finally {
reader.releaseLock();
}

// Calculate total length and combine chunks
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
const result = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}

return result;
}

onmessage = async (e) => {
let data = e.data;
if (data.kind === 'compress') {
// Create a gzip compression stream
const compressionStream = new CompressionStream('gzip');

// Write the data to the compression stream
const writer = compressionStream.writable.getWriter();
writer.write(data.arrayData);
writer.close();

// Read the compressed data back into a buffer
let result = await readableStreamToBuffer(compressionStream.readable);
postMessage(result, [result.buffer]);
} else if (data.kind === 'decompress') {
// Create a gzip compression stream
const decompressionStream = new DecompressionStream('gzip');

// Write the data to the compression stream
const writer = decompressionStream.writable.getWriter();
writer.write(data.arrayData);
writer.close();

// Read the compressed data back into a buffer
let result = await readableStreamToBuffer(decompressionStream.readable);
postMessage(result, [result.buffer]);
} else {
throw new Error('unknown message');
}
};
2 changes: 2 additions & 0 deletions src/test/fixtures/node-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ function getWorkerScript(file: string): string {
},
postMessage: parentPort.postMessage.bind(parentPort),
onmessage: function () {},
DecompressionStream,
CompressionStream,
Comment on lines +24 to +25

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did you figure out that this part was needed? I just hit the same issue for Response and I was not having a good time figuring it out.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, I was skimming at the jest setup code and kind of just got lucky with a guess.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, fair enough!

};

vm.runInNewContext(scriptContent, sandbox, { filename: "${file}" });
Expand Down
4 changes: 4 additions & 0 deletions src/test/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ if (process.env.TZ !== 'UTC') {
fetchMock.mockGlobal();
(global as any).fetchMock = fetchMock;

// Mock the effects of the file-loader which our Webpack config defines
// for JS files under res: The "default export" is the path to the file.
jest.mock('firefox-profiler-res/gz-worker.js', () => './res/gz-worker.js');

// Install a Worker class which is similar to the DOM Worker class.
(global as any).Worker = NodeWorker;

Expand Down
73 changes: 25 additions & 48 deletions src/utils/gz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,45 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

async function readableStreamToBuffer(
stream: ReadableStream<Uint8Array<ArrayBuffer>>
): Promise<Uint8Array<ArrayBuffer>> {
const reader = stream.getReader();
const chunks: Uint8Array[] = [];

try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) {
chunks.push(value);
}
}
} finally {
reader.releaseLock();
}

// Calculate total length and combine chunks
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
const result = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
import gzWorkerPath from 'firefox-profiler-res/gz-worker.js';

return result;
function runGzWorker(
kind: 'compress' | 'decompress',
arrayData: Uint8Array<ArrayBuffer>
): Promise<Uint8Array<ArrayBuffer>> {
return new Promise((resolve, reject) => {
// On-demand spawn the worker. If this is too slow we can look into keeping
// a pool of workers around.
const worker = new Worker(gzWorkerPath);

worker.onmessage = (e) => {
resolve(e.data as Uint8Array<ArrayBuffer>);
worker.terminate();
};

worker.onerror = (e) => {
reject(e.error);
worker.terminate();
};

worker.postMessage({ kind, arrayData }, [arrayData.buffer]);
});
}

// This will transfer `data` if it is an array buffer.
export async function compress(
data: string | Uint8Array<ArrayBuffer>
): Promise<Uint8Array<ArrayBuffer>> {
// Encode the data if it's a string
const arrayData =
typeof data === 'string' ? new TextEncoder().encode(data) : data;

// Create a gzip compression stream
const compressionStream = new CompressionStream('gzip');

// Write the data to the compression stream
const writer = compressionStream.writable.getWriter();
writer.write(arrayData);
writer.close();

// Read the compressed data back into a buffer
return readableStreamToBuffer(compressionStream.readable);
return runGzWorker('compress', arrayData);
}

export async function decompress(
data: Uint8Array<ArrayBuffer>
): Promise<Uint8Array<ArrayBuffer>> {
// Create a gzip compression stream
const decompressionStream = new DecompressionStream('gzip');

// Write the data to the compression stream
const writer = decompressionStream.writable.getWriter();
writer.write(data);
writer.close();

// Read the compressed data back into a buffer
return readableStreamToBuffer(decompressionStream.readable);
return runGzWorker('decompress', data);
}

export function isGzip(data: Uint8Array): boolean {
Expand Down
1 change: 1 addition & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const config = {
patterns: [
'res/_headers',
'res/_redirects',
'res/gz-worker.js',
'res/contribute.json',
'res/robots.txt',
'res/service-worker-compat.js',
Expand Down