From 9bf48dc8137817a944247e1fbe9923ee8298c3e7 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Fri, 19 Apr 2024 09:04:22 +0200 Subject: [PATCH 1/2] Use file attributes cache when checking subdirectories The file attributes at this point have already been requested in order to check whether the subPath is a regular file and so there is no need for an additional isDir call. --- src/main/java/io/github/classgraph/ClasspathElementDir.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index f5f1c9fe..42e4c674 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -472,7 +472,7 @@ private void scanPathRecursively(final Path path, final LogNode log) { // Recurse into subdirectories for (final Path subPath : pathsInDir) { try { - if (FileUtils.isDir(subPath)) { + if (getFileAttributes.get(subPath).isDirectory()) { scanPathRecursively(subPath, subLog); } } catch (final SecurityException e) { From 3d2ebc2fa6300dfe3b3c73cfd7c1bf5feab0aa89 Mon Sep 17 00:00:00 2001 From: Attila Puskas Date: Fri, 19 Apr 2024 09:08:24 +0200 Subject: [PATCH 2/2] Use Iterator.remove instead of List copy To point is that when we already know from some paths to be just regular files, but not directories then at the end we do not need to check if these are directories. But rather than copying the list we can use iterators to remove the already discovered files. --- .../io/github/classgraph/ClasspathElementDir.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/classgraph/ClasspathElementDir.java b/src/main/java/io/github/classgraph/ClasspathElementDir.java index 42e4c674..20ab5825 100644 --- a/src/main/java/io/github/classgraph/ClasspathElementDir.java +++ b/src/main/java/io/github/classgraph/ClasspathElementDir.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; @@ -409,11 +410,13 @@ private void scanPathRecursively(final Path path, final LogNode log) { // Only scan files in directory if directory is not only an ancestor of an accepted path if (parentMatchStatus != ScanSpecPathMatch.ANCESTOR_OF_ACCEPTED_PATH) { // Do preorder traversal (files in dir, then subdirs), to reduce filesystem cache misses - for (final Path subPath : new ArrayList<>(pathsInDir)) { + final Iterator pathsIterator = pathsInDir.iterator(); + while (pathsIterator.hasNext()) { + final Path subPath = pathsIterator.next(); // Process files in dir before recursing BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); if (fileAttributes.isRegularFile()) { - pathsInDir.remove(subPath); + pathsIterator.remove(); final Path subPathRelative = classpathEltPath.relativize(subPath); final String subPathRelativeStr = FastPathResolver.resolve(subPathRelative.toString()); // If this is a modular jar, ignore all classfiles other than "module-info.class" in the @@ -452,11 +455,13 @@ private void scanPathRecursively(final Path path, final LogNode log) { } } else if (scanSpec.enableClassInfo && dirRelativePathStr.equals("/")) { // Always check for module descriptor in package root, even if package root isn't in accept - for (final Path subPath : new ArrayList<>(pathsInDir)) { + final Iterator pathsIterator = pathsInDir.iterator(); + while (pathsIterator.hasNext()) { + final Path subPath = pathsIterator.next(); if (subPath.getFileName().toString().equals("module-info.class")) { BasicFileAttributes fileAttributes = getFileAttributes.get(subPath); if (fileAttributes.isRegularFile()) { - pathsInDir.remove(subPath); + pathsIterator.remove(); final Resource resource = newResource(subPath, fileAttributes); addAcceptedResource(resource, parentMatchStatus, /* isClassfileOnly = */ true, subLog); try {