8172921: Zip filesystem performance improvement and code cleanup

Reviewed-by: redestad
This commit is contained in:
Xueming Shen 2017-01-18 11:18:13 -08:00
parent ded129adbf
commit 95a45889e9
5 changed files with 207 additions and 230 deletions

View File

@ -135,7 +135,7 @@ class JarFileSystem extends ZipFileSystem {
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 @@ class JarFileSystem extends ZipFileSystem {
*/
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 @@ class JarFileSystem extends ZipFileSystem {
* 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);
}

View File

@ -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,33 +34,64 @@ import java.nio.charset.CoderResult;
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 {
String toString(byte[] ba, int length) {
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);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
cr = cd.flush(cb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
return new String(ca, 0, cb.position());
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);
}
@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) {
return toString(ba, ba.length);
CharsetDecoder cd = decoder().reset();
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());
cr = cd.flush(cb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
return new String(ca, 0, cb.position());
}
byte[] getBytes(String s) {
@ -69,62 +100,29 @@ final class ZipCoder {
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 @@ final class ZipCoder {
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 @@ final class ZipCoder {
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;
}

View File

@ -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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
} 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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
}
boolean isDir() {
return name != null &&
(name.length == 0 || name[name.length - 1] == '/');
return isdir;
}
public boolean equals(Object other) {
@ -1865,8 +1877,9 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
}
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 @@ class ZipFileSystem extends FileSystem {
locoff = CENOFF(cen, pos);
pos += CENHDR;
this.name = inode.name;
this.isdir = inode.isdir;
this.hashcode = inode.hashcode;
pos += nlen;
@ -1974,9 +1989,10 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
}
///////////////////// 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 @@ class ZipFileSystem extends FileSystem {
}
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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 @@ class ZipFileSystem extends FileSystem {
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;

View File

@ -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 @@ final class ZipPath implements Path {
} 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 @@ final class ZipPath implements Path {
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 @@ final class ZipPath implements Path {
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 @@ final class ZipPath implements Path {
@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())
return o;
int olen = o.path.length;
if (olen == 0)
ZipPath o = checkPath(other);
if (o.path.length == 0)
return this;
if (o.isAbsolute() || this.path.length == 0)
return o;
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 @@ final class ZipPath implements Path {
@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 @@ final class ZipPath implements Path {
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 @@ final class ZipPath implements Path {
// 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 @@ final class ZipPath implements Path {
// 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 @@ final class ZipPath implements Path {
}
return sb.toString();
}
}

View File

@ -31,7 +31,7 @@ import java.nio.file.Path;
/**
*
* @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 @@ public class PathOps {
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 @@ public class PathOps {
.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")