[Pkg-javascript-commits] [pdf.js] 01/246: Make QueueOptimizer easier to read.

David Prévot taffit at moszumanska.debian.org
Sun Sep 7 15:36:19 UTC 2014


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository pdf.js.

commit a483c80fc36c8059ff357c0e94f159b025b3895b
Author: Nicholas Nethercote <nnethercote at mozilla.com>
Date:   Mon Jun 23 23:34:54 2014 -0700

    Make QueueOptimizer easier to read.
    
    QueueOptimizer is really hard to read. Enough so that it's blocking my
    efforts to streamline the representation used for operator lists.
    
    This patch improves its readability in the following ways.
    
    - More descriptive variable names make the sequence checking much clearer,
      as do additional comments.
    
    - The addState() functions now return the index of the first op past the
      sequence, instead of setting context.currentOperation to the last op of
      the sequence.
    
    - The loop in optimize() is clearer.
    
    - The array modification in the fourth addState() function is much clearer
      -- we're just removing trios of ops.
    
    - All four |addState| functions are now more consistent with each other.
    
    I used some debug printfs to find documents where these optimizations are
    used and then checked that the number of optimized ops was the same before
    and after my changes.
---
 src/core/evaluator.js | 348 +++++++++++++++++++++++++++++++-------------------
 1 file changed, 214 insertions(+), 134 deletions(-)

diff --git a/src/core/evaluator.js b/src/core/evaluator.js
index 41945e5..72a9e41 100644
--- a/src/core/evaluator.js
+++ b/src/core/evaluator.js
@@ -2201,18 +2201,20 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
     state[pattern[pattern.length - 1]] = fn;
   }
 
