From bffd130b92b60e7b6f744fa6e0635d74f660d08d Mon Sep 17 00:00:00 2001
From: Kevin Jahns <kevin.jahns@rwth-aachen.de>
Date: Thu, 21 Apr 2016 18:04:46 +0200
Subject: [PATCH] fixed first two random cases, (gc seems still to be an issue
 ..)

---
 src/Database.js    | 25 +------------
 src/Struct.js      | 35 +++++++++++++++----
 src/Transaction.js | 87 +++++++++++++++++++++++++++++++---------------
 3 files changed, 89 insertions(+), 58 deletions(-)

diff --git a/src/Database.js b/src/Database.js
index d8a37ea8..964b7efa 100644
--- a/src/Database.js
+++ b/src/Database.js
@@ -367,30 +367,7 @@ module.exports = function (Y /* :any */) {
             yield* this.store.operationAdded(this, op)
 
             // if insertion, try to combine with left
-            if (op.left != null &&
-                op.content != null &&
-                op.left[0] === op.id[0] &&
-                Y.utils.compareIds(op.left, op.origin)
-            ) {
-              var left = yield* this.getInsertion(op.left)
-              if (left.content != null &&
-                  left.id[1] + left.content.length === op.id[1] &&
-                  left.originOf.length === 1 &&
-                  !left.gc && !left.deleted &&
-                  !op.gc && !op.deleted
-              ) {
-                // combine!
-                if (op.originOf != null){
-                  left.originOf = op.originOf
-                } else {
-                  delete left.originOf
-                }
-                left.content = left.content.concat(op.content)
-                left.right = op.right
-                yield* this.os.delete(op.id)
-                yield* this.setOperation(left)
-              }
-            }
+            yield* this.tryCombineWithLeft(op)
           }
         }
       }
