Skip to content

Commit 36a991f

Browse files
committed
Add tests for dir walking, and and optimize the directory stream
to not construct so many strings thru pattern matching. Also SPLIT is a regex. Also add JFR JMH profiler option, useful for inspecting traces Signed-off-by: cpw <cpw+github@weeksfamily.ca>
1 parent 417ea73 commit 36a991f

6 files changed

Lines changed: 77 additions & 19 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@
1616
/modlauncher-9.0.1.jar
1717
/test.jar
1818
/sjh-jmh/build/
19+
*.jfr

sjh-jmh/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ task jmh(type: JavaExec, dependsOn: sourceSets.main.output) {
3535
args '-r', '5s' // iteration time
3636
args '-w', '5s' // warmup time
3737
args '-wi', '2' // warmup iterations
38-
args '-prof', 'stack' // profilers
38+
args '-prof', 'stack'
39+
args '-prof', 'jfr' // profilers
3940
args '-tu', 'ns' // time unit
4041
args '-i', '2' // iterations
4142
args '-f', '1' // forks

sjh-jmh/src/main/java/cpw/mods/niofs/union/benchmarks/UnionFileSystemBenchmark.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,28 +78,33 @@ public void testNativeFileNotExistsNegative(Blackhole blackhole) throws Exceptio
7878
runNativeFileExists("ThisFileNotExists3.txt", false);
7979
}
8080

81-
// @Benchmark
81+
@Benchmark
8282
public void testDirectoryStream(Blackhole blackhole) throws Exception {
83-
runDirStream("cpw/mods/jarhandling", 5, blackhole); //jar 1
84-
runDirStream("net/minecraftforge/common", 72, blackhole); //jar 2
85-
runDirStream("cpw/mods/modlauncher/api", 34, blackhole); //jar 3
83+
runDirStream(fileSystem,"cpw/mods/jarhandling", 5, blackhole); //jar 1
84+
runDirStream(fileSystem,"net/minecraftforge/common", 72, blackhole); //jar 2
85+
runDirStream(fileSystem,"cpw/mods/modlauncher/api", 34, blackhole); //jar 3
8686
}
8787

88-
// @Benchmark
88+
@Benchmark
89+
public void testDirectoryStreamDir(Blackhole blackhole) throws Exception {
90+
runDirStream(dirFileSystem, "/", 4, blackhole); //jar 1
91+
}
92+
93+
@Benchmark
8994
public void testByteChannel(Blackhole blackhole) throws Exception {
9095
runByteChannel("cpw/mods/niofs/union/UnionPath.class", blackhole); //jar 1
9196
runByteChannel("net/minecraftforge/client/event/GuiOpenEvent.class", blackhole); //jar 2
9297
runByteChannel("cpw/mods/modlauncher/Launcher.class", blackhole); //jar 3
9398
}
9499

95-
// @Benchmark
100+
@Benchmark
96101
public void testReadAttributes(Blackhole blackhole) throws Exception {
97102
runReadAttributes("cpw/mods/niofs/union/UnionPath.class", 9550, blackhole); //jar 1
98103
runReadAttributes("net/minecraftforge/client/event/GuiOpenEvent.class", 782, blackhole); //jar 2
99104
runReadAttributes("cpw/mods/modlauncher/Launcher.class", 12648, blackhole); //jar 3
100105
}
101106

102-
// @Benchmark
107+
@Benchmark
103108
public void testCommonPathUtilities(Blackhole blackhole) throws Exception {
104109
var path = fileSystem.getPath("net/minecraftforge/client/event/GuiOpenEvent.class");
105110
blackhole.consume(path.getFileName());
@@ -126,9 +131,9 @@ private static void runExists(String pathString, boolean expected) throws Except
126131
}
127132
}
128133