-  function handlePaintSolidColorImageMask(index, count, fnArray, argsArray) {
-    // Handles special case of mainly LaTeX documents which
-    // use image masks to draw lines with the current fill style.
+  function handlePaintSolidColorImageMask(iFirstSave, count, fnArray,
+                                          argsArray) {
+    // Handles special case of mainly LaTeX documents which use image masks to
+    // draw lines with the current fill style.
     // 'count' groups of (save, transform, paintImageMaskXObject, restore)+
-    // have been found at index.
+    // have been found at iFirstSave.
+    var iFirstPIMXO = iFirstSave + 2;
     for (var i = 0; i < count; i++) {
-      var arg = argsArray[index + 4 * i + 2];
+      var arg = argsArray[iFirstPIMXO + 4 * i];
       var imageMask = arg.length === 1 && arg[0];
       if (imageMask && imageMask.width === 1 && imageMask.height === 1 &&
-          (!imageMask.data.length || (imageMask.data.length === 1 &&
-                                      imageMask.data[0] === 0))) {
-        fnArray[index + 4 * i + 2] = OPS.paintSolidColorImageMask;
+          (!imageMask.data.length ||
+           (imageMask.data.length === 1 && imageMask.data[0] === 0))) {
+        fnArray[iFirstPIMXO + 4 * i] = OPS.paintSolidColorImageMask;
         continue;
       }
       break;
@@ -2222,26 +2224,43 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
 
   var InitialState = [];
 
+  // This replaces (save, transform, paintInlineImageXObject, restore)+
+  // sequences with one |paintInlineImageXObjectGroup| operation.
   addState(InitialState,
     [OPS.save, OPS.transform, OPS.paintInlineImageXObject, OPS.restore],
     function foundInlineImageGroup(context) {
-      // grouping paintInlineImageXObject's into paintInlineImageXObjectGroup
-      // searching for (save, transform, paintInlineImageXObject, restore)+
       var MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10;
       var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;
       var MAX_WIDTH = 1000;
       var IMAGE_PADDING = 1;
 
       var fnArray = context.fnArray, argsArray = context.argsArray;
-      var j = context.currentOperation - 3, i = j + 4;
+      var curr = context.iCurr;
+      var iFirstSave = curr - 3;
+      var iFirstTransform = curr - 2;
+      var iFirstPIIXO = curr - 1;
+
+      // Look for the quartets.
+      var i = iFirstSave + 4;
       var ii = fnArray.length;
+      while (i + 3 < ii) {
+        if (fnArray[i] !== OPS.save ||
+            fnArray[i + 1] !== OPS.transform ||
+            fnArray[i + 2] !== OPS.paintInlineImageXObject ||
+            fnArray[i + 3] !== OPS.restore) {
+          break;    // ops don't match
+        }
+        i += 4;
+      }
 
-      for (; i < ii && fnArray[i - 4] === fnArray[i]; i++) {}
-      var count = Math.min((i - j) >> 2, MAX_IMAGES_IN_INLINE_IMAGES_BLOCK);
+      // At this point, i is the index of the first op past the last valid
+      // quartet.
+      var count = Math.min((i - iFirstSave) / 4,
+                           MAX_IMAGES_IN_INLINE_IMAGES_BLOCK);
       if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) {
-        context.currentOperation = i - 1;
-        return;
+        return i;
       }
+
       // assuming that heights of those image is too small (~1 pixel)
       // packing as much as possible by lines
       var maxX = 0;
@@ -2249,8 +2268,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
       var currentX = IMAGE_PADDING, currentY = IMAGE_PADDING;
       var q;
       for (q = 0; q < count; q++) {
-        var transform = argsArray[j + (q << 2) + 1];
-        var img = argsArray[j + (q << 2) + 2][0];
+        var transform = argsArray[iFirstTransform + (q << 2)];
+        var img = argsArray[iFirstPIIXO + (q << 2)][0];
         if (currentX + img.width > MAX_WIDTH) {
           // starting new line
           maxX = Math.max(maxX, currentX);
@@ -2271,8 +2290,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
       var imgData = new Uint8Array(imgWidth * imgHeight * 4);
       var imgRowSize = imgWidth << 2;
       for (q = 0; q < count; q++) {
-        var data = argsArray[j + (q << 2) + 2][0].data;
-        // copy image by lines and extends pixels into padding
+        var data = argsArray[iFirstPIIXO + (q << 2)][0].data;
+        // Copy image by lines and extends pixels into padding.
         var rowSize = map[q].w << 2;
         var dataOffset = 0;
         var offset = (map[q].x + map[q].y * imgWidth) << 2;
@@ -2295,48 +2314,72 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
           offset -= imgRowSize;
         }
       }
-      // replacing queue items
-      fnArray.splice(j, count * 4, OPS.paintInlineImageXObjectGroup);
-      argsArray.splice(j, count * 4,
+
+      // Replace queue items.
+      fnArray.splice(iFirstSave, count * 4, OPS.paintInlineImageXObjectGroup);
+      argsArray.splice(iFirstSave, count * 4,
         [{ width: imgWidth, height: imgHeight, kind: ImageKind.RGBA_32BPP,
            data: imgData }, map]);
-      context.currentOperation = j;
+
+      return iFirstSave + 1;
     });
 
+  // This replaces (save, transform, paintImageMaskXObject, restore)+
+  // sequences with one |paintImageMaskXObjectGroup| or one
+  // |paintImageMaskXObjectRepeat| operation.
   addState(InitialState,
     [OPS.save, OPS.transform, OPS.paintImageMaskXObject, OPS.restore],
     function foundImageMaskGroup(context) {
-      // grouping paintImageMaskXObject's into paintImageMaskXObjectGroup
-      // searching for (save, transform, paintImageMaskXObject, restore)+
       var MIN_IMAGES_IN_MASKS_BLOCK = 10;
       var MAX_IMAGES_IN_MASKS_BLOCK = 100;
       var MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000;
 
       var fnArray = context.fnArray, argsArray = context.argsArray;
-      var j = context.currentOperation - 3, i = j + 4;
-      var ii = fnArray.length, q;
+      var curr = context.iCurr;
+      var iFirstSave = curr - 3;
+      var iFirstTransform = curr - 2;
+      var iFirstPIMXO = curr - 1;
 
-      for (; i < ii && fnArray[i - 4] === fnArray[i]; i++) {}
-      var count = (i - j) >> 2;
-      count = handlePaintSolidColorImageMask(j, count, fnArray, argsArray);
+      // Look for the quartets.
+      var i = iFirstSave + 4;
+      var ii = fnArray.length;
+      while (i + 3 < ii) {
+        if (fnArray[i] !== OPS.save ||
+            fnArray[i + 1] !== OPS.transform ||
+            fnArray[i + 2] !== OPS.paintImageMaskXObject ||
+            fnArray[i + 3] !== OPS.restore) {
+          break;    // ops don't match
+        }
+        i += 4;
+      }
+
+      // At this point, i is the index of the first op past the last valid
+      // quartet.
+      var count = (i - iFirstSave) / 4;
+      count = handlePaintSolidColorImageMask(iFirstSave, count, fnArray,
+                                             argsArray);
       if (count < MIN_IMAGES_IN_MASKS_BLOCK) {
-        context.currentOperation = i - 1;
-        return;
+        return i;
       }
 
+      var q;
       var isSameImage = false;
-      var transformArgs;
-      if (argsArray[j + 1][1] === 0 && argsArray[j + 1][2] === 0) {
-        i = j + 4;
+      var iTransform, transformArgs;
+      var firstPIMXOArg0 = argsArray[iFirstPIMXO][0];
+      if (argsArray[iFirstTransform][1] === 0 &&
+          argsArray[iFirstTransform][2] === 0) {
         isSameImage = true;
-        for (q = 1; q < count; q++, i += 4) {
-          var prevTransformArgs = argsArray[i - 3];
-          transformArgs = argsArray[i + 1];
-          if (argsArray[i - 2][0] !== argsArray[i + 2][0] ||
-              prevTransformArgs[0] !== transformArgs[0] ||
-              prevTransformArgs[1] !== transformArgs[1] ||
-              prevTransformArgs[2] !== transformArgs[2] ||
-              prevTransformArgs[3] !== transformArgs[3]) {
+        var firstTransformArg0 = argsArray[iFirstTransform][0];
+        var firstTransformArg3 = argsArray[iFirstTransform][3];
+        iTransform = iFirstTransform + 4;
+        var iPIMXO = iFirstPIMXO + 4;
+        for (q = 1; q < count; q++, iTransform += 4, iPIMXO += 4) {
+          transformArgs = argsArray[iTransform];
+          if (argsArray[iPIMXO][0] !== firstPIMXOArg0 ||
+              transformArgs[0] !== firstTransformArg0 ||
+              transformArgs[1] !== 0 ||
+              transformArgs[2] !== 0 ||
+              transformArgs[3] !== firstTransformArg3) {
             if (q < MIN_IMAGES_IN_MASKS_BLOCK) {
               isSameImage = false;
             } else {
@@ -2350,39 +2393,39 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
       if (isSameImage) {
         count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK);
         var positions = new Float32Array(count * 2);
-        i = j + 1;
-        for (q = 0; q < count; q++) {
-          transformArgs = argsArray[i];
+        iTransform = iFirstTransform;
+        for (q = 0; q < count; q++, iTransform += 4) {
+          transformArgs = argsArray[iTransform];
           positions[(q << 1)] = transformArgs[4];
           positions[(q << 1) + 1] = transformArgs[5];
-          i += 4;
         }
 
-        // replacing queue items
-        fnArray.splice(j, count * 4, OPS.paintImageMaskXObjectRepeat);
-        argsArray.splice(j, count * 4, [argsArray[j + 2][0],
-                         argsArray[j + 1][0], argsArray[j + 1][3], positions]);
-
-        context.currentOperation = j;
+        // Replace queue items.
+        fnArray.splice(iFirstSave, count * 4, OPS.paintImageMaskXObjectRepeat);
+        argsArray.splice(iFirstSave, count * 4,
+          [firstPIMXOArg0, firstTransformArg0, firstTransformArg3, positions]);
       } else {
         count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK);
         var images = [];
         for (q = 0; q < count; q++) {
-          transformArgs = argsArray[j + (q << 2) + 1];
-          var maskParams = argsArray[j + (q << 2) + 2][0];
+          transformArgs = argsArray[iFirstTransform + (q << 2)];
+          var maskParams = argsArray[iFirstPIMXO + (q << 2)][0];
           images.push({ data: maskParams.data, width: maskParams.width,
                         height: maskParams.height,
                         transform: transformArgs });
         }
 
-        // replacing queue items
-        fnArray.splice(j, count * 4, OPS.paintImageMaskXObjectGroup);
-        argsArray.splice(j, count * 4, [images]);
-
-        context.currentOperation = j;
+        // Replace queue items.
+        fnArray.splice(iFirstSave, count * 4, OPS.paintImageMaskXObjectGroup);
+        argsArray.splice(iFirstSave, count * 4, [images]);
       }
+
+      return iFirstSave + 1;
     });
 
+  // This replaces (save, transform, paintImageXObject, restore)+ sequences
+  // with one paintImageXObjectRepeat operation, if the |transform| and
+  // |paintImageXObjectRepeat| ops are appropriate.
   addState(InitialState,
     [OPS.save, OPS.transform, OPS.paintImageXObject, OPS.restore],
     function (context) {
@@ -2390,103 +2433,135 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
       var MAX_IMAGES_IN_BLOCK = 1000;
 
       var fnArray = context.fnArray, argsArray = context.argsArray;
-      var j = context.currentOperation - 3, i = j + 4;
-      if (argsArray[j + 1][1] !== 0 || argsArray[j + 1][2] !== 0) {
-        return;
-      }
+      var curr = context.iCurr;
+      var iFirstSave = curr - 3;
+      var iFirstTransform = curr - 2;
+      var iFirstPIXO = curr - 1;
+      var iFirstRestore = curr;
+
+      if (argsArray[iFirstTransform][1] !== 0 ||
+          argsArray[iFirstTransform][2] !== 0) {
+        return iFirstRestore + 1;   // transform has the wrong form
+      }
+
+      // Look for the quartets.
+      var firstPIXOArg0 = argsArray[iFirstPIXO][0];
+      var firstTransformArg0 = argsArray[iFirstTransform][0];
+      var firstTransformArg3 = argsArray[iFirstTransform][3];
+      var i = iFirstSave + 4;
       var ii = fnArray.length;
-      var transformArgs;
-      for (; i + 3 < ii && fnArray[i - 4] === fnArray[i]; i += 4) {
-        if (fnArray[i - 3] !== fnArray[i + 1] ||
-            fnArray[i - 2] !== fnArray[i + 2] ||
-            fnArray[i - 1] !== fnArray[i + 3]) {
-          break;
+      while (i + 3 < ii) {
+        if (fnArray[i] !== OPS.save ||
+            fnArray[i + 1] !== OPS.transform ||
+            fnArray[i + 2] !== OPS.paintImageXObject ||
+            fnArray[i + 3] !== OPS.restore) {
+          break;    // ops don't match
         }
-        if (argsArray[i - 2][0] !== argsArray[i + 2][0]) {
-          break; // different image
+        if (argsArray[i + 1][0] !== firstTransformArg0 ||
+            argsArray[i + 1][1] !== 0 ||
+            argsArray[i + 1][2] !== 0 ||
+            argsArray[i + 1][3] !== firstTransformArg3) {
+          break;    // transforms don't match
         }
-        var prevTransformArgs = argsArray[i - 3];
-        transformArgs = argsArray[i + 1];
-        if (prevTransformArgs[0] !== transformArgs[0] ||
-            prevTransformArgs[1] !== transformArgs[1] ||
-            prevTransformArgs[2] !== transformArgs[2] ||
-            prevTransformArgs[3] !== transformArgs[3]) {
-          break; // different transform
+        if (argsArray[i + 2][0] !== firstPIXOArg0) {
+          break;    // images don't match
         }
+        i += 4;
       }
-      var count = Math.min((i - j) >> 2, MAX_IMAGES_IN_BLOCK);
+
+      // At this point, i is the index of the first op past the last valid
+      // quartet.
+      var count = Math.min((i - iFirstSave) / 4, MAX_IMAGES_IN_BLOCK);
       if (count < MIN_IMAGES_IN_BLOCK) {
-        context.currentOperation = i - 1;
-        return;
+        return i;
       }
 
+      // Extract the (x,y) positions from all of the matching transforms.
       var positions = new Float32Array(count * 2);
-      i = j + 1;
-      for (var q = 0; q < count; q++) {
-        transformArgs = argsArray[i];
+      var iTransform = iFirstTransform;
+      for (var q = 0; q < count; q++, iTransform += 4) {
+        var transformArgs = argsArray[iTransform];
         positions[(q << 1)] = transformArgs[4];
         positions[(q << 1) + 1] = transformArgs[5];
-        i += 4;
       }
-      var args = [argsArray[j + 2][0], argsArray[j + 1][0],
-                  argsArray[j + 1][3], positions];
-      // replacing queue items
-      fnArray.splice(j, count * 4, OPS.paintImageXObjectRepeat);
-      argsArray.splice(j, count * 4, args);
 
-      context.currentOperation = j;
+      // Replace queue items.
+      var args = [firstPIXOArg0, firstTransformArg0, firstTransformArg3,
+                  positions];
+      fnArray.splice(iFirstSave, count * 4, OPS.paintImageXObjectRepeat);
+      argsArray.splice(iFirstSave, count * 4, args);
+
+      return iFirstSave + 1;
     });
 
+  // This replaces (beginText, setFont, setTextMatrix, showText, endText)+
+  // sequences with (beginText, setFont, (setTextMatrix, showText)+, endText)+
+  // sequences, if the font for each one is the same.
   addState(InitialState,
     [OPS.beginText, OPS.setFont, OPS.setTextMatrix, OPS.showText, OPS.endText],
     function (context) {
-      // moving single chars with same font into beginText/endText groups
-      // searching for (beginText, setFont, setTextMatrix, showText, endText)+
       var MIN_CHARS_IN_BLOCK = 3;
       var MAX_CHARS_IN_BLOCK = 1000;
 
       var fnArray = context.fnArray, argsArray = context.argsArray;
-      var j = context.currentOperation - 4, i = j + 5;
+      var curr = context.iCurr;
+      var iFirstBeginText = curr - 4;
+      var iFirstSetFont = curr - 3;
+      var iFirstSetTextMatrix = curr - 2;
+      var iFirstShowText = curr - 1;
+      var iFirstEndText = curr;
+
+      // Look for the quintets.
+      var firstSetFontArg0 = argsArray[iFirstSetFont][0];
+      var firstSetFontArg1 = argsArray[iFirstSetFont][1];
+      var i = iFirstBeginText + 5;
       var ii = fnArray.length;
-
-      for (; i < ii && fnArray[i - 5] === fnArray[i]; i++) {
-        if (fnArray[i] === OPS.setFont) {
-          if (argsArray[i - 5][0] !== argsArray[i][0] ||
-              argsArray[i - 5][1] !== argsArray[i][1]) {
-            break;
-          }
+      while (i + 4 < ii) {
+        if (fnArray[i] !== OPS.beginText ||
+            fnArray[i + 1] !== OPS.setFont ||
+            fnArray[i + 2] !== OPS.setTextMatrix ||
+            fnArray[i + 3] !== OPS.showText ||
+            fnArray[i + 4] !== OPS.endText) {
+          break;    // ops don't match
+        }
+        if (argsArray[i + 1][0] !== firstSetFontArg0 ||
+            argsArray[i + 1][1] !== firstSetFontArg1) {
+          break;    // fonts don't match
         }
+        i += 5;
       }
-      var count = Math.min(((i - j) / 5) | 0, MAX_CHARS_IN_BLOCK);
+
+      // At this point, i is the index of the first op past the last valid
+      // quintet.
+      var count = Math.min(((i - iFirstBeginText) / 5), MAX_CHARS_IN_BLOCK);
       if (count < MIN_CHARS_IN_BLOCK) {
-        context.currentOperation = i - 1;
-        return;
-      }
-      if (j >= 4 && fnArray[j - 4] === fnArray[j + 1] &&
-          fnArray[j - 3] === fnArray[j + 2] &&
-          fnArray[j - 2] === fnArray[j + 3] &&
-          fnArray[j - 1] === fnArray[j + 4] &&
-          argsArray[j - 4][0] === argsArray[j + 1][0] &&
-          argsArray[j - 4][1] === argsArray[j + 1][1]) {
-        // extending one block ahead (very first block might have 'dependency')
+        return i;
+      }
+
+      // If the preceding quintet is (<something>, setFont, setTextMatrix,
+      // showText, endText), include that as well. (E.g. <something> might be
+      // |dependency|.)
+      var iFirst = iFirstBeginText;
+      if (iFirstBeginText >= 4 &&
+          fnArray[iFirstBeginText - 4] === fnArray[iFirstSetFont] &&
+          fnArray[iFirstBeginText - 3] === fnArray[iFirstSetTextMatrix] &&
+          fnArray[iFirstBeginText - 2] === fnArray[iFirstShowText] &&
+          fnArray[iFirstBeginText - 1] === fnArray[iFirstEndText] &&
+          argsArray[iFirstBeginText - 4][0] === firstSetFontArg0 &&
+          argsArray[iFirstBeginText - 4][1] === firstSetFontArg1) {
         count++;
-        j -= 5;
+        iFirst -= 5;
       }
-      var k = j + 7;
-      i = j + 4;
+
+      // Remove (endText, beginText, setFont) trios.
+      var iEndText = iFirst + 4;
       for (var q = 1; q < count; q++) {
-        fnArray[i] = fnArray[k];
-        argsArray[i] = argsArray[k];
-        fnArray[i + 1] = fnArray[k + 1];
-        argsArray[i + 1] = argsArray[k + 1];
-        i += 2;
-        k += 5;
-      }
-      var removed = (count - 1) * 3;
-      fnArray.splice(i, removed);
-      argsArray.splice(i, removed);
-
-      context.currentOperation = i;
+        fnArray.splice(iEndText, 3);
+        argsArray.splice(iEndText, 3);
+        iEndText += 2;
+      }
+
+      return iEndText + 1;
     });
 
   function QueueOptimizer() {}
@@ -2495,19 +2570,24 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
     optimize: function QueueOptimizer_optimize(queue) {
       var fnArray = queue.fnArray, argsArray = queue.argsArray;
       var context = {
-        currentOperation: 0,
+        iCurr: 0,
         fnArray: fnArray,
         argsArray: argsArray
       };
-      var i, ii = argsArray.length;
       var state;
-      for (i = 0; i < ii; i++) {
+      var i = 0, ii = fnArray.length;
+      while (i < ii) {
         state = (state || InitialState)[fnArray[i]];
         if (typeof state === 'function') { // we found some handler
-          context.currentOperation = i;
-          state = state(context);
-          i = context.currentOperation;
+          context.iCurr = i;
+          // state() returns the index of the first non-matching op (if we
+          // didn't match) or the first op past the modified ops (if we did
+          // match and replace).
+          i = state(context);
+          state = undefined;    // reset the state machine
           ii = context.fnArray.length;
+        } else {
+          i++;
         }
       }
     }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/pdf.js.git



More information about the Pkg-javascript-commits mailing list