From dbd52c81e07da4440f3afc2e248c5b036f6bbec7 Mon Sep 17 00:00:00 2001
From: Andreas Gohr <andi@splitbrain.org>
Date: Thu, 2 Jun 2016 20:32:50 +0200
Subject: [PATCH] minor optimization how cell counts are fixed in tables

array splice operations are expensive. when many cells in a table row
are missing, the operation should be executed only once instead for each
missing cell.
---
 _test/tests/inc/parser/parser_table.test.php | 117 ++++++++++++++++---
 inc/parser/handler.php                       |  14 ++-
 2 files changed, 108 insertions(+), 23 deletions(-)

diff --git a/_test/tests/inc/parser/parser_table.test.php b/_test/tests/inc/parser/parser_table.test.php
index a9b4e284c..f05dd29aa 100644
--- a/_test/tests/inc/parser/parser_table.test.php
+++ b/_test/tests/inc/parser/parser_table.test.php
@@ -93,7 +93,7 @@ def');
 abc
 |
 def');
-        
+
         $calls = array (
             array('document_start',array()),
             array('p_open',array()),
@@ -111,14 +111,14 @@ def');
 
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
     }
-    
+
     function testTableHeaders() {
         $this->P->addMode('table',new Doku_Parser_Mode_Table());
         $this->P->parse('
 abc
 ^ X | Y ^ Z |
 def');
-    
+
         $calls = array (
             array('document_start',array()),
             array('p_open',array()),
@@ -291,14 +291,14 @@ def');
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
 
     }
-    
+
     function testCellAlignment() {
         $this->P->addMode('table',new Doku_Parser_Mode_Table());
         $this->P->parse('
 abc
 |  X | Y  ^  Z  |
 def');
-    
+
         $calls = array (
             array('document_start',array()),
             array('p_open',array()),
@@ -325,7 +325,7 @@ def');
 
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
     }
-    
+
     function testCellSpan() {
         $this->P->addMode('table',new Doku_Parser_Mode_Table());
         $this->P->parse('
@@ -334,7 +334,7 @@ abc
 | f ^ ^|
 ||||
 def');
-        
+
         $calls = array (
             array('document_start',array()),
             array('p_open',array()),
@@ -367,7 +367,7 @@ def');
         );
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
     }
-    
+
     function testCellRowSpan() {
         $this->P->addMode('table',new Doku_Parser_Mode_Table());
         $this->P->parse('
@@ -376,7 +376,7 @@ abc
 |:::^ d  | e|
 |b  ^  ::: |:::f|
 def');
-        
+
         $calls = array (
             array('document_start',array()),
             array('p_open',array()),
@@ -473,7 +473,7 @@ def');
         );
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
     }
-    
+
     function testRowSpanTableHead() {
         $this->P->addMode('table',new Doku_Parser_Mode_Table());
         $this->P->parse('
@@ -606,7 +606,7 @@ def');
 abc
 |  **X** | Y  ^  Z  |
 def');
-    
+
         $calls = array (
             array('document_start',array()),
             array('p_open',array()),
@@ -634,11 +634,11 @@ def');
             array('p_close',array()),
             array('document_end',array()),
         );
- 
+
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
-        
+
     }
-    
+
     function testTableEol() {
         $this->P->addMode('table',new Doku_Parser_Mode_Table());
         $this->P->addMode('eol',new Doku_Parser_Mode_Eol());
@@ -683,7 +683,7 @@ def');
         );
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
     }
-    
+
     // This is really a failing test - formatting able to spread across cols
     // Problem is fixing it would mean a major rewrite of table handling
     function testTableStrong() {
@@ -738,7 +738,7 @@ def');
         );
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
     }
-    
+
     // This is really a failing test - unformatted able to spread across cols
     // Problem is fixing it would mean a major rewrite of table handling
     function testTableUnformatted() {
@@ -789,7 +789,7 @@ def');
         );
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
     }
-    
+
     function testTableLinebreak() {
         $this->P->addMode('table',new Doku_Parser_Mode_Table());
         $this->P->addMode('linebreak',new Doku_Parser_Mode_Linebreak());
@@ -837,7 +837,7 @@ def');
 
         $this->assertEquals($calls,array_map('stripbyteindex',$this->H->calls));
     }
-    
+
     // This is really a failing test - footnote able to spread across cols
     // Problem is fixing it would mean a major rewrite of table handling
     function testTableFootnote() {
@@ -915,4 +915,85 @@ def');
         $this->assertEquals(array_map('stripbyteindex',$this->H->calls),$calls);
     }
 
+    /**
+     * missing cells in one row get filled up...
+     */
+    function testTable_CellFix() {
+        $syntax = "\n| r1c1 | r1c2 | r1c3 |\n| r2c1 |\n";
+        $this->P->addMode('table',new Doku_Parser_Mode_Table());
+        $this->P->parse($syntax);
+        $calls = array (
+            array('document_start',array()),
+            array('table_open',array(3, 2, 2)),
+
+            array('tablerow_open',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array(' r1c1 ')),
+            array('tablecell_close',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array(' r1c2 ')),
+            array('tablecell_close',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array(' r1c3 ')),
+            array('tablecell_close',array()),
+            array('tablerow_close',array()),
+
+            array('tablerow_open',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array(' r2c1 ')),
+            array('tablecell_close',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array('')),
+            array('tablecell_close',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array('')),
+            array('tablecell_close',array()),
+            array('tablerow_close',array()),
+
+            array('table_close',array(strlen($syntax))),
+            array('document_end',array()),
+        );
+        $this->assertEquals($calls, array_map('stripbyteindex',$this->H->calls));
+    }
+
+    /**
+     * ... even if the longer row comes later
+     */
+    function testTable_CellFix2() {
+        $syntax = "\n| r1c1 |\n| r2c1 | r2c2 | r2c3 |\n";
+        $this->P->addMode('table',new Doku_Parser_Mode_Table());
+        $this->P->parse($syntax);
+        $calls = array (
+            array('document_start',array()),
+            array('table_open',array(3, 2, 2)),
+
+            array('tablerow_open',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array(' r1c1 ')),
+            array('tablecell_close',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array('')),
+            array('tablecell_close',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array('')),
+            array('tablecell_close',array()),
+            array('tablerow_close',array()),
+
+            array('tablerow_open',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array(' r2c1 ')),
+            array('tablecell_close',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array(' r2c2 ')),
+            array('tablecell_close',array()),
+            array('tablecell_open',array(1,null,1)),
+            array('cdata',array(' r2c3 ')),
+            array('tablecell_close',array()),
+            array('tablerow_close',array()),
+
+            array('table_close',array(strlen($syntax))),
+            array('document_end',array()),
+        );
+        $this->assertEquals($calls, array_map('stripbyteindex',$this->H->calls));
+    }
 }
diff --git a/inc/parser/handler.php b/inc/parser/handler.php
index 815ac39c5..f477d36f8 100644
--- a/inc/parser/handler.php
+++ b/inc/parser/handler.php
@@ -1470,12 +1470,16 @@ class Doku_Handler_Table implements Doku_Handler_CallWriter_Interface {
                 case 'tablerow_close':
 
                     // Fix broken tables by adding missing cells
+                    $moreCalls = array();
                     while (++$lastCell < $this->maxCols) {
-                        array_splice($this->tableCalls, $key, 0, array(
-                               array('tablecell_open', array(1, null, 1), $call[2]),
-                               array('cdata', array(''), $call[2]),
-                               array('tablecell_close', array(), $call[2])));
-                        $key += 3;
+                        $moreCalls[] = array('tablecell_open', array(1, null, 1), $call[2]);
+                        $moreCalls[] = array('cdata', array(''), $call[2]);
+                        $moreCalls[] = array('tablecell_close', array(), $call[2]);
+                    }
+                    $moreCallsLength = count($moreCalls);
+                    if($moreCallsLength) {
+                        array_splice($this->tableCalls, $key, 0, $moreCalls);
+                        $key += $moreCallsLength;
                     }
 
                     if($this->countTableHeadRows == $lastRow) {
-- 
GitLab