Refactors the new, non-adjacent optimizer.
authorJakub Pawlowicz <jakub@goalsmashers.com>
Sun, 23 Feb 2014 16:12:05 +0000 (16:12 +0000)
committerJakub Pawlowicz <jakub@goalsmashers.com>
Sun, 23 Feb 2014 16:24:59 +0000 (16:24 +0000)
* Extracts common parts from simple & complex scenarios.
* Adds performance boost so its speed is comparable with the old one.

lib/selectors/optimizer.js

index c55c155..cbf3e7f 100644 (file)
@@ -161,54 +161,27 @@ module.exports = function Optimizer(data, context, options) {
     for (var i = 0, l = matches.length; i < l; i++) {
       var selector = matches[i];
       var data = positions[selector];
-      var bodies = [];
-      var joinsAt = [];
-      var processedTokens = [];
-
-      for (var j = data.length - 1, m = 0; j >= 0; j--) {
-        if (data[j].partial && bodies.length === 0)
-          continue;
-
-        var where = data[j].where;
-        var token = tokens[where];
-        var body = token.body;
-        bodies.push(body);
-        processedTokens.push(where);
-      }
-
-      for (j = 0, m = bodies.length; j < m; j++) {
-        if (bodies[j].length > 0)
-          joinsAt.push((joinsAt[j - 1] || 0) + bodies[j].split(';').length);
-      }
-
-      var optimizedBody = propertyOptimizer.process(bodies.join(';'), joinsAt);
-      var optimizedProperties = optimizedBody.split(';');
 
-      var processedCount = processedTokens.length;
-      var propertyIdx = optimizedProperties.length - 1;
-      var tokenIdx = processedCount - 1;
+      if (data.length < 2)
+        continue;
 
-      while (tokenIdx >= 0) {
-        if ((tokenIdx === 0 || bodies[tokenIdx].indexOf(optimizedProperties[propertyIdx]) > -1) && propertyIdx > -1) {
-          propertyIdx--;
-          continue;
+      /* jshint loopfunc: true */
+      _reduceSelector(tokens, selector, data, {
+        filterOut: function(idx, bodies) {
+          return data[idx].partial && bodies.length === 0;
+        },
+        callback: function(token, newBody, processedCount, tokenIdx) {
+          if (!data[processedCount - tokenIdx - 1].partial)
+            token.body = newBody.join(';');
         }
-
-        var newBody = optimizedProperties.splice(propertyIdx + 1);
-        if (!data[processedCount - tokenIdx - 1].partial)
-          tokens[processedTokens[tokenIdx]].body = newBody.join(';');
-
-        tokenIdx--;
-      }
+      });
     }
   };
 
   var _reduceComplexNonAdjacentCases = function(tokens, positions) {
-    for (var i = 0, matches = Object.keys(positions), l = matches.length; i < l; i++) {
-      var complexSelector = matches[i];
-      var allSame = true;
-
-      if (complexSelector.indexOf(',') == -1) // another assumption which is wrong in case of :not() selector
+    allSelectors:
+    for (var complexSelector in positions) {
+      if (complexSelector.indexOf(',') == -1) // simplification, as :not() can have commas too
         continue;
 
       var intoPosition = positions[complexSelector].pop().where;
@@ -220,51 +193,67 @@ module.exports = function Optimizer(data, context, options) {
       for (var j = 0, m = selectors.length; j < m; j++) {
         var selector = selectors[j];
         var data = positions[selector];
-        var bodies = [];
-        var joinsAt = [];
-        var processedTokens = [];
-
-        for (var k = data.length - 1, n = 0; k >= 0; k--) {
-          var where = data[k].where;
-          if (where < intoPosition)
-            continue;
-
-          var token = tokens[where];
-          var body = token.body;
-          bodies.push(body);
-          processedTokens.push(where);
-        }
 
-        for (k = 0, n = bodies.length; k < n; k++) {
-          if (bodies[k].length > 0)
-            joinsAt.push((joinsAt[k - 1] || 0) + bodies[k].split(';').length);
-        }
+        if (data.length < 2)
+          continue allSelectors;
+
+        /* jshint loopfunc: true */
+        _reduceSelector(tokens, selector, data, {
+          filterOut: function(idx) {
+            return data[idx].where < intoPosition;
+          },
+          callback: function(token, newBody, processedCount, tokenIdx) {
+            if (tokenIdx === 0)
+              reducedBodies.push(newBody.join(';'));
+          }
+        });
 
-        var optimizedBody = propertyOptimizer.process(bodies.join(';'), joinsAt);
-        var optimizedProperties = optimizedBody.split(';');
+        if (reducedBodies[reducedBodies.length - 1] != reducedBodies[0])
+          continue allSelectors;
+      }
 
-        var processedCount = processedTokens.length;
-        var propertyIdx = optimizedProperties.length - 1;
-        var tokenIdx = processedCount - 1;
+      intoToken.body = reducedBodies[0];
+    }
+  };
 
-        while (tokenIdx >= 0) {
-          if ((tokenIdx === 0 || bodies[tokenIdx].indexOf(optimizedProperties[propertyIdx]) > -1) && propertyIdx > -1) {
-            propertyIdx--;
-            continue;
-          }
+  var _reduceSelector = function(tokens, selector, data, options) {
+    var bodies = [];
+    var joinsAt = [];
+    var processedTokens = [];
 
-          var newBody = optimizedProperties.splice(propertyIdx + 1);
-          if (tokenIdx === 0)
-            reducedBodies.push(newBody.join(';'));
+    for (var j = data.length - 1, m = 0; j >= 0; j--) {
+      if (options.filterOut(j, bodies))
+        continue;
 
-          tokenIdx--;
-        }
+      var where = data[j].where;
+      var token = tokens[where];
+      var body = token.body;
+      bodies.push(body);
+      processedTokens.push(where);
+    }
 
-        allSame = allSame && reducedBodies[reducedBodies.length - 1] == reducedBodies[0];
+    for (j = 0, m = bodies.length; j < m; j++) {
+      if (bodies[j].length > 0)
+        joinsAt.push((joinsAt[j - 1] || 0) + bodies[j].split(';').length);
+    }
+
+    var optimizedBody = propertyOptimizer.process(bodies.join(';'), joinsAt);
+    var optimizedProperties = optimizedBody.split(';');
+
+    var processedCount = processedTokens.length;
+    var propertyIdx = optimizedProperties.length - 1;
+    var tokenIdx = processedCount - 1;
+
+    while (tokenIdx >= 0) {
+      if ((tokenIdx === 0 || bodies[tokenIdx].indexOf(optimizedProperties[propertyIdx]) > -1) && propertyIdx > -1) {
+        propertyIdx--;
+        continue;
       }
 
-      if (allSame)
-        intoToken.body = reducedBodies[0];
+      var newBody = optimizedProperties.splice(propertyIdx + 1);
+      options.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
+
+      tokenIdx--;
     }
   };