<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><style type="text/css"><!--
#msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; }
#msg ul, pre { overflow: auto; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<title>[12174] trunk/wp-includes/pomo: pomo performance improvements.</title>
</head>
<body>

<div id="msg">
<dl>
<dt>Revision</dt> <dd><a href="http://trac.wordpress.org/changeset/12174">12174</a></dd>
<dt>Author</dt> <dd>ryan</dd>
<dt>Date</dt> <dd>2009-11-12 16:05:43 +0000 (Thu, 12 Nov 2009)</dd>
</dl>

<h3>Log Message</h3>
<pre>pomo performance improvements. Props nbachiyski. fixes <a href="http://trac.wordpress.org/ticket/10165">#10165</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkwpincludespomomophp">trunk/wp-includes/pomo/mo.php</a></li>
<li><a href="#trunkwpincludespomostreamsphp">trunk/wp-includes/pomo/streams.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkwpincludespomomophp"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/pomo/mo.php (12173 => 12174)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/pomo/mo.php        2009-11-12 14:43:15 UTC (rev 12173)
+++ trunk/wp-includes/pomo/mo.php        2009-11-12 16:05:43 UTC (rev 12174)
</span><span class="lines">@@ -2,7 +2,7 @@
</span><span class="cx"> /**
</span><span class="cx">  * Class for working with MO files
</span><span class="cx">  *
</span><del>- * @version $Id: mo.php 221 2009-09-07 21:08:21Z nbachiyski $
</del><ins>+ * @version $Id: mo.php 293 2009-11-12 15:43:50Z nbachiyski $
</ins><span class="cx">  * @package pomo
</span><span class="cx">  * @subpackage mo
</span><span class="cx">  */
</span><span class="lines">@@ -21,10 +21,9 @@
</span><span class="cx">          * @param string $filename MO file to load
</span><span class="cx">          */
</span><span class="cx">         function import_from_file($filename) {
</span><del>-                $reader = new POMO_CachedIntFileReader($filename);
-                if (isset($reader-&gt;error)) {
</del><ins>+                $reader = new POMO_FileReader($filename);
+                if (!$reader-&gt;is_resource())
</ins><span class="cx">                         return false;
</span><del>-                }
</del><span class="cx">                 return $this-&gt;import_from_reader($reader);
</span><span class="cx">         }
</span><span class="cx">         
</span><span class="lines">@@ -113,61 +112,111 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         function import_from_reader($reader) {
</span><del>-                $reader-&gt;setEndian('little');
-                $endian = MO::get_byteorder($reader-&gt;readint32());
-                if (false === $endian) {
</del><ins>+                $endian_string = MO::get_byteorder($reader-&gt;readint32());
+                if (false === $endian_string) {
</ins><span class="cx">                         return false;
</span><span class="cx">                 }
</span><del>-                $reader-&gt;setEndian($endian);
</del><ins>+                $reader-&gt;setEndian($endian_string);
</ins><span class="cx"> 
</span><del>-                $revision = $reader-&gt;readint32();
-                $total = $reader-&gt;readint32();
-                // get addresses of array of lenghts and offsets for original string and translations
-                $originals_lenghts_addr = $reader-&gt;readint32();
-                $translations_lenghts_addr = $reader-&gt;readint32();
</del><ins>+                $endian = ('big' == $endian_string)? 'N' : 'V';
</ins><span class="cx"> 
</span><ins>+                $header = $reader-&gt;read(24);
+                if ($reader-&gt;strlen($header) != 24)
+                        return false;
+
+                // parse header
+                $header = unpack(&quot;{$endian}revision/{$endian}total/{$endian}originals_lenghts_addr/{$endian}translations_lenghts_addr/{$endian}hash_length/{$endian}hash_addr&quot;, $header);
+                if (!is_array($header))
+                        return false;
+
+                extract( $header );
+
+                // support revision 0 of MO format specs, only
+                if ($revision != 0)
+                        return false;
+
+                // seek to data blocks
</ins><span class="cx">                 $reader-&gt;seekto($originals_lenghts_addr);
</span><del>-                $originals_lenghts = $reader-&gt;readint32array($total * 2); // each of 
-                $reader-&gt;seekto($translations_lenghts_addr);
-                $translations_lenghts = $reader-&gt;readint32array($total * 2);
</del><span class="cx"> 
</span><del>-                $length = create_function('$i', 'return $i * 2 + 1;');
-                $offset = create_function('$i', 'return $i * 2 + 2;');
</del><ins>+                // read originals' indices
+                $originals_lengths_length = $translations_lenghts_addr - $originals_lenghts_addr;
+                if ( $originals_lengths_length != $total * 8 )
+                        return false;
</ins><span class="cx"> 
</span><del>-                for ($i = 0; $i &lt; $total; ++$i) {
-                        $reader-&gt;seekto($originals_lenghts[$offset($i)]);
-                        $original = $reader-&gt;read($originals_lenghts[$length($i)]);
-                        $reader-&gt;seekto($translations_lenghts[$offset($i)]);
-                        $translation = $reader-&gt;read($translations_lenghts[$length($i)]);
-                        if ('' == $original) {
</del><ins>+                $originals = $reader-&gt;read($originals_lengths_length);
+                if ( $reader-&gt;strlen( $originals ) != $originals_lengths_length )
+                        return false;
+
+                // read translations' indices
+                $translations_lenghts_length = $hash_addr - $translations_lenghts_addr;
+                if ( $translations_lenghts_length != $total * 8 )
+                        return false;
+
+                $translations = $reader-&gt;read($translations_lenghts_length);
+                if ( $reader-&gt;strlen( $translations ) != $translations_lenghts_length )
+                        return false;
+
+                // transform raw data into set of indices
+                $originals    = $reader-&gt;str_split( $originals, 8 );
+                $translations = $reader-&gt;str_split( $translations, 8 );
+
+                // skip hash table
+                $strings_addr = $hash_addr + $hash_length * 4;
+
+                $reader-&gt;seekto($strings_addr);
+
+                $strings = $reader-&gt;read_all();
+                $reader-&gt;close();
+
+                for ( $i = 0; $i &lt; $total; $i++ ) {
+                        $o = unpack( &quot;{$endian}length/{$endian}pos&quot;, $originals[$i] );
+                        $t = unpack( &quot;{$endian}length/{$endian}pos&quot;, $translations[$i] );
+                        if ( !$o || !$t ) return false;
+
+                        // adjust offset due to reading strings to separate space before
+                        $o['pos'] -= $strings_addr;
+                        $t['pos'] -= $strings_addr;
+
+                        $original    = $reader-&gt;substr( $strings, $o['pos'], $o['length'] );
+                        $translation = $reader-&gt;substr( $strings, $t['pos'], $t['length'] );
+
+                        if ('' === $original) {
</ins><span class="cx">                                 $this-&gt;set_headers($this-&gt;make_headers($translation));
</span><span class="cx">                         } else {
</span><del>-                                $this-&gt;add_entry($this-&gt;make_entry($original, $translation));
</del><ins>+                                $entry = &amp;$this-&gt;make_entry($original, $translation);
+                                $this-&gt;entries[$entry-&gt;key()] = &amp;$entry;
</ins><span class="cx">                         }
</span><span class="cx">                 }
</span><span class="cx">                 return true;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         /**
</span><ins>+         * Build a Translation_Entry from original string and translation strings,
+         * found in a MO file
+         * 
</ins><span class="cx">          * @static
</span><ins>+         * @param string $original original string to translate from MO file. Might contain
+         *         0x04 as context separator or 0x00 as singular/plural separator
+         * @param string $translation translation string from MO file. Might contain
+         *         0x00 as a plural translations separator
</ins><span class="cx">          */
</span><span class="cx">         function &amp;make_entry($original, $translation) {
</span><del>-                $args = array();
</del><ins>+                $entry = &amp; new Translation_Entry();
</ins><span class="cx">                 // look for context
</span><span class="cx">                 $parts = explode(chr(4), $original);
</span><span class="cx">                 if (isset($parts[1])) {
</span><span class="cx">                         $original = $parts[1];
</span><del>-                        $args['context'] = $parts[0];
</del><ins>+                        $entry-&gt;context = $parts[0];
</ins><span class="cx">                 }
</span><span class="cx">                 // look for plural original
</span><span class="cx">                 $parts = explode(chr(0), $original);
</span><del>-                $args['singular'] = $parts[0];
</del><ins>+                $entry-&gt;singular = $parts[0];
</ins><span class="cx">                 if (isset($parts[1])) {
</span><del>-                        $args['plural'] = $parts[1];
</del><ins>+                        $entry-&gt;is_plural = true;
+                        $entry-&gt;plural = $parts[1];
</ins><span class="cx">                 }
</span><span class="cx">                 // plural translations are also separated by \0
</span><del>-                $args['translations'] = explode(chr(0), $translation);
-                $entry = &amp; new Translation_Entry($args);
</del><ins>+                $entry-&gt;translations = explode(chr(0), $translation);
</ins><span class="cx">                 return $entry;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -178,7 +227,5 @@
</span><span class="cx">         function get_plural_forms_count() {
</span><span class="cx">                 return $this-&gt;_nplurals;
</span><span class="cx">         }
</span><del>-
-
</del><span class="cx"> }
</span><span class="cx"> endif;
</span><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkwpincludespomostreamsphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/pomo/streams.php (12173 => 12174)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/pomo/streams.php        2009-11-12 14:43:15 UTC (rev 12173)
+++ trunk/wp-includes/pomo/streams.php        2009-11-12 16:05:43 UTC (rev 12174)
</span><span class="lines">@@ -3,64 +3,182 @@
</span><span class="cx">  * Classes, which help reading streams of data from files.
</span><span class="cx">  * Based on the classes from Danilo Segan &lt;danilo@kvota.net&gt;
</span><span class="cx">  *
</span><del>- * @version $Id: streams.php 223 2009-09-07 21:20:13Z nbachiyski $
</del><ins>+ * @version $Id: streams.php 293 2009-11-12 15:43:50Z nbachiyski $
</ins><span class="cx">  * @package pomo
</span><span class="cx">  * @subpackage streams
</span><span class="cx">  */
</span><span class="cx"> 
</span><ins>+if ( !class_exists( 'POMO_Reader' ) ):
+class POMO_Reader {
+        
+        var $endian = 'little';
+        var $_post = '';
+        
+        function POMO_Reader() {
+                $this-&gt;is_overloaded = ((ini_get(&quot;mbstring.func_overload&quot;) &amp; 2) != 0) &amp;&amp; function_exists('mb_substr');
+                $this-&gt;_pos = 0;
+        }
+        
+        /**
+         * Sets the endianness of the file.
+         *
+         * @param $endian string 'big' or 'little'
+         */
+        function setEndian($endian) {
+                $this-&gt;endian = $endian;
+        }
</ins><span class="cx"> 
</span><del>-if ( !class_exists( 'POMO_StringReader' ) ):
-/**
- * Provides file-like methods for manipulating a string instead
- * of a physical file.
- */
-class POMO_StringReader {
-  var $_pos;
-  var $_str;
</del><ins>+        /**
+         * Reads a 32bit Integer from the Stream
+         *
+         * @return mixed The integer, corresponding to the next 32 bits from
+         *         the stream of false if there are not enough bytes or on error
+         */
+        function readint32() {
+                $bytes = $this-&gt;read(4);
+                if (4 != $this-&gt;strlen($bytes))
+                        return false;
+                $endian_letter = ('big' == $this-&gt;endian)? 'N' : 'V';
+                $int = unpack($endian_letter, $bytes);
+                return array_shift($int);
+        }
</ins><span class="cx"> 
</span><del>-        function POMO_StringReader($str = '') {
-                $this-&gt;_str = $str;
-                $this-&gt;_pos = 0;
-                $this-&gt;is_overloaded = ((ini_get(&quot;mbstring.func_overload&quot;) &amp; 2) != 0) &amp;&amp; function_exists('mb_substr');
</del><ins>+        /**
+         * Reads an array of 32-bit Integers from the Stream
+         *
+         * @param integer count How many elements should be read
+         * @return mixed Array of integers or false if there isn't
+         *         enough data or on error
+         */
+        function readint32array($count) {
+                $bytes = $this-&gt;read(4 * $count);
+                if (4*$count != $this-&gt;strlen($bytes))
+                        return false;
+                $endian_letter = ('big' == $this-&gt;endian)? 'N' : 'V';
+                return unpack($endian_letter.$count, $bytes);
</ins><span class="cx">         }
</span><del>-
-        function _substr($string, $start, $length) {
</del><ins>+        
+        
+        function substr($string, $start, $length) {
</ins><span class="cx">                 if ($this-&gt;is_overloaded) {
</span><del>-                        return mb_substr($string,$start,$length,'ascii');
</del><ins>+                        return mb_substr($string, $start, $length, 'ascii');
</ins><span class="cx">                 } else {
</span><del>-                        return substr($string,$start,$length);
</del><ins>+                        return substr($string, $start, $length);
</ins><span class="cx">                 }
</span><span class="cx">         }
</span><span class="cx">         
</span><del>-        function _strlen($string) {
</del><ins>+        function strlen($string) {
</ins><span class="cx">                 if ($this-&gt;is_overloaded) {
</span><del>-                        return mb_strlen($string,'ascii');
</del><ins>+                        return mb_strlen($string, 'ascii');
</ins><span class="cx">                 } else {
</span><span class="cx">                         return strlen($string);
</span><span class="cx">                 }
</span><span class="cx">         }
</span><ins>+        
+        function str_split($string, $chunk_size) {
+                if (!function_exists('str_split')) {
+                        $length = $this-&gt;strlen($string);
+                        $out = array();
+                        for ($i = 0; $i &lt; $length; $i += $chunk_size)
+                                $out[] = $this-&gt;substr($string, $i, $chunk_size);
+                        return $out;
+                } else {
+                        return str_split( $string, $chunk_size );
+                }
+        }
+        
+                
+        function pos() {
+                return $this-&gt;_pos;
+        }
</ins><span class="cx"> 
</span><ins>+        function is_resource() {
+                return true;
+        }
+        
+        function close() {
+                return true;
+        }
+}
+endif;
+
+if ( !class_exists( 'POMO_FileReader' ) ):
+class POMO_FileReader extends POMO_Reader {
+        function POMO_FileReader($filename) {
+                parent::POMO_Reader();
+                $this-&gt;_f = fopen($filename, 'r');
+        }
+        
</ins><span class="cx">         function read($bytes) {
</span><del>-                $data = $this-&gt;_substr($this-&gt;_str, $this-&gt;_pos, $bytes);
</del><ins>+                return fread($this-&gt;_f, $bytes);
+        }
+        
+        function seekto($pos) {
+                if ( -1 == fseek($this-&gt;_f, $pos, SEEK_SET)) {
+                        return false;
+                }
+                $this-&gt;_pos = $pos;
+                return true;
+        }
+        
+        function is_resource() {
+                return is_resource($this-&gt;_f);
+        }
+        
+        function feof() {
+                return feof($this-&gt;_f);
+        }
+        
+        function close() {
+                return fclose($this-&gt;_f);
+        }
+        
+        function read_all() {
+                $all = '';
+                while ( !$this-&gt;feof() )
+                        $all .= $this-&gt;read(4096);
+                return $all;
+        }
+}
+endif;
+
+if ( !class_exists( 'POMO_StringReader' ) ):
+/**
+ * Provides file-like methods for manipulating a string instead
+ * of a physical file.
+ */
+class POMO_StringReader extends POMO_Reader {
+        
+        var $_str = '';
+        
+        function POMO_StringReader($str = '') {
+                parent::POMO_Reader();
+                $this-&gt;_str = $str;
+                $this-&gt;_pos = 0;
+        }
+
+
+        function read($bytes) {
+                $data = $this-&gt;substr($this-&gt;_str, $this-&gt;_pos, $bytes);
</ins><span class="cx">                 $this-&gt;_pos += $bytes;
</span><del>-                if ($this-&gt;_strlen($this-&gt;_str) &lt; $this-&gt;_pos) $this-&gt;_pos = $this-&gt;_strlen($this-&gt;_str);
</del><ins>+                if ($this-&gt;strlen($this-&gt;_str) &lt; $this-&gt;_pos) $this-&gt;_pos = $this-&gt;strlen($this-&gt;_str);
</ins><span class="cx">                 return $data;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         function seekto($pos) {
</span><span class="cx">                 $this-&gt;_pos = $pos;
</span><del>-                if ($this-&gt;_strlen($this-&gt;_str) &lt; $this-&gt;_pos) $this-&gt;_pos = $this-&gt;_strlen($this-&gt;_str);
</del><ins>+                if ($this-&gt;strlen($this-&gt;_str) &lt; $this-&gt;_pos) $this-&gt;_pos = $this-&gt;strlen($this-&gt;_str);
</ins><span class="cx">                 return $this-&gt;_pos;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        function pos() {
-                return $this-&gt;_pos;
</del><ins>+        function length() {
+                return $this-&gt;strlen($this-&gt;_str);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        function length() {
-                return $this-&gt;_strlen($this-&gt;_str);
</del><ins>+        function read_all() {
+                return $this-&gt;substr($this-&gt;_str, $this-&gt;_pos, $this-&gt;strlen($this-&gt;_str));
</ins><span class="cx">         }
</span><del>-
</del><ins>+        
</ins><span class="cx"> }
</span><span class="cx"> endif;
</span><span class="cx"> 
</span><span class="lines">@@ -81,61 +199,11 @@
</span><span class="cx"> 
</span><span class="cx"> if ( !class_exists( 'POMO_CachedIntFileReader' ) ):
</span><span class="cx"> /**
</span><del>- * Allows reading integers from a file.
</del><ins>+ * Reads the contents of the file in the beginning.
</ins><span class="cx">  */
</span><span class="cx"> class POMO_CachedIntFileReader extends POMO_CachedFileReader {
</span><del>-
-        var $endian = 'little';
-
-        /**
-         * Opens a file and caches it.
-         *
-         * @param $filename string name of the file to be opened
-         * @param $endian string endianness of the words in the file, allowed
-         *         values are 'little' or 'big'. Default value is 'little'
-         */
-        function POMO_CachedIntFileReader($filename, $endian = 'little') {
-                $this-&gt;endian = $endian;
</del><ins>+        function POMO_CachedIntFileReader($filename) {
</ins><span class="cx">                 parent::POMO_CachedFileReader($filename);
</span><span class="cx">         }
</span><del>-
-        /**
-         * Sets the endianness of the file.
-         *
-         * @param $endian string 'big' or 'little'
-         */
-        function setEndian($endian) {
-                $this-&gt;endian = $endian;
-        }
-
-        /**
-         * Reads a 32bit Integer from the Stream
-         *
-         * @return mixed The integer, corresponding to the next 32 bits from
-         *         the stream of false if there are not enough bytes or on error
-         */
-        function readint32() {
-                $bytes = $this-&gt;read(4);
-                if (4 != $this-&gt;_strlen($bytes))
-                        return false;
-                $endian_letter = ('big' == $this-&gt;endian)? 'N' : 'V';
-                $int = unpack($endian_letter, $bytes);
-                return array_shift($int);
-        }
-
-        /**
-         * Reads an array of 32-bit Integers from the Stream
-         *
-         * @param integer count How many elements should be read
-         * @return mixed Array of integers or false if there isn't
-         *         enough data or on error
-         */
-        function readint32array($count) {
-                $bytes = $this-&gt;read(4 * $count);
-                if (4*$count != $this-&gt;_strlen($bytes))
-                        return false;
-                $endian_letter = ('big' == $this-&gt;endian)? 'N' : 'V';
-                return unpack($endian_letter.$count, $bytes);
-        }
</del><span class="cx"> }
</span><span class="cx"> endif;
</span><span class="cx">\ No newline at end of file
</span></span></pre>
</div>
</div>

</body>
</html>