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
48 changes: 28 additions & 20 deletions src/lib/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function sandBox(
}
});
} else {
// IO-3: Object.assign statt Object.keys().forEach() – kein temporäres Keys-Array
// IO-3: Object.assign instead of Object.keys().forEach() – no temporary keys array
adapter.getForeignStates(pattern, (_err, _states) => _states && Object.assign(states, _states));
}
} else {
Expand Down Expand Up @@ -533,7 +533,7 @@ export function sandBox(
if (!(adapter.config as JavaScriptAdapterConfig).subscribe && context.interimStateValues[id]) {
// if the state is changed, we will compare it with interimStateValues
const oldState = context.interimStateValues[id];
// IO-1: for…in statt Object.keys().filter().every() – kein temporäres Array pro Aufruf
// IO-1: for…in instead of Object.keys().filter().every() – no temporary array per call
let stateHasChanged = false;
for (const attr in stateAsObject) {
if (attr === 'ts') {
Expand Down Expand Up @@ -1027,7 +1027,7 @@ export function sandBox(
}
}

// IO-2: O(1) Deduplizierung via Set statt O(n²) resUnique.includes()
// IO-2: O(1) deduplication via Set instead of O(n²) resUnique.includes()
const resUnique: string[] = [...new Set(res)];

for (let i = 0; i < resUnique.length; i++) {
Expand Down Expand Up @@ -1691,7 +1691,7 @@ export function sandBox(
if (oPattern?.id && Array.isArray(oPattern.id)) {
const result: (IobSchedule | string | null | undefined)[] = [];
for (let t = 0; t < oPattern.id.length; t++) {
// IO-4: Spread statt JSON.parse(JSON.stringify()) – kein tiefer Clone nötig (nur primitive Felder)
// IO-4: Spread instead of JSON.parse(JSON.stringify()) – no deep clone needed (only primitive fields)
const pa: Pattern = { ...oPattern, id: oPattern.id[t] };
result.push(
sandbox.subscribe(pa, callbackOrChangeTypeOrId, value) as
Expand Down Expand Up @@ -1944,7 +1944,7 @@ export function sandBox(

// Subscribe to all members of enum
for (const objId of members) {
// IO-6: `in` Operator statt Object.keys().includes() – O(1) statt O(n)
// IO-6: `in` operator instead of Object.keys().includes() – O(1) instead of O(n)
if (!(objId in subscriptions)) {
if (objects?.[objId]?.type === 'state') {
// Just subscribe to states
Expand Down Expand Up @@ -2653,14 +2653,14 @@ export function sandBox(
Object.keys(context.scripts).forEach(
name =>
context.scripts[name].schedules &&
// IO-8: Spread statt JSON.parse(JSON.stringify()) – _ioBroker hat nur primitive Felder
// IO-8: Spread instead of JSON.parse(JSON.stringify()) – _ioBroker has only primitive fields
context.scripts[name].schedules.forEach(s =>
schedules.push({ ...s._ioBroker } as unknown as ScheduleName),
),
);
} else {
script.schedules &&
// IO-8: Spread statt JSON.parse(JSON.stringify())
// IO-8: Spread instead of JSON.parse(JSON.stringify())
script.schedules.forEach(s => schedules.push({ ...s._ioBroker } as unknown as ScheduleName));
}
return schedules;
Expand Down Expand Up @@ -2846,13 +2846,14 @@ export function sandBox(
const removedScripts = new Set<string>();
for (let i = timers[id].length - 1; i >= 0; i--) {
if (timerId === undefined || timers[id][i].id === timerId) {
const clearedTimerId = timers[id][i].id;
removedScripts.add(timers[id][i].scriptName);
clearTimeout(timers[id][i].t);
if (timerId !== undefined) {
timers[id].splice(i, 1);
}
if (sandbox.verbose) {
sandbox.log(`clearStateDelayed: clear timer ${timers[id][i]?.id ?? timerId}`, 'info');
sandbox.log(`clearStateDelayed: clear timer ${clearedTimerId}`, 'info');
}
}
}
Expand All @@ -2863,15 +2864,22 @@ export function sandBox(
delete timers[id];
}
}
// IO-7: timersByScript Reverse-Index aktualisieren wenn State keine Timer mehr hat
if (!timers[id]) {
// IO-7: keep the timersByScript reverse-index in sync. For every script whose
// timer(s) we just removed, drop `id` from its set – unless that script still has
// another timer for this state (other scripts' timers may keep timers[id] alive).
if (removedScripts.size) {
const remaining = timers[id]; // undefined if the whole entry was deleted
for (const scriptName of removedScripts) {
const stateIds = context.timersByScript.get(scriptName);
if (stateIds) {
stateIds.delete(id);
if (!stateIds.size) {
context.timersByScript.delete(scriptName);
}
if (!stateIds) {
continue;
}
if (remaining?.some(e => e.scriptName === scriptName)) {
continue;
}
stateIds.delete(id);
if (!stateIds.size) {
context.timersByScript.delete(scriptName);
}
}
}
Expand Down Expand Up @@ -4093,7 +4101,7 @@ export function sandBox(
errorInCallback(err as Error);
}
}, ms);
// IO-10: Set.add() – O(1) statt Array.push()
// IO-10: Set.add() – O(1) instead of Array.push()
script.intervals.add(int);

if (sandbox.verbose) {
Expand All @@ -4105,7 +4113,7 @@ export function sandBox(
return null;
},
clearInterval: function (id: NodeJS.Timeout): void {
// IO-10: Set.has/delete – O(1) statt Array.indexOf+splice O(n)
// IO-10: Set.has/delete – O(1) instead of Array.indexOf+splice O(n)
if (script.intervals.has(id)) {
if (sandbox.verbose) {
sandbox.log('clearInterval() => cleared', 'info');
Expand All @@ -4121,7 +4129,7 @@ export function sandBox(
setTimeout: function (callback: (args?: any[]) => void, ms: number, ...args: any[]): NodeJS.Timeout | null {
if (typeof callback === 'function') {
const to = setTimeout(() => {
// IO-10: Set.delete – O(1) statt Array.indexOf+splice O(n)
// IO-10: Set.delete – O(1) instead of Array.indexOf+splice O(n)
script.timeouts.delete(to);

try {
Expand All @@ -4133,15 +4141,15 @@ export function sandBox(
if (sandbox.verbose) {
sandbox.log(`setTimeout(ms=${ms})`, 'info');
}
// IO-10: Set.add – O(1) statt Array.push
// IO-10: Set.add – O(1) instead of Array.push
script.timeouts.add(to);
return to;
}
sandbox.log(`Invalid callback for setTimeout! - ${typeof callback}`, 'error');
return null;
},
clearTimeout: function (id: NodeJS.Timeout): void {
// IO-10: Set.has/delete – O(1) statt Array.indexOf+splice O(n)
// IO-10: Set.has/delete – O(1) instead of Array.indexOf+splice O(n)
if (script.timeouts.has(id)) {
if (sandbox.verbose) {
sandbox.log('clearTimeout() => cleared', 'info');
Expand Down
6 changes: 2 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ class JavaScript extends Adapter {
/** Fast O(1) lookup set – always kept in sync with stateIds */
private readonly stateIdSet: Set<string> = new Set();

/** Precomputed "from" string for prepareStateObject – avoids string alloc on every setState */
private readonly subscriptions: SubscriptionResult[] = [];
private readonly subscriptionsFile: FileSubscriptionResult[] = [];
private readonly subscriptionsObject: SubscribeObject[] = [];
Expand Down Expand Up @@ -849,8 +848,6 @@ class JavaScript extends Adapter {
async onReady(): Promise<void> {
this.errorLogFunction = this.log;
this.context.errorLogFunction = this.log;
// Precompute once – avoids string template alloc on every setState call

this.config.maxSetStatePerMinute = parseInt(this.config.maxSetStatePerMinute as unknown as string, 10) || 1000;
this.config.maxTriggersPerScript = parseInt(this.config.maxTriggersPerScript as unknown as string, 10) || 100;

Expand Down Expand Up @@ -2186,7 +2183,8 @@ class JavaScript extends Adapter {

// remember all IDs – sort once to guarantee the sorted invariant
// required by binaryIndexOf() / sortedInsert() used later
for (const id of Object.keys(res).sort()) {
const keys = Object.keys(res).sort();
for (const id of keys) {
this.stateIds.push(id);
this.stateIdSet.add(id);
}
Expand Down
Loading
Loading