http://www.mediawiki.org/wiki/Special:Code/MediaWiki/73196

Revision: 73196
Author:   tstarling
Date:     2010-09-17 07:59:39 +0000 (Fri, 17 Sep 2010)

Log Message:
-----------
In JSMin:
* Don't delete line breaks, it's annoying. Replace multi-line comments with the 
same number of line breaks, to keep line numbers the same. Tested line break 
count for jquery.js, it's the same now with or without minification.
* Remove the line break from the start of the output, which is there due to 
$this->a being initialised to "\n".
* Simplified get() by moving the character translations to __construct(). This 
enables some useful optimisations. 
* Straightened out lookAhead/peek logic, which was poorly ported from C, where 
it was simulated using getc(stdin).
* Optimised scanning for end of comment, using strcspn() and strpos(). Now does 
multi-line comments at 14700 KB/s instead of 200 KB/s. Same trick with string 
and regex literals.
* For clarity, don't use $this->a as a temporary variable in the DELETE_A 
branch of action(). The only way the loop can exit is by it being the same as 
$this->b, which it was to start with anyway, so there's no point changing it in 
every iteration. 
* Inlined get() and peek() into next() for a small performance gain. Removed 
peek() since it is no longer used.
* Overall, time to minify jquery.js reduced from 1.6s to 0.9s. 

Modified Paths:
--------------
    trunk/phase3/includes/libs/JSMin.php

Modified: trunk/phase3/includes/libs/JSMin.php
===================================================================
--- trunk/phase3/includes/libs/JSMin.php        2010-09-17 07:35:54 UTC (rev 
73195)
+++ trunk/phase3/includes/libs/JSMin.php        2010-09-17 07:59:39 UTC (rev 
73196)
@@ -69,7 +69,7 @@
        // -- Public Static Methods 
--------------------------------------------------
 
        public static function minify( $js ) {
-               $jsmin = new JSMin( $js );
+               $jsmin = new self( $js );
                $ret = $jsmin->min();
                return $ret;
        }
@@ -77,7 +77,10 @@
        // -- Public Instance Methods 
------------------------------------------------
 
        public function __construct( $input ) {
+               // Fix line endings
                $this->input       = str_replace( "\r\n", "\n", $input );
+               // Replace tabs and other control characters (except LF) with 
spaces
+               $this->input = preg_replace( '/[\x00-\x09\x0b-\x1f]/', ' ', 
$this->input );
                $this->inputLength = strlen( $this->input );
        }
 
