# HG changeset patch # User sherman # Date 1484767093 28800 # Node ID a8e490921d20759a55e4a280b77612dc13977919 # Parent 0c05ac48a30bf1106f80b77c1ab4c024d22ea743 8172921: Zip filesystem performance improvement and code cleanup Reviewed-by: redestad diff -r 0c05ac48a30b -r a8e490921d20 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java --- 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 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); } diff -r 0c05ac48a30b -r a8e490921d20 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipCoder.java --- 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 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; } diff -r 0c05ac48a30b -r a8e490921d20 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java --- 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 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 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; diff -r 0c05ac48a30b -r a8e490921d20 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java --- 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(); } - } diff -r 0c05ac48a30b -r a8e490921d20 jdk/test/jdk/nio/zipfs/PathOps.java --- 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")