Skip to content

Commit

Permalink
TODO: add commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuyz0112 committed Aug 16, 2020
1 parent 5e9e1a2 commit ef9b2e2
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 11 deletions.
161 changes: 151 additions & 10 deletions src/record/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,88 @@ import {
} from '../types';
import { mirror, isBlocked, isAncestorRemoved } from '../utils';

type DoubleLinkedListNode = {
previous: DoubleLinkedListNode | null;
next: DoubleLinkedListNode | null;
value: NodeInLinkedList;
};
type NodeInLinkedList = Node & {
__ln: DoubleLinkedListNode;
};

function isNodeInLinkedList(n: Node | NodeInLinkedList): n is NodeInLinkedList {
return '__ln' in n;
}
class DoubleLinkedList {
public length = 0;
public head: DoubleLinkedListNode | null = null;

public get(position: number) {
if (position >= this.length) {
throw new Error('Position outside of list range');
}

let current = this.head;
for (let index = 0; index < position; index++) {
current = current?.next || null;
}
return current;
}

public addNode(n: Node) {
const node: DoubleLinkedListNode = {
value: n as NodeInLinkedList,
previous: null,
next: null,
};
(n as NodeInLinkedList).__ln = node;
if (n.previousSibling && isNodeInLinkedList(n.previousSibling)) {
const current = n.previousSibling.__ln.next;
node.next = current;
node.previous = n.previousSibling.__ln;
n.previousSibling.__ln.next = node;
if (current) {
current.previous = node;
}
} else if (n.nextSibling && isNodeInLinkedList(n.nextSibling)) {
const current = n.nextSibling.__ln.previous;
node.previous = current;
node.next = n.nextSibling.__ln;
n.nextSibling.__ln.previous = node;
if (current) {
current.next = node;
}
} else {
if (this.head) {
this.head.previous = node;
}
node.next = this.head;
this.head = node;
}
this.length++;
}

public removeNode(n: NodeInLinkedList) {
const current = n.__ln;
if (!this.head) {
return;
}

if (!current.previous) {
this.head = current.next;
if (this.head) {
this.head.previous = null;
}
} else {
current.previous.next = current.next;
if (current.next) {
current.next.previous = current.previous;
}
}
this.length--;
}
}

const moveKey = (id: number, parentId: number) => `${id}@${parentId}`;
function isINode(n: Node | INode): n is INode {
return '__sn' in n;
Expand Down Expand Up @@ -70,22 +152,25 @@ export default class MutationBuffer {
}

public processMutations = (mutations: mutationRecord[]) => {
mutations.forEach(this.processMutation);
mutations.reverse().forEach(this.processMutation);

/**
* Sometimes child node may be pushed before its newly added
* parent, so we init a queue to store these nodes.
*/
const addQueue: Node[] = [];
const addQueue = new DoubleLinkedList();
const pushAdd = (n: Node) => {
if (!n.parentNode) {
return;
}
const parentId = mirror.getId((n.parentNode as Node) as INode);
const nextId =
let nextId =
n.nextSibling && mirror.getId((n.nextSibling as unknown) as INode);
if (nextId === -1 && isBlocked(n.nextSibling, this.blockClass)) {
nextId = null;
}
if (parentId === -1 || nextId === -1) {
return addQueue.push(n);
return addQueue.addNode(n);
}
this.adds.push({
parentId,
Expand Down Expand Up @@ -119,22 +204,78 @@ export default class MutationBuffer {
}
}

const s = {
get: 0,
shortcut: 0,
};
let candidate: DoubleLinkedListNode | null = null;
while (addQueue.length) {
if (
addQueue.every(
(n) => mirror.getId((n.parentNode as Node) as INode) === -1,
)
) {
let node: DoubleLinkedListNode | null = null;
if (candidate) {
const parentId = mirror.getId(
(candidate.value.parentNode as Node) as INode,
);
let nextId =
candidate.value.nextSibling &&
mirror.getId((candidate.value.nextSibling as unknown) as INode);
if (
nextId === -1 &&
isBlocked(candidate.value.nextSibling, this.blockClass)
) {
nextId = null;
}
if (parentId !== -1 && nextId !== -1) {
node = candidate;
s.shortcut++;
}
}
if (!node) {
for (let index = addQueue.length - 1; index >= 0; index--) {
const _node = addQueue.get(index)!;
const parentId = mirror.getId(
(_node.value.parentNode as Node) as INode,
);
let nextId =
_node.value.nextSibling &&
mirror.getId((_node.value.nextSibling as unknown) as INode);
if (
nextId === -1 &&
isBlocked(_node.value.nextSibling, this.blockClass)
) {
nextId = null;
}
if (parentId !== -1 && nextId !== -1) {
node = _node;
s.get++;
break;
}
}
}
if (!node) {
/**
* If all nodes in queue could not find a serialized parent,
* it may be a bug or corner case. We need to escape the
* dead while loop at once.
*/
console.log('break here', addQueue.length);
for (let index = 0; index < addQueue.length; index++) {
const _node = addQueue.get(index)!;
const parentId = mirror.getId(
(_node.value.parentNode as Node) as INode,
);
const nextId =
_node.value.nextSibling &&
mirror.getId((_node.value.nextSibling as unknown) as INode);
console.log(_node, parentId, nextId, _node.value.nextSibling);
}
break;
}
pushAdd(addQueue.shift()!);
candidate = node.previous;
addQueue.removeNode(node.value);
pushAdd(node.value);
}

console.log('count', s);
this.emit();
};

Expand Down
3 changes: 2 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"arrow-parens": false,
"only-arrow-functions": false,
"max-line-length": false,
"no-empty": false
"no-empty": false,
"max-classes-per-file": false
},
"rulesDirectory": []
}

0 comments on commit ef9b2e2

Please sign in to comment.