/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2010 by Trifork
*
* 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 erjang.driver.zlib;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import com.jcraft.jzlib.CRC32;
import com.jcraft.jzlib.Deflater;
import com.jcraft.jzlib.Inflater;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZStream;
import com.jcraft.jzlib.ZStreamException;
import kilim.Pausable;
import kilim.Task;
import erjang.EBinary;
import erjang.EHandle;
import erjang.EPID;
import erjang.EString;
import erjang.NotImplemented;
import erjang.driver.EAsync;
import erjang.driver.EDriver;
import erjang.driver.EDriverInstance;
import erjang.driver.IO;
public class ZLibDriver extends EDriverInstance {
// flush argument encoding
public static final int Z_NO_FLUSH = 0;
public static final int Z_SYNC_FLUSH = 2;
public static final int Z_FULL_FLUSH = 3;
public static final int Z_FINISH = 4;
// compression level
public static final int Z_NO_COMPRESSION = 0;
public static final int Z_BEST_SPEED = 1;
public static final int Z_BEST_COMPRESSION = 9;
public static final int Z_DEFAULT_COMPRESSION = (-1);
// compresssion strategy
public static final int Z_FILTERED = 1;
public static final int Z_HUFFMAN_ONLY = 2;
public static final int Z_DEFAULT_STRATEGY = 0;
// deflate compression method
public static final int Z_DEFLATED = 8;
public static final int Z_NULL = 0;
public static final int MAX_WBITS = 15;
// gzip defs (rfc 1952)
public static final int ID1 = 0x1f;
public static final int ID2 = 0x8b;
public static final int FTEXT = 0x01;
public static final int FHCRC = 0x02;
public static final int FEXTRA = 0x04;
public static final int FNAME = 0x08;
public static final int FCOMMENT = 0x10;
public static final int RESERVED = 0xE0;
public static final int OS_MDDOS = 0;
public static final int OS_AMIGA = 1;
public static final int OS_OPENVMS = 2;
public static final int OS_UNIX = 3;
public static final int OS_VMCMS = 4;
public static final int OS_ATARI = 5;
public static final int OS_OS2 = 6;
public static final int OS_MAC = 7;
public static final int OS_ZSYS = 8;
public static final int OS_CPM = 9;
public static final int OS_TOP20 = 10;
public static final int OS_NTFS = 11;
public static final int OS_QDOS = 12;
public static final int OS_ACORN = 13;
public static final int OS_UNKNOWN = 255;
public static final int DEFLATE_INIT = 1;
public static final int DEFLATE_INIT2 = 2;
public static final int DEFLATE_SETDICT = 3;
public static final int DEFLATE_RESET = 4;
public static final int DEFLATE_END = 5;
public static final int DEFLATE_PARAMS = 6;
public static final int DEFLATE = 7;
public static final int INFLATE_INIT = 8;
public static final int INFLATE_INIT2 = 9;
public static final int INFLATE_SETDICT = 10;
public static final int INFLATE_SYNC = 11;
public static final int INFLATE_RESET = 12;
public static final int INFLATE_END = 13;
public static final int INFLATE = 14;
public static final int CRC32_0 = 15;
public static final int CRC32_1 = 16;
public static final int CRC32_2 = 17;
public static final int SET_BUFSZ = 18;
public static final int GET_BUFSZ = 19;
public static final int GET_QSIZE = 20;
public static final int ADLER32_1 = 21;
public static final int ADLER32_2 = 22;
public static final int CRC32_COMBINE = 23;
public static final int ADLER32_COMBINE = 24;
private Inflater inflater;
private Deflater deflater;
public ZLibDriver(EDriver driver, EString command) {
super(driver);
}
ArrayList<ByteBuffer> inbuf = new ArrayList<ByteBuffer>();
@Override
protected void output(EHandle caller, ByteBuffer buf) throws IOException,
Pausable {
inbuf.add(buf);
}
@Override
protected void outputv(EHandle caller, ByteBuffer[] buf) throws IOException,
Pausable {
for (int i = 0; i < buf.length; i++) {
inbuf.add(buf[i]);
}
}
int do_inflate(int flush_mode) throws Pausable {
int err = JZlib.Z_OK;
read_loop:
for (int i = 0; err==JZlib.Z_OK && i < inbuf.size(); i++) {
ByteBuffer bb = inbuf.get(i);
inflater.setInput(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining(), true);
int in_size = bb.remaining();
ByteBuffer out = ByteBuffer.allocate(1024);
do {
long read_before = inflater.total_in;
long wrote_before = inflater.total_out;
inflater.next_out = out.array();
inflater.next_out_index = out.arrayOffset() + out.position();
inflater.avail_out = out.remaining();
err = inflater.inflate(flush_mode);
if (err == JZlib.Z_OK || err == JZlib.Z_STREAM_END) {
long read_after = inflater.total_in;
long wrote_after = inflater.total_out;
in_size -= (read_after-read_before);
int written = (int) (wrote_after - wrote_before);
out.position(written);
driver_output(out);
out = ByteBuffer.allocate(1024);
}
} while(err==JZlib.Z_OK && in_size > 0);
}
inbuf.clear();
if (flush_mode == Z_FINISH) {
inflater.free();
}
return err;
}
void do_deflate(int flush_mode) throws Pausable {
int err = JZlib.Z_OK;
read_loop:
for (int i = 0; err==JZlib.Z_OK && i < inbuf.size(); i++) {
ByteBuffer bb = inbuf.get(i);
deflater.next_in = bb.array();
deflater.next_in_index = bb.arrayOffset() + bb.position();
deflater.avail_in = bb.remaining();
// inf.setInput(bb.array(), bb.arrayOffset()+bb.position(), bb.limit());
int in_size = bb.remaining();
do {
ByteBuffer out = ByteBuffer.allocate(1024);
long read_before = deflater.total_in;
long wrote_before = deflater.total_out;
deflater.next_out = out.array();
deflater.next_out_index = out.arrayOffset() + out.position();
deflater.avail_out = out.remaining();
err = deflater.deflate(flush_mode);
if (err == JZlib.Z_OK || err == JZlib.Z_STREAM_END) {
long read_after = deflater.total_in;
long wrote_after = deflater.total_out;
in_size -= (read_after-read_before);
int written = (int) (wrote_after - wrote_before);
out.position(written);
driver_output(out);
}
} while(err==JZlib.Z_OK && in_size > 0);
}
inbuf.clear();
if (flush_mode == Z_FINISH) {
deflater.free();
}
}
@Override
protected ByteBuffer control(EPID caller, int command, ByteBuffer cmd)
throws Pausable {
switch (command) {
case INFLATE_INIT:
{
this.inflater = new Inflater();
this.inflater.inflateInit(true);
return reply_ok();
}
case INFLATE_INIT2:
{
int ws = cmd.getInt();
this.inflater = new Inflater();
this.inflater.inflateInit(ws);
return reply_ok();
}
case DEFLATE_INIT2:
{
int level = cmd.getInt();
int method = cmd.getInt();
int bits = cmd.getInt();
int memlevel = cmd.getInt();
int strategy = cmd.getInt();
this.deflater = new Deflater();
this.deflater.deflateInit(level, bits, memlevel);
this.deflater.deflateParams(level, strategy);
return reply_ok();
}
case INFLATE: {
int flush_mode = cmd.getInt();
int err = do_inflate(flush_mode);
if (err == JZlib.Z_NEED_DICT) {
return reply_need_dict(inflater.getAdler());
}
return reply_ok();
}
case INFLATE_END: {
do_inflate(Z_FINISH);
return reply_ok();
}
case DEFLATE: {
int flush_mode = cmd.getInt();
do_deflate(flush_mode);
return reply_ok();
}
case DEFLATE_END: {
do_deflate(Z_FINISH);
return reply_ok();
}
case CRC32_0: {
if (deflater != null) {
return reply_int( deflater.getAdler() );
}
if (inflater != null) {
return reply_int( inflater.getAdler() );
}
return reply_int(0);
}
case CRC32_1: {
CRC32 crc = new CRC32();
crc.update(cmd.array(), cmd.arrayOffset()+cmd.position(), cmd.remaining());
return reply_int((int) crc.getValue());
}
case CRC32_2: {
CRC32 crc = new CRC32();
long init = cmd.getInt() & 0xffffffffL;
crc.reset(init);
crc.update(cmd.array(), cmd.arrayOffset()+cmd.position(), cmd.remaining());
return reply_int((int) crc.getValue());
}
}
throw new erjang.NotImplemented("command="+command+"; data="+EBinary.make(cmd));
}
private ByteBuffer reply_ok() {
ByteBuffer ok = ByteBuffer.allocate(3);
ok.put((byte) 0);
ok.put((byte) 'o');
ok.put((byte) 'k');
return ok;
}
private ByteBuffer reply_error(String atom) {
ByteBuffer err = ByteBuffer.allocate(1 + atom.length());
err.put((byte) 1);
err.put( atom.getBytes(StandardCharsets.UTF_8) );
return err;
}
private ByteBuffer reply_int(long l) {
ByteBuffer ok = ByteBuffer.allocate(5);
ok.put((byte) 2);
ok.putInt((int)l);
return ok;
}
private ByteBuffer reply_need_dict(long l) {
ByteBuffer ok = ByteBuffer.allocate(5);
ok.put((byte) 3);
ok.putInt((int) l);
return ok;
}
@Override
protected void readyAsync(EAsync data) throws Pausable {
data.ready();
}
}