Skip to content

Commit

Permalink
Optimise path finding to remove the last exists test (#45)
Browse files Browse the repository at this point in the history
* Optimise path finding and remove the last exists test

* Docs
  • Loading branch information
Matyrobbrt authored Nov 9, 2023
1 parent 1dedb06 commit 482f95d
Showing 1 changed file with 56 additions and 23 deletions.
79 changes: 56 additions & 23 deletions src/main/java/cpw/mods/niofs/union/UnionFileSystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public class UnionFileSystem extends FileSystem {
static final String SEP_STRING = "/";



static {
try {
var hackfield = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
Expand All @@ -69,8 +68,7 @@ public InputStream buildInputStream(final UnionPath path) {
try {
var bytes = Files.readAllBytes(path);
return new ByteArrayInputStream(bytes);
} catch (IOException ioe)
{
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
Expand All @@ -79,6 +77,7 @@ private static class NoSuchFileException extends java.nio.file.NoSuchFileExcepti
public NoSuchFileException(final String file) {
super(file);
}

@Override
public synchronized Throwable fillInStackTrace() {
return this;
Expand All @@ -95,36 +94,40 @@ public synchronized Throwable fillInStackTrace() {
return this;
}
}

private final UnionPath root = new UnionPath(this, "/");
private final UnionPath notExistingPath = new UnionPath(this, "/SNOWMAN");
private final UnionFileSystemProvider provider;
private final String key;
private final List<Path> basepaths;
private final int lastElementIndex;
private final BiPredicate<String, String> pathFilter;
private final Map<Path,EmbeddedFileSystemMetadata> embeddedFileSystems;
private final Map<Path, EmbeddedFileSystemMetadata> embeddedFileSystems;

public Path getPrimaryPath() {
return basepaths.get(basepaths.size()-1);
return basepaths.get(basepaths.size() - 1);
}

public BiPredicate<String, String> getFilesystemFilter() {
return pathFilter;
}

String getKey() {
String getKey() {
return this.key;
}

private record EmbeddedFileSystemMetadata(Path path, FileSystem fs, SeekableByteChannel fsCh) {}
private record EmbeddedFileSystemMetadata(Path path, FileSystem fs, SeekableByteChannel fsCh) {
}

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

private Optional<Path> findFirstPathAt(final UnionPath path) {
return this.basepaths.stream()
.map(p->toRealPath(p , path))
.filter(p->p!=notExistingPath)
.map(p -> toRealPath(p, path))
.filter(p -> p != notExistingPath)
.filter(Files::exists)
.findFirst();
}

private static boolean zipFsExists(UnionFileSystem ufs, Path path) {
try {
if (Optional.ofNullable(ufs.embeddedFileSystems.get(path.getFileSystem())).filter(efs->!efs.fsCh.isOpen()).isPresent()) throw new IllegalStateException("The zip file has closed!");
if (Optional.ofNullable(ufs.embeddedFileSystems.get(path.getFileSystem())).filter(efs -> !efs.fsCh.isOpen()).isPresent())
throw new IllegalStateException("The zip file has closed!");
return (boolean) ZIPFS_EXISTS.invoke(path);
} catch (Throwable t) {
throw new IllegalStateException(t);
}
}
private Optional<Path> findFirstFiltered(final UnionPath path) {
for (Path p : this.basepaths) {
Path realPath = toRealPath(p, path);

/**
* Finds the first real {@link Path} that matches the {@link UnionPath#toString() path} of the given {@code unionPath}, and the {@link #pathFilter filter}
* of the file system.
*
* @param unionPath the path to find
* @return an optional containing the first real path that {@link Files#exists(Path, LinkOption...) exists},
* or otherwise the last path, if this file system has at least one {@link #basepaths base path}
*/
private Optional<Path> findFirstFiltered(final UnionPath unionPath) {
// Iterate the first base paths to try to find matching existing files
for (int i = 0; i < lastElementIndex; i++) {
final Path p = this.basepaths.get(i);
final Path realPath = toRealPath(p, unionPath);
// Test if the real path exists and matches the filter of this file system
if (realPath != notExistingPath && testFilter(realPath, p)) {
if (realPath.getFileSystem() == FileSystems.getDefault()) {
if (realPath.toFile().exists()) {
Expand All @@ -271,10 +287,21 @@ private Optional<Path> findFirstFiltered(final UnionPath path) {
}
}
}

// Otherwise, if we still haven't found an existing path, return the last possibility without checking its existence
if (lastElementIndex >= 0) {
final Path last = basepaths.get(lastElementIndex);
final Path realPath = toRealPath(last, unionPath);
// We still care about the FS filter, but not about the existence of the real path
if (realPath != notExistingPath && testFilter(realPath, last)) {
return Optional.of(realPath);
}
}

return Optional.empty();
}

private <T> Stream<T> streamPathList(final Function<Path,Optional<T>> function) {
private <T> Stream<T> streamPathList(final Function<Path, Optional<T>> function) {
return this.basepaths.stream()
.map(function)
.flatMap(Optional::stream);
Expand All @@ -289,7 +316,7 @@ public <A extends BasicFileAttributes> A readAttributes(final UnionPath path, fi
Path realPath = toRealPath(base, path);
if (realPath != notExistingPath) {
Optional<BasicFileAttributes> fileAttributes = this.getFileAttributes(realPath);
if (fileAttributes.isPresent() && testFilter(realPath, base)) {
if (fileAttributes.isPresent() && testFilter(realPath, base)) {
return (A) fileAttributes.get();
}
}
Expand All @@ -302,19 +329,25 @@ public <A extends BasicFileAttributes> A readAttributes(final UnionPath path, fi

public void checkAccess(final UnionPath p, final AccessMode... modes) throws IOException {
try {
findFirstFiltered(p).ifPresentOrElse(path-> {
findFirstFiltered(p).ifPresentOrElse(path -> {
try {
if (modes.length == 0 && path.getFileSystem() == FileSystems.getDefault()) {
if (!path.toFile().exists()) {
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
if (modes.length == 0) {
if (path.getFileSystem() == FileSystems.getDefault()) {
if (!path.toFile().exists()) {
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
}
} else if (path.getFileSystem().provider().getScheme().equals("jar")) {
if (!zipFsExists(this, path)) {
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
}
}
} else {
path.getFileSystem().provider().checkAccess(path, modes);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}, ()->{
}, () -> {
throw new UncheckedIOException(new NoSuchFileException(p.toString()));
});
} catch (UncheckedIOException e) {
Expand Down Expand Up @@ -359,15 +392,15 @@ public DirectoryStream<Path> newDirStream(final UnionPath path, final DirectoryS
continue;
} else if (dir.getFileSystem() == FileSystems.getDefault() && !dir.toFile().exists()) {
continue;
} else if (dir.getFileSystem().provider().getScheme() == "jar" && !zipFsExists(this, dir)) {
} else if (dir.getFileSystem().provider().getScheme().equals("jar") && !zipFsExists(this, dir)) {
continue;
} else if (Files.notExists(dir)) {
continue;
}
final var isSimple = embeddedFileSystems.containsKey(bp);
try (final var ds = Files.newDirectoryStream(dir, filter)) {
StreamSupport.stream(ds.spliterator(), false)
.filter(p->testFilter(p, bp))
.filter(p -> testFilter(p, bp))
.map(other -> StreamSupport.stream(Spliterators.spliteratorUnknownSize((isSimple ? other : bp.relativize(other)).iterator(), Spliterator.ORDERED), false)
.map(Path::getFileName).map(Path::toString).toArray(String[]::new))
.map(this::fastPath)
Expand Down

0 comments on commit 482f95d

Please sign in to comment.