mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-13 12:38:07 +00:00
8172921: Zip filesystem performance improvement and code cleanup
Reviewed-by: redestad
This commit is contained in:
parent
ded129adbf
commit
95a45889e9
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user