function addMutation(idMutations, typeMutations, action, mutation) {
const {
block
} = mutation;
const mutations = idMutations[block.id] || new BlockMutations();
idMutations[block.id] = mutations;
mutations.add(mutation);
const mutations2 = typeMutations[block.type] || new BlockMutations();
typeMutations[block.type] = mutations2;
mutations2.add(mutation);
}
BlockMutations = class BlockMutations {
constructor(useFilter) {
this.useFilter = useFilter;
this.mutations = [];
this.idMutations = {};
this.typeMutations = {};
this.actionMutations = [];
this.keyedMutations = {};
this.filters = [
mutation => mutation.block.type,
];
}
add(mutation) {
if (!this.actionMutations[mutation.action]) {
this.actionMutations[mutation.action] = [];
}
this.actionMutations[mutation.action].push(mutation);
}
finalize() {
const {
removed,
added,
updated
} = this.actionMutations;
this.finalizeOne(removed);
this.finalizeOne(added);
this.finalizeOne(updated);
}
finalizeOne(mutations) {
const keyedMutations = this.keyedMutations;
const filters = this.filters;
for (const mutation of mutations) {
this.mutations.push(mutation);
const {
block
} = mutation;
const ims = this.idMutations[block.id] || (this.idMutations[block.id] = []);
ims.push(mutation);
if (this.useFilter) {
for (const filter of filters) {
const key = filter(mutation);
if (key) {
const mutations = keyedMutations[key] || (keyedMutations[key] = []);
mutations.push(mutation);
}
}
} else {
const tms = this.typeMutations[block.id] || (this.typeMutations[block.type] = []);
tms.push(mutation);
}
}
}
dispatch(listeners) {
this.finalize();
for (const {
callback,
condition
} of listeners) {
if (condition) {
const {
type,
id
} = condition;
const mutations = type ? (this.useFilter ? this.keyedMutations[type] : this.typeMutations[type]) : id ? this.idMutations[id] : null;
if (mutations) {
callback(mutations);
}
} else {
callback(this.mutations);
}
}
}
}
class BlockMutationsIterator {
constructor(mutations, firstIndex, indices) {
this.mutations = mutations;
this.firstIndex = firstIndex;
this.indices = indices;
this.cursor = -2;
}
hasNext() {
return this.cursor === -2 || this.cursor >= 0;
}
next() {
const cursor = this.cursor === -2 ? this.firstIndex : this.cursor;
if (cursor < 0) {
return;
}
const index = this.indices[cursor];
this.cursor = this.indices[cursor + 1];
return this.mutations[index];
}
}
BlockMutations2 = class BlockMutations2 {
constructor(x) {
this.mutations = [];
this.actionMutationMap = {};
this.idMutationMap = {};
this.typeMutationMap = {};
}
add(mutation) {
const {
action,
block
} = mutation;
const {
actionMutationMap,
idMutationMap,
typeMutationMap
} = this;
this.mutations.push(mutation);
if (!actionMutationMap[action]) {
actionMutationMap[action] = [];
}
actionMutationMap[action].push(mutation);
if (!idMutationMap[block.id]) {
idMutationMap[block.id] = [];
}
idMutationMap[block.id].push(mutation);
if (!typeMutationMap[block.type]) {
typeMutationMap[block.type] = [];
}
typeMutationMap[block.type].push(mutation);
}
finalize() {}
dispatch(listeners) {
const {
typeMutationMap,
idMutationMap,
actionMutationMap
} = this;
for (const {
callback,
condition
} of listeners) {
if (condition) {
const {
type,
id
} = condition;
if (type) {
if (typeMutationMap[type]) {
callback(typeMutationMap[type]);
}
continue;
}
if (id) {
if (idMutationMap[id]) {
callback(idMutationMap[id]);
}
continue;
}
} else {
callback(actionMutationMap);
}
}
}
}
const DEFAULT_ID = 0;
const ACTION_TYPES = {
added: 0,
removed: 1,
updated: 2,
moved: 3,
};
function updateIndex(index, offset, lasts, keys, firsts, key, action) {
const lastIndex = lasts[offset];
const lastActionIndex = lasts[offset + action];
const keyOffset = keys.length;
if (lastIndex >= 0) {
keys[lastIndex] = keyOffset + 1;
keys[lastActionIndex] = keyOffset + 2;
}
lasts[offset] = keyOffset + 1;
lasts[offset + action] = keyOffset + 2;
keys[keyOffset] = index;
keys[keyOffset + 1] = -1;
keys[keyOffset + 2] = -1;
if (firsts[offset] === undefined) {
firsts[offset] = keyOffset;
}
if (firsts[offset + action] === undefined) {
firsts[offset + action] = keyOffset;
}
}
BlockMutations3 = class BlockMutations3 {
constructor() {
this.mutations = [];
this.idOffsetMap = {};
this.typeOffsetMap = {};
this.idFirsts = [];
this.typeFirsts = [];
this.idLasts = [];
this.typeLasts = [];
this.ids = [];
this.types = [];
}
add(mutation) {
const {
block,
action
} = mutation;
const {
id,
type
} = block;
const index = this.mutations.push(mutation) - 1;
const actionIndex = ACTION_TYPES[action] + 1;
const idOffset = this.idOffsetMap[id] || (this.idOffsetMap[id] = this.idFirsts.length);
const typeOffset = this.typeOffsetMap[type] || (this.typeOffsetMap[type] = this.typeFirsts.length);
updateIndex(index, idOffset, this.idLasts, this.ids, this.idFirsts, block.id, actionIndex);
updateIndex(index, typeOffset, this.typeLasts, this.types, this.typeFirsts, block.type, actionIndex);
}
dispatch(listeners) {
const {
mutations,
typeOffsetMap,
idOffsetMap,
types,
ids
} = this;
for (const {
callback,
condition
} of listeners) {
if (condition) {
const {
type,
id
} = condition;
if (type) {
if (typeOffsetMap[type]) {
callback(this);
}
continue;
}
if (id) {
if (idOffsetMap[id]) {
callback(this);
}
continue;
}
} else {
callback(this);
}
}
}
}
const Subject = rxjs.Subject;
const cb = () => {};
class BlockModel {
constructor(id, type) {
this.id = id;
this.type = type;
this.update$ = new Subject();
this.attach$ = new Subject();
this.detach$ = new Subject();
this.structUpdate$ = new Subject();
this.destroy$ = new Subject();
this.update$.subscribe(cb);
this.attach$.subscribe(cb);
this.detach$.subscribe(cb);
this.structUpdate$.subscribe(cb);
this.destroy$.subscribe(cb);
}
}
blockManager = new class BlockModelManager {
constructor() {
this.blockModelCreate$ = new Subject();
this.blockModelUpdate$ = new Subject();
this.blockModelAttached$ = new Subject();
this.blockModelDetached$ = new Subject();
this.blockModelDestroy$ = new Subject();
this.blockModelStructUpdate$ = new Subject();
this.blockModelCreate$.subscribe(cb);
this.blockModelUpdate$.subscribe(cb);
this.blockModelAttached$.subscribe(cb);
this.blockModelDetached$.subscribe(cb);
this.blockModelDestroy$.subscribe(cb);
this.blockModelStructUpdate$.subscribe(cb);
}
run(blocks) {
blocks.forEach(({
block,
action
}) => {
this.process(block, action);
});
}
process(block, action) {
switch (action) {
case 'added':
this.blockModelCreate$.next({
block
});
block.attach$.next({});
this.blockModelAttached$.next({
block
});
block.structUpdate$.next({
block
});
this.blockModelStructUpdate$.next({
block
});
this.blockModelUpdate$.next({
block
});
break;
case 'removed':
block.detach$.next({});
this.blockModelDetached$.next({
block
});
block.destroy$.next({});
this.blockModelDestroy$.next({
block
});
block.structUpdate$.next({
block
});
this.blockModelStructUpdate$.next({
block
});
this.blockModelUpdate$.next({
block
});
break;
case 'updated':
block.update$.next({});
this.blockModelUpdate$.next({
block
});
break;
}
}
}
function range(start, end) {
const xs = [];
for (let i = start; i < end; ++i) {
xs[i] = start + i;
}
return xs;
}
const counts = [20000];
const actions = ['added', 'removed', 'updated'];
const types = range(0, 52).map(i => String.fromCharCode('A'.charCodeAt(0) + i));
const testIds = counts.map(count => range(0, count));
const testBlocks = testIds.map(is => is.map(id => ({
id,
type: types[id % types.length]
})));
const testActions = testIds.map(is => is.map(i => actions[i % actions.length]));
const testListeners = testBlocks.map(blocks => [...blocks, ...blocks].map((block, i) => {
return {
callback: () => {},
condition: Math.random() < 0.5 ? null : {
type: Math.random() < 0.5 ? null : types[i % types.length],
id: Math.random() < 0.5 ? null : block.id,
},
};
}));
const testSubjects = testListeners.map(listeners => {
const subject = new Subject();
listeners.forEach(({
callback
}) => {
subject.subscribe(callback);
});
return subject;
});
const testBlockModels = testIds.map((is, i) => is.map((id, j) => {
const block = new BlockModel(id, types[id % types.length]);
return {
block,
action: testActions[i][j],
}
}));
testForAdd = (index, mutations) => {
const blocks = testBlocks[index];
const actions = testActions[index];
blocks.forEach((block, i) => {
mutations.add({
action: actions[i],
block
});
});
};
testForDispatch = (index, mutations) => {
const listeners = testListeners[index];
mutations.listeners = listeners;
mutations.dispatch(listeners);
};
testForSubjects = (index, mutations) => {
const subject = testSubjects[index];
mutations.finalize();
subject.next(mutations);
};
testForBlockModelSubject = index => {
const blocks = testBlockModels[index];
blockManager.run(blocks);
};