8172921: Zip filesystem performance improvement and code cleanup
Reviewed-by: redestad
--- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java Wed Jan 18 11:08:46 2017 -0800
+++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java Wed Jan 18 11:18:13 2017 -0800
@@ -135,7 +135,7 @@
TreeMap<Integer,IndexNode> map = new TreeMap<>();
IndexNode child = metaInfVersions.child;
while (child != null) {
- Integer key = getVersion(child.name, metaInfVersions.name.length);
+ Integer key = getVersion(child.name, metaInfVersions.name.length + 1);
if (key != null && key <= version) {
map.put(key, child);
}
@@ -149,7 +149,7 @@
*/
private Integer getVersion(byte[] name, int offset) {
try {
- return Integer.parseInt(getString(Arrays.copyOfRange(name, offset, name.length-1)));
+ return Integer.parseInt(getString(Arrays.copyOfRange(name, offset, name.length)));
} catch (NumberFormatException x) {
// ignore this even though it might indicate issues with the JAR structure
return null;
@@ -176,7 +176,7 @@
* returns foo/bar.class
*/
private byte[] getRootName(IndexNode prefix, IndexNode inode) {
- int offset = prefix.name.length - 1;
+ int offset = prefix.name.length;
byte[] fullName = inode.name;
return Arrays.copyOfRange(fullName, offset, fullName.length);
}
--- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipCoder.java Wed Jan 18 11:08:46 2017 -0800
+++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipCoder.java Wed Jan 18 11:18:13 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,97 +34,95 @@
import java.nio.charset.CodingErrorAction;
import java.util.Arrays;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+
/**
* Utility class for zipfile name and comment decoding and encoding
*
* @author Xueming Shen
*/
-final class ZipCoder {
+class ZipCoder {
+
+ static class UTF8 extends ZipCoder {
+ UTF8() {
+ super(UTF_8);
+ }
+
+ @Override
+ byte[] getBytes(String s) { // fast pass for ascii
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) > 0x7f) return super.getBytes(s);
+ }
+ return s.getBytes(ISO_8859_1);
+ }
- String toString(byte[] ba, int length) {
+ @Override
+ String toString(byte[] ba) {
+ for (byte b : ba) {
+ if (b < 0) return super.toString(ba);
+ }
+ return new String(ba, ISO_8859_1);
+ }
+ }
+
+ private static ZipCoder utf8 = new UTF8();
+
+ public static ZipCoder get(String csn) {
+ Charset cs = Charset.forName(csn);
+ if (cs.name().equals("UTF-8")) {
+ return utf8;
+ }
+ return new ZipCoder(cs);
+ }
+
+ String toString(byte[] ba) {
CharsetDecoder cd = decoder().reset();
- int len = (int)(length * cd.maxCharsPerByte());
- char[] ca = new char[len];
- if (len == 0)
- return new String(ca);
- ByteBuffer bb = ByteBuffer.wrap(ba, 0, length);
+ int clen = (int)(ba.length * cd.maxCharsPerByte());
+ char[] ca = new char[clen];
+ if (clen == 0)
+ return new String(ca);
+ ByteBuffer bb = ByteBuffer.wrap(ba, 0, ba.length);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow())
- throw new IllegalArgumentException(cr.toString());
+ throw new IllegalArgumentException(cr.toString());
cr = cd.flush(cb);
if (!cr.isUnderflow())
- throw new IllegalArgumentException(cr.toString());
+ throw new IllegalArgumentException(cr.toString());
return new String(ca, 0, cb.position());
}
- String toString(byte[] ba) {
- return toString(ba, ba.length);
- }
-
byte[] getBytes(String s) {
CharsetEncoder ce = encoder().reset();
char[] ca = s.toCharArray();
int len = (int)(ca.length * ce.maxBytesPerChar());
byte[] ba = new byte[len];
if (len == 0)
- return ba;
+ return ba;
ByteBuffer bb = ByteBuffer.wrap(ba);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = ce.encode(cb, bb, true);
if (!cr.isUnderflow())
- throw new IllegalArgumentException(cr.toString());
+ throw new IllegalArgumentException(cr.toString());
cr = ce.flush(bb);
if (!cr.isUnderflow())
- throw new IllegalArgumentException(cr.toString());
+ throw new IllegalArgumentException(cr.toString());
if (bb.position() == ba.length) // defensive copy?
- return ba;
+ return ba;
else
- return Arrays.copyOf(ba, bb.position());
- }
-
- // assume invoked only if "this" is not utf8
- byte[] getBytesUTF8(String s) {
- if (isutf8)
- return getBytes(s);
- if (utf8 == null)
- utf8 = new ZipCoder(Charset.forName("UTF-8"));
- return utf8.getBytes(s);
- }
-
- String toStringUTF8(byte[] ba, int len) {
- if (isutf8)
- return toString(ba, len);
- if (utf8 == null)
- utf8 = new ZipCoder(Charset.forName("UTF-8"));
- return utf8.toString(ba, len);
+ return Arrays.copyOf(ba, bb.position());
}
boolean isUTF8() {
- return isutf8;
+ return cs == UTF_8;
}
private Charset cs;
- private boolean isutf8;
- private ZipCoder utf8;
private ZipCoder(Charset cs) {
this.cs = cs;
- this.isutf8 = cs.name().equals("UTF-8");
- }
-
- static ZipCoder get(Charset charset) {
- return new ZipCoder(charset);
- }
-
- static ZipCoder get(String csn) {
- try {
- return new ZipCoder(Charset.forName(csn));
- } catch (Throwable t) {
- t.printStackTrace();
- }
- return new ZipCoder(Charset.defaultCharset());
}
private final ThreadLocal<CharsetDecoder> decTL = new ThreadLocal<>();
@@ -133,10 +131,10 @@
private CharsetDecoder decoder() {
CharsetDecoder dec = decTL.get();
if (dec == null) {
- dec = cs.newDecoder()
- .onMalformedInput(CodingErrorAction.REPORT)
- .onUnmappableCharacter(CodingErrorAction.REPORT);
- decTL.set(dec);
+ dec = cs.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ decTL.set(dec);
}
return dec;
}
@@ -144,10 +142,10 @@
private CharsetEncoder encoder() {
CharsetEncoder enc = encTL.get();
if (enc == null) {
- enc = cs.newEncoder()
- .onMalformedInput(CodingErrorAction.REPORT)
- .onUnmappableCharacter(CodingErrorAction.REPORT);
- encTL.set(enc);
+ enc = cs.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ encTL.set(enc);
}
return enc;
}
--- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java Wed Jan 18 11:08:46 2017 -0800
+++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java Wed Jan 18 11:18:13 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -314,8 +314,8 @@
IndexNode inode = getInode(path);
if (inode == null)
return null;
- e = new Entry(inode.name); // pseudo directory
- e.method = METHOD_STORED; // STORED for dir
+ e = new Entry(inode.name, inode.isdir); // pseudo directory
+ e.method = METHOD_STORED; // STORED for dir
e.mtime = e.atime = e.ctime = zfsDefaultTimeStamp;
}
} finally {
@@ -400,7 +400,8 @@
List<Path> list = new ArrayList<>();
IndexNode child = inode.child;
while (child != null) {
- ZipPath zp = new ZipPath(this, child.name);
+ // assume all path from zip file itself is "normalized"
+ ZipPath zp = new ZipPath(this, child.name, true);
if (filter == null || filter.accept(zp))
list.add(zp);
child = child.sibling;
@@ -415,14 +416,14 @@
throws IOException
{
checkWritable();
- dir = toDirectoryPath(dir);
+ // dir = toDirectoryPath(dir);
beginWrite();
try {
ensureOpen();
if (dir.length == 0 || exists(dir)) // root dir, or exiting dir
throw new FileAlreadyExistsException(getString(dir));
checkParents(dir);
- Entry e = new Entry(dir, Entry.NEW);
+ Entry e = new Entry(dir, Entry.NEW, true);
e.method = METHOD_STORED; // STORED for dir
update(e);
} finally {
@@ -463,7 +464,7 @@
} else {
checkParents(dst);
}
- Entry u = new Entry(eSrc, Entry.COPY); // copy eSrc entry
+ Entry u = new Entry(eSrc, Entry.COPY); // copy eSrc entry
u.name(dst); // change name
if (eSrc.type == Entry.NEW || eSrc.type == Entry.FILECH)
{
@@ -533,7 +534,7 @@
if (!hasCreate && !hasCreateNew)
throw new NoSuchFileException(getString(path));
checkParents(path);
- return getOutputStream(new Entry(path, Entry.NEW));
+ return getOutputStream(new Entry(path, Entry.NEW, false));
}
} finally {
endRead();
@@ -887,7 +888,7 @@
int off = getParentOff(path);
if (off <= 1)
return ROOTPATH;
- return Arrays.copyOf(path, off + 1);
+ return Arrays.copyOf(path, off);
}
private static int getParentOff(byte[] path) {
@@ -1075,11 +1076,9 @@
if (pos + CENHDR + nlen > limit) {
zerror("invalid CEN header (bad header size)");
}
- byte[] name = new byte[nlen + 1];
- System.arraycopy(cen, pos + CENHDR, name, 1, nlen);
- name[0] = '/';
- IndexNode inode = new IndexNode(name, pos);
+ IndexNode inode = new IndexNode(cen, pos + CENHDR, nlen, pos);
inodes.put(inode, inode);
+
// skip ext and comment
pos += (CENHDR + nlen + elen + clen);
}
@@ -1112,7 +1111,7 @@
private boolean hasUpdate = false;
// shared key. consumer guarantees the "writeLock" before use it.
- private final IndexNode LOOKUPKEY = IndexNode.keyOf(null);
+ private final IndexNode LOOKUPKEY = new IndexNode(null, -1);
private void updateDelete(IndexNode inode) {
beginWrite();
@@ -1312,16 +1311,7 @@
IndexNode getInode(byte[] path) {
if (path == null)
throw new NullPointerException("path");
- IndexNode key = IndexNode.keyOf(path);
- IndexNode inode = inodes.get(key);
- if (inode == null &&
- (path.length == 0 || path[path.length -1] != '/')) {
- // if does not ends with a slash
- path = Arrays.copyOf(path, path.length + 1);
- path[path.length - 1] = '/';
- inode = inodes.get(key.as(path));
- }
- return inode;
+ return inodes.get(IndexNode.keyOf(path));
}
Entry getEntry(byte[] path) throws IOException {
@@ -1782,8 +1772,11 @@
int hashcode; // node is hashable/hashed by its name
int pos = -1; // position in cen table, -1 menas the
// entry does not exists in zip file
- IndexNode(byte[] name) {
+ boolean isdir;
+
+ IndexNode(byte[] name, boolean isdir) {
name(name);
+ this.isdir = isdir;
this.pos = -1;
}
@@ -1792,8 +1785,28 @@
this.pos = pos;
}
+ // constructor for cenInit()
+ IndexNode(byte[] cen, int noff, int nlen, int pos) {
+ if (cen[noff + nlen - 1] == '/') {
+ isdir = true;
+ nlen--;
+ }
+ name = new byte[nlen + 1];
+ System.arraycopy(cen, pos + CENHDR, name, 1, nlen);
+ name[0] = '/';
+ name(name);
+ this.pos = pos;
+ }
+
+ private static final ThreadLocal<IndexNode> cachedKey = new ThreadLocal<>();
+
final static IndexNode keyOf(byte[] name) { // get a lookup key;
- return new IndexNode(name, -1);
+ IndexNode key = cachedKey.get();
+ if (key == null) {
+ key = new IndexNode(name, -1);
+ cachedKey.set(key);
+ }
+ return key.as(name);
}
final void name(byte[] name) {
@@ -1807,8 +1820,7 @@
}
boolean isDir() {
- return name != null &&
- (name.length == 0 || name[name.length - 1] == '/');
+ return isdir;
}
public boolean equals(Object other) {
@@ -1865,8 +1877,9 @@
Entry() {}
- Entry(byte[] name) {
+ Entry(byte[] name, boolean isdir) {
name(name);
+ this.isdir = isdir;
this.mtime = this.ctime = this.atime = System.currentTimeMillis();
this.crc = 0;
this.size = 0;
@@ -1874,13 +1887,14 @@
this.method = METHOD_DEFLATED;
}
- Entry(byte[] name, int type) {
- this(name);
+ Entry(byte[] name, int type, boolean isdir) {
+ this(name, isdir);
this.type = type;
}
Entry (Entry e, int type) {
name(e.name);
+ this.isdir = e.isdir;
this.version = e.version;
this.ctime = e.ctime;
this.atime = e.atime;
@@ -1902,7 +1916,7 @@
}
Entry (byte[] name, Path file, int type) {
- this(name, type);
+ this(name, type, false);
this.file = file;
this.method = METHOD_STORED;
}
@@ -1948,6 +1962,7 @@
locoff = CENOFF(cen, pos);
pos += CENHDR;
this.name = inode.name;
+ this.isdir = inode.isdir;
this.hashcode = inode.hashcode;
pos += nlen;
@@ -1974,9 +1989,10 @@
int elenEXTT = 0; // extra for Extended Timestamp
boolean foundExtraTime = false; // if time stamp NTFS, EXTT present
- // confirm size/length
+ byte[] zname = isdir ? toDirectoryPath(name) : name;
- int nlen = (name != null) ? name.length - 1 : 0; // name has [0] as "slash"
+ // confirm size/length
+ int nlen = (zname != null) ? zname.length - 1 : 0; // name has [0] as "slash"
int elen = (extra != null) ? extra.length : 0;
int eoff = 0;
int clen = (comment != null) ? comment.length : 0;
@@ -2037,7 +2053,7 @@
writeShort(os, 0); // internal file attributes (unused)
writeInt(os, 0); // external file attributes (unused)
writeInt(os, locoff0); // relative offset of local header
- writeBytes(os, name, 1, nlen);
+ writeBytes(os, zname, 1, nlen);
if (elen64 != 0) {
writeShort(os, EXTID_ZIP64);// Zip64 extra
writeShort(os, elen64 - 4); // size of "this" extra block
@@ -2075,87 +2091,13 @@
}
///////////////////// LOC //////////////////////
- static Entry readLOC(ZipFileSystem zipfs, long pos)
- throws IOException
- {
- return readLOC(zipfs, pos, new byte[1024]);
- }
-
- static Entry readLOC(ZipFileSystem zipfs, long pos, byte[] buf)
- throws IOException
- {
- return new Entry().loc(zipfs, pos, buf);
- }
-
- Entry loc(ZipFileSystem zipfs, long pos, byte[] buf)
- throws IOException
- {
- assert (buf.length >= LOCHDR);
- if (zipfs.readFullyAt(buf, 0, LOCHDR , pos) != LOCHDR)
- throw new ZipException("loc: reading failed");
- if (!locSigAt(buf, 0))
- throw new ZipException("loc: wrong sig ->"
- + Long.toString(getSig(buf, 0), 16));
- //startPos = pos;
- version = LOCVER(buf);
- flag = LOCFLG(buf);
- method = LOCHOW(buf);
- mtime = dosToJavaTime(LOCTIM(buf));
- crc = LOCCRC(buf);
- csize = LOCSIZ(buf);
- size = LOCLEN(buf);
- int nlen = LOCNAM(buf);
- int elen = LOCEXT(buf);
-
- name = new byte[nlen + 1];
- name[0] = '/';
- if (zipfs.readFullyAt(name, 1, nlen, pos + LOCHDR) != nlen) {
- throw new ZipException("loc: name reading failed");
- }
- if (elen > 0) {
- extra = new byte[elen];
- if (zipfs.readFullyAt(extra, 0, elen, pos + LOCHDR + nlen)
- != elen) {
- throw new ZipException("loc: ext reading failed");
- }
- }
- pos += (LOCHDR + nlen + elen);
- if ((flag & FLAG_DATADESCR) != 0) {
- // Data Descriptor
- Entry e = zipfs.getEntry(name); // get the size/csize from cen
- if (e == null)
- throw new ZipException("loc: name not found in cen");
- size = e.size;
- csize = e.csize;
- pos += (method == METHOD_STORED ? size : csize);
- if (size >= ZIP64_MINVAL || csize >= ZIP64_MINVAL)
- pos += 24;
- else
- pos += 16;
- } else {
- if (extra != null &&
- (size == ZIP64_MINVAL || csize == ZIP64_MINVAL)) {
- // zip64 ext: must include both size and csize
- int off = 0;
- while (off + 20 < elen) { // HeaderID+DataSize+Data
- int sz = SH(extra, off + 2);
- if (SH(extra, off) == EXTID_ZIP64 && sz == 16) {
- size = LL(extra, off + 4);
- csize = LL(extra, off + 12);
- break;
- }
- off += (sz + 4);
- }
- }
- pos += (method == METHOD_STORED ? size : csize);
- }
- return this;
- }
int writeLOC(OutputStream os) throws IOException {
writeInt(os, LOCSIG); // LOC header signature
int version = version();
- int nlen = (name != null) ? name.length - 1 : 0; // [0] is slash
+
+ byte[] zname = isdir ? toDirectoryPath(name) : name;
+ int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
int elen = (extra != null) ? extra.length : 0;
boolean foundExtraTime = false; // if extra timestamp present
int eoff = 0;
@@ -2214,7 +2156,7 @@
}
writeShort(os, nlen);
writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
- writeBytes(os, name, 1, nlen);
+ writeBytes(os, zname, 1, nlen);
if (elen64 != 0) {
writeShort(os, EXTID_ZIP64);
writeShort(os, 16);
@@ -2551,7 +2493,7 @@
private void buildNodeTree() throws IOException {
beginWrite();
try {
- IndexNode root = new IndexNode(ROOTPATH);
+ IndexNode root = new IndexNode(ROOTPATH, true);
IndexNode[] nodes = inodes.keySet().toArray(new IndexNode[0]);
inodes.put(root, root);
ParentLookup lookup = new ParentLookup();
@@ -2564,7 +2506,7 @@
root.child = node;
break;
}
- lookup = lookup.as(node.name, off + 1);
+ lookup = lookup.as(node.name, off);
if (inodes.containsKey(lookup)) {
parent = inodes.get(lookup);
node.sibling = parent.child;
@@ -2572,7 +2514,7 @@
break;
}
// add new pseudo directory entry
- parent = new IndexNode(Arrays.copyOf(node.name, off + 1));
+ parent = new IndexNode(Arrays.copyOf(node.name, off), true);
inodes.put(parent, parent);
node.sibling = parent.child;
parent.child = node;
--- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java Wed Jan 18 11:08:46 2017 -0800
+++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java Wed Jan 18 11:18:13 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -59,8 +59,7 @@
} else {
if (zfs.zc.isUTF8()) {
this.path = normalize(path);
- } else {
- // see normalize(String);
+ } else { // see normalize(String);
this.path = normalize(zfs.getString(path));
}
}
@@ -68,12 +67,7 @@
ZipPath(ZipFileSystem zfs, String path) {
this.zfs = zfs;
- if (zfs.zc.isUTF8()) {
- this.path = normalize(zfs.getBytes(path));
- } else {
- // see normalize(String);
- this.path = normalize(path);
- }
+ this.path = normalize(path);
}
@Override
@@ -84,33 +78,31 @@
return null;
}
- @Override
+ @Override
public Path getFileName() {
- initOffsets();
- int count = offsets.length;
- if (count == 0)
- return null; // no elements so no name
- if (count == 1 && path[0] != '/')
+ int off = path.length;
+ if (off == 0 || off == 1 && path[0] == '/')
+ return null;
+ while (--off >= 0 && path[off] != '/') {}
+ if (off < 0)
return this;
- int lastOffset = offsets[count-1];
- int len = path.length - lastOffset;
- byte[] result = new byte[len];
- System.arraycopy(path, lastOffset, result, 0, len);
- return new ZipPath(zfs, result);
+ off++;
+ byte[] result = new byte[path.length - off];
+ System.arraycopy(path, off, result, 0, result.length);
+ return new ZipPath(getFileSystem(), result, true);
}
@Override
public ZipPath getParent() {
- initOffsets();
- int count = offsets.length;
- if (count == 0) // no elements so no parent
+ int off = path.length;
+ if (off == 0 || off == 1 && path[0] == '/')
return null;
- int len = offsets[count-1] - 1;
- if (len <= 0) // parent is root only (may be null)
+ while (--off >= 0 && path[off] != '/') {}
+ if (off <= 0)
return getRoot();
- byte[] result = new byte[len];
- System.arraycopy(path, 0, result, 0, len);
- return new ZipPath(zfs, result);
+ byte[] result = new byte[off];
+ System.arraycopy(path, 0, result, 0, off);
+ return new ZipPath(getFileSystem(), result, true);
}
@Override
@@ -277,30 +269,36 @@
@Override
public boolean isAbsolute() {
- return (this.path.length > 0 && path[0] == '/');
+ return path.length > 0 && path[0] == '/';
}
@Override
public ZipPath resolve(Path other) {
- final ZipPath o = checkPath(other);
- int tlen = this.path.length;
- if (tlen == 0 || o.isAbsolute())
+ ZipPath o = checkPath(other);
+ if (o.path.length == 0)
+ return this;
+ if (o.isAbsolute() || this.path.length == 0)
return o;
- int olen = o.path.length;
- if (olen == 0)
- return this;
+ return resolve(o.path);
+ }
+
+ // opath is normalized, just concat
+ private ZipPath resolve(byte[] opath) {
byte[] resolved = null;
- if (this.path[tlen - 1] == '/') {
+ byte[] tpath = this.path;
+ int tlen = tpath.length;
+ int olen = opath.length;
+ if (path[tlen - 1] == '/') {
resolved = new byte[tlen + olen];
- System.arraycopy(path, 0, resolved, 0, tlen);
- System.arraycopy(o.path, 0, resolved, tlen, olen);
+ System.arraycopy(tpath, 0, resolved, 0, tlen);
+ System.arraycopy(opath, 0, resolved, tlen, olen);
} else {
resolved = new byte[tlen + 1 + olen];
- System.arraycopy(path, 0, resolved, 0, tlen);
+ System.arraycopy(tpath, 0, resolved, 0, tlen);
resolved[tlen] = '/';
- System.arraycopy(o.path, 0, resolved, tlen + 1, olen);
+ System.arraycopy(opath, 0, resolved, tlen + 1, olen);
}
- return new ZipPath(zfs, resolved);
+ return new ZipPath(zfs, resolved, true);
}
@Override
@@ -351,7 +349,12 @@
@Override
public ZipPath resolve(String other) {
- return resolve(zfs.getPath(other));
+ byte[] opath = normalize(other);
+ if (opath.length == 0)
+ return this;
+ if (opath[0] == '/' || this.path.length == 0)
+ return new ZipPath(zfs, opath, true);
+ return resolve(opath);
}
@Override
@@ -455,8 +458,9 @@
return normalize(path, i - 1);
prevC = c;
}
- if (len > 1 && prevC == '/')
+ if (len > 1 && prevC == '/') {
return Arrays.copyOf(path, len - 1);
+ }
return path;
}
@@ -490,6 +494,8 @@
// to avoid incorrectly normalizing byte '0x5c' (as '\')
// to '/'.
private byte[] normalize(String path) {
+ if (zfs.zc.isUTF8())
+ return normalize(zfs.getBytes(path));
int len = path.length();
if (len == 0)
return new byte[0];
@@ -533,7 +539,8 @@
// Remove DotSlash(./) and resolve DotDot (..) components
private byte[] getResolved() {
for (int i = 0; i < path.length; i++) {
- if (path[i] == (byte)'.') {
+ if (path[i] == (byte)'.' &&
+ (i + 1 == path.length || path[i + 1] == '/')) {
return resolve0();
}
}
@@ -976,5 +983,4 @@
}
return sb.toString();
}
-
}
--- a/jdk/test/jdk/nio/zipfs/PathOps.java Wed Jan 18 11:08:46 2017 -0800
+++ b/jdk/test/jdk/nio/zipfs/PathOps.java Wed Jan 18 11:18:13 2017 -0800
@@ -31,7 +31,7 @@
/**
*
* @test
- * @bug 8038500 8040059 8139956 8146754
+ * @bug 8038500 8040059 8139956 8146754 8172921
* @summary Tests path operations for zip provider.
*
* @run main PathOps
@@ -180,6 +180,13 @@
return this;
}
+ PathOps resolvePath(String other, String expected) {
+ out.format("test resolve %s\n", other);
+ checkPath();
+ check(path.resolve(fs.getPath(other)), expected);
+ return this;
+ }
+
PathOps resolveSibling(String other, String expected) {
out.format("test resolveSibling %s\n", other);
checkPath();
@@ -384,6 +391,30 @@
.resolve("", "")
.resolve("foo", "foo")
.resolve("/foo", "/foo");
+ test("/")
+ .resolve("", "/")
+ .resolve("foo", "/foo")
+ .resolve("/foo", "/foo")
+ .resolve("/foo/", "/foo");
+
+ // resolve(Path)
+ test("/tmp")
+ .resolvePath("foo", "/tmp/foo")
+ .resolvePath("/foo", "/foo")
+ .resolvePath("", "/tmp");
+ test("tmp")
+ .resolvePath("foo", "tmp/foo")
+ .resolvePath("/foo", "/foo")
+ .resolvePath("", "tmp");
+ test("")
+ .resolvePath("", "")
+ .resolvePath("foo", "foo")
+ .resolvePath("/foo", "/foo");
+ test("/")
+ .resolvePath("", "/")
+ .resolvePath("foo", "/foo")
+ .resolvePath("/foo", "/foo")
+ .resolvePath("/foo/", "/foo");
// resolveSibling
test("foo")