use a linked list for filter runs. (#5206)

* Changed the filterrunprefixes to use LinkedList

* Testing for Linked List

* Finishing touches to LinkedList

* Minor corrections to link-list coding style

* Corrected for sneaky bug in linkedList
This commit is contained in:
Cameron Fischer
2020-12-06 08:54:57 +00:00
committed by GitHub
parent c4dcf510ef
commit a857b4ab9a
11 changed files with 268 additions and 18 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ Export our filter prefix function
*/
exports.all = function(operationSubFunction) {
return function(results,source,widget) {
Array.prototype.push.apply(results,operationSubFunction(source,widget));
results.push.apply(results, operationSubFunction(source,widget));
};
};
+3 -3
View File
@@ -19,9 +19,9 @@ Export our filter prefix function
exports.and = function(operationSubFunction,options) {
return function(results,source,widget) {
// This replaces all the elements of the array, but keeps the actual array so that references to it are preserved
source = options.wiki.makeTiddlerIterator(results);
results.splice(0,results.length);
$tw.utils.pushTop(results,operationSubFunction(source,widget));
source = options.wiki.makeTiddlerIterator(results.toArray());
results.clear();
results.pushTop(operationSubFunction(source,widget));
};
};
+1 -1
View File
@@ -19,7 +19,7 @@ exports.else = function(operationSubFunction) {
return function(results,source,widget) {
if(results.length === 0) {
// Main result so far is empty
$tw.utils.pushTop(results,operationSubFunction(source,widget));
results.pushTop(operationSubFunction(source,widget));
}
};
};
+1 -1
View File
@@ -18,7 +18,7 @@ Export our filter prefix function
*/
exports.except = function(operationSubFunction) {
return function(results,source,widget) {
$tw.utils.removeArrayEntries(results,operationSubFunction(source,widget));
results.remove(operationSubFunction(source,widget));
};
};
+2 -2
View File
@@ -17,13 +17,13 @@ exports.filter = function(operationSubFunction,options) {
return function(results,source,widget) {
if(results.length > 0) {
var resultsToRemove = [];
$tw.utils.each(results,function(result) {
results.each(function(result) {
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([result]),widget);
if(filtered.length === 0) {
resultsToRemove.push(result);
}
});
$tw.utils.removeArrayEntries(results,resultsToRemove);
results.remove(resultsToRemove);
}
}
};
@@ -17,7 +17,8 @@ exports.intersection = function(operationSubFunction) {
return function(results,source,widget) {
if(results.length !== 0) {
var secondRunResults = operationSubFunction(source,widget);
var firstRunResults = results.splice(0);
var firstRunResults = results.toArray();
results.clear();
$tw.utils.each(firstRunResults,function(title) {
if(secondRunResults.indexOf(title) !== -1) {
results.push(title);
@@ -27,4 +28,4 @@ exports.intersection = function(operationSubFunction) {
};
};
})();
})();
+1 -1
View File
@@ -17,7 +17,7 @@ Export our filter prefix function
*/
exports.or = function(operationSubFunction) {
return function(results,source,widget) {
$tw.utils.pushTop(results,operationSubFunction(source,widget));
results.pushTop(operationSubFunction(source,widget));
};
};
+6 -5
View File
@@ -16,9 +16,9 @@ exports.reduce = function(operationSubFunction,options) {
return function(results,source,widget) {
if(results.length > 0) {
var accumulator = "";
for(var index=0; index<results.length; index++) {
var title = results[index],
list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
var index = 0;
results.each(function(title) {
var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
getVariable: function(name) {
switch(name) {
case "currentTiddler":
@@ -39,8 +39,9 @@ exports.reduce = function(operationSubFunction,options) {
if(list.length > 0) {
accumulator = "" + list[0];
}
}
results.splice(0,results.length);
++index;
});
results.clear();
results.push(accumulator);
}
}
+2 -2
View File
@@ -311,11 +311,11 @@ exports.compileFilter = function(filterString) {
} else if(typeof source === "object") { // Array or hashmap
source = self.makeTiddlerIterator(source);
}
var results = [];
var results = new $tw.utils.LinkedList();
$tw.utils.each(operationFunctions,function(operationFunction) {
operationFunction(results,source,widget);
});
return results;
return results.toArray();
});
};
+118
View File
@@ -0,0 +1,118 @@
/*\
module-type: utils
title: $:/core/modules/utils/linkedlist.js
type: application/javascript
This is a doubly-linked indexed list intended for manipulation, particularly
pushTop, which it does with significantly better performance than an array.
\*/
(function(){
function LinkedList() {
this.clear();
};
LinkedList.prototype.clear = function() {
this.index = Object.create(null);
// LinkedList performs the duty of both the head and tail node
this.next = this;
this.prev = this;
this.length = 0;
};
LinkedList.prototype.remove = function(value) {
if($tw.utils.isArray(value)) {
for(var t=0; t<value.length; t++) {
this._removeOne(value[t]);
}
} else {
this._removeOne(value);
}
};
LinkedList.prototype._removeOne = function(value) {
var node = this.index[value];
if(node) {
node.prev.next = node.next;
node.next.prev = node.prev;
this.length -= 1;
// Point index to the next instance of the same value, maybe nothing.
this.index[value] = node.copy;
}
return node;
};
LinkedList.prototype._linkToEnd = function(node) {
// Sticks the given node onto the end of the list.
this.prev.next = node;
node.prev = this.prev;
this.prev = node;
node.next = this;
this.length += 1;
};
LinkedList.prototype.push = function(/* values */) {
for(var i = 0; i < arguments.length; i++) {
var value = arguments[i];
var node = {value: value};
var preexistingNode = this.index[value];
this._linkToEnd(node);
if(preexistingNode) {
// We want to keep pointing to the first instance, but we want
// to have that instance (or chain of instances) point to the
// new one.
while (preexistingNode.copy) {
preexistingNode = preexistingNode.copy;
}
preexistingNode.copy = node;
} else {
this.index[value] = node;
}
}
};
LinkedList.prototype.pushTop = function(value) {
if($tw.utils.isArray(value)) {
for(var t=0; t<value.length; t++) {
this._removeOne(value[t]);
}
this.push.apply(this, value);
} else {
var node = this._removeOne(value);
if(!node) {
node = {value: value};
this.index[value] = node;
} else {
// Put this node at the end of the copy chain.
var preexistingNode = node;
while(preexistingNode.copy) {
preexistingNode = preexistingNode.copy;
}
// The order of these three statements is important,
// because sometimes preexistingNode == node.
preexistingNode.copy = node;
this.index[value] = node.copy;
node.copy = undefined;
}
this._linkToEnd(node);
}
};
LinkedList.prototype.each = function(callback) {
for(var ptr = this.next; ptr !== this; ptr = ptr.next) {
callback(ptr.value);
}
};
LinkedList.prototype.toArray = function() {
var output = [];
for(var ptr = this.next; ptr !== this; ptr = ptr.next) {
output.push(ptr.value);
}
return output;
};
exports.LinkedList = LinkedList;
})();