From 27264bb9d93e24c8c1c8a8eae54b0828d1bd57f4 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 20 Nov 2014 23:01:53 +0400 Subject: [PATCH] fix infinite loop when inserting more than 0xF000 lines at once --- lib/ace/document.js | 36 ++++++++++++++++++++++++------------ lib/ace/document_test.js | 19 +++++++++++++++++++ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index e92aa969..9207b3f5 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -575,14 +575,14 @@ var Document = function(textOrLines) { return; if (isInsert && delta.lines.length > 0xF000) - this.$splitAndapplyLargeDelta(delta); + this.$splitAndapplyLargeDelta(delta, 0xF000); // Apply. applyDelta(this.$lines, delta, doNotValidate); this._signal("change", delta); }; - this.$splitAndapplyLargeDelta = function(delta) { + this.$splitAndapplyLargeDelta = function(delta, MAX) { // Split large insert deltas. This is necessary because: // 1. We need to support splicing delta lines into the document via $lines.splice.apply(...) // 2. fn.apply() doesn't work for a large number of params. The smallest threshold is on safari 0xFFFF. @@ -592,18 +592,30 @@ var Document = function(textOrLines) { // delta handling is too slow. If we make delete delta handling faster we can split all large deltas // as shown in https://gist.github.com/aldendaniels/8367109#file-document-snippet-js // If we do this, update validateDelta() to limit the number of lines in a delete delta. - while (delta.lines.length > 0xF000) { - // Get split deltas. - var lines = delta.lines.splice(0, 0xF000); - lines.push(""); - var start = delta.start; + var lines = delta.lines; + var l = lines.length; + var row = delta.start.row; + var column = delta.start.column; + var from = 0, to = 0; + do { + from = to; + to += MAX - 1; + var chunk = lines.slice(from, to); + if (to > l) { + // Update remaining delta. + delta.lines = chunk; + delta.start.row = row + from; + delta.start.column = column; + break; + } + chunk.push(""); this.applyDelta({ - start: this.pos(start.row, start.column), - end: this.pos(start.row += 0xF000, start.column = 0), // Updates remaining delta. + start: this.pos(row + from, column), + end: this.pos(row + to, column = 0), action: delta.action, - lines: lines + lines: chunk }, true); - } + } while(true); }; /** @@ -615,7 +627,7 @@ var Document = function(textOrLines) { this.applyDelta({ start: range.start, end: range.end, - action: delta.action == "insert" ? "remove" : "insert", + action: (delta.action == "insert" ? "remove" : "insert"), lines: delta.lines.slice() }); }; diff --git a/lib/ace/document_test.js b/lib/ace/document_test.js index ee27f00b..d7cc7ccb 100644 --- a/lib/ace/document_test.js +++ b/lib/ace/document_test.js @@ -296,6 +296,25 @@ module.exports = { "test: empty document has to contain one line": function() { var doc = new Document(""); assert.equal(doc.$lines.length, 1); + }, + + "test: inserting huge delta": function() { + var doc = new Document(""); + var val = ""; + var MAX = 0xF000; + for (var i = 0; i < 10 * MAX; i++) { + val += i + "\n" + } + doc.setValue(val); + assert.equal(doc.getValue(), val); + + for (var i = 3 * MAX + 2; i >= 3 * MAX - 2; i--) { + val = doc.getLines(0, i).join("\n"); + doc.setValue("\nab"); + assert.equal(doc.getValue(), "\nab"); + doc.insert({row: 1, column: 1}, val); + assert.equal(doc.getValue(), "\na" + val + "b"); + } } };