|
|
@ -1,17 +1,5 @@ |
|
|
|
'use strict'; |
|
|
|
|
|
|
|
const validateOpts = (opts, chunkMultiple) => { |
|
|
|
opts = Object.assign({}, { chunkSize: 10000 }, opts); |
|
|
|
|
|
|
|
opts.chunkSize = Math.ceil(opts.chunkSize / chunkMultiple) * chunkMultiple; |
|
|
|
|
|
|
|
if (opts.chunkSize === 0) { |
|
|
|
throw new Error('opts.chunkSize must be larger than 0'); |
|
|
|
} |
|
|
|
|
|
|
|
return opts; |
|
|
|
}; |
|
|
|
|
|
|
|
const b64 = (input, opts) => { |
|
|
|
if (input instanceof Buffer || typeof input === 'string') { |
|
|
|
const method = input instanceof Buffer ? 'encode' : 'decode'; |
|
|
@ -21,21 +9,26 @@ const b64 = (input, opts) => { |
|
|
|
return Promise.reject(new TypeError('input must be a buffer or string')); |
|
|
|
}; |
|
|
|
|
|
|
|
b64.encode = (input, opts) => new Promise(resolve => { |
|
|
|
const chunkMultiple = 3; |
|
|
|
opts = validateOpts(opts, chunkMultiple); |
|
|
|
const createCodec = codecOpts => (input, opts) => new Promise(resolve => { |
|
|
|
const { chunkMultiple, inputTypeCheck, initialOutput, updateOutput } = codecOpts; |
|
|
|
|
|
|
|
opts = Object.assign({}, { chunkSize: 10000 }, opts); |
|
|
|
|
|
|
|
opts.chunkSize = Math.ceil(opts.chunkSize / chunkMultiple) * chunkMultiple; |
|
|
|
|
|
|
|
if (!(input instanceof Buffer)) { |
|
|
|
throw new TypeError('input must be a buffer'); |
|
|
|
if (opts.chunkSize === 0) { |
|
|
|
throw new Error('opts.chunkSize must be larger than 0'); |
|
|
|
} |
|
|
|
|
|
|
|
inputTypeCheck(input); |
|
|
|
|
|
|
|
const bufferLength = input.length; |
|
|
|
let currentIndex = 0; |
|
|
|
let output = ''; |
|
|
|
let output = initialOutput; |
|
|
|
|
|
|
|
setImmediate(function encodeChunk() { |
|
|
|
const chunk = input.slice(currentIndex, currentIndex + opts.chunkSize); |
|
|
|
output += chunk.toString('base64'); |
|
|
|
output = updateOutput(output, chunk); |
|
|
|
currentIndex += opts.chunkSize; |
|
|
|
if (currentIndex < bufferLength) { |
|
|
|
setImmediate(encodeChunk); |
|
|
@ -45,28 +38,29 @@ b64.encode = (input, opts) => new Promise(resolve => { |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
b64.decode = (input, opts) => new Promise(resolve => { |
|
|
|
const chunkMultiple = 4; |
|
|
|
opts = validateOpts(opts, chunkMultiple); |
|
|
|
|
|
|
|
if (typeof input !== 'string') { |
|
|
|
throw new TypeError('input must be a base64 string'); |
|
|
|
b64.encode = createCodec({ |
|
|
|
chunkMultiple: 3, |
|
|
|
inputTypeCheck: input => { |
|
|
|
if (!(input instanceof Buffer)) { |
|
|
|
throw new TypeError('input must be a buffer'); |
|
|
|
} |
|
|
|
}, |
|
|
|
initialOutput: '', |
|
|
|
updateOutput: (output, chunk) => { |
|
|
|
output += chunk.toString('base64'); |
|
|
|
return output; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
const stringLength = input.length; |
|
|
|
let currentIndex = 0; |
|
|
|
let output = Buffer.alloc(0); |
|
|
|
|
|
|
|
setImmediate(function encodeChunk() { |
|
|
|
const chunk = input.slice(currentIndex, currentIndex + opts.chunkSize); |
|
|
|
output = Buffer.concat([output, Buffer.from(chunk, 'base64')]); |
|
|
|
currentIndex += opts.chunkSize; |
|
|
|
if (currentIndex < stringLength) { |
|
|
|
setImmediate(encodeChunk); |
|
|
|
} else { |
|
|
|
resolve(output); |
|
|
|
b64.decode = createCodec({ |
|
|
|
chunkMultiple: 4, |
|
|
|
inputTypeCheck: input => { |
|
|
|
if (typeof input !== 'string') { |
|
|
|
throw new TypeError('input must be a base64 string'); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
initialOutput: Buffer.alloc(0), |
|
|
|
updateOutput: (output, chunk) => Buffer.concat([output, Buffer.from(chunk, 'base64')]) |
|
|
|
}); |
|
|
|
|
|
|
|
module.exports = b64; |
|
|
|