@@ -94,35 +97,35 @@
        protected function action( $d ) {
                switch( $d ) {
                        case self::OUTPUT:
-                               // Output A. Copy B to A. Get the next B.
                                $this->output .= $this->a;
 
                        case self::DELETE_A:
-                               // Copy B to A. Get the next B. (Delete A).
                                $this->a = $this->b;
 
                                if ( $this->a === "'" || $this->a === '"' ) {
+                                       $interestingChars = $this->a . "\\\n";
+                                       $this->output .= $this->a;
                                        for ( ; ; ) {
-                                               $this->output .= $this->a;
-                                               $this->a       = $this->get();
+                                               $runLength = strcspn( 
$this->input, $interestingChars, $this->inputIndex );
+                                               $this->output .= substr( 
$this->input, $this->inputIndex, $runLength );
+                                               $this->inputIndex += $runLength;
+                                               $this->a = $this->get();
 
                                                if ( $this->a === $this->b ) {
                                                        break;
                                                }
 
-                                               if ( ord( $this->a ) <= 
self::ORD_LF ) {
+                                               if ( $this->a === "\n" || 
$this->a === null ) {
                                                        throw new 
JSMinException( 'Unterminated string literal.' );
                                                }
 
                                                if ( $this->a === '\\' ) {
-                                                       $this->output .= 
$this->a;
-                                                       $this->a       = 
$this->get();
+                                                       $this->output .= 
$this->a . $this->get();
                                                }
                                        }
                                }
 
                        case self::DELETE_B:
-                               // Get the next B. (Delete B).
                                $this->b = $this->next();
 
                                if ( $this->b === '/' && (
@@ -133,6 +136,9 @@
                                        $this->output .= $this->a . $this->b;
 
                                        for ( ; ; ) {
+                                               $runLength = strcspn( 
$this->input, "/\\\n", $this->inputIndex );
+                                               $this->output .= substr( 
$this->input, $this->inputIndex, $runLength );
+                                               $this->inputIndex += $runLength;
                                                $this->a = $this->get();
 
                                                if ( $this->a === '/' ) {
@@ -140,7 +146,7 @@
                                                } elseif ( $this->a === '\\' ) {
                                                        $this->output .= 
$this->a;
                                                        $this->a       = 
$this->get();
-                                               } elseif ( ord( $this->a ) <= 
self::ORD_LF ) {
+                                               } elseif ( $this->a === "\n" || 
$this->a === null ) {
                                                        throw new 
JSMinException( 'Unterminated regular expression ' .
                                                                        
'literal.' );
                                                }
@@ -159,27 +165,11 @@
      * linefeed.
         */
        protected function get() {
-               $c = $this->lookAhead;
-               $this->lookAhead = null;
-
-               if ( $c === null ) {
-                       if ( $this->inputIndex < $this->inputLength ) {
-                               $c = substr( $this->input, $this->inputIndex, 1 
);
-                               $this->inputIndex += 1;
-                       } else {
-                               $c = null;
-                       }
+               if ( $this->inputIndex < $this->inputLength ) {
+                       return $this->input[$this->inputIndex++];
+               } else {
+                       return null;
                }
-
-               if ( $c === "\r" ) {
-                       return "\n";
-               }
-
-               if ( $c === null || $c === "\n" || ord( $c ) >= self::ORD_SPACE 
) {
-                       return $c;
-               }
-
-               return ' ';
        }
 
        /**
@@ -212,25 +202,12 @@
 
                                case "\n":
                                        switch ( $this->b ) {
-                                               case '{':
-                                               case '[':
-                                               case '(':
-                                               case '+':
-                                               case '-':
-                                                       $this->action( 
self::OUTPUT );
-                                                       break;
-
                                                case ' ':
                                                        $this->action( 
self::DELETE_B );
                                                        break;
 
                                                default:
-                                                       if ( $this->isAlphaNum( 
$this->b ) ) {
-                                                               $this->action( 
self::OUTPUT );
-                                                       }
-                                                       else {
-                                                               $this->action( 
self::DELETE_A );
-                                                       }
+                                                       $this->action( 
self::OUTPUT );
                                        }
                                        break;
 
@@ -244,29 +221,6 @@
 
                                                        $this->action( 
self::DELETE_B );
                                                        break;
-
-                                               case "\n":
-                                                       switch ( $this->a ) {
-                                                               case '}':
-                                                               case ']':
-                                                               case ')':
-                                                               case '+':
-                                                               case '-':
-                                                               case '"':
-                                                               case "'":
-                                                                       
$this->action( self::OUTPUT );
-                                                                       break;
-
-                                                               default:
-                                                                       if ( 
$this->isAlphaNum( $this->a ) ) {
-                                                                               
$this->action( self::OUTPUT );
-                                                                       }
-                                                                       else {
-                                                                               
$this->action( self::DELETE_B );
-                                                                       }
-                                                       }
-                                                       break;
-
                                                default:
                                                        $this->action( 
self::OUTPUT );
                                                        break;
@@ -274,44 +228,48 @@
                        }
                }
 
-               return $this->output;
+               // Remove initial line break
+               if ( $this->output[0] !== "\n" ) {
+                       throw new JSMinException( 'Unexpected lack of line 
break.' );
+               }
+               if ( $this->output === "\n" ) {
+                       return '';
+               } else {
+                       return substr( $this->output, 1 );
+               }
        }
 
        /**
-        * Get the next character, excluding comments. peek() is used to see
-     * if a '/' is followed by a '/' or '*'.
+        * Get the next character, excluding comments.
         */
        protected function next() {
-               $c = $this->get();
+               if ( $this->inputIndex >= $this->inputLength ) {
+                       return null;
+               }
+               $c = $this->input[$this->inputIndex++];
 
+               if ( $this->inputIndex >= $this->inputLength ) {
+                       return $c;
+               }
+
                if ( $c === '/' ) {
-                       switch( $this->peek() ) {
+                       switch( $this->input[$this->inputIndex] ) {
                                case '/':
-                                       for ( ; ; ) {
-                                               $c = $this->get();
-
-                                               if ( ord( $c ) <= self::ORD_LF 
) {
-                                                       return $c;
-                                               }
-                                       }
-
+                                       $this->inputIndex += strcspn( 
$this->input, "\n", $this->inputIndex ) + 1;
+                                       return "\n";
                                case '*':
-                                       $this->get();
-
-                                       for ( ; ; ) {
-                                               switch( $this->get() ) {
-                                                       case '*':
-                                                               if ( 
$this->peek() === '/' ) {
-                                                                       
$this->get();
-                                                                       return 
' ';
-                                                               }
-                                                               break;
-
-                                                       case null:
-                                                               throw new 
JSMinException( 'Unterminated comment.' );
-                                               }
+                                       $endPos = strpos( $this->input, '*/', 
$this->inputIndex + 1 );
+                                       if ( $endPos === false ) {
+                                               throw new JSMinException( 
'Unterminated comment.' );
                                        }
-
+                                       $numLines = substr_count( $this->input, 
"\n", $this->inputIndex, 
+                                               $endPos - $this->inputIndex );
+                                       $this->inputIndex = $endPos + 2;
+                                       if ( $numLines ) {
+                                               return str_repeat( "\n", 
$numLines );
+                                       } else {
+                                               return ' ';
+                                       }
                                default:
                                        return $c;
                        }
@@ -319,14 +277,6 @@
 
                return $c;
        }
-
-       /** 
-        * Get the next character without getting it.
-        */
-       protected function peek() {
-               $this->lookAhead = $this->get();
-               return $this->lookAhead;
-       }
 }
 
 // -- Exceptions 
---------------------------------------------------------------



_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to