mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Add support for more java.util.zip
This commit is contained in:
parent
93549818ee
commit
86d151d953
|
@ -20,7 +20,6 @@
|
||||||
<module name="LeftCurly"/>
|
<module name="LeftCurly"/>
|
||||||
<module name="NeedBraces"/>
|
<module name="NeedBraces"/>
|
||||||
<module name="RightCurly"/>
|
<module name="RightCurly"/>
|
||||||
<module name="InterfaceIsType"/>
|
|
||||||
<module name="HideUtilityClassConstructor"/>
|
<module name="HideUtilityClassConstructor"/>
|
||||||
<module name="EmptyStatement"/>
|
<module name="EmptyStatement"/>
|
||||||
<module name="EqualsHashCode"/>
|
<module name="EqualsHashCode"/>
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import com.jcraft.jzlib.Adler32;
|
||||||
|
|
||||||
|
public class TAdler32 implements TChecksum {
|
||||||
|
private Adler32 impl = new Adler32();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getValue() {
|
||||||
|
return impl.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
impl.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(int i) {
|
||||||
|
update(new byte[] { (byte) i });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(byte[] buf) {
|
||||||
|
update(buf, 0, buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] buf, int off, int nbytes) {
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) {
|
||||||
|
impl.update(buf, off, nbytes);
|
||||||
|
} else {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import com.jcraft.jzlib.CRC32;
|
||||||
|
|
||||||
|
public class TCRC32 implements TChecksum {
|
||||||
|
private CRC32 impl = new CRC32();
|
||||||
|
long tbytes;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getValue() {
|
||||||
|
return impl.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
impl.reset();
|
||||||
|
tbytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(int val) {
|
||||||
|
impl.update(new byte[] { (byte) val }, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(byte[] buf) {
|
||||||
|
update(buf, 0, buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] buf, int off, int nbytes) {
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) {
|
||||||
|
impl.update(buf, off, nbytes);
|
||||||
|
tbytes += nbytes;
|
||||||
|
} else {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class TCheckedInputStream extends FilterInputStream {
|
||||||
|
private final TChecksum check;
|
||||||
|
|
||||||
|
public TCheckedInputStream(InputStream is, TChecksum csum) {
|
||||||
|
super(is);
|
||||||
|
check = csum;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
int x = in.read();
|
||||||
|
if (x != -1) {
|
||||||
|
check.update(x);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] buf, int off, int nbytes) throws IOException {
|
||||||
|
int x = in.read(buf, off, nbytes);
|
||||||
|
if (x != -1) {
|
||||||
|
check.update(buf, off, x);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TChecksum getChecksum() {
|
||||||
|
return check;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long nbytes) throws IOException {
|
||||||
|
if (nbytes < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
long skipped = 0;
|
||||||
|
byte[] b = new byte[(int) Math.min(nbytes, 2048L)];
|
||||||
|
int x;
|
||||||
|
int v;
|
||||||
|
while (skipped != nbytes) {
|
||||||
|
v = (int) (nbytes - skipped);
|
||||||
|
x = in.read(b, 0, v > b.length ? b.length : v);
|
||||||
|
if (x == -1) {
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
check.update(b, 0, x);
|
||||||
|
skipped += x;
|
||||||
|
}
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import java.io.FilterOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public class TCheckedOutputStream extends FilterOutputStream {
|
||||||
|
private final TChecksum check;
|
||||||
|
|
||||||
|
public TCheckedOutputStream(OutputStream os, TChecksum cs) {
|
||||||
|
super(os);
|
||||||
|
check = cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TChecksum getChecksum() {
|
||||||
|
return check;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int val) throws IOException {
|
||||||
|
out.write(val);
|
||||||
|
check.update(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] buf, int off, int nbytes) throws IOException {
|
||||||
|
out.write(buf, off, nbytes);
|
||||||
|
check.update(buf, off, nbytes);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
public interface TChecksum {
|
||||||
|
long getValue();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void update(byte[] buf, int off, int nbytes);
|
||||||
|
|
||||||
|
void update(int val);
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 Alexey Andreev.
|
* Copyright 2017 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,16 +13,15 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.teavm.classlib.java.util.zip;
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
public class TDataFormatException extends Exception {
|
public class TDataFormatException extends Exception {
|
||||||
private static final long serialVersionUID = 7856637411580418624L;
|
|
||||||
|
|
||||||
public TDataFormatException() {
|
public TDataFormatException() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TDataFormatException(String message) {
|
public TDataFormatException(String detailMessage) {
|
||||||
super(message);
|
super(detailMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import com.jcraft.jzlib.Deflater;
|
||||||
|
import com.jcraft.jzlib.GZIPException;
|
||||||
|
import com.jcraft.jzlib.JZlib;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class TDeflater {
|
||||||
|
public static final int BEST_COMPRESSION = 9;
|
||||||
|
public static final int BEST_SPEED = 1;
|
||||||
|
public static final int DEFAULT_COMPRESSION = -1;
|
||||||
|
public static final int DEFAULT_STRATEGY = 0;
|
||||||
|
public static final int DEFLATED = 8;
|
||||||
|
public static final int FILTERED = 1;
|
||||||
|
public static final int HUFFMAN_ONLY = 2;
|
||||||
|
public static final int NO_COMPRESSION = 0;
|
||||||
|
static final int Z_NO_FLUSH = 0;
|
||||||
|
static final int Z_SYNC_FLUSH = 2;
|
||||||
|
static final int Z_FINISH = 4;
|
||||||
|
private int flushParm = Z_NO_FLUSH;
|
||||||
|
private boolean finished;
|
||||||
|
private int compressLevel = DEFAULT_COMPRESSION;
|
||||||
|
private int strategy = DEFAULT_STRATEGY;
|
||||||
|
private Deflater impl;
|
||||||
|
private int inRead;
|
||||||
|
private int inLength;
|
||||||
|
private boolean nowrap;
|
||||||
|
|
||||||
|
public TDeflater() {
|
||||||
|
this(DEFAULT_COMPRESSION, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TDeflater(int level) {
|
||||||
|
this(level, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TDeflater(int level, boolean noHeader) {
|
||||||
|
super();
|
||||||
|
if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
compressLevel = level;
|
||||||
|
try {
|
||||||
|
impl = new Deflater(compressLevel, strategy, noHeader);
|
||||||
|
} catch (GZIPException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
nowrap = noHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int deflate(byte[] buf) {
|
||||||
|
return deflate(buf, 0, buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int deflate(byte[] buf, int off, int nbytes) {
|
||||||
|
return deflate(buf, off, nbytes, flushParm);
|
||||||
|
}
|
||||||
|
|
||||||
|
int deflate(byte[] buf, int off, int nbytes, int flushParam) {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if (off > buf.length || nbytes < 0 || off < 0 || buf.length - off < nbytes) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
long sin = impl.total_in;
|
||||||
|
long sout = impl.total_out;
|
||||||
|
impl.setOutput(buf, off, nbytes);
|
||||||
|
int err = impl.deflate(flushParam);
|
||||||
|
switch (err) {
|
||||||
|
case JZlib.Z_OK:
|
||||||
|
break;
|
||||||
|
case JZlib.Z_STREAM_END:
|
||||||
|
finished = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Error: " + err);
|
||||||
|
}
|
||||||
|
|
||||||
|
inRead += impl.total_in - sin;
|
||||||
|
return (int) (impl.total_out - sout);
|
||||||
|
}
|
||||||
|
public void end() {
|
||||||
|
impl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() {
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finish() {
|
||||||
|
flushParm = Z_FINISH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean finished() {
|
||||||
|
return finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAdler() {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) impl.getAdler();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalIn() {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) impl.getTotalIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalOut() {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) impl.getTotalOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean needsInput() {
|
||||||
|
return inRead == inLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
|
||||||
|
flushParm = Z_NO_FLUSH;
|
||||||
|
finished = false;
|
||||||
|
impl.init(compressLevel, strategy, nowrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDictionary(byte[] buf) {
|
||||||
|
setDictionary(buf, 0, buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDictionary(byte[] buf, int off, int nbytes) {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) {
|
||||||
|
impl.setDictionary(Arrays.copyOfRange(buf, off, buf.length), nbytes);
|
||||||
|
} else {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInput(byte[] buf) {
|
||||||
|
setInput(buf, 0, buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInput(byte[] buf, int off, int nbytes) {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) {
|
||||||
|
inLength = nbytes;
|
||||||
|
inRead = 0;
|
||||||
|
if (impl.next_in == null) {
|
||||||
|
impl.init(compressLevel, strategy, nowrap);
|
||||||
|
}
|
||||||
|
impl.setInput(buf, off, nbytes, false);
|
||||||
|
} else {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevel(int level) {
|
||||||
|
if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
compressLevel = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStrategy(int strategy) {
|
||||||
|
if (strategy < DEFAULT_STRATEGY || strategy > HUFFMAN_ONLY) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
this.strategy = strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getBytesRead() {
|
||||||
|
// Throw NPE here
|
||||||
|
if (impl == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
return impl.getTotalIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getBytesWritten() {
|
||||||
|
// Throw NPE here
|
||||||
|
if (impl == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
return impl.getTotalOut();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 Alexey Andreev.
|
* Copyright 2017 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,16 +13,93 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.teavm.classlib.java.util.zip;
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
import com.jcraft.jzlib.DeflaterOutputStream;
|
|
||||||
import java.io.FilterOutputStream;
|
import java.io.FilterOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public class TDeflaterOutputStream extends FilterOutputStream {
|
public class TDeflaterOutputStream extends FilterOutputStream {
|
||||||
public TDeflaterOutputStream(OutputStream out) throws IOException {
|
static final int BUF_SIZE = 512;
|
||||||
super(out);
|
protected byte[] buf;
|
||||||
this.out = new DeflaterOutputStream(out);
|
protected TDeflater def;
|
||||||
|
boolean done;
|
||||||
|
|
||||||
|
public TDeflaterOutputStream(OutputStream os, TDeflater def) {
|
||||||
|
this(os, def, BUF_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TDeflaterOutputStream(OutputStream os) {
|
||||||
|
this(os, new TDeflater());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TDeflaterOutputStream(OutputStream os, TDeflater def, int bsize) {
|
||||||
|
super(os);
|
||||||
|
if (os == null || def == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
if (bsize <= 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
this.def = def;
|
||||||
|
buf = new byte[bsize];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void deflate() throws IOException {
|
||||||
|
int x;
|
||||||
|
do {
|
||||||
|
x = def.deflate(buf);
|
||||||
|
out.write(buf, 0, x);
|
||||||
|
} while (!def.needsInput());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (!def.finished()) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
def.end();
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finish() throws IOException {
|
||||||
|
if (done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
def.finish();
|
||||||
|
int x = 0;
|
||||||
|
while (!def.finished()) {
|
||||||
|
if (def.needsInput()) {
|
||||||
|
def.setInput(buf, 0, 0);
|
||||||
|
}
|
||||||
|
x = def.deflate(buf);
|
||||||
|
out.write(buf, 0, x);
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int i) throws IOException {
|
||||||
|
byte[] b = new byte[1];
|
||||||
|
b[0] = (byte) i;
|
||||||
|
write(b, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] buffer, int off, int nbytes) throws IOException {
|
||||||
|
if (done) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if (off <= buffer.length && nbytes >= 0 && off >= 0 && buffer.length - off >= nbytes) {
|
||||||
|
if (!def.needsInput()) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
def.setInput(buffer, off, nbytes);
|
||||||
|
deflate();
|
||||||
|
} else {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 Alexey Andreev.
|
* Copyright 2017 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,20 +13,165 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.teavm.classlib.java.util.zip;
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
import com.jcraft.jzlib.GZIPInputStream;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class TGZIPInputStream extends TInflaterInputStream {
|
public class TGZIPInputStream extends TInflaterInputStream {
|
||||||
public TGZIPInputStream(InputStream in, int size) throws IOException {
|
private static final int FCOMMENT = 16;
|
||||||
super(in);
|
private static final int FEXTRA = 4;
|
||||||
this.in = new GZIPInputStream(in, size, false);
|
private static final int FHCRC = 2;
|
||||||
|
private static final int FNAME = 8;
|
||||||
|
public final static int GZIP_MAGIC = 0x8b1f;
|
||||||
|
protected TCRC32 crc = new TCRC32();
|
||||||
|
protected boolean eos;
|
||||||
|
|
||||||
|
public TGZIPInputStream(InputStream is) throws IOException {
|
||||||
|
this(is, BUF_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TGZIPInputStream(InputStream in) throws IOException {
|
public TGZIPInputStream(InputStream is, int size) throws IOException {
|
||||||
super(in);
|
super(is, new TInflater(true), size);
|
||||||
this.in = new GZIPInputStream(in);
|
byte[] header = new byte[10];
|
||||||
|
readFully(header, 0, header.length);
|
||||||
|
if (getShort(header, 0) != GZIP_MAGIC) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
int flags = header[3];
|
||||||
|
boolean hcrc = (flags & FHCRC) != 0;
|
||||||
|
if (hcrc) {
|
||||||
|
crc.update(header, 0, header.length);
|
||||||
|
}
|
||||||
|
if ((flags & FEXTRA) != 0) {
|
||||||
|
readFully(header, 0, 2);
|
||||||
|
if (hcrc) {
|
||||||
|
crc.update(header, 0, 2);
|
||||||
|
}
|
||||||
|
int length = getShort(header, 0);
|
||||||
|
while (length > 0) {
|
||||||
|
int max = length > buf.length ? buf.length : length;
|
||||||
|
int result = in.read(buf, 0, max);
|
||||||
|
if (result == -1) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
if (hcrc) {
|
||||||
|
crc.update(buf, 0, result);
|
||||||
|
}
|
||||||
|
length -= result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((flags & FNAME) != 0) {
|
||||||
|
readZeroTerminated(hcrc);
|
||||||
|
}
|
||||||
|
if ((flags & FCOMMENT) != 0) {
|
||||||
|
readZeroTerminated(hcrc);
|
||||||
|
}
|
||||||
|
if (hcrc) {
|
||||||
|
readFully(header, 0, 2);
|
||||||
|
int crc16 = getShort(header, 0);
|
||||||
|
if ((crc.getValue() & 0xffff) != crc16) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
crc.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
eos = true;
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getLong(byte[] buffer, int off) {
|
||||||
|
long l = 0;
|
||||||
|
l |= buffer[off] & 0xFF;
|
||||||
|
l |= (buffer[off + 1] & 0xFF) << 8;
|
||||||
|
l |= (buffer[off + 2] & 0xFF) << 16;
|
||||||
|
l |= ((long) (buffer[off + 3] & 0xFF)) << 24;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getShort(byte[] buffer, int off) {
|
||||||
|
return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] buffer, int off, int nbytes) throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
if (eos) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// avoid int overflow, check null buffer
|
||||||
|
if (off > buffer.length || nbytes < 0 || off < 0 || buffer.length - off < nbytes) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesRead;
|
||||||
|
try {
|
||||||
|
bytesRead = super.read(buffer, off, nbytes);
|
||||||
|
} finally {
|
||||||
|
eos = eof; // update eos after every read(), even when it throws
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesRead != -1) {
|
||||||
|
crc.update(buffer, off, bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eos) {
|
||||||
|
verifyCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyCrc() throws IOException {
|
||||||
|
// Get non-compressed bytes read by fill
|
||||||
|
int size = inf.getRemaining();
|
||||||
|
final int trailerSize = 8; // crc (4 bytes) + total out (4 bytes)
|
||||||
|
byte[] b = new byte[trailerSize];
|
||||||
|
int copySize = (size > trailerSize) ? trailerSize : size;
|
||||||
|
|
||||||
|
System.arraycopy(buf, len - size, b, 0, copySize);
|
||||||
|
readFully(b, copySize, trailerSize - copySize);
|
||||||
|
|
||||||
|
if (getLong(b, 0) != crc.getValue()) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
if ((int) getLong(b, 4) != inf.getTotalOut()) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readFully(byte[] buffer, int offset, int length) throws IOException {
|
||||||
|
int result;
|
||||||
|
while (length > 0) {
|
||||||
|
result = in.read(buffer, offset, length);
|
||||||
|
if (result == -1) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
offset += result;
|
||||||
|
length -= result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readZeroTerminated(boolean hcrc) throws IOException {
|
||||||
|
int result;
|
||||||
|
while ((result = in.read()) > 0) {
|
||||||
|
if (hcrc) {
|
||||||
|
crc.update(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == -1) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
// Add the zero
|
||||||
|
if (hcrc) {
|
||||||
|
crc.update(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 Alexey Andreev.
|
* Copyright 2017 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,27 +13,62 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.teavm.classlib.java.util.zip;
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
import com.jcraft.jzlib.GZIPOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public class TGZIPOutputStream extends TDeflaterOutputStream {
|
public class TGZIPOutputStream extends TDeflaterOutputStream {
|
||||||
public TGZIPOutputStream(OutputStream out, int size, boolean syncFlush) throws IOException {
|
protected TCRC32 crc = new TCRC32();
|
||||||
super(out);
|
|
||||||
this.out = new GZIPOutputStream(out, size, syncFlush);
|
public TGZIPOutputStream(OutputStream os) throws IOException {
|
||||||
|
this(os, BUF_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TGZIPOutputStream(OutputStream out, boolean syncFlush) throws IOException {
|
public TGZIPOutputStream(OutputStream os, int size) throws IOException {
|
||||||
this(out, 512, syncFlush);
|
super(os, new TDeflater(java.util.zip.Deflater.DEFAULT_COMPRESSION, true), size);
|
||||||
|
writeShort(TGZIPInputStream.GZIP_MAGIC);
|
||||||
|
out.write(java.util.zip.Deflater.DEFLATED);
|
||||||
|
out.write(0); // flags
|
||||||
|
writeLong(0); // mod time
|
||||||
|
out.write(0); // extra flags
|
||||||
|
out.write(0); // operating system
|
||||||
}
|
}
|
||||||
|
|
||||||
public TGZIPOutputStream(OutputStream out, int size) throws IOException {
|
@Override
|
||||||
this(out, size, true);
|
public void flush() throws IOException {
|
||||||
|
int count = def.deflate(buf, 0, buf.length, TDeflater.Z_SYNC_FLUSH);
|
||||||
|
out.write(buf, 0, count);
|
||||||
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TGZIPOutputStream(OutputStream out) throws IOException {
|
@Override
|
||||||
this(out, 512, true);
|
public void finish() throws IOException {
|
||||||
|
super.finish();
|
||||||
|
writeLong(crc.getValue());
|
||||||
|
writeLong(crc.tbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] buffer, int off, int nbytes) throws IOException {
|
||||||
|
super.write(buffer, off, nbytes);
|
||||||
|
crc.update(buffer, off, nbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long writeLong(long i) throws IOException {
|
||||||
|
// Write out the long value as an unsigned int
|
||||||
|
int unsigned = (int) i;
|
||||||
|
out.write(unsigned & 0xFF);
|
||||||
|
out.write((unsigned >> 8) & 0xFF);
|
||||||
|
out.write((unsigned >> 16) & 0xFF);
|
||||||
|
out.write((unsigned >> 24) & 0xFF);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int writeShort(int i) throws IOException {
|
||||||
|
out.write(i & 0xFF);
|
||||||
|
out.write((i >> 8) & 0xFF);
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import com.jcraft.jzlib.GZIPException;
|
||||||
|
import com.jcraft.jzlib.Inflater;
|
||||||
|
import com.jcraft.jzlib.JZlib;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class TInflater {
|
||||||
|
private boolean finished;
|
||||||
|
private boolean nowrap;
|
||||||
|
int inLength;
|
||||||
|
int inRead;
|
||||||
|
private boolean needsDictionary;
|
||||||
|
private Inflater impl;
|
||||||
|
|
||||||
|
public TInflater() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TInflater(boolean noHeader) {
|
||||||
|
nowrap = noHeader;
|
||||||
|
try {
|
||||||
|
impl = new Inflater(noHeader);
|
||||||
|
} catch (GZIPException e) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void end() {
|
||||||
|
inRead = 0;
|
||||||
|
inLength = 0;
|
||||||
|
impl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() {
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean finished() {
|
||||||
|
return finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAdler() {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
return (int) impl.getAdler();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native int getAdlerImpl(long handle);
|
||||||
|
|
||||||
|
public long getBytesRead() {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
return impl.getTotalIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getBytesWritten() {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
return impl.getTotalOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemaining() {
|
||||||
|
return inLength - inRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalIn() {
|
||||||
|
return (int) getBytesRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native long getTotalInImpl(long handle);
|
||||||
|
|
||||||
|
public int getTotalOut() {
|
||||||
|
return (int) getBytesWritten();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native long getTotalOutImpl(long handle);
|
||||||
|
|
||||||
|
public int inflate(byte[] buf) throws TDataFormatException {
|
||||||
|
return inflate(buf, 0, buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int inflate(byte[] buf, int off, int nbytes) throws TDataFormatException {
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if (off > buf.length || nbytes < 0 || off < 0 || buf.length - off < nbytes) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsInput()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long lastInSize = impl.total_in;
|
||||||
|
long lastOutSize = impl.total_out;
|
||||||
|
boolean neededDict = needsDictionary;
|
||||||
|
needsDictionary = false;
|
||||||
|
impl.setOutput(buf, off, nbytes);
|
||||||
|
|
||||||
|
int errCode = impl.inflate(0);
|
||||||
|
switch (errCode) {
|
||||||
|
case JZlib.Z_OK:
|
||||||
|
break;
|
||||||
|
case JZlib.Z_NEED_DICT:
|
||||||
|
needsDictionary = true;
|
||||||
|
break;
|
||||||
|
case JZlib.Z_STREAM_END:
|
||||||
|
finished = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new TDataFormatException("Error occurred: " + errCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsDictionary && neededDict) {
|
||||||
|
throw new TDataFormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
inRead += impl.total_in - lastInSize;
|
||||||
|
return (int) (impl.total_out - lastOutSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean needsDictionary() {
|
||||||
|
return needsDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean needsInput() {
|
||||||
|
return inRead == inLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
finished = false;
|
||||||
|
needsDictionary = false;
|
||||||
|
inLength = 0;
|
||||||
|
inRead = 0;
|
||||||
|
impl.init(nowrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void resetImpl(long handle);
|
||||||
|
|
||||||
|
public void setDictionary(byte[] buf) {
|
||||||
|
setDictionary(buf, 0, buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDictionary(byte[] buf, int off, int nbytes) {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) {
|
||||||
|
if (off > 0) {
|
||||||
|
buf = Arrays.copyOfRange(buf, off, buf.length);
|
||||||
|
}
|
||||||
|
impl.setDictionary(buf, nbytes);
|
||||||
|
} else {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void setInput(byte[] buf) {
|
||||||
|
setInput(buf, 0, buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInput(byte[] buf, int off, int nbytes) {
|
||||||
|
if (impl == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) {
|
||||||
|
inRead = 0;
|
||||||
|
inLength = nbytes;
|
||||||
|
impl.setInput(buf, off, nbytes, false);
|
||||||
|
} else {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 Alexey Andreev.
|
* Copyright 2017 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -13,16 +13,173 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.teavm.classlib.java.util.zip;
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
import com.jcraft.jzlib.InflaterInputStream;
|
import java.io.EOFException;
|
||||||
import java.io.FilterInputStream;
|
import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class TInflaterInputStream extends FilterInputStream {
|
public class TInflaterInputStream extends FilterInputStream {
|
||||||
public TInflaterInputStream(InputStream in) throws IOException {
|
protected TInflater inf;
|
||||||
super(in);
|
protected byte[] buf;
|
||||||
this.in = new InflaterInputStream(in);
|
protected int len;
|
||||||
|
boolean closed;
|
||||||
|
boolean eof;
|
||||||
|
static final int BUF_SIZE = 512;
|
||||||
|
|
||||||
|
public TInflaterInputStream(InputStream is) {
|
||||||
|
this(is, new TInflater(), BUF_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TInflaterInputStream(InputStream is, TInflater inf) {
|
||||||
|
this(is, inf, BUF_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TInflaterInputStream(InputStream is, TInflater inf, int bsize) {
|
||||||
|
super(is);
|
||||||
|
if (is == null || inf == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
if (bsize <= 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
this.inf = inf;
|
||||||
|
buf = new byte[bsize];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
byte[] b = new byte[1];
|
||||||
|
if (read(b, 0, 1) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return b[0] & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] buffer, int off, int nbytes) throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
throw new IOException("Stream is closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == buffer) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (off < 0 || nbytes < 0 || off + nbytes > buffer.length) {
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbytes == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eof) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid int overflow, check null buffer
|
||||||
|
if (off > buffer.length || nbytes < 0 || off < 0 || buffer.length - off < nbytes) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (inf.needsInput()) {
|
||||||
|
fill();
|
||||||
|
}
|
||||||
|
// Invariant: if reading returns -1 or throws, eof must be true.
|
||||||
|
// It may also be true if the next read() should return -1.
|
||||||
|
try {
|
||||||
|
int result = inf.inflate(buffer, off, nbytes);
|
||||||
|
eof = inf.finished();
|
||||||
|
if (result > 0) {
|
||||||
|
return result;
|
||||||
|
} else if (eof) {
|
||||||
|
return -1;
|
||||||
|
} else if (inf.needsDictionary()) {
|
||||||
|
eof = true;
|
||||||
|
return -1;
|
||||||
|
} else if (len == -1) {
|
||||||
|
eof = true;
|
||||||
|
throw new EOFException();
|
||||||
|
// If result == 0, fill() and try again
|
||||||
|
}
|
||||||
|
} catch (TDataFormatException e) {
|
||||||
|
eof = true;
|
||||||
|
if (len == -1) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fill() throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
len = in.read(buf);
|
||||||
|
if (len > 0) {
|
||||||
|
inf.setInput(buf, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long nbytes) throws IOException {
|
||||||
|
if (nbytes >= 0) {
|
||||||
|
if (buf == null) {
|
||||||
|
buf = new byte[(int) Math.min(nbytes, BUF_SIZE)];
|
||||||
|
}
|
||||||
|
long count = 0;
|
||||||
|
long rem;
|
||||||
|
while (count < nbytes) {
|
||||||
|
rem = nbytes - count;
|
||||||
|
int x = read(buf, 0, rem > buf.length ? buf.length : (int) rem);
|
||||||
|
if (x == -1) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
count += x;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
throw new IOException("Stream is closed");
|
||||||
|
}
|
||||||
|
if (eof) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (!closed) {
|
||||||
|
inf.end();
|
||||||
|
closed = true;
|
||||||
|
eof = true;
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mark(int readlimit) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() throws IOException {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
interface TZipConstants {
|
||||||
|
|
||||||
|
long LOCSIG = 0x4034b50;
|
||||||
|
long EXTSIG = 0x8074b50;
|
||||||
|
long CENSIG = 0x2014b50;
|
||||||
|
long ENDSIG = 0x6054b50;
|
||||||
|
|
||||||
|
int LOCHDR = 30;
|
||||||
|
int EXTHDR = 16;
|
||||||
|
int CENHDR = 46;
|
||||||
|
int ENDHDR = 22;
|
||||||
|
int LOCVER = 4;
|
||||||
|
int LOCFLG = 6;
|
||||||
|
int LOCHOW = 8;
|
||||||
|
int LOCTIM = 10;
|
||||||
|
int LOCCRC = 14;
|
||||||
|
int LOCSIZ = 18;
|
||||||
|
int LOCLEN = 22;
|
||||||
|
int LOCNAM = 26;
|
||||||
|
int LOCEXT = 28;
|
||||||
|
int EXTCRC = 4;
|
||||||
|
int EXTSIZ = 8;
|
||||||
|
int EXTLEN = 12;
|
||||||
|
int CENVEM = 4;
|
||||||
|
int CENVER = 6;
|
||||||
|
int CENFLG = 8;
|
||||||
|
int CENHOW = 10;
|
||||||
|
int CENTIM = 12;
|
||||||
|
int CENCRC = 16;
|
||||||
|
int CENSIZ = 20;
|
||||||
|
int CENLEN = 24;
|
||||||
|
int CENNAM = 28;
|
||||||
|
int CENEXT = 30;
|
||||||
|
int CENCOM = 32;
|
||||||
|
int CENDSK = 34;
|
||||||
|
int CENATT = 36;
|
||||||
|
int CENATX = 38;
|
||||||
|
int CENOFF = 42;
|
||||||
|
int ENDSUB = 8;
|
||||||
|
int ENDTOT = 10;
|
||||||
|
int ENDSIZ = 12;
|
||||||
|
int ENDOFF = 16;
|
||||||
|
int ENDCOM = 20;
|
||||||
|
}
|
|
@ -0,0 +1,310 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
|
public class TZipEntry implements TZipConstants, Cloneable {
|
||||||
|
String name;
|
||||||
|
String comment;
|
||||||
|
long compressedSize = -1;
|
||||||
|
long crc = -1;
|
||||||
|
long size = -1;
|
||||||
|
int compressionMethod = -1;
|
||||||
|
int time = -1;
|
||||||
|
int modDate = -1;
|
||||||
|
byte[] extra;
|
||||||
|
int nameLen = -1;
|
||||||
|
long mLocalHeaderRelOffset = -1;
|
||||||
|
|
||||||
|
public static final int DEFLATED = 8;
|
||||||
|
public static final int STORED = 0;
|
||||||
|
|
||||||
|
public TZipEntry(String name) {
|
||||||
|
if (name == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
if (name.length() > 0xFFFF) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCompressedSize() {
|
||||||
|
return compressedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCrc() {
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getExtra() {
|
||||||
|
return extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMethod() {
|
||||||
|
return compressionMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTime() {
|
||||||
|
if (time != -1) {
|
||||||
|
GregorianCalendar cal = new GregorianCalendar();
|
||||||
|
cal.set(Calendar.MILLISECOND, 0);
|
||||||
|
cal.set(1980 + ((modDate >> 9) & 0x7f), ((modDate >> 5) & 0xf) - 1,
|
||||||
|
modDate & 0x1f, (time >> 11) & 0x1f, (time >> 5) & 0x3f,
|
||||||
|
(time & 0x1f) << 1);
|
||||||
|
return cal.getTime().getTime();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return name.charAt(name.length() - 1) == '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComment(String string) {
|
||||||
|
if (string == null || string.length() <= 0xFFFF) {
|
||||||
|
comment = string;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompressedSize(long value) {
|
||||||
|
compressedSize = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCrc(long value) {
|
||||||
|
if (value >= 0 && value <= 0xFFFFFFFFL) {
|
||||||
|
crc = value;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtra(byte[] data) {
|
||||||
|
if (data == null || data.length <= 0xFFFF) {
|
||||||
|
extra = data;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMethod(int value) {
|
||||||
|
if (value != STORED && value != DEFLATED) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
compressionMethod = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(long value) {
|
||||||
|
if (value >= 0 && value <= 0xFFFFFFFFL) {
|
||||||
|
size = value;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTime(long value) {
|
||||||
|
GregorianCalendar cal = new GregorianCalendar();
|
||||||
|
cal.setTime(new Date(value));
|
||||||
|
int year = cal.get(Calendar.YEAR);
|
||||||
|
if (year < 1980) {
|
||||||
|
modDate = 0x21;
|
||||||
|
time = 0;
|
||||||
|
} else {
|
||||||
|
modDate = cal.get(Calendar.DATE);
|
||||||
|
modDate = (cal.get(Calendar.MONTH) + 1 << 5) | modDate;
|
||||||
|
modDate = ((cal.get(Calendar.YEAR) - 1980) << 9) | modDate;
|
||||||
|
time = cal.get(Calendar.SECOND) >> 1;
|
||||||
|
time = (cal.get(Calendar.MINUTE) << 5) | time;
|
||||||
|
time = (cal.get(Calendar.HOUR_OF_DAY) << 11) | time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TZipEntry(TZipEntry ze) {
|
||||||
|
name = ze.name;
|
||||||
|
comment = ze.comment;
|
||||||
|
time = ze.time;
|
||||||
|
size = ze.size;
|
||||||
|
compressedSize = ze.compressedSize;
|
||||||
|
crc = ze.crc;
|
||||||
|
compressionMethod = ze.compressionMethod;
|
||||||
|
modDate = ze.modDate;
|
||||||
|
extra = ze.extra;
|
||||||
|
nameLen = ze.nameLen;
|
||||||
|
mLocalHeaderRelOffset = ze.mLocalHeaderRelOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object clone() {
|
||||||
|
return new TZipEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal constructor. Creates a new TZipEntry by reading the
|
||||||
|
* Central Directory Entry from "in", which must be positioned at
|
||||||
|
* the CDE signature.
|
||||||
|
*
|
||||||
|
* On exit, "in" will be positioned at the start of the next entry.
|
||||||
|
*/
|
||||||
|
TZipEntry(LittleEndianReader ler, InputStream in) throws IOException {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We're seeing performance issues when we call readShortLE and
|
||||||
|
* readIntLE, so we're going to read the entire header at once
|
||||||
|
* and then parse the results out without using any function calls.
|
||||||
|
* Uglier, but should be much faster.
|
||||||
|
*
|
||||||
|
* Note that some lines look a bit different, because the corresponding
|
||||||
|
* fields or locals are long and so we need to do & 0xffffffffl to avoid
|
||||||
|
* problems induced by sign extension.
|
||||||
|
*/
|
||||||
|
|
||||||
|
byte[] hdrBuf = ler.hdrBuf;
|
||||||
|
myReadFully(in, hdrBuf);
|
||||||
|
|
||||||
|
long sig = (hdrBuf[0] & 0xff) | ((hdrBuf[1] & 0xff) << 8)
|
||||||
|
| ((hdrBuf[2] & 0xff) << 16) | ((hdrBuf[3] << 24) & 0xffffffffL);
|
||||||
|
if (sig != CENSIG) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
|
||||||
|
compressionMethod = (hdrBuf[10] & 0xff) | ((hdrBuf[11] & 0xff) << 8);
|
||||||
|
time = (hdrBuf[12] & 0xff) | ((hdrBuf[13] & 0xff) << 8);
|
||||||
|
modDate = (hdrBuf[14] & 0xff) | ((hdrBuf[15] & 0xff) << 8);
|
||||||
|
crc = (hdrBuf[16] & 0xff) | ((hdrBuf[17] & 0xff) << 8)
|
||||||
|
| ((hdrBuf[18] & 0xff) << 16)
|
||||||
|
| ((hdrBuf[19] << 24) & 0xffffffffL);
|
||||||
|
compressedSize = (hdrBuf[20] & 0xff) | ((hdrBuf[21] & 0xff) << 8)
|
||||||
|
| ((hdrBuf[22] & 0xff) << 16)
|
||||||
|
| ((hdrBuf[23] << 24) & 0xffffffffL);
|
||||||
|
size = (hdrBuf[24] & 0xff) | ((hdrBuf[25] & 0xff) << 8)
|
||||||
|
| ((hdrBuf[26] & 0xff) << 16)
|
||||||
|
| ((hdrBuf[27] << 24) & 0xffffffffL);
|
||||||
|
nameLen = (hdrBuf[28] & 0xff) | ((hdrBuf[29] & 0xff) << 8);
|
||||||
|
int extraLen = (hdrBuf[30] & 0xff) | ((hdrBuf[31] & 0xff) << 8);
|
||||||
|
int commentLen = (hdrBuf[32] & 0xff) | ((hdrBuf[33] & 0xff) << 8);
|
||||||
|
mLocalHeaderRelOffset = (hdrBuf[42] & 0xff) | ((hdrBuf[43] & 0xff) << 8)
|
||||||
|
| ((hdrBuf[44] & 0xff) << 16)
|
||||||
|
| ((hdrBuf[45] << 24) & 0xffffffffL);
|
||||||
|
|
||||||
|
byte[] nameBytes = new byte[nameLen];
|
||||||
|
myReadFully(in, nameBytes);
|
||||||
|
|
||||||
|
byte[] commentBytes = null;
|
||||||
|
if (commentLen > 0) {
|
||||||
|
commentBytes = new byte[commentLen];
|
||||||
|
myReadFully(in, commentBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extraLen > 0) {
|
||||||
|
extra = new byte[extraLen];
|
||||||
|
myReadFully(in, extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* The actual character set is "IBM Code Page 437". As of
|
||||||
|
* Sep 2006, the Zip spec (APPNOTE.TXT) supports UTF-8. When
|
||||||
|
* bit 11 of the GP flags field is set, the file name and
|
||||||
|
* comment fields are UTF-8.
|
||||||
|
*
|
||||||
|
* TODO: add correct UTF-8 support.
|
||||||
|
*/
|
||||||
|
name = new String(nameBytes, "ISO-8859-1");
|
||||||
|
comment = commentBytes != null ? new String(commentBytes, "ISO-8859-1") : null;
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
throw new InternalError(uee.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void myReadFully(InputStream in, byte[] b) throws IOException {
|
||||||
|
int len = b.length;
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
int count = in.read(b, off, len);
|
||||||
|
if (count <= 0) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
off += count;
|
||||||
|
len -= count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static long readIntLE(RandomAccessFile raf) throws IOException {
|
||||||
|
int b0 = raf.read();
|
||||||
|
int b1 = raf.read();
|
||||||
|
int b2 = raf.read();
|
||||||
|
int b3 = raf.read();
|
||||||
|
|
||||||
|
if (b3 < 0) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); // ATTENTION: DOES SIGN EXTENSION: IS THIS WANTED?
|
||||||
|
}
|
||||||
|
|
||||||
|
static class LittleEndianReader {
|
||||||
|
private byte[] b = new byte[4];
|
||||||
|
byte[] hdrBuf = new byte[CENHDR];
|
||||||
|
|
||||||
|
int readShortLE(InputStream in) throws IOException {
|
||||||
|
if (in.read(b, 0, 2) == 2) {
|
||||||
|
return (b[0] & 0XFF) | ((b[1] & 0XFF) << 8);
|
||||||
|
} else {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long readIntLE(InputStream in) throws IOException {
|
||||||
|
if (in.read(b, 0, 4) == 4) {
|
||||||
|
return ((b[0] & 0XFF)
|
||||||
|
| ((b[1] & 0XFF) << 8)
|
||||||
|
| ((b[2] & 0XFF) << 16)
|
||||||
|
| ((b[3] & 0XFF) << 24))
|
||||||
|
& 0XFFFFFFFFL; // Here for sure NO sign extension is wanted.
|
||||||
|
} else {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class TZipException extends IOException {
|
||||||
|
public TZipException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TZipException(String detailMessage) {
|
||||||
|
super(detailMessage);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,343 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import org.teavm.classlib.java.util.zip.TZipEntry.LittleEndianReader;
|
||||||
|
|
||||||
|
public class TZipFile implements TZipConstants {
|
||||||
|
public static final int OPEN_READ = 1;
|
||||||
|
public static final int OPEN_DELETE = 4;
|
||||||
|
|
||||||
|
private final String fileName;
|
||||||
|
private File fileToDeleteOnClose;
|
||||||
|
private RandomAccessFile mRaf;
|
||||||
|
private final LittleEndianReader ler = new LittleEndianReader();
|
||||||
|
|
||||||
|
private final LinkedHashMap<String, TZipEntry> mEntries = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public TZipFile(File file) throws TZipException, IOException {
|
||||||
|
this(file, OPEN_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TZipFile(File file, int mode) throws IOException {
|
||||||
|
fileName = file.getPath();
|
||||||
|
if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE)) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mode & OPEN_DELETE) != 0) {
|
||||||
|
fileToDeleteOnClose = file; // file.deleteOnExit();
|
||||||
|
} else {
|
||||||
|
fileToDeleteOnClose = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mRaf = new RandomAccessFile(fileName, "r");
|
||||||
|
|
||||||
|
readCentralDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TZipFile(String name) throws IOException {
|
||||||
|
this(new File(name), OPEN_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws IOException {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
RandomAccessFile raf = mRaf;
|
||||||
|
|
||||||
|
if (raf != null) {
|
||||||
|
mRaf = null;
|
||||||
|
raf.close();
|
||||||
|
if (fileToDeleteOnClose != null) {
|
||||||
|
new File(fileName).delete();
|
||||||
|
// fileToDeleteOnClose.delete();
|
||||||
|
fileToDeleteOnClose = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNotClosed() {
|
||||||
|
if (mRaf == null) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration<? extends TZipEntry> entries() {
|
||||||
|
checkNotClosed();
|
||||||
|
final Iterator<TZipEntry> iterator = mEntries.values().iterator();
|
||||||
|
|
||||||
|
return new Enumeration<TZipEntry>() {
|
||||||
|
@Override
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
checkNotClosed();
|
||||||
|
return iterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TZipEntry nextElement() {
|
||||||
|
checkNotClosed();
|
||||||
|
return iterator.next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public TZipEntry getEntry(String entryName) {
|
||||||
|
checkNotClosed();
|
||||||
|
if (entryName == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
|
||||||
|
TZipEntry ze = mEntries.get(entryName);
|
||||||
|
if (ze == null) {
|
||||||
|
ze = mEntries.get(entryName + "/");
|
||||||
|
}
|
||||||
|
return ze;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream(TZipEntry entry) throws IOException {
|
||||||
|
/*
|
||||||
|
* Make sure this TZipEntry is in this Zip file. We run it through
|
||||||
|
* the name lookup.
|
||||||
|
*/
|
||||||
|
entry = getEntry(entry.getName());
|
||||||
|
if (entry == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a TZipInputStream at the right part of the file.
|
||||||
|
*/
|
||||||
|
RandomAccessFile raf = mRaf;
|
||||||
|
// We don't know the entry data's start position. All we have is the
|
||||||
|
// position of the entry's local header. At position 28 we find the
|
||||||
|
// length of the extra data. In some cases this length differs from
|
||||||
|
// the one coming in the central header.
|
||||||
|
RAFStream rafstrm = new RAFStream(raf, entry.mLocalHeaderRelOffset + 28);
|
||||||
|
int localExtraLenOrWhatever = ler.readShortLE(rafstrm);
|
||||||
|
// Skip the name and this "extra" data or whatever it is:
|
||||||
|
rafstrm.skip(entry.nameLen + localExtraLenOrWhatever);
|
||||||
|
rafstrm.mLength = rafstrm.mOffset + entry.compressedSize;
|
||||||
|
if (entry.compressionMethod == java.util.zip.ZipEntry.DEFLATED) {
|
||||||
|
int bufSize = Math.max(1024, (int) Math.min(entry.getSize(), 65535L));
|
||||||
|
return new ZipInflaterInputStream(rafstrm, new TInflater(true), bufSize, entry);
|
||||||
|
} else {
|
||||||
|
return rafstrm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
checkNotClosed();
|
||||||
|
return mEntries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the central directory and read the contents.
|
||||||
|
*
|
||||||
|
* <p>The central directory can be followed by a variable-length comment
|
||||||
|
* field, so we have to scan through it backwards. The comment is at
|
||||||
|
* most 64K, plus we have 18 bytes for the end-of-central-dir stuff
|
||||||
|
* itself, plus apparently sometimes people throw random junk on the end
|
||||||
|
* just for the fun of it.
|
||||||
|
*
|
||||||
|
* <p>This is all a little wobbly. If the wrong value ends up in the EOCD
|
||||||
|
* area, we're hosed. This appears to be the way that everybody handles
|
||||||
|
* it though, so we're in good company if this fails.
|
||||||
|
*/
|
||||||
|
private void readCentralDir() throws IOException {
|
||||||
|
/*
|
||||||
|
* Scan back, looking for the End Of Central Directory field. If
|
||||||
|
* the archive doesn't have a comment, we'll hit it on the first
|
||||||
|
* try.
|
||||||
|
*
|
||||||
|
* No need to synchronize mRaf here -- we only do this when we
|
||||||
|
* first open the Zip file.
|
||||||
|
*/
|
||||||
|
long scanOffset = mRaf.length() - ENDHDR;
|
||||||
|
if (scanOffset < 0) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
|
||||||
|
long stopOffset = scanOffset - 65536;
|
||||||
|
if (stopOffset < 0) {
|
||||||
|
stopOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
mRaf.seek(scanOffset);
|
||||||
|
if (TZipEntry.readIntLE(mRaf) == 101010256L) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
scanOffset--;
|
||||||
|
if (scanOffset < stopOffset) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Found it, read the EOCD.
|
||||||
|
*
|
||||||
|
* For performance we want to use buffered I/O when reading the
|
||||||
|
* file. We wrap a buffered stream around the random-access file
|
||||||
|
* object. If we just read from the RandomAccessFile we'll be
|
||||||
|
* doing a read() system call every time.
|
||||||
|
*/
|
||||||
|
RAFStream rafs = new RAFStream(mRaf, mRaf.getFilePointer());
|
||||||
|
BufferedInputStream bin = new BufferedInputStream(rafs, ENDHDR);
|
||||||
|
|
||||||
|
int diskNumber = ler.readShortLE(bin);
|
||||||
|
int diskWithCentralDir = ler.readShortLE(bin);
|
||||||
|
int numEntries = ler.readShortLE(bin);
|
||||||
|
int totalNumEntries = ler.readShortLE(bin);
|
||||||
|
/*centralDirSize =*/ ler.readIntLE(bin);
|
||||||
|
long centralDirOffset = ler.readIntLE(bin);
|
||||||
|
/*commentLen =*/ ler.readShortLE(bin);
|
||||||
|
|
||||||
|
if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Seek to the first CDE and read all entries.
|
||||||
|
* However, when Z_SYNC_FLUSH is used the offset may not point directly
|
||||||
|
* to the CDE so skip over until we find it.
|
||||||
|
* At most it will be 6 bytes away (one or two bytes for empty block, 4 bytes for
|
||||||
|
* empty block signature).
|
||||||
|
*/
|
||||||
|
scanOffset = centralDirOffset;
|
||||||
|
stopOffset = scanOffset + 6;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
mRaf.seek(scanOffset);
|
||||||
|
if (TZipEntry.readIntLE(mRaf) == CENSIG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
scanOffset++;
|
||||||
|
if (scanOffset > stopOffset) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If CDE is found then go and read all the entries
|
||||||
|
rafs = new RAFStream(mRaf, scanOffset);
|
||||||
|
bin = new BufferedInputStream(rafs, 4096);
|
||||||
|
for (int i = 0; i < numEntries; i++) {
|
||||||
|
TZipEntry newEntry = new TZipEntry(ler, bin);
|
||||||
|
mEntries.put(newEntry.getName(), newEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RAFStream extends InputStream {
|
||||||
|
|
||||||
|
RandomAccessFile mSharedRaf;
|
||||||
|
long mOffset;
|
||||||
|
long mLength;
|
||||||
|
|
||||||
|
public RAFStream(RandomAccessFile raf, long pos) throws IOException {
|
||||||
|
mSharedRaf = raf;
|
||||||
|
mOffset = pos;
|
||||||
|
mLength = raf.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
if (mLength > mOffset) {
|
||||||
|
if (mLength - mOffset < Integer.MAX_VALUE) {
|
||||||
|
return (int) (mLength - mOffset);
|
||||||
|
} else {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
byte[] singleByteBuf = new byte[1];
|
||||||
|
if (read(singleByteBuf, 0, 1) == 1) {
|
||||||
|
return singleByteBuf[0] & 0XFF;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
mSharedRaf.seek(mOffset);
|
||||||
|
if (len > mLength - mOffset) {
|
||||||
|
len = (int) (mLength - mOffset);
|
||||||
|
}
|
||||||
|
int count = mSharedRaf.read(b, off, len);
|
||||||
|
if (count > 0) {
|
||||||
|
mOffset += count;
|
||||||
|
return count;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
if (n > mLength - mOffset) {
|
||||||
|
n = mLength - mOffset;
|
||||||
|
}
|
||||||
|
mOffset += n;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ZipInflaterInputStream extends TInflaterInputStream {
|
||||||
|
TZipEntry entry;
|
||||||
|
long bytesRead;
|
||||||
|
|
||||||
|
public ZipInflaterInputStream(InputStream is, TInflater inf, int bsize, TZipEntry entry) {
|
||||||
|
super(is, inf, bsize);
|
||||||
|
this.entry = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] buffer, int off, int nbytes) throws IOException {
|
||||||
|
int i = super.read(buffer, off, nbytes);
|
||||||
|
if (i != -1) {
|
||||||
|
bytesRead += i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
return super.available() == 0 ? 0 : (int) (entry.getSize() - bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,330 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PushbackInputStream;
|
||||||
|
|
||||||
|
public class TZipInputStream extends TInflaterInputStream implements TZipConstants {
|
||||||
|
static final int DEFLATED = 8;
|
||||||
|
static final int STORED = 0;
|
||||||
|
static final int ZIPDataDescriptorFlag = 8;
|
||||||
|
static final int ZIPLocalHeaderVersionNeeded = 20;
|
||||||
|
private boolean entriesEnd;
|
||||||
|
private boolean hasDD;
|
||||||
|
private int entryIn;
|
||||||
|
private int inRead;
|
||||||
|
private int lastRead;
|
||||||
|
TZipEntry currentEntry;
|
||||||
|
private final byte[] hdrBuf = new byte[LOCHDR - LOCVER];
|
||||||
|
private final TCRC32 crc = new TCRC32();
|
||||||
|
private byte[] nameBuf = new byte[256];
|
||||||
|
private char[] charBuf = new char[256];
|
||||||
|
|
||||||
|
public TZipInputStream(InputStream stream) {
|
||||||
|
super(new PushbackInputStream(stream, 512), new TInflater(true));
|
||||||
|
if (stream == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (!closed) {
|
||||||
|
closeEntry(); // Close the current entry
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeEntry() throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
if (currentEntry == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following code is careful to leave the TZipInputStream in a
|
||||||
|
* consistent state, even when close() results in an exception. It does
|
||||||
|
* so by:
|
||||||
|
* - pushing bytes back into the source stream
|
||||||
|
* - reading a data descriptor footer from the source stream
|
||||||
|
* - resetting fields that manage the entry being closed
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Ensure all entry bytes are read
|
||||||
|
Exception failure = null;
|
||||||
|
try {
|
||||||
|
skip(Long.MAX_VALUE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
failure = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
int inB;
|
||||||
|
int out;
|
||||||
|
if (currentEntry.compressionMethod == DEFLATED) {
|
||||||
|
inB = inf.getTotalIn();
|
||||||
|
out = inf.getTotalOut();
|
||||||
|
} else {
|
||||||
|
inB = inRead;
|
||||||
|
out = inRead;
|
||||||
|
}
|
||||||
|
int diff = entryIn - inB;
|
||||||
|
// Pushback any required bytes
|
||||||
|
if (diff != 0) {
|
||||||
|
((PushbackInputStream) in).unread(buf, len - diff, diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
readAndVerifyDataDescriptor(inB, out);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (failure == null) { // otherwise we're already going to throw
|
||||||
|
failure = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inf.reset();
|
||||||
|
lastRead = 0;
|
||||||
|
inRead = 0;
|
||||||
|
entryIn = 0;
|
||||||
|
len = 0;
|
||||||
|
crc.reset();
|
||||||
|
currentEntry = null;
|
||||||
|
|
||||||
|
if (failure != null) {
|
||||||
|
if (failure instanceof IOException) {
|
||||||
|
throw (IOException) failure;
|
||||||
|
} else if (failure instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) failure;
|
||||||
|
}
|
||||||
|
throw new AssertionError(failure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readAndVerifyDataDescriptor(int inB, int out) throws IOException {
|
||||||
|
if (hasDD) {
|
||||||
|
in.read(hdrBuf, 0, EXTHDR);
|
||||||
|
if (getLong(hdrBuf, 0) != EXTSIG) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
currentEntry.crc = getLong(hdrBuf, EXTCRC);
|
||||||
|
currentEntry.compressedSize = getLong(hdrBuf, EXTSIZ);
|
||||||
|
currentEntry.size = getLong(hdrBuf, EXTLEN);
|
||||||
|
}
|
||||||
|
if (currentEntry.crc != crc.getValue()) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
if (currentEntry.compressedSize != inB || currentEntry.size != out) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TZipEntry getNextEntry() throws IOException {
|
||||||
|
closeEntry();
|
||||||
|
if (entriesEnd) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
int count = 0;
|
||||||
|
while (count != 4) {
|
||||||
|
x = in.read(hdrBuf, count, 4 - count);
|
||||||
|
count += x;
|
||||||
|
if (x == -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long hdr = getLong(hdrBuf, 0);
|
||||||
|
if (hdr == CENSIG) {
|
||||||
|
entriesEnd = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (hdr != LOCSIG) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the local header
|
||||||
|
count = 0;
|
||||||
|
while (count != LOCHDR - LOCVER) {
|
||||||
|
x = in.read(hdrBuf, count, (LOCHDR - LOCVER) - count);
|
||||||
|
count += x;
|
||||||
|
if (x == -1) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int version = getShort(hdrBuf, 0) & 0xff;
|
||||||
|
if (version > ZIPLocalHeaderVersionNeeded) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
int flags = getShort(hdrBuf, LOCFLG - LOCVER);
|
||||||
|
hasDD = (flags & ZIPDataDescriptorFlag) == ZIPDataDescriptorFlag;
|
||||||
|
int cetime = getShort(hdrBuf, LOCTIM - LOCVER);
|
||||||
|
int cemodDate = getShort(hdrBuf, LOCTIM - LOCVER + 2);
|
||||||
|
int cecompressionMethod = getShort(hdrBuf, LOCHOW - LOCVER);
|
||||||
|
long cecrc = 0;
|
||||||
|
long cecompressedSize = 0;
|
||||||
|
long cesize = -1;
|
||||||
|
if (!hasDD) {
|
||||||
|
cecrc = getLong(hdrBuf, LOCCRC - LOCVER);
|
||||||
|
cecompressedSize = getLong(hdrBuf, LOCSIZ - LOCVER);
|
||||||
|
cesize = getLong(hdrBuf, LOCLEN - LOCVER);
|
||||||
|
}
|
||||||
|
int flen = getShort(hdrBuf, LOCNAM - LOCVER);
|
||||||
|
if (flen == 0) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
int elen = getShort(hdrBuf, LOCEXT - LOCVER);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
if (flen > nameBuf.length) {
|
||||||
|
nameBuf = new byte[flen];
|
||||||
|
charBuf = new char[flen];
|
||||||
|
}
|
||||||
|
while (count != flen) {
|
||||||
|
x = in.read(nameBuf, count, flen - count);
|
||||||
|
count += x;
|
||||||
|
if (x == -1) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentEntry = createZipEntry(new String(nameBuf, 0, flen, "UTF-8"));
|
||||||
|
currentEntry.time = cetime;
|
||||||
|
currentEntry.modDate = cemodDate;
|
||||||
|
currentEntry.setMethod(cecompressionMethod);
|
||||||
|
if (cesize != -1) {
|
||||||
|
currentEntry.setCrc(cecrc);
|
||||||
|
currentEntry.setSize(cesize);
|
||||||
|
currentEntry.setCompressedSize(cecompressedSize);
|
||||||
|
}
|
||||||
|
if (elen > 0) {
|
||||||
|
count = 0;
|
||||||
|
byte[] e = new byte[elen];
|
||||||
|
while (count != elen) {
|
||||||
|
x = in.read(e, count, elen - count);
|
||||||
|
count += x;
|
||||||
|
if (x == -1) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentEntry.setExtra(e);
|
||||||
|
}
|
||||||
|
return currentEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] buffer, int start, int length) throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
if (inf.finished() || currentEntry == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// avoid int overflow, check null buffer
|
||||||
|
if (start > buffer.length || length < 0 || start < 0 || buffer.length - start < length) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentEntry.compressionMethod == STORED) {
|
||||||
|
int csize = (int) currentEntry.size;
|
||||||
|
if (inRead >= csize) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (lastRead >= len) {
|
||||||
|
lastRead = 0;
|
||||||
|
len = in.read(buf);
|
||||||
|
if (len == -1) {
|
||||||
|
eof = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
entryIn += len;
|
||||||
|
}
|
||||||
|
int toRead = length > len - lastRead ? len - lastRead : length;
|
||||||
|
if ((csize - inRead) < toRead) {
|
||||||
|
toRead = csize - inRead;
|
||||||
|
}
|
||||||
|
System.arraycopy(buf, lastRead, buffer, start, toRead);
|
||||||
|
lastRead += toRead;
|
||||||
|
inRead += toRead;
|
||||||
|
crc.update(buffer, start, toRead);
|
||||||
|
return toRead;
|
||||||
|
}
|
||||||
|
if (inf.needsInput()) {
|
||||||
|
fill();
|
||||||
|
if (len > 0) {
|
||||||
|
entryIn += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int read;
|
||||||
|
try {
|
||||||
|
read = inf.inflate(buffer, start, length);
|
||||||
|
} catch (TDataFormatException e) {
|
||||||
|
throw new TZipException(e.getMessage());
|
||||||
|
}
|
||||||
|
if (read == 0 && inf.finished()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
crc.update(buffer, start, read);
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long value) throws IOException {
|
||||||
|
if (value < 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
long skipped = 0;
|
||||||
|
byte[] b = new byte[(int) Math.min(value, 2048L)];
|
||||||
|
while (skipped != value) {
|
||||||
|
long rem = value - skipped;
|
||||||
|
int x = read(b, 0, (int) (b.length > rem ? rem : b.length));
|
||||||
|
if (x == -1) {
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
skipped += x;
|
||||||
|
}
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
if (closed) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
return (currentEntry == null || inRead < currentEntry.size) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TZipEntry createZipEntry(String name) {
|
||||||
|
return new TZipEntry(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getShort(byte[] buffer, int off) {
|
||||||
|
return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getLong(byte[] buffer, int off) {
|
||||||
|
long l = 0;
|
||||||
|
l |= buffer[off] & 0xFF;
|
||||||
|
l |= (buffer[off + 1] & 0xFF) << 8;
|
||||||
|
l |= (buffer[off + 2] & 0xFF) << 16;
|
||||||
|
l |= ((long) (buffer[off + 3] & 0xFF)) << 24;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,330 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.util.zip;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TZipOutputStream extends TDeflaterOutputStream implements TZipConstants {
|
||||||
|
public static final int DEFLATED = 8;
|
||||||
|
public static final int STORED = 0;
|
||||||
|
|
||||||
|
static final int ZIPDataDescriptorFlag = 8;
|
||||||
|
static final int ZIPLocalHeaderVersionNeeded = 20;
|
||||||
|
private String comment;
|
||||||
|
private final List<String> entries = new ArrayList<>();
|
||||||
|
private int compressMethod = DEFLATED;
|
||||||
|
private int compressLevel = TDeflater.DEFAULT_COMPRESSION;
|
||||||
|
private ByteArrayOutputStream cDir = new ByteArrayOutputStream();
|
||||||
|
private TZipEntry currentEntry;
|
||||||
|
private final TCRC32 crc = new TCRC32();
|
||||||
|
private int offset;
|
||||||
|
private int curOffset;
|
||||||
|
private int nameLength;
|
||||||
|
private byte[] nameBytes;
|
||||||
|
|
||||||
|
public TZipOutputStream(OutputStream p1) {
|
||||||
|
super(p1, new TDeflater(TDeflater.DEFAULT_COMPRESSION, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (out != null) {
|
||||||
|
finish();
|
||||||
|
out.close();
|
||||||
|
out = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeEntry() throws IOException {
|
||||||
|
if (cDir == null) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
if (currentEntry == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentEntry.getMethod() == DEFLATED) {
|
||||||
|
super.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify values for STORED types
|
||||||
|
if (currentEntry.getMethod() == STORED) {
|
||||||
|
if (crc.getValue() != currentEntry.crc) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
if (currentEntry.size != crc.tbytes) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curOffset = LOCHDR;
|
||||||
|
|
||||||
|
// Write the DataDescriptor
|
||||||
|
if (currentEntry.getMethod() != STORED) {
|
||||||
|
curOffset += EXTHDR;
|
||||||
|
writeLong(out, EXTSIG);
|
||||||
|
currentEntry.crc = crc.getValue();
|
||||||
|
writeLong(out, currentEntry.crc);
|
||||||
|
currentEntry.compressedSize = def.getTotalOut();
|
||||||
|
writeLong(out, currentEntry.compressedSize);
|
||||||
|
currentEntry.size = def.getTotalIn();
|
||||||
|
writeLong(out, currentEntry.size);
|
||||||
|
}
|
||||||
|
// Update the CentralDirectory
|
||||||
|
writeLong(cDir, CENSIG);
|
||||||
|
writeShort(cDir, ZIPLocalHeaderVersionNeeded); // Version created
|
||||||
|
writeShort(cDir, ZIPLocalHeaderVersionNeeded); // Version to extract
|
||||||
|
writeShort(cDir, currentEntry.getMethod() == STORED ? 0 : ZIPDataDescriptorFlag);
|
||||||
|
writeShort(cDir, currentEntry.getMethod());
|
||||||
|
writeShort(cDir, currentEntry.time);
|
||||||
|
writeShort(cDir, currentEntry.modDate);
|
||||||
|
writeLong(cDir, crc.getValue());
|
||||||
|
if (currentEntry.getMethod() == DEFLATED) {
|
||||||
|
curOffset += writeLong(cDir, def.getTotalOut());
|
||||||
|
writeLong(cDir, def.getTotalIn());
|
||||||
|
} else {
|
||||||
|
curOffset += writeLong(cDir, crc.tbytes);
|
||||||
|
writeLong(cDir, crc.tbytes);
|
||||||
|
}
|
||||||
|
curOffset += writeShort(cDir, nameLength);
|
||||||
|
if (currentEntry.extra != null) {
|
||||||
|
curOffset += writeShort(cDir, currentEntry.extra.length);
|
||||||
|
} else {
|
||||||
|
writeShort(cDir, 0);
|
||||||
|
}
|
||||||
|
String c = currentEntry.getComment();
|
||||||
|
writeShort(cDir, c != null ? c.length() : 0);
|
||||||
|
writeShort(cDir, 0); // Disk Start
|
||||||
|
writeShort(cDir, 0); // Internal File Attributes
|
||||||
|
writeLong(cDir, 0); // External File Attributes
|
||||||
|
writeLong(cDir, offset);
|
||||||
|
cDir.write(nameBytes);
|
||||||
|
nameBytes = null;
|
||||||
|
if (currentEntry.extra != null) {
|
||||||
|
cDir.write(currentEntry.extra);
|
||||||
|
}
|
||||||
|
offset += curOffset;
|
||||||
|
if (c != null) {
|
||||||
|
cDir.write(c.getBytes());
|
||||||
|
}
|
||||||
|
currentEntry = null;
|
||||||
|
crc.reset();
|
||||||
|
def.reset();
|
||||||
|
done = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() throws IOException {
|
||||||
|
if (out == null) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
if (cDir == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (entries.size() == 0) {
|
||||||
|
throw new TZipException();
|
||||||
|
}
|
||||||
|
if (currentEntry != null) {
|
||||||
|
closeEntry();
|
||||||
|
}
|
||||||
|
int cdirSize = cDir.size();
|
||||||
|
// Write Central Dir End
|
||||||
|
writeLong(cDir, ENDSIG);
|
||||||
|
writeShort(cDir, 0); // Disk Number
|
||||||
|
writeShort(cDir, 0); // Start Disk
|
||||||
|
writeShort(cDir, entries.size()); // Number of entries
|
||||||
|
writeShort(cDir, entries.size()); // Number of entries
|
||||||
|
writeLong(cDir, cdirSize); // Size of central dir
|
||||||
|
writeLong(cDir, offset); // Offset of central dir
|
||||||
|
if (comment != null) {
|
||||||
|
writeShort(cDir, comment.length());
|
||||||
|
cDir.write(comment.getBytes());
|
||||||
|
} else {
|
||||||
|
writeShort(cDir, 0);
|
||||||
|
}
|
||||||
|
// Write the central dir
|
||||||
|
out.write(cDir.toByteArray());
|
||||||
|
cDir = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putNextEntry(TZipEntry ze) throws IOException {
|
||||||
|
if (currentEntry != null) {
|
||||||
|
closeEntry();
|
||||||
|
}
|
||||||
|
if (ze.getMethod() == STORED
|
||||||
|
|| (compressMethod == STORED && ze.getMethod() == -1)) {
|
||||||
|
if (ze.crc == -1) {
|
||||||
|
throw new TZipException("Crc mismatch");
|
||||||
|
}
|
||||||
|
if (ze.size == -1 && ze.compressedSize == -1) {
|
||||||
|
throw new TZipException("Size mismatch");
|
||||||
|
}
|
||||||
|
if (ze.size != ze.compressedSize && ze.compressedSize != -1 && ze.size != -1) {
|
||||||
|
throw new TZipException("Size mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cDir == null) {
|
||||||
|
throw new IOException("Stream is closed");
|
||||||
|
}
|
||||||
|
if (entries.contains(ze.name)) {
|
||||||
|
throw new TZipException("Entry already exists: " + ze.name);
|
||||||
|
}
|
||||||
|
nameLength = utf8Count(ze.name);
|
||||||
|
if (nameLength > 0xffff) {
|
||||||
|
throw new IllegalArgumentException("Name too long: " + ze.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
def.setLevel(compressLevel);
|
||||||
|
currentEntry = ze;
|
||||||
|
entries.add(currentEntry.name);
|
||||||
|
if (currentEntry.getMethod() == -1) {
|
||||||
|
currentEntry.setMethod(compressMethod);
|
||||||
|
}
|
||||||
|
writeLong(out, LOCSIG); // Entry header
|
||||||
|
writeShort(out, ZIPLocalHeaderVersionNeeded); // Extraction version
|
||||||
|
writeShort(out, currentEntry.getMethod() == STORED ? 0 : ZIPDataDescriptorFlag);
|
||||||
|
writeShort(out, currentEntry.getMethod());
|
||||||
|
if (currentEntry.getTime() == -1) {
|
||||||
|
currentEntry.setTime(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
writeShort(out, currentEntry.time);
|
||||||
|
writeShort(out, currentEntry.modDate);
|
||||||
|
|
||||||
|
if (currentEntry.getMethod() == STORED) {
|
||||||
|
if (currentEntry.size == -1) {
|
||||||
|
currentEntry.size = currentEntry.compressedSize;
|
||||||
|
} else if (currentEntry.compressedSize == -1) {
|
||||||
|
currentEntry.compressedSize = currentEntry.size;
|
||||||
|
}
|
||||||
|
writeLong(out, currentEntry.crc);
|
||||||
|
writeLong(out, currentEntry.size);
|
||||||
|
writeLong(out, currentEntry.size);
|
||||||
|
} else {
|
||||||
|
writeLong(out, 0);
|
||||||
|
writeLong(out, 0);
|
||||||
|
writeLong(out, 0);
|
||||||
|
}
|
||||||
|
writeShort(out, nameLength);
|
||||||
|
writeShort(out, currentEntry.extra != null ? currentEntry.extra.length : 0);
|
||||||
|
nameBytes = toUTF8Bytes(currentEntry.name, nameLength);
|
||||||
|
out.write(nameBytes);
|
||||||
|
if (currentEntry.extra != null) {
|
||||||
|
out.write(currentEntry.extra);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComment(String comment) {
|
||||||
|
if (comment.length() > 0xFFFF) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
this.comment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevel(int level) {
|
||||||
|
if (level < TDeflater.DEFAULT_COMPRESSION || level > TDeflater.BEST_COMPRESSION) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
compressLevel = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMethod(int method) {
|
||||||
|
if (method != STORED && method != DEFLATED) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
compressMethod = method;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private long writeLong(OutputStream os, long i) throws IOException {
|
||||||
|
// Write out the long value as an unsigned int
|
||||||
|
os.write((int) (i & 0xFF));
|
||||||
|
os.write((int) (i >> 8) & 0xFF);
|
||||||
|
os.write((int) (i >> 16) & 0xFF);
|
||||||
|
os.write((int) (i >> 24) & 0xFF);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int writeShort(OutputStream os, int i) throws IOException {
|
||||||
|
os.write(i & 0xFF);
|
||||||
|
os.write((i >> 8) & 0xFF);
|
||||||
|
return i;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes data for the current entry to the underlying stream.
|
||||||
|
*
|
||||||
|
* @exception IOException
|
||||||
|
* If an error occurs writing to the stream
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void write(byte[] buffer, int off, int nbytes)
|
||||||
|
throws IOException {
|
||||||
|
// avoid int overflow, check null buf
|
||||||
|
if ((off < 0 || (nbytes < 0) || off > buffer.length) || (buffer.length - off < nbytes)) {
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentEntry == null) {
|
||||||
|
throw new TZipException("No active entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentEntry.getMethod() == STORED) {
|
||||||
|
out.write(buffer, off, nbytes);
|
||||||
|
} else {
|
||||||
|
super.write(buffer, off, nbytes);
|
||||||
|
}
|
||||||
|
crc.update(buffer, off, nbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int utf8Count(String value) {
|
||||||
|
int total = 0;
|
||||||
|
for (int i = value.length(); --i >= 0;) {
|
||||||
|
char ch = value.charAt(i);
|
||||||
|
if (ch < 0x80) {
|
||||||
|
total++;
|
||||||
|
} else if (ch < 0x800) {
|
||||||
|
total += 2;
|
||||||
|
} else {
|
||||||
|
total += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] toUTF8Bytes(String value, int length) {
|
||||||
|
byte[] result = new byte[length];
|
||||||
|
int pos = result.length;
|
||||||
|
for (int i = value.length(); --i >= 0;) {
|
||||||
|
char ch = value.charAt(i);
|
||||||
|
if (ch < 0x80) {
|
||||||
|
result[--pos] = (byte) ch;
|
||||||
|
} else if (ch < 0x800) {
|
||||||
|
result[--pos] = (byte) (0x80 | (ch & 0x3f));
|
||||||
|
result[--pos] = (byte) (0xc0 | (ch >> 6));
|
||||||
|
} else {
|
||||||
|
result[--pos] = (byte) (0x80 | (ch & 0x3f));
|
||||||
|
result[--pos] = (byte) (0x80 | ((ch >> 6) & 0x3f));
|
||||||
|
result[--pos] = (byte) (0xe0 | (ch >> 12));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user