Improved performance of str/array buffer conversions

This commit is contained in:
n1474335 2023-02-03 17:10:33 +00:00
parent 7a2517fd61
commit 659325c85a
8 changed files with 30 additions and 23 deletions

View File

@ -474,6 +474,7 @@ class Utils {
static strToArrayBuffer(str) { static strToArrayBuffer(str) {
log.debug(`Converting string[${str?.length}] to array buffer`); log.debug(`Converting string[${str?.length}] to array buffer`);
if (!str) return new ArrayBuffer; if (!str) return new ArrayBuffer;
const arr = new Uint8Array(str.length); const arr = new Uint8Array(str.length);
let i = str.length, b; let i = str.length, b;
while (i--) { while (i--) {
@ -502,9 +503,10 @@ class Utils {
static strToUtf8ArrayBuffer(str) { static strToUtf8ArrayBuffer(str) {
log.debug(`Converting string[${str?.length}] to UTF8 array buffer`); log.debug(`Converting string[${str?.length}] to UTF8 array buffer`);
if (!str) return new ArrayBuffer; if (!str) return new ArrayBuffer;
const utf8Str = utf8.encode(str);
if (str.length !== utf8Str.length) { const buffer = new TextEncoder("utf-8").encode(str);
if (str.length !== buffer.length) {
if (isWorkerEnvironment() && self && typeof self.setOption === "function") { if (isWorkerEnvironment() && self && typeof self.setOption === "function") {
self.setOption("attemptHighlight", false); self.setOption("attemptHighlight", false);
} else if (isWebEnvironment()) { } else if (isWebEnvironment()) {
@ -512,7 +514,7 @@ class Utils {
} }
} }
return Utils.strToArrayBuffer(utf8Str); return buffer.buffer;
} }
@ -627,20 +629,24 @@ class Utils {
static byteArrayToUtf8(byteArray) { static byteArrayToUtf8(byteArray) {
log.debug(`Converting byte array[${byteArray?.length}] to UTF8`); log.debug(`Converting byte array[${byteArray?.length}] to UTF8`);
if (!byteArray || !byteArray.length) return ""; if (!byteArray || !byteArray.length) return "";
const str = Utils.byteArrayToChars(byteArray); if (!(byteArray instanceof Uint8Array))
byteArray = new Uint8Array(byteArray);
try { try {
const utf8Str = utf8.decode(str); const str = new TextDecoder("utf-8", {fatal: true}).decode(byteArray);
if (str.length !== utf8Str.length) {
if (str.length !== byteArray.length) {
if (isWorkerEnvironment()) { if (isWorkerEnvironment()) {
self.setOption("attemptHighlight", false); self.setOption("attemptHighlight", false);
} else if (isWebEnvironment()) { } else if (isWebEnvironment()) {
window.app.options.attemptHighlight = false; window.app.options.attemptHighlight = false;
} }
} }
return utf8Str;
return str;
} catch (err) { } catch (err) {
// If it fails, treat it as ANSI // If it fails, treat it as ANSI
return str; return Utils.byteArrayToChars(byteArray);
} }
} }
@ -662,9 +668,10 @@ class Utils {
log.debug(`Converting byte array[${byteArray?.length}] to chars`); log.debug(`Converting byte array[${byteArray?.length}] to chars`);
if (!byteArray || !byteArray.length) return ""; if (!byteArray || !byteArray.length) return "";
let str = ""; let str = "";
// String concatenation appears to be faster than an array join // Maxiumum arg length for fromCharCode is 65535, but the stack may already be fairly deep,
for (let i = 0; i < byteArray.length;) { // so don't get too near it.
str += String.fromCharCode(byteArray[i++]); for (let i = 0; i < byteArray.length; i += 20000) {
str += String.fromCharCode(...(byteArray.slice(i, i+20000)));
} }
return str; return str;
} }

View File

@ -25,12 +25,12 @@ import OperationError from "../errors/OperationError.mjs";
*/ */
export function toBase64(data, alphabet="A-Za-z0-9+/=") { export function toBase64(data, alphabet="A-Za-z0-9+/=") {
if (!data) return ""; if (!data) return "";
if (typeof data == "string") {
data = Utils.strToArrayBuffer(data);
}
if (data instanceof ArrayBuffer) { if (data instanceof ArrayBuffer) {
data = new Uint8Array(data); data = new Uint8Array(data);
} }
if (typeof data == "string") {
data = Utils.strToByteArray(data);
}
alphabet = Utils.expandAlphRange(alphabet).join(""); alphabet = Utils.expandAlphRange(alphabet).join("");
if (alphabet.length !== 64 && alphabet.length !== 65) { // Allow for padding if (alphabet.length !== 64 && alphabet.length !== 65) { // Allow for padding

View File

@ -84,7 +84,7 @@ class FromBCD extends Operation {
break; break;
case "Raw": case "Raw":
default: default:
byteArray = Utils.strToByteArray(input); byteArray = new Uint8Array(Utils.strToArrayBuffer(input));
byteArray.forEach(b => { byteArray.forEach(b => {
nibbles.push(b >>> 4); nibbles.push(b >>> 4);
nibbles.push(b & 15); nibbles.push(b & 15);

View File

@ -26,7 +26,7 @@ class FromCharcode extends Operation {
this.description = "Converts unicode character codes back into text.<br><br>e.g. <code>0393 03b5 03b9 03ac 20 03c3 03bf 03c5</code> becomes <code>Γειά σου</code>"; this.description = "Converts unicode character codes back into text.<br><br>e.g. <code>0393 03b5 03b9 03ac 20 03c3 03bf 03c5</code> becomes <code>Γειά σου</code>";
this.infoURL = "https://wikipedia.org/wiki/Plane_(Unicode)"; this.infoURL = "https://wikipedia.org/wiki/Plane_(Unicode)";
this.inputType = "string"; this.inputType = "string";
this.outputType = "byteArray"; this.outputType = "ArrayBuffer";
this.args = [ this.args = [
{ {
"name": "Delimiter", "name": "Delimiter",
@ -44,7 +44,7 @@ class FromCharcode extends Operation {
/** /**
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {byteArray} * @returns {ArrayBuffer}
* *
* @throws {OperationError} if base out of range * @throws {OperationError} if base out of range
*/ */
@ -77,7 +77,7 @@ class FromCharcode extends Operation {
for (i = 0; i < bites.length; i++) { for (i = 0; i < bites.length; i++) {
latin1 += Utils.chr(parseInt(bites[i], base)); latin1 += Utils.chr(parseInt(bites[i], base));
} }
return Utils.strToByteArray(latin1); return Utils.strToArrayBuffer(latin1);
} }
} }

View File

@ -68,8 +68,8 @@ class HammingDistance extends Operation {
samples[0] = fromHex(samples[0]); samples[0] = fromHex(samples[0]);
samples[1] = fromHex(samples[1]); samples[1] = fromHex(samples[1]);
} else { } else {
samples[0] = Utils.strToByteArray(samples[0]); samples[0] = new Uint8Array(Utils.strToArrayBuffer(samples[0]));
samples[1] = Utils.strToByteArray(samples[1]); samples[1] = new Uint8Array(Utils.strToArrayBuffer(samples[1]));
} }
let dist = 0; let dist = 0;

View File

@ -49,7 +49,7 @@ class ParseIPv4Header extends Operation {
if (format === "Hex") { if (format === "Hex") {
input = fromHex(input); input = fromHex(input);
} else if (format === "Raw") { } else if (format === "Raw") {
input = Utils.strToByteArray(input); input = new Uint8Array(Utils.strToArrayBuffer(input));
} else { } else {
throw new OperationError("Unrecognised input format."); throw new OperationError("Unrecognised input format.");
} }

View File

@ -71,7 +71,7 @@ class ParseX509Certificate extends Operation {
cert.readCertHex(toHex(fromBase64(input, null, "byteArray"), "")); cert.readCertHex(toHex(fromBase64(input, null, "byteArray"), ""));
break; break;
case "Raw": case "Raw":
cert.readCertHex(toHex(Utils.strToByteArray(input), "")); cert.readCertHex(toHex(Utils.strToArrayBuffer(input), ""));
break; break;
default: default:
undefinedInputFormat = true; undefinedInputFormat = true;

View File

@ -77,7 +77,7 @@ class PlayMedia extends Operation {
* Displays an audio or video element that may be able to play the media * Displays an audio or video element that may be able to play the media
* file. * file.
* *
* @param data {byteArray} Data containing an audio or video file. * @param {byteArray} data Data containing an audio or video file.
* @returns {string} Markup to display a media player. * @returns {string} Markup to display a media player.
*/ */
async present(data) { async present(data) {