# HG changeset patch # User Yu Taninari # Date 1321959113 -32400 # Node ID 79046b4e599086c8cce8d8b265d8fafb5dda72fe # Parent 545183e14d4e33ca9887cb04ca4fc059ba19e011 add binary diff -r 545183e14d4e -r 79046b4e5990 bin/myVncProxy/MyRfbProto$1.class Binary file bin/myVncProxy/MyRfbProto$1.class has changed diff -r 545183e14d4e -r 79046b4e5990 bin/myVncProxy/MyRfbProto$2.class Binary file bin/myVncProxy/MyRfbProto$2.class has changed diff -r 545183e14d4e -r 79046b4e5990 bin/myVncProxy/MyRfbProto$3.class Binary file bin/myVncProxy/MyRfbProto$3.class has changed diff -r 545183e14d4e -r 79046b4e5990 bin/myVncProxy/MyRfbProto$4.class Binary file bin/myVncProxy/MyRfbProto$4.class has changed diff -r 545183e14d4e -r 79046b4e5990 bin/myVncProxy/MyRfbProto$5$1.class Binary file bin/myVncProxy/MyRfbProto$5$1.class has changed diff -r 545183e14d4e -r 79046b4e5990 bin/myVncProxy/MyRfbProto$5.class Binary file bin/myVncProxy/MyRfbProto$5.class has changed diff -r 545183e14d4e -r 79046b4e5990 bin/myVncProxy/MyRfbProto.class Binary file bin/myVncProxy/MyRfbProto.class has changed diff -r 545183e14d4e -r 79046b4e5990 bin/myVncProxy/MyRfbProto.java~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/myVncProxy/MyRfbProto.java~ Tue Nov 22 19:51:53 2011 +0900 @@ -0,0 +1,1030 @@ +package myVncProxy; + +import static org.junit.Assert.*; + +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.BindException; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.LinkedList; + +import javax.imageio.ImageIO; + +import org.junit.Test; + +import myVncProxy.MulticastQueue.Client; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; +import java.io.OutputStream; + +public +class MyRfbProto extends RfbProto { + final static String versionMsg_3_855 = "RFB 003.855\n"; + /** + * CheckMillis is one of new msgType for RFB 3.855. + */ + final static byte SpeedCheckMillis = 4; + + // Secyrity type of OS X + final static int SecTypeReqAccess = 32; + + // Supported authentication types + final static int AuthAccess = 32; + + + + private static final int INFLATE_BUFSIZE = 1024*100; + boolean printStatusFlag = false; + long startCheckTime; + + private int messageType; + private int rectangles; + private int rectX; + private int rectY; + private int rectW; + private int rectH; + private int encoding; + private int zLen; + private boolean clicomp = false; + + private ServerSocket servSock; + protected int acceptPort; + private byte initData[]; + private LinkedList cliListTmp; + private LinkedList cliList; + boolean createBimgFlag; + boolean proxyFlag = true; + + ExecutorService executor; + + byte[] pngBytes; + + // private MulticastQueue> multicastqueue = new MostRecentMultiCast>(10); + private MulticastQueue> multicastqueue = new MulticastQueue>(); + private int clients = 0; + private Inflater inflater = new Inflater(); + private Deflater deflater = new Deflater(); + private CreateThread geth; +// private Thread requestThread; + private RequestScreenThread rThread; + private Thread requestThread; + + public + MyRfbProto() throws IOException { + } + + MyRfbProto(String h, int p, VncViewer v) throws IOException { + super(h, p, v); + + rThread = new RequestScreenThread(this); + requestThread = new Thread(rThread); +// requestThread = new Thread(new RequestScreenThread(this)); + } + + MyRfbProto(String h, int p, CreateThread geth) throws IOException { + super(h, p); + this.geth = geth; + proxyFlag = true; + + rThread = new RequestScreenThread(this); + requestThread = new Thread(rThread); +// requestThread = new Thread(new RequestScreenThread(this)); + } + + MyRfbProto(String h, int p) throws IOException { + super(h, p); + + rThread = new RequestScreenThread(this); + requestThread = new Thread(rThread); +// requestThread = new Thread(new RequestScreenThread(this)); + } + + // over write + void writeVersionMsg() throws IOException { + clientMajor = 3; + if (serverMinor == 855) { + clientMinor = 855; + os.write(versionMsg_3_855.getBytes()); + } else if (serverMajor > 3 || serverMinor >= 8) { + clientMinor = 8; + os.write(versionMsg_3_8.getBytes()); + } else if (serverMinor >= 7) { + clientMinor = 7; + os.write(versionMsg_3_7.getBytes()); + } else { + clientMinor = 3; + os.write(versionMsg_3_3.getBytes()); + } + protocolTightVNC = false; + initCapabilities(); + } + + void initServSock(int port) throws IOException { + servSock = new ServerSocket(port); + acceptPort = port; + } + + void authenticationRequestAccess() throws IOException { + + byte[] headBuf = new byte[2]; + is.read(headBuf); + if(headBuf[1] == 2) { + byte[] b = new byte[258]; + is.read(b); + +<<<<<<< local + byte[] outBuf = new byte[192]; + os.write(outBuf); + os.flush(); +======= + byte[] outBuf = new byte[256]; + os.write(outBuf); + os.flush(); + }else if(headBuf[1] == 23) { + byte[] b = new byte[130]; + is.read(b); + byte[] outBuf = new byte[192]; + os.write(outBuf); + os.flush(); + } +>>>>>>> other + + int result = readU32(); + if(result != 0) { + System.out.println("faild authentication "); + throw new IOException(); + } + + + + + } + + /* + * default port number is 5999. + */ + void selectPort(int p) { + if(servSock != null ) return ; + int port = p; + while (true) { + try { + initServSock(port); + break; + } catch (BindException e) { + port++; + continue; + } catch (IOException e) { + + } + } + System.out.println("accept port = " + port); + } + + int getAcceptPort() { + return acceptPort; + } + + void setSoTimeout(int num) throws IOException { + servSock.setSoTimeout(num); + } + + Socket accept() throws IOException { + return servSock.accept(); + } + + void addSock(Socket sock) { + cliList.add(sock); + } + + void addSockTmp(Socket sock) { + System.out.println("connected " + sock.getInetAddress()); + cliListTmp.add(sock); + } + + boolean markSupported() { + return is.markSupported(); + } + + void readServerInit() throws IOException { + + is.mark(255); + skipBytes(20); + int nlen = readU32(); + int blen = 20 + 4 + nlen; + initData = new byte[blen]; + is.reset(); + + is.mark(blen); + readFully(initData); + is.reset(); + + framebufferWidth = readU16(); + framebufferHeight = readU16(); + bitsPerPixel = readU8(); + depth = readU8(); + bigEndian = (readU8() != 0); + trueColour = (readU8() != 0); + redMax = readU16(); + greenMax = readU16(); + blueMax = readU16(); + redShift = readU8(); + greenShift = readU8(); + blueShift = readU8(); + byte[] pad = new byte[3]; + readFully(pad); + int nameLength = readU32(); + byte[] name = new byte[nameLength]; + readFully(name); + desktopName = new String(name); + + // Read interaction capabilities (TightVNC protocol extensions) + if (protocolTightVNC) { + int nServerMessageTypes = readU16(); + int nClientMessageTypes = readU16(); + int nEncodingTypes = readU16(); + readU16(); + readCapabilityList(serverMsgCaps, nServerMessageTypes); + readCapabilityList(clientMsgCaps, nClientMessageTypes); + readCapabilityList(encodingCaps, nEncodingTypes); + } + + inNormalProtocol = true; + } + + void sendRfbVersion(OutputStream os) throws IOException { +// os.write(versionMsg_3_8.getBytes()); + os.write(versionMsg_3_855.getBytes()); + } + + int readVersionMsg(InputStream is, OutputStream os) throws IOException { + + byte[] b = new byte[12]; + + is.read(b); + + if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ') + || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9') + || (b[6] < '0') || (b[6] > '9') || (b[7] != '.') + || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9') + || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) { + throw new IOException("Host " + host + " port " + port + + " is not an RFB server"); + } + + int rfbMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); + int rfbMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); + + if (rfbMajor < 3) { + throw new IOException( + "RFB server does not support protocol version 3"); + } + + if (rfbMinor == 855) { + sendProxyFlag(os); + if(proxyFlag)sendPortNumber(os); + } + return rfbMinor; + } + void sendProxyFlag(OutputStream os) throws IOException { + if(proxyFlag) os.write(1); + else os.write(0); + } + + boolean readProxyFlag() throws IOException{ + int flag = readU8(); + if(flag == 1) + return true; + else + return false; + } + + void sendPortNumber(OutputStream os) throws IOException { + byte[] b = new byte[4]; + b = castIntByte(geth.port); + os.write(b); + } + + void sendSecurityType(OutputStream os) throws IOException { + // number-of-security-types + os.write(1); + // security-types + // 1:None + os.write(1); + +/* + os.write(4); + os.write(30); + os.write(31); + os.write(32); + os.write(35); + os.flush(); +*/ + } + + void readSecType(InputStream is) throws IOException { + byte[] b = new byte[1]; + is.read(b); + } + + + void readSecType(InputStream is, OutputStream os) throws IOException { + byte[] b = new byte[1]; + is.read(b); + + int count = 260; + int[] data = {0, 2, 0, -128, -1, -1, -1, -1, -1, -1, -1, -1, -55, 15, -38, -94, 33, 104, -62, 52, -60, -58, 98, -117, -128, -36, 28, -47, 41, 2, 78, 8, -118, 103, -52, 116, 2, 11, -66, -90, 59, 19, -101, 34, 81, 74, 8, 121, -114, 52, 4, -35, -17, -107, 25, -77, -51, 58, 67, 27, 48, 43, 10, 109, -14, 95, 20, 55, 79, -31, 53, 109, 109, 81, -62, 69, -28, -123, -75, 118, 98, 94, 126, -58, -12, 76, 66, -23, -90, 55, -19, 107, 11, -1, 92, -74, -12, 6, -73, -19, -18, 56, 107, -5, 90, -119, -97, -91, -82, -97, 36, 17, 124, 75, 31, -26, 73, 40, 102, 81, -20, -26, 83, -127, -1, -1, -1, -1, -1, -1, -1, -1, -111, 73, -29, 30, 57, -67, -75, -77, -49, -50, -99, -76, -80, -80, 14, 65, 57, -105, -103, -54, -102, 3, 39, -44, 39, 35, 118, -84, -64, 37, -117, -21, 89, -31, -68, 70, 5, 122, -92, -119, 9, 121, 63, -112, -60, 122, -46, -69, -36, 92, -103, -92, 74, 92, -73, 87, 120, -8, 116, -47, 111, 20, -41, 110, 122, -3, -94, 14, 42, -51, -59, 48, -54, -125, 117, 60, 77, -52, -31, 98, 32, -2, -102, -15, -29, 58, -14, -106, -116, -32, -86, 50, -32, -16, -3, -123, 87, 88, -118, 10, 120, -107, -37, 125, -110, 59, 87, 93, -24, 124, -99, 18, 78, -13, -49, -34, -24, -27, 1, 114, -67, -98, -56, -3, 85, -67, -126, 77}; + for(int i=0; i>> 24) & 0xFF); + b[1] = (byte) ((len >>> 16) & 0xFF); + b[2] = (byte) ((len >>> 8) & 0xFF); + b[3] = (byte) ((len >>> 0) & 0xFF); + return b; + } + + BufferedImage createBimg() throws IOException { + BufferedImage bimg = ImageIO.read(new ByteArrayInputStream(pngBytes)); + return bimg; + } + + void printFramebufferUpdate() { + + System.out.println("messageType=" + messageType); + System.out.println("rectangles=" + rectangles); + System.out.println("encoding=" + encoding); + System.out.println("rectX = "+rectX+": rectY = "+rectY); + System.out.println("rectW = "+rectW+": rectH = "+rectH); + switch (encoding) { + case RfbProto.EncodingRaw: + System.out.println("rectW * rectH * 4 + 16 =" + rectW * rectH * 4 + + 16); + break; + default: + } + } + int returnMsgtype() { + return messageType; + } + + + void readSpeedCheck() throws IOException { + byte[] b = new byte[1]; + readFully(b); + } + + void startSpeedCheck() { + ByteBuffer b = ByteBuffer.allocate(10); + b.put((byte)SpeedCheckMillis); + b.flip(); + startCheckTime = System.currentTimeMillis(); + System.out.println("startChckTime = "+ startCheckTime); + LinkedListbufs = new LinkedList(); + bufs.add(b); + multicastqueue.put(bufs); + } + + void endSpeedCheck() { + long accTime = System.currentTimeMillis(); + long time = accTime - startCheckTime; + System.out.println("checkMillis: " + time); + } + + + synchronized void changeStatusFlag() { + printStatusFlag = true; + } + + void printMills() { + if(printStatusFlag) { + + changeStatusFlag(); + } else { + changeStatusFlag(); + } + } + + void speedCheckMillis() { + Runnable stdin = new Runnable() { + public void run() { + int c; + try { + while( (c = System.in.read()) != -1 ) { + switch(c) { + case 's': + break; + default: + startSpeedCheck(); + break; + } + } + }catch(IOException e){ + System.out.println(e); + } + } + }; + + new Thread(stdin).start(); + } + + void requestThreadStart() { + requestThread.start(); + } + + public synchronized void requestThreadNotify() { + rThread.reStart(); + } + + + /** + * gzip byte arrays + * @param deflater + * @param inputs + * byte data[] + * @param inputIndex + * @param outputs + * byte data[] + * @return byte length in last byte array + * @throws IOException + */ + public int zip(Deflater deflater,LinkedList inputs, int inputIndex, LinkedList outputs) throws IOException { + int len = 0; + ByteBuffer c1= ByteBuffer.allocate(INFLATE_BUFSIZE); + while(inputIndex < inputs.size() ) { + ByteBuffer b1 = inputs.get(inputIndex++); + deflater.setInput(b1.array(),b1.position(),b1.remaining()); + /** + * If we finish() stream and reset() it, Deflater start new gzip stream, this makes continuous zlib reader unhappy. + * if we remove finish(), Deflater.deflate() never flushes its output. The original zlib deflate has flush flag. I'm pretty + * sure this a kind of bug of Java library. + */ + if (inputIndex==inputs.size()) + deflater.finish(); + int len1 = 0; + do { + len1 = deflater.deflate(c1.array(),c1.position(),c1.remaining()); + if (len1>0) { + len += len1; + c1.position(c1.position()+len1); + if (c1.remaining()==0) { + c1.flip(); outputs.addLast(c1); + c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); + } + } + } while (len1 >0 || !deflater.needsInput()); // &&!deflater.finished()); + } + if (c1.position()!=0) { + c1.flip(); outputs.addLast(c1); + } + deflater.reset(); + return len; + } + + /** + * gunzip byte arrays + * @param inflater + * @param inputs + * byte data[] + * @param outputs + * byte data[] + *@return number of total bytes + * @throws IOException + */ + public int unzip(Inflater inflater, LinkedList inputs, int inputIndex, LinkedList outputs,int bufSize) + throws DataFormatException { + int len=0; + ByteBuffer buf = ByteBuffer.allocate(bufSize); + while (inputIndex < inputs.size()) { + ByteBuffer input = inputs.get(inputIndex++); + inflater.setInput(input.array(),input.position(),input.limit()); +// if (inputIndex==inputs.size()) if inflater/deflater has symmetry, we need this +// inflater.end(); but this won't work + do { + int len0 = inflater.inflate(buf.array(),buf.position(),buf.remaining()); + if (len0>0) { + buf.position(buf.position()+len0); + len += len0; + if (buf.remaining()==0) { + buf.flip(); + outputs.addLast(buf); + buf = ByteBuffer.allocate(bufSize); + } + } + } while (!inflater.needsInput()); + } + if (buf.position()!=0) { + buf.flip(); + outputs.addLast(buf); + } + return len; + } + + + float maxMag = 1; + /** + * send data to clients + * @param dataLen + * @throws IOException + * @throws DataFormatException + * + * Zlibed packet is compressed in context dependent way, that is, it have to send from the beginning. But this is + * impossible. So we have to compress it again for each clients. Separate deflater for each clients is necessary. + * + * Java's deflater does not support flush. This means to get the result, we have to finish the compression. Reseting + * start new compression, but it is not accepted well in zlib continuous reading. So we need new Encoding ZRLEE + * which reset decoder for each packet. ZRLEE can be invisible from user, but it have to be implemented in the clients. + * ZRLEE compression is not context dependent, so no recompression is necessary. + */ + void readSendData(int dataLen) throws IOException, DataFormatException { + LinkedListbufs = new LinkedList(); + ByteBuffer header = ByteBuffer.allocate(16); + readFully(header.array(),0,16); + header.limit(16); + if (header.get(0)==RfbProto.FramebufferUpdate) { + int encoding = header.getInt(12); + if (encoding==RfbProto.EncodingZRLE||encoding==RfbProto.EncodingZlib) { // ZRLEE is already recompressed + ByteBuffer len = ByteBuffer.allocate(4); + readFully(len.array(),0,4); len.limit(4); + ByteBuffer inputData = ByteBuffer.allocate(dataLen-20); + + startTiming(); + readFully(inputData.array(),0,inputData.capacity()); inputData.limit(dataLen-20); + stopTiming(); + + LinkedListinputs = new LinkedList(); + inputs.add(inputData); + + header.putInt(12, RfbProto.EncodingZRLEE); // means recompress every time + // using new Deflecter every time is incompatible with the protocol, clients have to be modified. + Deflater nDeflater = deflater; // new Deflater(); + LinkedList out = new LinkedList(); + unzip(inflater, inputs, 0 , out, INFLATE_BUFSIZE); + // dump32(inputs); + int len2 = zip(nDeflater, out, 0, bufs); + ByteBuffer blen = ByteBuffer.allocate(4); blen.putInt(len2); blen.flip(); + bufs.addFirst(blen); + + bufs.addFirst(header); + multicastqueue.put(bufs); +// is.reset(); + +/* + System.out.println("ZRLE = "+dataLen); + System.out.println("ZRLEE = "+(len2+20)); + float mag = (float)dataLen / (float)(len2 + 20); + System.out.println("ZRLE / ZRLEE = "+ mag); + if(mag > maxMag) maxMag = mag; + System.out.println("maxMag = "+maxMag); +*/ + return ; + } + bufs.add(header); + if (dataLen>16) { + ByteBuffer b = ByteBuffer.allocate(dataLen-16); + startTiming(); + readFully(b.array(),0,dataLen-16); b.limit(dataLen-16); + stopTiming(); + bufs.add(b); + } + multicastqueue.put(bufs); +// is.reset(); + return ; + } + is.reset(); + + // It may be compressed. We can inflate here to avoid repeating clients decompressing here, + // but it may generate too many large data. It is better to do it in each client. + // But we have do inflation for all input data, so we have to do it here. + } + + void newClient(AcceptThread acceptThread, final Socket newCli, + final OutputStream os, final InputStream is) throws IOException { + // createBimgFlag = true; + // rfb.addSockTmp(newCli); + // addSock(newCli); + final int myId = clients; + final Client > c = multicastqueue.newClient(); + final AtomicInteger writerRunning = new AtomicInteger(); + writerRunning.set(1); + /** + * Timeout thread. If a client is suspended, it has top of queue indefinitely, which caused memory + * overflow. After the timeout, we poll the queue and discard it. Start long wait if writer is running. + */ + final Runnable timer = new Runnable() { + public void run() { + int count = 0; + for(;;) { + long timeout = 30000/8; + try { + synchronized(this) { + int state,flag; + writerRunning.set(0); + wait(timeout); + flag = 0; + while((state=writerRunning.get())==0) { + c.poll(); // discard, should be timeout + count++; + if (flag==0) { + System.out.println("Discarding "+myId + " count="+ count); flag = 1; + } + wait(10); // if this is too short, writer cannot take the poll, if this is too long, memory will overflow... + } + if (flag==1) System.out.println("Resuming "+myId + " count="+count); + if (state!=1) { + System.out.println("Client died "+myId); + break; + } + } + } catch (InterruptedException e) { + } + } + } + }; + new Thread(timer).start(); + /** + * discard all incoming from clients + */ + final Runnable reader = new Runnable() { + public void run() { + byte b[] = new byte[4096]; + for(;;) { + try { + int c = is.read(b); + if (c<=0) throw new IOException(); + // System.out.println("client read "+c); + } catch (IOException e) { + try { + writerRunning.set(2); + os.close(); + is.close(); + } catch (IOException e1) { + } + return; + } + } + } + }; + /** + * send packets to a client + */ + Runnable sender = new Runnable() { + public void run() { + writerRunning.set(1); + try { + requestThreadNotify(); + + /** + * initial connection of RFB protocol + */ + sendRfbVersion(os); +// readVersionMsg(is); + int rfbMinor = readVersionMsg(is,os); + sendSecurityType(os); + readSecType(is); + sendSecResult(os); + readClientInit(is); + sendInitData(os); + new Thread(reader).start(); // discard incoming packet here after. +// writeFramebufferUpdateRequest(0,0, framebufferWidth, framebufferHeight, false ); + for (;;) { + LinkedList bufs = c.poll(); + int inputIndex = 0; + ByteBuffer header = bufs.get(inputIndex); + if (header==null) continue; + if (header.get(0)==RfbProto.FramebufferUpdate) { + // System.out.println("client "+ myId); + } + writeToClient(os, bufs, inputIndex); + writerRunning.set(1); // yes my client is awaking. + } + } catch (IOException e) { + try { + writerRunning.set(2); + os.close(); + } catch (IOException e1) { + } + /* if socket closed cliList.remove(newCli); */ + } + } + + public void writeToClient(final OutputStream os, + LinkedList bufs, int inputIndex) + throws IOException { + while(inputIndex < bufs.size()) { + ByteBuffer b = bufs.get(inputIndex++); + os.write(b.array(), b.position(), b.limit()); + } + os.flush(); + } + }; + clients++; + new Thread(sender).start(); + + } + + + public void dump32(LinkedListbufs) { + int len =0; + for(ByteBuffer b: bufs) len += b.remaining(); + ByteBuffer top = bufs.getFirst(); + ByteBuffer end = bufs.getLast(); + System.err.println("length: "+len); + System.err.print("head 0: "); + for(int i = 0; i<16 && i < top.remaining(); i++) { + System.err.print(" "+ top.get(i)); + } + System.err.print("tail 0: "); + for(int i = 0; i<16 && i < end.remaining(); i++) { + System.err.print(" "+end.get(i)); + } + System.err.println(); + } + + @Test + public void test1() { + try { + LinkedList in = new LinkedList(); + LinkedList out = new LinkedList(); + LinkedList out2 = new LinkedList(); +// if (false) { +// for(int i=0;i<10;i++) { +// in.add(ByteBuffer.wrap("test1".getBytes())); +// in.add(ByteBuffer.wrap("test2".getBytes())); +// in.add(ByteBuffer.wrap("test3".getBytes())); +// in.add(ByteBuffer.wrap("test44".getBytes())); +// } +// } else + { + String t = ""; + for(int i=0;i<10;i++) { + t += "test1"; + t += "test2"; + t += "test3"; + t += "test44"; + } + in.add(ByteBuffer.wrap(t.getBytes())); + } + + LinkedList in1 = clone(in); + + Deflater deflater = new Deflater(); + zip(deflater,in,0,out); + // LinkedList out3 = clone(out); zipped result is depend on deflator's state + unzip(inflater, out, 0,out2, INFLATE_BUFSIZE); + // inflater.reset(); + equalByteBuffers(in1, out2); + LinkedList out4 = new LinkedList(); + deflater = new Deflater(); + zip(deflater,out2,0,out4); + LinkedList out5 = new LinkedList(); + unzip(inflater,out4,0, out5, INFLATE_BUFSIZE); + int len = equalByteBuffers(in1,out5); + + System.out.println("Test Ok. "+len); + } catch (Exception e) { + assertEquals(0,1); + } + } + + private LinkedList clone(LinkedList in) { + LinkedList copy = new LinkedList(); + for(ByteBuffer b: in) { + ByteBuffer c = b.duplicate(); + copy.add(c); + } + return copy; + } + + + + public int equalByteBuffers(LinkedList in, + LinkedList out2) { + int len = 0; + Iterable i = byteBufferIterator(in); + Iterator o = byteBufferIterator(out2).iterator(); + + for(int b: i) { + len ++; + if (o.hasNext()) { + int c = o.next(); + assertEquals(b,c); + } else + assertEquals(0,1); + } + if (o.hasNext()) + assertEquals(0,1); + // System.out.println(); + return len; + } + + private Iterable byteBufferIterator(final LinkedList in) { + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + int bytes = 0; + int buffers = 0; + public boolean hasNext() { + for(;;) { + if (buffers>=in.size()) return false; + ByteBuffer b = in.get(buffers); + if (! (bytes