From 2483c0223de954d720a9a6c4682d14a849771366 Mon Sep 17 00:00:00 2001 From: alex-dunn Date: Wed, 11 Aug 2021 12:29:23 +0100 Subject: [PATCH] closes #150, Resolve stack overflow for SortedSet reduce Replace implementations of SortedSet.reduce and reduceRight. Current implementations are recursive and so will inevitably hit a stack overflow exception at some point. New implementations use the Iterator to traverse the tree. --- sorted-set.js | 88 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/sorted-set.js b/sorted-set.js index a4a570d..529d295 100644 --- a/sorted-set.js +++ b/sorted-set.js @@ -493,15 +493,31 @@ SortedSet.prototype.splayIndex = function (index) { }; SortedSet.prototype.reduce = function (callback, basis, thisp) { - if (this.root) { - basis = this.root.reduce(callback, basis, 0, thisp, this); + const iter = new this.Iterator(this, undefined, undefined, false); + let index = 0; + while (true) + { + const result = iter.next(); + if (result.done) break; + index++; + const node = result.value; + //Depth always 0 as splay brings current node to the root. + basis = callback.call(thisp, basis, node, index, this, node, 0); } return basis; }; SortedSet.prototype.reduceRight = function (callback, basis, thisp) { - if (this.root) { - basis = this.root.reduceRight(callback, basis, this.length - 1, thisp, this); + const iter = new this.Iterator(this, undefined, undefined, true); + let index = this.length - 1; + while (true) + { + const result = iter.next(); + if (result.done) break; + index--; + const node = result.value; + //Depth always 0 as splay brings current node to the root. + basis = callback.call(thisp, basis, node, index, this, node, 0); } return basis; }; @@ -732,26 +748,46 @@ Node.prototype.log = function (charmap, logNode, log, logAbove) { ); }; -function Iterator(set, start, end) { +function Iterator(set, start, end, reverse) { this.set = set; this.prev = null; - this.end = end; - if (start) { - var next = this.set.findLeastGreaterThanOrEqual(start); - if (next) { - this.set.splay(next.value); - this.prev = next.getPrevious(); + if (reverse) + { + this.start = start; + if (end) { + var next = this.set.findGreatestLessThanOrEqual(end); + if (next) { + this.set.splay(next.value); + this.prev = next.getNext(); + } + } + this.method = this.nextReverse; + } + else + { + this.end = end; + if (start) { + var next = this.set.findLeastGreaterThanOrEqual(start); + if (next) { + this.set.splay(next.value); + this.prev = next.getPrevious(); + } } + this.method = this.nextForward; } } Iterator.prototype.__iterationObject = null; Object.defineProperty(Iterator.prototype,"_iterationObject", { get: function() { - return this.__iterationObject || (this.__iterationObject = { done: false, value:null}); + return this.__iterationObject || (this.__iterationObject = { done: false, value:null }); } }); Iterator.prototype.next = function () { + return this.method(); +}; + +Iterator.prototype.nextForward = function () { var next; if (this.prev) { next = this.set.findLeastGreaterThan(this.prev.value); @@ -778,3 +814,31 @@ Iterator.prototype.next = function () { } return this._iterationObject; }; + +Iterator.prototype.nextReverse = function () { + var next; + if (this.prev) { + next = this.set.findGreatestLessThan(this.prev.value); + } else { + next = this.set.findGreatest(); + } + if (!next) { + this._iterationObject.done = true; + this._iterationObject.value = void 0; + } + else { + if ( + this.start !== undefined && + this.set.contentCompare(next.value, this.start) <= 0 + ) { + this._iterationObject.done = true; + this._iterationObject.value = void 0; + } + else { + this.prev = next; + this._iterationObject.value = next.value; + } + + } + return this._iterationObject; +};