Skip to content

Commit 482f95d

Browse files
authored
Optimise path finding to remove the last exists test (#45)
* Optimise path finding and remove the last exists test * Docs
1 parent 1dedb06 commit 482f95d

File tree

1 file changed

+56
-23
lines changed

1 file changed

+56
-23
lines changed

src/main/java/cpw/mods/niofs/union/UnionFileSystem.java

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ public class UnionFileSystem extends FileSystem {
4545
static final String SEP_STRING = "/";
4646

4747

48-
4948
static {
5049
try {
5150
var hackfield = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
@@ -69,8 +68,7 @@ public InputStream buildInputStream(final UnionPath path) {
6968
try {
7069
var bytes = Files.readAllBytes(path);
7170
return new ByteArrayInputStream(bytes);
72-
} catch (IOException ioe)
73-
{
71+
} catch (IOException ioe) {
7472
throw new UncheckedIOException(ioe);
7573
}
7674
}
@@ -79,6 +77,7 @@ private static class NoSuchFileException extends java.nio.file.NoSuchFileExcepti
7977
public NoSuchFileException(final String file) {
8078
super(file);
8179
}
80+
8281
@Override
8382
public synchronized Throwable fillInStackTrace() {
8483
return this;
@@ -95,36 +94,40 @@ public synchronized Throwable fillInStackTrace() {
9594
return this;
9695
}
9796
}
97+
9898
private final UnionPath root = new UnionPath(this, "/");
9999
private final UnionPath notExistingPath = new UnionPath(this, "/SNOWMAN");
100100
private final UnionFileSystemProvider provider;
101101
private final String key;
102102
private final List<Path> basepaths;
103+
private final int lastElementIndex;
103104
private final BiPredicate<String, String> pathFilter;
104-
private final Map<Path,EmbeddedFileSystemMetadata> embeddedFileSystems;
105+
private final Map<Path, EmbeddedFileSystemMetadata> embeddedFileSystems;
105106

106107
public Path getPrimaryPath() {
107-
return basepaths.get(basepaths.size()-1);
108+
return basepaths.get(basepaths.size() - 1);
108109
}
109110

110111
public BiPredicate<String, String> getFilesystemFilter() {
111112
return pathFilter;
112113
}
113114

114-
String getKey() {
115+
String getKey() {
115116
return this.key;
116117
}
117118

118-
private record EmbeddedFileSystemMetadata(Path path, FileSystem fs, SeekableByteChannel fsCh) {}
119+
private record EmbeddedFileSystemMetadata(Path path, FileSystem fs, SeekableByteChannel fsCh) {
120+
}
119121

120122
public UnionFileSystem(final UnionFileSystemProvider provider, final BiPredicate<String, String> pathFilter, final String key, final Path... basepaths) {
121123
this.pathFilter = pathFilter;
122124
this.provider = provider;
123125
this.key = key;
124126
this.basepaths = IntStream.range(0, basepaths.length)
125-
.mapToObj(i->basepaths[basepaths.length - i - 1])
127+
.mapToObj(i -> basepaths[basepaths.length - i - 1])
126128
.filter(Files::exists)
127129
.toList(); // we flip the list so later elements are first in search order.
130+
lastElementIndex = basepaths.length - 1;
128131
this.embeddedFileSystems = this.basepaths.stream().filter(path -> !Files.isDirectory(path))
129132
.map(UnionFileSystem::openFileSystem)
130133
.flatMap(Optional::stream)
@@ -240,23 +243,36 @@ private Optional<BasicFileAttributes> getFileAttributes(final Path path) {
240243

241244
private Optional<Path> findFirstPathAt(final UnionPath path) {
242245
return this.basepaths.stream()
243-
.map(p->toRealPath(p , path))
244-
.filter(p->p!=notExistingPath)
246+
.map(p -> toRealPath(p, path))
247+
.filter(p -> p != notExistingPath)
245248
.filter(Files::exists)
246249
.findFirst();
247250
}
248251

249252
private static boolean zipFsExists(UnionFileSystem ufs, Path path) {
250253
try {
251-
if (Optional.ofNullable(ufs.embeddedFileSystems.get(path.getFileSystem())).filter(efs->!efs.fsCh.isOpen()).isPresent()) throw new IllegalStateException("The zip file has closed!");
254+
if (Optional.ofNullable(ufs.embeddedFileSystems.get(path.getFileSystem())).filter(efs -> !efs.fsCh.isOpen()).isPresent())
255+
throw new IllegalStateException("The zip file has closed!");
252256
return (boolean) ZIPFS_EXISTS.invoke(path);
253257
} catch (Throwable t) {
254258
throw new IllegalStateException(t);
255259
}
256260
}
257-
private Optional<Path> findFirstFiltered(final UnionPath path) {
258-
for (Path p : this.basepaths) {
259-
Path realPath = toRealPath(p, path);
261+
262+
/**
263+
* Finds the first real {@link Path} that matches the {@link UnionPath#toString() path} of the given {@code unionPath}, and the {@link #pathFilter filter}
264+
* of the file system.
265+
*
266+
* @param unionPath the path to find
267+
* @return an optional containing the first real path that {@link Files#exists(Path, LinkOption...) exists},
268+
* or otherwise the last path, if this file system has at least one {@link #basepaths base path}
269+
*/
270+
private Optional<Path> findFirstFiltered(final UnionPath unionPath) {
271+
// Iterate the first base paths to try to find matching existing files
272+
for (int i = 0; i < lastElementIndex; i++) {
273+
final Path p = this.basepaths.get(i);
274+
final Path realPath = toRealPath(p, unionPath);
275+
// Test if the real path exists and matches the filter of this file system
260276
if (realPath != notExistingPath && testFilter(realPath, p)) {
261277
if (realPath.getFileSystem() == FileSystems.getDefault()) {
262278
if (realPath.toFile().exists()) {
@@ -271,10 +287,21 @@ private Optional<Path> findFirstFiltered(final UnionPath path) {
271287
}
272288
}
273289
}
290+
291+
// Otherwise, if we still haven't found an existing path, return the last possibility without checking its existence
292+
if (lastElementIndex >= 0) {
293+
final Path last = basepaths.get(lastElementIndex);
294+
final Path realPath = toRealPath(last, unionPath);
295+
// We still care about the FS filter, but not about the existence of the real path
296+
if (realPath != notExistingPath && testFilter(realPath, last)) {
297+
return Optional.of(realPath);
298+
}
299+
}
300+
274301
return Optional.empty();
275302
}
276303

277-
private <T> Stream<T> streamPathList(final Function<Path,Optional<T>> function) {
304+
private <T> Stream<T> streamPathList(final Function<Path, Optional<T>> function) {
278305
return this.basepaths.stream()
279306
.map(function)
280307
.flatMap(Optional::stream);
@@ -289,7 +316,7 @@ public <A extends BasicFileAttributes> A readAttributes(final UnionPath path, fi
289316
Path realPath = toRealPath(base, path);
290317
if (realPath != notExistingPath) {
291318
Optional<BasicFileAttributes> fileAttributes = this.getFileAttributes(realPath);
292-
if (fileAttributes.isPresent() && testFilter(realPath, base)) {
319+
if (fileAttributes.isPresent() && testFilter(realPath, base)) {
293320
return (A) fileAttributes.get();
294321
}
295322
}
@@ -302,19 +329,25 @@ public <A extends BasicFileAttributes> A readAttributes(final UnionPath path, fi
302329

303330
public void checkAccess(final UnionPath p, final AccessMode... modes) throws IOException {
304331
try {
305-
findFirstFiltered(p).ifPresentOrElse(path-> {
332+
findFirstFiltered(p).ifPresentOrElse(path -> {
306333
try {
307-
if (modes.length == 0 && path.getFileSystem() == FileSystems.getDefault()) {
308-
if (!path.toFile().exists()) {
309-
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
334+
if (modes.length == 0) {
335+
if (path.getFileSystem() == FileSystems.getDefault()) {
336+
if (!path.toFile().exists()) {
337+
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
338+
}
339+
} else if (path.getFileSystem().provider().getScheme().equals("jar")) {
340+
if (!zipFsExists(this, path)) {
341+
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
342+
}
310343
}
311344
} else {
312345
path.getFileSystem().provider().checkAccess(path, modes);
313346
}
314347
} catch (IOException e) {
315348
throw new UncheckedIOException(e);
316349
}
317-
}, ()->{
350+
}, () -> {
318351
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
319352
});
320353
} catch (UncheckedIOException e) {
@@ -359,15 +392,15 @@ public DirectoryStream<Path> newDirStream(final UnionPath path, final DirectoryS
359392
continue;
360393
} else if (dir.getFileSystem() == FileSystems.getDefault() && !dir.toFile().exists()) {
361394
continue;
362-
} else if (dir.getFileSystem().provider().getScheme() == "jar" && !zipFsExists(this, dir)) {
395+
} else if (dir.getFileSystem().provider().getScheme().equals("jar") && !zipFsExists(this, dir)) {
363396
continue;
364397
} else if (Files.notExists(dir)) {
365398
continue;
366399
}
367400
final var isSimple = embeddedFileSystems.containsKey(bp);
368401
try (final var ds = Files.newDirectoryStream(dir, filter)) {
369402
StreamSupport.stream(ds.spliterator(), false)
370-
.filter(p->testFilter(p, bp))
403+
.filter(p -> testFilter(p, bp))
371404
.map(other -> StreamSupport.stream(Spliterators.spliteratorUnknownSize((isSimple ? other : bp.relativize(other)).iterator(), Spliterator.ORDERED), false)
372405
.map(Path::getFileName).map(Path::toString).toArray(String[]::new))
373406
.map(this::fastPath)

0 commit comments

Comments
 (0)