1 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
2  * Copyright (C) 2011-2019 Brian P. Hinz
3  *
4  * This is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This software is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this software; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
17  * USA.
18  */
19 
20 //
21 // A ZlibInStream reads from a zlib.io.InputStream
22 //
23 
24 package com.tigervnc.rdr;
25 import com.jcraft.jzlib.*;
26 
27 public class ZlibInStream extends InStream {
28 
29   static final int defaultBufSize = 16384;
30 
ZlibInStream(int bufSize_)31   public ZlibInStream(int bufSize_)
32   {
33     bufSize = bufSize_;
34     b = new byte[bufSize];
35     bytesIn = offset = 0;
36     ptr = end = start = 0;
37     init();
38   }
39 
ZlibInStream()40   public ZlibInStream() { this(defaultBufSize); }
41 
setUnderlying(InStream is, int bytesIn_)42   public void setUnderlying(InStream is, int bytesIn_)
43   {
44     underlying = is;
45     bytesIn = bytesIn_;
46     ptr = end = start;
47   }
48 
pos()49   public int pos()
50   {
51     return offset + ptr - start;
52   }
53 
flushUnderlying()54   public void flushUnderlying()
55   {
56     ptr = end = start;
57 
58     while (bytesIn > 0) {
59       decompress(true);
60       end = start; // throw away any data
61     }
62 
63     setUnderlying(null, 0);
64   }
65 
reset()66   public void reset()
67   {
68     deinit();
69     init();
70   }
71 
init()72   public void init()
73   {
74     assert(zs == null);
75 
76     zs = new ZStream();
77     zs.next_in = null;
78     zs.next_in_index = 0;
79     zs.avail_in = 0;
80     if (zs.inflateInit() != JZlib.Z_OK) {
81       zs = null;
82       throw new Exception("ZlinInStream: inflateInit failed");
83     }
84   }
85 
deinit()86   public void deinit()
87   {
88     assert(zs != null);
89     setUnderlying(null, 0);
90     zs.inflateEnd();
91     zs = null;
92   }
93 
overrun(int itemSize, int nItems, boolean wait)94   protected int overrun(int itemSize, int nItems, boolean wait)
95   {
96     if (itemSize > bufSize)
97       throw new Exception("ZlibInStream overrun: max itemSize exceeded");
98 
99     if (end - ptr != 0)
100       System.arraycopy(b, ptr, b, start, end - ptr);
101 
102     offset += ptr - start;
103     end -= ptr - start;
104     ptr = start;
105 
106     while (end - ptr < itemSize) {
107       if (!decompress(wait))
108         return 0;
109     }
110 
111     int nAvail;
112     nAvail = (end - ptr) / itemSize;
113     if (nAvail < nItems)
114       return nAvail;
115 
116     return nItems;
117   }
118 
119   // decompress() calls the decompressor once.  Note that this won't
120   // necessarily generate any output data - it may just consume some input
121   // data.  Returns false if wait is false and we would block on the underlying
122   // stream.
123 
decompress(boolean wait)124   private boolean decompress(boolean wait)
125   {
126     if (underlying == null)
127       throw new Exception("ZlibInStream overrun: no underlying stream");
128 
129     zs.next_out = b;
130     zs.next_out_index = end;
131     zs.avail_out = start + bufSize - end;
132 
133     int n = underlying.check(1, 1, wait);
134     if (n == 0) return false;
135     zs.next_in = underlying.getbuf();
136     zs.next_in_index = underlying.getptr();
137     zs.avail_in = underlying.getend() - underlying.getptr();
138     if (zs.avail_in > bytesIn)
139       zs.avail_in = bytesIn;
140 
141     int rc = zs.inflate(JZlib.Z_SYNC_FLUSH);
142     if (rc != JZlib.Z_OK) {
143       throw new Exception("ZlibInStream: inflate failed");
144     }
145 
146     bytesIn -= zs.next_in_index - underlying.getptr();
147     end = zs.next_out_index;
148     underlying.setptr(zs.next_in_index);
149     return true;
150   }
151 
152   private InStream underlying;
153   private int bufSize;
154   private int offset;
155   private com.jcraft.jzlib.ZStream zs;
156   private int bytesIn;
157   private int start;
158 }
159