path: refactor normalizeArray()
authorNathan Woltman <nwoltman@outlook.com>
Fri, 21 Nov 2014 08:22:07 +0000 (03:22 -0500)
committerBert Belder <bertbelder@gmail.com>
Tue, 9 Dec 2014 16:57:09 +0000 (17:57 +0100)
The normalizeArray() function now avoids using the slow Array#splice()
method to improve performance and now also filters out empty path parts.

Code that pre-filtered empty parts has been removed.

PR-URL: https://github.com/joyent/node/pull/8724
Reviewed-by: Trevor Norris <trev.norris@gmail.com>
lib/path.js

index c7a5f03..1972bdc 100644 (file)
@@ -26,33 +26,30 @@ var util = require('util');
 
 
 // resolves . and .. elements in a path array with directory names there
-// must be no slashes, empty elements, or device names (c:\) in the array
+// must be no slashes or device names (c:\) in the array
 // (so also no leading and trailing slashes - it does not distinguish
 // relative and absolute paths)
 function normalizeArray(parts, allowAboveRoot) {
-  // if the path tries to go above the root, `up` ends up > 0
-  var up = 0;
-  for (var i = parts.length - 1; i >= 0; i--) {
-    var last = parts[i];
-    if (last === '.') {
-      parts.splice(i, 1);
-    } else if (last === '..') {
-      parts.splice(i, 1);
-      up++;
-    } else if (up) {
-      parts.splice(i, 1);
-      up--;
-    }
-  }
+  var res = [];
+  for (var i = 0; i < parts.length; i++) {
+    var p = parts[i];
+
+    // ignore empty parts
+    if (!p || p === '.')
+      continue;
 
-  // if the path is allowed to go above the root, restore leading ..s
-  if (allowAboveRoot) {
-    for (; up--; up) {
-      parts.unshift('..');
+    if (p === '..') {
+      if (res.length && res[res.length - 1] !== '..') {
+        res.pop();
+      } else if (allowAboveRoot) {
+        res.push('..');
+      }
+    } else {
+      res.push(p);
     }
   }
 
-  return parts;
+  return res;
 }
 
 // Regex to split a windows path into three parts: [*, device, slash,
@@ -154,12 +151,7 @@ win32.resolve = function() {
   // fails)
 
   // Normalize the tail path
-
-  function f(p) {
-    return !!p;
-  }
-
-  resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f),
+  resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/),
                                 !resolvedAbsolute).join('\\');
 
   return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) ||
@@ -176,9 +168,7 @@ win32.normalize = function(path) {
       trailingSlash = /[\\\/]$/.test(tail);
 
   // Normalize the tail path
-  tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) {
-    return !!p;
-  }), !isAbsolute).join('\\');
+  tail = normalizeArray(tail.split(/[\\\/]+/), !isAbsolute).join('\\');
 
   if (!tail && !isAbsolute) {
     tail = '.';
@@ -443,9 +433,8 @@ posix.resolve = function() {
   // handle relative paths to be safe (might happen when process.cwd() fails)
 
   // Normalize the path
-  resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) {
-    return !!p;
-  }), !resolvedAbsolute).join('/');
+  resolvedPath = normalizeArray(resolvedPath.split('/'),
+                                !resolvedAbsolute).join('/');
 
   return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
 };
@@ -454,17 +443,10 @@ posix.resolve = function() {
 // posix version
 posix.normalize = function(path) {
   var isAbsolute = posix.isAbsolute(path),
-      trailingSlash = path.substr(-1) === '/',
-      segments = path.split('/'),
-      nonEmptySegments = [];
+      trailingSlash = path.substr(-1) === '/';
 
   // Normalize the path
-  for (var i = 0; i < segments.length; i++) {
-    if (segments[i]) {
-      nonEmptySegments.push(segments[i]);
-    }
-  }
-  path = normalizeArray(nonEmptySegments, !isAbsolute).join('/');
+  path = normalizeArray(path.split('/'), !isAbsolute).join('/');
 
   if (!path && !isAbsolute) {
     path = '.';