From 92996836d0bf2641aa6acbf5f98b2f915ece86f2 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Sun, 8 Dec 2013 20:13:59 +1100 Subject: [PATCH] MDL-41674 backup: Use a trie for grouped path detection. --- .../grouped_parser_processor.class.php | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/backup/util/xml/parser/processors/grouped_parser_processor.class.php b/backup/util/xml/parser/processors/grouped_parser_processor.class.php index 748daace21b..718b57d8773 100644 --- a/backup/util/xml/parser/processors/grouped_parser_processor.class.php +++ b/backup/util/xml/parser/processors/grouped_parser_processor.class.php @@ -1,5 +1,4 @@ a => b + // => b + // => c => d + // => e => f. + // Grouped paths here are; /a/b, /b, /c/d, /c/e/f. + // There are no nested parent paths, that is an enforced rule so + // we store an empty array to designate that the particular XML path element + // is in fact a grouped path. + // eg; $this->groupedparentprefixtree['a']['b'] = array(); + /** @var array Search tree storing the grouped paths. */ + protected $groupedparentprefixtree; + /** * Keep cache of parent directory paths for XML parsing. * @var array @@ -78,6 +93,18 @@ abstract class grouped_parser_processor extends simplified_parser_processor { throw new progressive_parser_exception('xml_grouped_child_found', $a); } $this->groupedpaths[$path] = true; + + // We check earlier in the function if there is a parent that is above the path + // to be added so we can be sure no parent exists in the tree. + $patharray = explode('/', $path); + $currentpos = &$this->groupedparentprefixtree; + foreach ($patharray as $item) { + if (!isset($currentpos[$item])) { + $currentpos[$item] = array(); + } + // Update the current array position using a reference to allow in-place updates to the array. + $currentpos = &$currentpos[$item]; + } } parent::add_path($path); } @@ -162,13 +189,29 @@ abstract class grouped_parser_processor extends simplified_parser_processor { * false if not */ protected function grouped_parent_exists($path) { - $parentpath = $this->get_parent_path($path); - - while ($parentpath != '/') { - if ($this->path_is_grouped($parentpath)) { - return $parentpath; + // Search the tree structure to find out if one of the paths + // above the $path is a grouped path. + $patharray = explode('/', $this->get_parent_path($path)); + $groupedpath = ''; + $currentpos = &$this->groupedparentprefixtree; + foreach ($patharray as $item) { + // When the item isn't set in the array we know + // there is no parent grouped path. + if (!isset($currentpos[$item])) { + return false; } - $parentpath = $this->get_parent_path($parentpath); + + // When we aren't at the start of the path, continue to build + // a string representation of the path that is traversed. We will + // return the grouped path to the caller if we find one. + if ($item != '') { + $groupedpath .= '/'.$item; + } + + if ($currentpos[$item] == array()) { + return $groupedpath; + } + $currentpos = &$currentpos[$item]; } return false; }