optimize code for speed and size

This commit is contained in:
proddy
2025-10-19 16:21:52 +02:00
parent 16f7a454db
commit f48d67d9e7

View File

@@ -1,45 +1,17 @@
let decoder; // @ts-nocheck - Optimized MessagePack unpacking library for EMS-ESP32
try { let decoder, src, srcEnd, position = 0, strings = [], stringPosition = 0, currentUnpackr = {}, currentStructures, srcString, srcStringStart = 0, srcStringEnd = 0, bundledStrings, referenceMap, dataView;
decoder = new TextDecoder(); const EMPTY_ARRAY = [], currentExtensions = [];
} catch (error) {} const defaultOptions = { useRecords: false, mapsAsObjects: true };
let src; try { decoder = new TextDecoder(); } catch (error) { }
let srcEnd; class C1Type { }
let position = 0; const C1 = new C1Type();
const EMPTY_ARRAY = [];
let strings = EMPTY_ARRAY;
let stringPosition = 0;
let currentUnpackr = {};
let currentStructures;
let srcString;
let srcStringStart = 0;
let srcStringEnd = 0;
let bundledStrings;
let referenceMap;
const currentExtensions = [];
let dataView;
const defaultOptions = {
useRecords: false,
mapsAsObjects: true
};
export class C1Type {}
export const C1 = new C1Type();
C1.name = 'MessagePack 0xC1'; C1.name = 'MessagePack 0xC1';
let sequentialMode = false; let sequentialMode = false, inlineObjectReadThreshold = 2, readStruct, onLoadedStructures, onSaveState;
let inlineObjectReadThreshold = 2; try { new Function(''); } catch (error) { inlineObjectReadThreshold = Infinity; }
let readStruct, onLoadedStructures, onSaveState;
// no-eval build
try {
new Function('');
} catch (error) {
// if eval variants are not supported, do not create inline object readers ever
inlineObjectReadThreshold = Infinity;
}
export class Unpackr { export class Unpackr {
constructor(options) { constructor(options) {
if (options) { if (options) {
if (options.useRecords === false && options.mapsAsObjects === undefined) if (options.useRecords === false && options.mapsAsObjects === undefined) options.mapsAsObjects = true;
options.mapsAsObjects = true;
if (options.sequential && options.trusted !== false) { if (options.sequential && options.trusted !== false) {
options.trusted = true; options.trusted = true;
if (!options.structures && options.useRecords != false) { if (!options.structures && options.useRecords != false) {
@@ -47,22 +19,17 @@ export class Unpackr {
if (!options.maxSharedStructures) options.maxSharedStructures = 0; if (!options.maxSharedStructures) options.maxSharedStructures = 0;
} }
} }
if (options.structures) if (options.structures) options.structures.sharedLength = options.structures.length;
options.structures.sharedLength = options.structures.length;
else if (options.getStructures) { else if (options.getStructures) {
(options.structures = []).uninitialized = true; // this is what we use to denote an uninitialized structures (options.structures = []).uninitialized = true;
options.structures.sharedLength = 0; options.structures.sharedLength = 0;
} }
if (options.int64AsNumber) { if (options.int64AsNumber) options.int64AsType = 'number';
options.int64AsType = 'number';
}
} }
Object.assign(this, options); Object.assign(this, options);
} }
unpack(source, options?: any) { unpack(source, options?: any) {
if (src) { if (src) {
// re-entrant execution, save the state and restore it after we do this unpack
return saveState(() => { return saveState(() => {
clearSource(); clearSource();
return this return this
@@ -86,9 +53,6 @@ export class Unpackr {
strings = EMPTY_ARRAY; strings = EMPTY_ARRAY;
bundledStrings = null; bundledStrings = null;
src = source; src = source;
// this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
// technique for getting data from a database where it can be copied into an existing buffer instead of creating
// new ones
try { try {
dataView = dataView =
source.dataView || source.dataView ||
@@ -103,9 +67,9 @@ export class Unpackr {
if (source instanceof Uint8Array) throw error; if (source instanceof Uint8Array) throw error;
throw new Error( throw new Error(
'Source must be a Uint8Array or Buffer but was a ' + 'Source must be a Uint8Array or Buffer but was a ' +
(source && typeof source == 'object' (source && typeof source == 'object'
? source.constructor.name ? source.constructor.name
: typeof source) : typeof source)
); );
} }
if (this instanceof Unpackr) { if (this instanceof Unpackr) {
@@ -191,10 +155,10 @@ export class Unpackr {
return this.unpack(source, end); return this.unpack(source, end);
} }
} }
export function getPosition() { function getPosition() {
return position; return position;
} }
export function checkedRead(options: any) { function checkedRead(options: any) {
try { try {
if (!currentUnpackr.trusted && !sequentialMode) { if (!currentUnpackr.trusted && !sequentialMode) {
const sharedLength = currentStructures.sharedLength || 0; const sharedLength = currentStructures.sharedLength || 0;
@@ -264,7 +228,7 @@ function restoreStructures() {
currentStructures.restoreStructures = null; currentStructures.restoreStructures = null;
} }
export function read() { function read() {
let token = src[position++]; let token = src[position++];
if (token < 0xa0) { if (token < 0xa0) {
if (token < 0x80) { if (token < 0x80) {
@@ -542,18 +506,18 @@ function createStructureReader(structure, firstId) {
const readObject = (structure.read = new Function( const readObject = (structure.read = new Function(
'r', 'r',
'return function(){return ' + 'return function(){return ' +
(currentUnpackr.freezeData ? 'Object.freeze' : '') + (currentUnpackr.freezeData ? 'Object.freeze' : '') +
'({' + '({' +
structure structure
.map((key) => .map((key) =>
key === '__proto__' key === '__proto__'
? '__proto_:r()' ? '__proto_:r()'
: validName.test(key) : validName.test(key)
? key + ':r()' ? key + ':r()'
: '[' + JSON.stringify(key) + ']:r()' : '[' + JSON.stringify(key) + ']:r()'
) )
.join(',') + .join(',') +
'})}' '})}'
)(read)); )(read));
if (structure.highByte === 0) if (structure.highByte === 0)
structure.read = createSecondByteReader(firstId, structure.read); structure.read = createSecondByteReader(firstId, structure.read);
@@ -589,7 +553,7 @@ const createSecondByteReader = (firstId, read0) =>
return structure.read(); return structure.read();
}; };
export function loadStructures() { function loadStructures() {
const loadedStructures = saveState(() => { const loadedStructures = saveState(() => {
// save the state in case getStructures modifies our buffer // save the state in case getStructures modifies our buffer
src = null; src = null;
@@ -605,9 +569,8 @@ var readFixedString = readStringJS;
var readString8 = readStringJS; var readString8 = readStringJS;
var readString16 = readStringJS; var readString16 = readStringJS;
var readString32 = readStringJS; var readString32 = readStringJS;
export let isNativeAccelerationEnabled = false; let isNativeAccelerationEnabled = false;
function setExtractor(extractStrings) {
export function setExtractor(extractStrings) {
isNativeAccelerationEnabled = true; isNativeAccelerationEnabled = true;
readFixedString = readString(1); readFixedString = readString(1);
readString8 = readString(2); readString8 = readString(2);
@@ -701,7 +664,7 @@ function readStringJS(length) {
return result; return result;
} }
export function readString(source, start, length) { function readString(source, start, length) {
const existingSrc = src; const existingSrc = src;
src = source; src = source;
position = start; position = start;
@@ -918,7 +881,7 @@ function readOnlyJSString() {
function readBin(length) { function readBin(length) {
return currentUnpackr.copyBuffers return currentUnpackr.copyBuffers
? // specifically use the copying slice (not the node one) ? // specifically use the copying slice (not the node one)
Uint8Array.prototype.slice.call(src, position, (position += length)) Uint8Array.prototype.slice.call(src, position, (position += length))
: src.subarray(position, (position += length)); : src.subarray(position, (position += length));
} }
function readExt(length) { function readExt(length) {
@@ -1026,7 +989,7 @@ const recordDefinition = (id, highByte) => {
structure.read = createStructureReader(structure, firstByte); structure.read = createStructureReader(structure, firstByte);
return structure.read(); return structure.read();
}; };
currentExtensions[0] = () => {}; // notepack defines extension 0 to mean undefined, so use that as the default here currentExtensions[0] = () => { }; // notepack defines extension 0 to mean undefined, so use that as the default here
currentExtensions[0].noBuffer = true; currentExtensions[0].noBuffer = true;
const glbl = typeof globalThis === 'object' ? globalThis : window; const glbl = typeof globalThis === 'object' ? globalThis : window;
@@ -1065,7 +1028,7 @@ currentExtensions[0x70] = (data) => {
currentExtensions[0x73] = () => new Set(read()); currentExtensions[0x73] = () => new Set(read());
export const typedArrays = [ const typedArrays = [
'Int8', 'Int8',
'Uint8', 'Uint8',
'Uint8Clamped', 'Uint8Clamped',
@@ -1114,25 +1077,25 @@ currentExtensions[0xff] = (data) => {
else if (data.length == 8) else if (data.length == 8)
return new Date( return new Date(
((data[0] << 22) + (data[1] << 14) + (data[2] << 6) + (data[3] >> 2)) / ((data[0] << 22) + (data[1] << 14) + (data[2] << 6) + (data[3] >> 2)) /
1000000 + 1000000 +
((data[3] & 0x3) * 0x100000000 + ((data[3] & 0x3) * 0x100000000 +
data[4] * 0x1000000 + data[4] * 0x1000000 +
(data[5] << 16) + (data[5] << 16) +
(data[6] << 8) + (data[6] << 8) +
data[7]) * data[7]) *
1000 1000
); );
else if (data.length == 12) else if (data.length == 12)
return new Date( return new Date(
((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]) / 1000000 + ((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]) / 1000000 +
((data[4] & 0x80 ? -0x1000000000000 : 0) + ((data[4] & 0x80 ? -0x1000000000000 : 0) +
data[6] * 0x10000000000 + data[6] * 0x10000000000 +
data[7] * 0x100000000 + data[7] * 0x100000000 +
data[8] * 0x1000000 + data[8] * 0x1000000 +
(data[9] << 16) + (data[9] << 16) +
(data[10] << 8) + (data[10] << 8) +
data[11]) * data[11]) *
1000 1000
); );
else return new Date('invalid'); else return new Date('invalid');
}; // notepack defines extension 0 to mean undefined, so use that as the default here }; // notepack defines extension 0 to mean undefined, so use that as the default here
@@ -1177,44 +1140,20 @@ function saveState(callback) {
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength); dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
return value; return value;
} }
export function clearSource() { function clearSource() {
src = null; src = null;
referenceMap = null; referenceMap = null;
currentStructures = null; currentStructures = null;
} }
export function addExtension(extension) { function addExtension(extension) {
if (extension.unpack) currentExtensions[extension.type] = extension.unpack; if (extension.unpack) currentExtensions[extension.type] = extension.unpack;
else currentExtensions[extension.type] = extension; else currentExtensions[extension.type] = extension;
} }
export const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding const mult10 = new Array(147);
for (let i = 0; i < 256; i++) { for (let i = 0; i < 256; i++) {
mult10[i] = +('1e' + Math.floor(45.15 - i * 0.30103)); mult10[i] = +('1e' + Math.floor(45.15 - i * 0.30103));
} }
export const Decoder = Unpackr; const defaultUnpackr = new Unpackr({ useRecords: false });
var defaultUnpackr = new Unpackr({ useRecords: false });
export const unpack = defaultUnpackr.unpack; export const unpack = defaultUnpackr.unpack;
export const unpackMultiple = defaultUnpackr.unpackMultiple;
export const decode = defaultUnpackr.unpack;
export const FLOAT32_OPTIONS = {
NEVER: 0,
ALWAYS: 1,
DECIMAL_ROUND: 3,
DECIMAL_FIT: 4
};
const f32Array = new Float32Array(1);
const u8Array = new Uint8Array(f32Array.buffer, 0, 4);
export function roundFloat32(float32Number) {
f32Array[0] = float32Number;
const multiplier = mult10[((u8Array[3] & 0x7f) << 1) | (u8Array[2] >> 7)];
return (
((multiplier * float32Number + (float32Number > 0 ? 0.5 : -0.5)) >> 0) /
multiplier
);
}
export function setReadStruct(updatedReadStruct, loadedStructs, saveState) {
readStruct = updatedReadStruct;
onLoadedStructures = loadedStructs;
onSaveState = saveState;
}