|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- 'use strict';
- const fs = require('fs');
- const path = require('path');
- const crypto = require('crypto');
- const isStream = require('is-stream');
-
- const {Worker} = (() => {
- try {
- return require('worker_threads');
- } catch (_) {
- return {};
- }
- })();
-
- let worker; // Lazy
- let taskIdCounter = 0;
- const tasks = new Map();
-
- const recreateWorkerError = sourceError => {
- const error = new Error(sourceError.message);
-
- for (const [key, value] of Object.entries(sourceError)) {
- if (key !== 'message') {
- error[key] = value;
- }
- }
-
- return error;
- };
-
- const createWorker = () => {
- worker = new Worker(path.join(__dirname, 'thread.js'));
-
- worker.on('message', message => {
- const task = tasks.get(message.id);
- tasks.delete(message.id);
-
- if (tasks.size === 0) {
- worker.unref();
- }
-
- if (message.error === undefined) {
- task.resolve(message.value);
- } else {
- task.reject(recreateWorkerError(message.error));
- }
- });
-
- worker.on('error', error => {
- // Any error here is effectively an equivalent of segfault, and have no scope, so we just throw it on callback level
- throw error;
- });
- };
-
- const taskWorker = (method, args, transferList) => new Promise((resolve, reject) => {
- const id = taskIdCounter++;
- tasks.set(id, {resolve, reject});
-
- if (worker === undefined) {
- createWorker();
- }
-
- worker.ref();
- worker.postMessage({id, method, args}, transferList);
- });
-
- const hasha = (input, options = {}) => {
- let outputEncoding = options.encoding || 'hex';
-
- if (outputEncoding === 'buffer') {
- outputEncoding = undefined;
- }
-
- const hash = crypto.createHash(options.algorithm || 'sha512');
-
- const update = buffer => {
- const inputEncoding = typeof buffer === 'string' ? 'utf8' : undefined;
- hash.update(buffer, inputEncoding);
- };
-
- if (Array.isArray(input)) {
- input.forEach(update);
- } else {
- update(input);
- }
-
- return hash.digest(outputEncoding);
- };
-
- hasha.stream = (options = {}) => {
- let outputEncoding = options.encoding || 'hex';
-
- if (outputEncoding === 'buffer') {
- outputEncoding = undefined;
- }
-
- const stream = crypto.createHash(options.algorithm || 'sha512');
- stream.setEncoding(outputEncoding);
- return stream;
- };
-
- hasha.fromStream = async (stream, options = {}) => {
- if (!isStream(stream)) {
- throw new TypeError('Expected a stream');
- }
-
- return new Promise((resolve, reject) => {
- // TODO: Use `stream.pipeline` and `stream.finished` when targeting Node.js 10
- stream
- .on('error', reject)
- .pipe(hasha.stream(options))
- .on('error', reject)
- .on('finish', function () {
- resolve(this.read());
- });
- });
- };
-
- if (Worker === undefined) {
- hasha.fromFile = async (filePath, options) => hasha.fromStream(fs.createReadStream(filePath), options);
- hasha.async = async (input, options) => hasha(input, options);
- } else {
- hasha.fromFile = async (filePath, {algorithm = 'sha512', encoding = 'hex'} = {}) => {
- const hash = await taskWorker('hashFile', [algorithm, filePath]);
-
- if (encoding === 'buffer') {
- return Buffer.from(hash);
- }
-
- return Buffer.from(hash).toString(encoding);
- };
-
- hasha.async = async (input, {algorithm = 'sha512', encoding = 'hex'} = {}) => {
- if (encoding === 'buffer') {
- encoding = undefined;
- }
-
- const hash = await taskWorker('hash', [algorithm, input]);
-
- if (encoding === undefined) {
- return Buffer.from(hash);
- }
-
- return Buffer.from(hash).toString(encoding);
- };
- }
-
- hasha.fromFileSync = (filePath, options) => hasha(fs.readFileSync(filePath), options);
-
- module.exports = hasha;
|