{"ScriptPreparationCode":"function addMutation(idMutations, typeMutations, action, mutation) {\r\n const {\r\n block\r\n } = mutation;\r\n const mutations = idMutations[block.id] || new BlockMutations();\r\n idMutations[block.id] = mutations;\r\n mutations.add(mutation);\r\n\r\n const mutations2 = typeMutations[block.type] || new BlockMutations();\r\n typeMutations[block.type] = mutations2;\r\n mutations2.add(mutation);\r\n}\r\n\r\nBlockMutations = class BlockMutations {\r\n constructor(useFilter) {\r\n this.useFilter = useFilter;\r\n this.mutations = [];\r\n this.idMutations = {};\r\n this.typeMutations = {};\r\n this.actionMutations = [];\r\n this.keyedMutations = {};\r\n this.filters = [\r\n mutation =\u003E mutation.block.type,\r\n ];\r\n }\r\n\r\n add(mutation) {\r\n if (!this.actionMutations[mutation.action]) {\r\n this.actionMutations[mutation.action] = [];\r\n }\r\n this.actionMutations[mutation.action].push(mutation);\r\n }\r\n\r\n finalize() {\r\n const {\r\n removed,\r\n added,\r\n updated\r\n } = this.actionMutations;\r\n this.finalizeOne(removed);\r\n this.finalizeOne(added);\r\n this.finalizeOne(updated);\r\n }\r\n\r\n finalizeOne(mutations) {\r\n const keyedMutations = this.keyedMutations;\r\n const filters = this.filters;\r\n\r\n for (const mutation of mutations) {\r\n this.mutations.push(mutation);\r\n\r\n const {\r\n block\r\n } = mutation;\r\n const ims = this.idMutations[block.id] || (this.idMutations[block.id] = []);\r\n ims.push(mutation);\r\n\r\n if (this.useFilter) {\r\n for (const filter of filters) {\r\n const key = filter(mutation);\r\n if (key) {\r\n const mutations = keyedMutations[key] || (keyedMutations[key] = []);\r\n mutations.push(mutation);\r\n }\r\n }\r\n } else {\r\n const tms = this.typeMutations[block.id] || (this.typeMutations[block.type] = []);\r\n tms.push(mutation);\r\n }\r\n }\r\n }\r\n\r\n dispatch(listeners) {\r\n this.finalize();\r\n for (const {\r\n callback,\r\n condition\r\n } of listeners) {\r\n if (condition) {\r\n const {\r\n type,\r\n id\r\n } = condition;\r\n const mutations = type ? (this.useFilter ? this.keyedMutations[type] : this.typeMutations[type]) : id ? this.idMutations[id] : null;\r\n if (mutations) {\r\n callback(mutations);\r\n }\r\n } else {\r\n callback(this.mutations);\r\n }\r\n }\r\n }\r\n}\r\n\r\nclass BlockMutationsIterator {\r\n constructor(mutations, firstIndex, indices) {\r\n this.mutations = mutations;\r\n this.firstIndex = firstIndex;\r\n this.indices = indices;\r\n this.cursor = -2;\r\n }\r\n\r\n hasNext() {\r\n return this.cursor === -2 || this.cursor \u003E= 0;\r\n }\r\n\r\n next() {\r\n const cursor = this.cursor === -2 ? this.firstIndex : this.cursor;\r\n if (cursor \u003C 0) {\r\n return;\r\n }\r\n const index = this.indices[cursor];\r\n this.cursor = this.indices[cursor \u002B 1];\r\n return this.mutations[index];\r\n }\r\n}\r\n\r\nBlockMutations2 = class BlockMutations2 {\r\n constructor(x) {\r\n this.mutations = [];\r\n this.actionMutationMap = {};\r\n this.idMutationMap = {};\r\n this.typeMutationMap = {};\r\n }\r\n\r\n add(mutation) {\r\n const {\r\n action,\r\n block\r\n } = mutation;\r\n const {\r\n actionMutationMap,\r\n idMutationMap,\r\n typeMutationMap\r\n } = this;\r\n\r\n this.mutations.push(mutation);\r\n if (!actionMutationMap[action]) {\r\n actionMutationMap[action] = [];\r\n }\r\n actionMutationMap[action].push(mutation);\r\n\r\n if (!idMutationMap[block.id]) {\r\n idMutationMap[block.id] = [];\r\n }\r\n idMutationMap[block.id].push(mutation);\r\n\r\n if (!typeMutationMap[block.type]) {\r\n typeMutationMap[block.type] = [];\r\n }\r\n typeMutationMap[block.type].push(mutation);\r\n }\r\n\r\n finalize() {}\r\n\r\n dispatch(listeners) {\r\n const {\r\n typeMutationMap,\r\n idMutationMap,\r\n actionMutationMap\r\n } = this;\r\n for (const {\r\n callback,\r\n condition\r\n } of listeners) {\r\n if (condition) {\r\n const {\r\n type,\r\n id\r\n } = condition;\r\n if (type) {\r\n if (typeMutationMap[type]) {\r\n callback(typeMutationMap[type]);\r\n }\r\n continue;\r\n }\r\n if (id) {\r\n if (idMutationMap[id]) {\r\n callback(idMutationMap[id]);\r\n }\r\n continue;\r\n }\r\n } else {\r\n callback(actionMutationMap);\r\n }\r\n }\r\n }\r\n}\r\n\r\nconst DEFAULT_ID = 0;\r\nconst ACTION_TYPES = {\r\n added: 0,\r\n removed: 1,\r\n updated: 2,\r\n moved: 3,\r\n};\r\n\r\nfunction updateIndex(index, offset, lasts, keys, firsts, key, action) {\r\n const lastIndex = lasts[offset];\r\n const lastActionIndex = lasts[offset \u002B action];\r\n\r\n const keyOffset = keys.length;\r\n if (lastIndex \u003E= 0) {\r\n keys[lastIndex] = keyOffset \u002B 1;\r\n keys[lastActionIndex] = keyOffset \u002B 2;\r\n }\r\n\r\n lasts[offset] = keyOffset \u002B 1;\r\n lasts[offset \u002B action] = keyOffset \u002B 2;\r\n keys[keyOffset] = index;\r\n keys[keyOffset \u002B 1] = -1;\r\n keys[keyOffset \u002B 2] = -1;\r\n\r\n if (firsts[offset] === undefined) {\r\n firsts[offset] = keyOffset;\r\n }\r\n\r\n if (firsts[offset \u002B action] === undefined) {\r\n firsts[offset \u002B action] = keyOffset;\r\n }\r\n}\r\n\r\nBlockMutations3 = class BlockMutations3 {\r\n constructor() {\r\n this.mutations = [];\r\n this.idOffsetMap = {};\r\n this.typeOffsetMap = {};\r\n // self, action1, action2, action3, ...\r\n this.idFirsts = [];\r\n // self, action1, action2, action3, ...\r\n this.typeFirsts = [];\r\n // self, action1, action2, action3, ...\r\n this.idLasts = [];\r\n // self, action1, action2, action3, ...\r\n this.typeLasts = [];\r\n // mutation index, same id (without action) next, same action next, \r\n this.ids = [];\r\n // mutation index, same type (without action) next, same action next\r\n this.types = [];\r\n }\r\n\r\n add(mutation) {\r\n const {\r\n block,\r\n action\r\n } = mutation;\r\n const {\r\n id,\r\n type\r\n } = block;\r\n const index = this.mutations.push(mutation) - 1;\r\n const actionIndex = ACTION_TYPES[action] \u002B 1;\r\n const idOffset = this.idOffsetMap[id] || (this.idOffsetMap[id] = this.idFirsts.length);\r\n const typeOffset = this.typeOffsetMap[type] || (this.typeOffsetMap[type] = this.typeFirsts.length);\r\n\r\n updateIndex(index, idOffset, this.idLasts, this.ids, this.idFirsts, block.id, actionIndex);\r\n updateIndex(index, typeOffset, this.typeLasts, this.types, this.typeFirsts, block.type, actionIndex);\r\n }\r\n\r\n dispatch(listeners) {\r\n const {\r\n mutations,\r\n typeOffsetMap,\r\n idOffsetMap,\r\n types,\r\n ids\r\n } = this;\r\n for (const {\r\n callback,\r\n condition\r\n } of listeners) {\r\n if (condition) {\r\n const {\r\n type,\r\n id\r\n } = condition;\r\n if (type) {\r\n if (typeOffsetMap[type]) {\r\n callback(this);\r\n }\r\n continue;\r\n }\r\n if (id) {\r\n if (idOffsetMap[id]) {\r\n callback(this);\r\n }\r\n continue;\r\n }\r\n } else {\r\n callback(this);\r\n }\r\n }\r\n }\r\n}\r\n\r\nconst Subject = rxjs.Subject;\r\nconst cb = () =\u003E {};\r\n\r\nclass BlockModel {\r\n constructor(id, type) {\r\n this.id = id;\r\n this.type = type;\r\n this.update$ = new Subject();\r\n this.attach$ = new Subject();\r\n this.detach$ = new Subject();\r\n this.structUpdate$ = new Subject();\r\n this.destroy$ = new Subject();\r\n\r\n this.update$.subscribe(cb);\r\n this.attach$.subscribe(cb);\r\n this.detach$.subscribe(cb);\r\n this.structUpdate$.subscribe(cb);\r\n this.destroy$.subscribe(cb);\r\n }\r\n}\r\n\r\nblockManager = new class BlockModelManager {\r\n constructor() {\r\n this.blockModelCreate$ = new Subject();\r\n this.blockModelUpdate$ = new Subject();\r\n this.blockModelAttached$ = new Subject();\r\n this.blockModelDetached$ = new Subject();\r\n this.blockModelDestroy$ = new Subject();\r\n this.blockModelStructUpdate$ = new Subject();\r\n\r\n this.blockModelCreate$.subscribe(cb);\r\n this.blockModelUpdate$.subscribe(cb);\r\n this.blockModelAttached$.subscribe(cb);\r\n this.blockModelDetached$.subscribe(cb);\r\n this.blockModelDestroy$.subscribe(cb);\r\n this.blockModelStructUpdate$.subscribe(cb);\r\n }\r\n\r\n run(blocks) {\r\n blocks.forEach(({\r\n block,\r\n action\r\n }) =\u003E {\r\n this.process(block, action);\r\n });\r\n }\r\n\r\n process(block, action) {\r\n switch (action) {\r\n case \u0027added\u0027:\r\n this.blockModelCreate$.next({\r\n block\r\n });\r\n block.attach$.next({});\r\n this.blockModelAttached$.next({\r\n block\r\n });\r\n block.structUpdate$.next({\r\n block\r\n });\r\n this.blockModelStructUpdate$.next({\r\n block\r\n });\r\n this.blockModelUpdate$.next({\r\n block\r\n });\r\n break;\r\n case \u0027removed\u0027:\r\n block.detach$.next({});\r\n this.blockModelDetached$.next({\r\n block\r\n });\r\n block.destroy$.next({});\r\n this.blockModelDestroy$.next({\r\n block\r\n });\r\n block.structUpdate$.next({\r\n block\r\n });\r\n this.blockModelStructUpdate$.next({\r\n block\r\n });\r\n this.blockModelUpdate$.next({\r\n block\r\n });\r\n break;\r\n case \u0027updated\u0027:\r\n block.update$.next({});\r\n this.blockModelUpdate$.next({\r\n block\r\n });\r\n break;\r\n // case \u0027moved\u0027:\r\n // block.detach$.next({});\r\n // this.blockModelDetached$.next({ block });\r\n // block.destroy$.next({});\r\n // this.blockModelDestroy$.next({ block });\r\n // this.blockModelCreate$.next({ block });\r\n // block.attach$.next({});\r\n // this.blockModelAttached$.next({ block });\r\n // block.structUpdate$.next({ block });\r\n // this.blockModelStructUpdate$.next({ block });\r\n // this.blockModelUpdate$.next({ block });\r\n // break;\r\n }\r\n }\r\n}\r\n\r\nfunction range(start, end) {\r\n const xs = [];\r\n for (let i = start; i \u003C end; \u002B\u002Bi) {\r\n xs[i] = start \u002B i;\r\n }\r\n return xs;\r\n}\r\n\r\nconst counts = [20000];\r\nconst actions = [\u0027added\u0027, \u0027removed\u0027, \u0027updated\u0027];\r\nconst types = range(0, 52).map(i =\u003E String.fromCharCode(\u0027A\u0027.charCodeAt(0) \u002B i));\r\nconst testIds = counts.map(count =\u003E range(0, count));\r\nconst testBlocks = testIds.map(is =\u003E is.map(id =\u003E ({\r\n id,\r\n type: types[id % types.length]\r\n})));\r\n\r\nconst testActions = testIds.map(is =\u003E is.map(i =\u003E actions[i % actions.length]));\r\nconst testListeners = testBlocks.map(blocks =\u003E [...blocks, ...blocks].map((block, i) =\u003E {\r\n return {\r\n callback: () =\u003E {},\r\n condition: Math.random() \u003C 0.5 ? null : {\r\n type: Math.random() \u003C 0.5 ? null : types[i % types.length],\r\n id: Math.random() \u003C 0.5 ? null : block.id,\r\n },\r\n };\r\n}));\r\nconst testSubjects = testListeners.map(listeners =\u003E {\r\n const subject = new Subject();\r\n listeners.forEach(({\r\n callback\r\n }) =\u003E {\r\n subject.subscribe(callback);\r\n });\r\n return subject;\r\n});\r\n\r\nconst testBlockModels = testIds.map((is, i) =\u003E is.map((id, j) =\u003E {\r\n const block = new BlockModel(id, types[id % types.length]);\r\n return {\r\n block,\r\n action: testActions[i][j],\r\n }\r\n}));\r\n\r\ntestForAdd = (index, mutations) =\u003E {\r\n const blocks = testBlocks[index];\r\n const actions = testActions[index];\r\n blocks.forEach((block, i) =\u003E {\r\n mutations.add({\r\n action: actions[i],\r\n block\r\n });\r\n });\r\n};\r\n\r\ntestForDispatch = (index, mutations) =\u003E {\r\n const listeners = testListeners[index];\r\n mutations.listeners = listeners;\r\n mutations.dispatch(listeners);\r\n};\r\n\r\ntestForSubjects = (index, mutations) =\u003E {\r\n const subject = testSubjects[index];\r\n mutations.finalize();\r\n subject.next(mutations);\r\n};\r\n\r\ntestForBlockModelSubject = index =\u003E {\r\n const blocks = testBlockModels[index];\r\n blockManager.run(blocks);\r\n};","TestCases":[{"Name":"bm1","Code":"mutations = new BlockMutations2();\r\ntestForAdd(0, mutations);\r\ntestForDispatch(0, mutations);","IsDeferred":false},{"Name":"bm2","Code":"mutations = new BlockMutations(true);\r\ntestForAdd(0, mutations);\r\ntestForDispatch(0, mutations);","IsDeferred":false},{"Name":"bm3","Code":"mutations = new BlockMutations(false);\r\ntestForAdd(0, mutations);\r\ntestForDispatch(0, mutations);","IsDeferred":false},{"Name":"bm4","Code":"testForBlockModelSubject(0);","IsDeferred":false},{"Name":"bm5","Code":"mutations = new BlockMutations(true);\r\ntestForAdd(0, mutations);\r\ntestForSubjects(0, mutations);","IsDeferred":false},{"Name":"bm6","Code":"mutations = new BlockMutations2();\r\ntestForAdd(0, mutations);\r\ntestForSubjects(0, mutations);","IsDeferred":false},{"Name":"bm7","Code":"mutations = new BlockMutations(false);\r\ntestForAdd(0, mutations);\r\ntestForSubjects(0, mutations);","IsDeferred":false}]}