129-
private static void runDirStream(String pathString, int expectedEntries, Blackhole blackhole) throws Exception {
134+
private static void runDirStream(UnionFileSystem fs, String pathString, int expectedEntries, Blackhole blackhole) throws Exception {
130135
int count = 0;
131-
try (var dirStream = Files.newDirectoryStream(fileSystem.getPath(pathString))) {
136+
try (var dirStream = Files.newDirectoryStream(fs.getPath(pathString))) {
132137
for (Path subpath : dirStream) {
133138
count++;
134139
blackhole.consume(subpath);

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import java.util.Map;
2727
import java.util.Optional;
2828
import java.util.Set;
29+
import java.util.Spliterator;
30+
import java.util.Spliterators;
2931
import java.util.function.BiPredicate;
3032
import java.util.function.Function;
3133
import java.util.stream.Collectors;
@@ -168,6 +170,10 @@ public Path getPath(final String first, final String... more) {
168170
return new UnionPath(this, first);
169171
}
170172

173+
private Path fastPath(final String... parts) {
174+
return new UnionPath(this, false, parts);
175+
}
176+
171177
@Override
172178
public PathMatcher getPathMatcher(final String syntaxAndPattern) {
173179
throw new UnsupportedOperationException();
@@ -191,6 +197,8 @@ private Optional<BasicFileAttributes> getFileAttributes(final Path path) {
191197
try {
192198
if (path.getFileSystem() == FileSystems.getDefault() && !path.toFile().exists()) {
193199
return Optional.empty();
200+
} else if (path.getFileSystem().provider().getScheme().equals("jar") && !zipFsExists(path)) {
201+
return Optional.empty();
194202
} else {
195203
return Optional.of(path.getFileSystem().provider().readAttributes(path, BasicFileAttributes.class));
196204
}
@@ -319,15 +327,18 @@ public DirectoryStream<Path> newDirStream(final UnionPath path, final DirectoryS
319327
continue;
320328
} else if (dir.getFileSystem() == FileSystems.getDefault() && !dir.toFile().exists()) {
321329
continue;
330+
} else if (dir.getFileSystem().provider().getScheme() == "jar" && !zipFsExists(dir)) {
331+
continue;
322332
} else if (Files.notExists(dir)) {
323333
continue;
324334
}
325335
final var isSimple = embeddedFileSystems.containsKey(bp);
326336
try (final var ds = Files.newDirectoryStream(dir, filter)) {
327337
StreamSupport.stream(ds.spliterator(), false)
328338
.filter(p->testFilter(p, bp))
329-
.map(other -> (isSimple ? other : bp.relativize(other)).toString())
330-
.map(this::getPath)
339+
.map(other -> StreamSupport.stream(Spliterators.spliteratorUnknownSize((isSimple ? other : bp.relativize(other)).iterator(), Spliterator.ORDERED), false)
340+
.map(Path::getFileName).map(Path::toString).toArray(String[]::new))
341+
.map(this::fastPath)
331342
.forEachOrdered(allpaths::add);
332343
}
333344
}

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,15 @@
33
import java.io.IOException;
44
import java.net.URI;
55
import java.net.URISyntaxException;
6-
import java.nio.file.*;
7-
import java.util.*;
6+
import java.nio.file.LinkOption;
7+
import java.nio.file.Path;
8+
import java.nio.file.WatchEvent;
9+
import java.nio.file.WatchKey;
10+
import java.nio.file.WatchService;
11+
import java.util.ArrayDeque;
12+
import java.util.Arrays;
13+
import java.util.Deque;
14+
import java.util.Objects;
815
import java.util.function.IntBinaryOperator;
916
import java.util.regex.Pattern;
1017
import java.util.stream.Collectors;
@@ -14,10 +21,13 @@ public class UnionPath implements Path {
1421
private static final Pattern SEPARATOR_BEGIN_END;
1522
private static final Pattern SEPARATOR_DUPLICATES;
1623

24+
private static final Pattern SEPARATOR_SPLIT;
25+
1726
static {
1827
var sep = "(?:"+ Pattern.quote(UnionFileSystem.SEP_STRING) + ")";
1928
SEPARATOR_BEGIN_END = Pattern.compile("^" + sep + "*|" + sep + "*$");
2029
SEPARATOR_DUPLICATES = Pattern.compile(sep + "(?=" + sep + ")");
30+
SEPARATOR_SPLIT = Pattern.compile(sep);
2131
}
2232
private final UnionFileSystem fileSystem;
2333
private final boolean absolute;
@@ -42,7 +52,7 @@ public class UnionPath implements Path {
4252
}
4353

4454
// Private constructor only for known correct split and extra value for absolute
45-
private UnionPath(final UnionFileSystem fileSystem, boolean absolute, final String... pathParts) {
55+
UnionPath(final UnionFileSystem fileSystem, boolean absolute, final String... pathParts) {
4656
this(fileSystem, absolute, false, pathParts);
4757
}
4858

@@ -63,7 +73,7 @@ private String[] getPathParts(final String longstring) {
6373
if (clean.isEmpty())
6474
return new String[0];
6575
else
66-
return clean.split(UnionFileSystem.SEP_STRING);
76+
return SEPARATOR_SPLIT.split(clean);
6777
}
6878

6979
@Override

src/test/java/cpw/mods/niofs/union/TestUnionFS.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
import org.junit.jupiter.api.Test;
44

55
import java.io.IOException;
6-
import java.lang.invoke.MethodHandles;
7-
import java.lang.invoke.MethodType;
8-
import java.nio.file.*;
6+
import java.nio.file.FileSystems;
7+
import java.nio.file.Files;
8+
import java.nio.file.NoSuchFileException;
9+
import java.nio.file.Paths;
910
import java.nio.file.attribute.BasicFileAttributeView;
1011
import java.nio.file.attribute.BasicFileAttributes;
1112
import java.nio.file.attribute.FileAttributeView;
@@ -14,6 +15,7 @@
1415
import java.util.Map;
1516
import java.util.Set;
1617
import java.util.stream.Collectors;
18+
import java.util.stream.StreamSupport;
1719

1820
import static org.junit.jupiter.api.Assertions.*;
1921

@@ -237,4 +239,32 @@ void testFileAttributes() {
237239
// Ensure the attributes are the same through both methods
238240
assertEquals(validAttributes, validViewAttributes);
239241
}
242+
243+
@Test
244+
public void testDirectoryVisitorJar() throws Exception {
245+
final var jar1 = Paths.get("sjh-jmh","src", "testjars", "testjar1.jar").toAbsolutePath().normalize();
246+
final var jar2 = Paths.get("sjh-jmh","src", "testjars", "testjar2.jar").toAbsolutePath().normalize();
247+
final var jar3 = Paths.get("sjh-jmh","src", "testjars", "testjar3.jar").toAbsolutePath().normalize();
248+
249+
final var fileSystem = UFSP.newFileSystem(jar1, Map.of("additional", List.of(jar2, jar3)));
250+
var root = fileSystem.getPath("/");
251+
try (var dirStream = Files.newDirectoryStream(root)) {
252+
assertAll(
253+
StreamSupport.stream(dirStream.spliterator(), false).map(p->()->Files.exists(p))
254+
);
255+
}
256+
}
257+
@Test
258+
public void testDirectoryVisitorDirs() throws Exception {
259+
final var dir1 = Paths.get("src", "test", "resources", "dir1").toAbsolutePath().normalize();
260+
final var dir2 = Paths.get("src", "test", "resources", "dir2").toAbsolutePath().normalize();
261+
262+
final var fileSystem = UFSP.newFileSystem(dir1, Map.of("additional", List.of(dir2)));
263+
var root = fileSystem.getPath("/");
264+
try (var dirStream = Files.newDirectoryStream(root)) {
265+
assertAll(
266+
StreamSupport.stream(dirStream.spliterator(), false).map(p->()->Files.exists(p))
267+
);
268+
}
269+
}
240270
}

0 commit comments

Comments
 (0)