diff --git a/src/Struct.js b/src/Struct.js
index 21a53c88..82bed80a 100644
--- a/src/Struct.js
+++ b/src/Struct.js
@@ -124,10 +124,13 @@ module.exports = function (Y/* :any */) {
       # case 3: $origin > $o.origin
       #         $this insert_position is to the left of $o (forever!)
       */
-      execute: function *(op) {
+      execute: function * (op) {
         var i // loop counter
-        var distanceToOrigin = i = yield* Struct.Insert.getDistanceToOrigin.call(this, op) // most cases: 0 (starts from 0)
-
+        
+        // during this function some ops may get split into two pieces (e.g. with getInsertionCleanEnd)
+        // We try to merge them later, if possible
+        var tryToRemergeLater = []
+        
         if (op.origin != null) { // TODO: !== instead of !=
           // we save in origin that op originates in it
           // we need that later when we eventually garbage collect origin (see transaction)
@@ -137,7 +140,11 @@ module.exports = function (Y/* :any */) {
           }
           origin.originOf.push(op.id)
           yield* this.setOperation(origin)
+          if (origin.right != null) {
+            tryToRemergeLater.push(origin.right)
+          }
         }
+        var distanceToOrigin = i = yield* Struct.Insert.getDistanceToOrigin.call(this, op) // most cases: 0 (starts from 0)
 
         // now we begin to insert op in the list of insertions..
         var o
@@ -147,14 +154,24 @@ module.exports = function (Y/* :any */) {
         // find o. o is the first conflicting operation
         if (op.left != null) {
           o = yield* this.getInsertionCleanEnd(op.left)
-          o = (o.right == null) ? null : yield* this.getInsertionCleanStart(o.right)
+          if (!Y.utils.compareIds(op.left, op.origin) && o.right != null) {
+            // only if not added previously
+            tryToRemergeLater.push(o.right)
+          }
+          o = (o.right == null) ? null : yield* this.getOperation(o.right)
         } else { // left == null
           parent = yield* this.getOperation(op.parent)
           let startId = op.parentSub ? parent.map[op.parentSub] : parent.start
           start = startId == null ? null : yield* this.getOperation(startId)
           o = start
         }
-
+        
+        // make sure to split op.right if necessary (also add to tryCombineWithLeft)
+        if (op.right != null) {
+          tryToRemergeLater.push(op.right)
+          yield* this.getInsertionCleanStart(op.right)
+        }
+        
         // handle conflicts
         while (true) {
           if (o != null && !Y.utils.compareIds(o.id, op.right)) {
@@ -176,7 +193,7 @@ module.exports = function (Y/* :any */) {
             }
             i++
             if (o.right != null) {
-              o = yield* this.getInsertionCleanStart(o.right)
+              o = yield* this.getInsertion(o.right)
             } else {
               o = null
             }
@@ -249,6 +266,12 @@ module.exports = function (Y/* :any */) {
             yield* this.setOperation(parent)
           }
         }
+        
+        // try to merge original op.left and op.origin
+        for (var i = 0; i < tryToRemergeLater.length; i++) {
+          var m = yield* this.getOperation(tryToRemergeLater[i])
+          yield* this.tryCombineWithLeft(m)
+        }
       }
     },
     List: {
diff --git a/src/Transaction.js b/src/Transaction.js
index e178c710..37ce364e 100644
--- a/src/Transaction.js
+++ b/src/Transaction.js
@@ -421,38 +421,37 @@ module.exports = function (Y/* :any */) {
     */
     * garbageCollectAfterSync () {
       yield* this.os.iterate(this, null, null, function * (op) {
-        var opLength = op.content != null ? op.content.length : 1
         if (op.gc) {
-          this.store.gc1.push(op.id)
-        } else {
-          if (op.parent != null) {
-            var parentDeleted = yield* this.isDeleted(op.parent)
-            if (parentDeleted) {
-              op.gc = true
-              if (!op.deleted) {
-                yield* this.markDeleted(op.id, opLength)
-                op.deleted = true
-                if (op.opContent != null) {
-                  yield* this.deleteOperation(op.opContent)
-                }
-                if (op.requires != null) {
-                  for (var i = 0; i < op.requires.length; i++) {
-                    yield* this.deleteOperation(op.requires[i])
-                  }
+          delete op.gc
+          yield* this.setOperation(op)
+        }
+        if (op.parent != null) {
+          var parentDeleted = yield* this.isDeleted(op.parent)
+          if (parentDeleted) {
+            op.gc = true
+            if (!op.deleted) {
+              yield* this.markDeleted(op.id, op.content != null ? op.content.length : 1)
+              op.deleted = true
+              if (op.opContent != null) {
+                yield* this.deleteOperation(op.opContent)
+              }
+              if (op.requires != null) {
+                for (var i = 0; i < op.requires.length; i++) {
+                  yield* this.deleteOperation(op.requires[i])
                 }
               }
-              yield* this.setOperation(op)
-              this.store.gc1.push(op.id)
-              return
             }
+            yield* this.setOperation(op)
+            this.store.gc1.push(op.id)
+            return
           }
-          if (op.deleted) {
-            var left = null
-            if (op.left != null) {
-              left = yield* this.getInsertion(op.left)
-            }
-            yield* this.store.addToGarbageCollector.call(this, op, left)
+        }
+        if (op.deleted) {
+          var left = null
+          if (op.left != null) {
+            left = yield* this.getInsertion(op.left)
           }
+          yield* this.store.addToGarbageCollector.call(this, op, left)
         }
       })
     }
@@ -715,8 +714,11 @@ module.exports = function (Y/* :any */) {
             var counter = del[1] + del[2]
             while (counter >= del[1]) {
               var o = yield* this.os.findWithUpperBound([del[0], counter - 1])
+              if (o == null) {
+                break
+              }
               var oLen = o.content != null ? o.content.length : 1
-              if (o.id[0] !== del[0] || del[1] < o.id[1] + oLen) {
+              if (o.id[0] !== del[0] || o.id[1] + oLen <= del[1]) {
                 // not in range
                 break
               }
@@ -728,8 +730,8 @@ module.exports = function (Y/* :any */) {
                 // overlaps left
                 o = yield* this.getInsertionCleanStart([del[0], del[1]])
               }
-              yield* this.garbageCollectOperation(o.id)
               counter = o.id[1]
+              yield* this.garbageCollectOperation(o.id)
             }
           }
         } else {
@@ -796,6 +798,35 @@ module.exports = function (Y/* :any */) {
         this.store.y.connector.broadcastOps([op])
       }
     }
+    // if insertion, try to combine with left insertion (if both have content property)
+    * tryCombineWithLeft (op) {
+      if (
+        op != null &&
+        op.left != null &&
+        op.content != null &&
+        op.left[0] === op.id[0] &&
+        Y.utils.compareIds(op.left, op.origin)
+      ) {
+        var left = yield* this.getInsertion(op.left)
+        if (left.content != null &&
+            left.id[1] + left.content.length === op.id[1] &&
+            left.originOf.length === 1 &&
+            !left.gc && !left.deleted &&
+            !op.gc && !op.deleted
+        ) {
+          // combine!
+          if (op.originOf != null){
+            left.originOf = op.originOf
+          } else {
+            delete left.originOf
+          }
+          left.content = left.content.concat(op.content)
+          left.right = op.right
+          yield* this.os.delete(op.id)
+          yield* this.setOperation(left)
+        }
+      }
+    }
     * getInsertion (id) {
       var ins = yield* this.os.findWithUpperBound(id)
       if (ins == null) {
-- 
GitLab