From 941812fd0b5892bf55a7fed0cc7111dc105e1451 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 2 Jan 2017 19:22:04 +0200 Subject: [PATCH] Memory usage improvements in KeybindingResolver --- src/vs/base/common/keyCodes.ts | 44 ++++++++------ .../keybinding/common/keybindingResolver.ts | 59 +++++++++++++------ .../test/common/keybindingService.test.ts | 2 +- 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/src/vs/base/common/keyCodes.ts b/src/vs/base/common/keyCodes.ts index c8fa0e01247..688b1ee951a 100644 --- a/src/vs/base/common/keyCodes.ts +++ b/src/vs/base/common/keyCodes.ts @@ -404,40 +404,48 @@ export namespace KeyCodeUtils { } } -// Binary encoding strategy: -// 15: 1 bit for ctrlCmd -// 14: 1 bit for shift -// 13: 1 bit for alt -// 12: 1 bit for winCtrl -// 0: 12 bits for keyCode (up to a maximum keyCode of 4096. Given we have 83 at this point thats good enough) +/** + * Binary encoding strategy: + * ``` + * 1111 11 + * 5432 1098 7654 3210 + * ---- CSAW KKKK KKKK + * C = bit 11 = ctrlCmd flag + * S = bit 10 = shift flag + * A = bit 9 = alt flag + * W = bit 8 = winCtrl flag + * K = bits 0-7 = key code + * ``` + */ const enum BinaryKeybindingsMask { - CtrlCmd = 1 << 15, - Shift = 1 << 14, - Alt = 1 << 13, - WinCtrl = 1 << 12, - KeyCode = 0x00000fff, + CtrlCmd = (1 << 11) >>> 0, + Shift = (1 << 10) >>> 0, + Alt = (1 << 9) >>> 0, + WinCtrl = (1 << 8) >>> 0, + KeyCode = 0x000000ff, ModifierMask = CtrlCmd | Shift | Alt | WinCtrl } export const enum KeyMod { - CtrlCmd = 1 << 15, - Shift = 1 << 14, - Alt = 1 << 13, - WinCtrl = 1 << 12, + CtrlCmd = (1 << 11) >>> 0, + Shift = (1 << 10) >>> 0, + Alt = (1 << 9) >>> 0, + WinCtrl = (1 << 8) >>> 0, } export function KeyChord(firstPart: number, secondPart: number): number { - return firstPart | ((secondPart & 0x0000ffff) << 16); + let chordPart = ((secondPart & 0x0000ffff) << 16) >>> 0; + return (firstPart | chordPart) >>> 0; } export class BinaryKeybindings { public static extractFirstPart(keybinding: number): number { - return keybinding & 0x0000ffff; + return (keybinding & 0x0000ffff) >>> 0; } public static extractChordPart(keybinding: number): number { - return (keybinding >> 16) & 0x0000ffff; + return (keybinding & 0xffff0000) >>> 16; } public static hasChord(keybinding: number): boolean { diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 0984c895bc0..5faf82b128f 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -66,13 +66,11 @@ export class KeybindingResolver { private _defaultBoundCommands: IBoundCommands; private _map: ICommandMap; private _chords: IChordsMap; - private _lookupMap: { - [commandId: string]: NormalizedKeybindingItem[]; - }; - private _lookupMapUnreachable: { - // The value contains the keybinding or first part of a chord - [commandId: string]: number[]; - }; + private _lookupMap: Map; + /** + * The value contains the keybinding or first part of a chord + */ + private _lookupMapUnreachable: Map; private _shouldWarnOnConflict: boolean; constructor(defaultKeybindings: IKeybindingItem[], overrides: IKeybindingItem[], shouldWarnOnConflict: boolean = true) { @@ -87,8 +85,8 @@ export class KeybindingResolver { } this._map = Object.create(null); - this._lookupMap = Object.create(null); - this._lookupMapUnreachable = Object.create(null); + this._lookupMap = new Map(); + this._lookupMapUnreachable = new Map(); this._chords = Object.create(null); let allKeybindings = KeybindingResolver.combine(defaultKeybindings, overrides); @@ -194,8 +192,7 @@ export class KeybindingResolver { if (this._shouldWarnOnConflict && item.isDefault) { console.warn('Conflict detected, command `' + conflict.commandId + '` cannot be triggered by ' + KeybindingLabels.toUserSettingsLabel(keypress) + ' due to ' + item.command); } - this._lookupMapUnreachable[conflict.commandId] = this._lookupMapUnreachable[conflict.commandId] || []; - this._lookupMapUnreachable[conflict.commandId].push(conflict.keybinding); + KeybindingResolver._push(this._lookupMapUnreachable, conflict.commandId, conflict.keybinding); } } @@ -237,12 +234,40 @@ export class KeybindingResolver { return true; } + private static _push(map: Map, key: string, value: number): void { + let oldRawEntries = map.get(key); + if (typeof oldRawEntries === 'undefined') { + let entries = new Uint32Array(1); + entries[0] = value; + map.set(key, entries.buffer); + } else { + let oldEntries = new Uint32Array(oldRawEntries); + let newEntries = new Uint32Array(oldEntries.length + 1); + newEntries.set(oldEntries, 0); + newEntries[newEntries.length - 1] = value; + map.set(key, newEntries.buffer); + } + } + + private static _read(map: Map, key: string): number[] { + let arrBuff = map.get(key); + if (typeof arrBuff === 'undefined') { + return null; + } + + let arr = new Uint32Array(arrBuff); + let result: number[] = []; + for (let i = 0, len = arr.length; i < len; i++) { + result[i] = arr[i]; + } + return result; + } + private _addToLookupMap(item: NormalizedKeybindingItem): void { if (!item.command) { return; } - this._lookupMap[item.command] = this._lookupMap[item.command] || []; - this._lookupMap[item.command].push(item); + KeybindingResolver._push(this._lookupMap, item.command, item.keybinding); } public getDefaultBoundCommands(): IBoundCommands { @@ -267,14 +292,12 @@ export class KeybindingResolver { } public lookupKeybinding(commandId: string): Keybinding[] { - let rawPossibleTriggers = this._lookupMap[commandId]; - if (!rawPossibleTriggers) { + let possibleTriggers = KeybindingResolver._read(this._lookupMap, commandId); + if (!possibleTriggers) { return []; } - let possibleTriggers = rawPossibleTriggers.map(possibleTrigger => possibleTrigger.keybinding); - - let remove = this._lookupMapUnreachable[commandId]; + let remove = KeybindingResolver._read(this._lookupMapUnreachable, commandId); if (remove) { possibleTriggers = possibleTriggers.filter((possibleTrigger) => { return remove.indexOf(possibleTrigger) === -1; diff --git a/src/vs/platform/keybinding/test/common/keybindingService.test.ts b/src/vs/platform/keybinding/test/common/keybindingService.test.ts index 5b7cd0f0d3b..2195933bfa5 100644 --- a/src/vs/platform/keybinding/test/common/keybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingService.test.ts @@ -493,7 +493,7 @@ suite('Keybinding Service', () => { let lookupResult = resolver.lookupKeybinding(commandId); assert.equal(lookupResult.length, expectedKeys.length, 'Length mismatch @ commandId ' + commandId + '; GOT: ' + JSON.stringify(lookupResult, null, '\t')); for (let i = 0, len = lookupResult.length; i < len; i++) { - assert.equal(lookupResult[i].value, expectedKeys[i]); + assert.equal(lookupResult[i].value, expectedKeys[i], 'value mismatch @ commandId ' + commandId); } }; -- GitLab