1 /* CompressedOutputStream.java --
2    Copyright (C) 2003, 2004  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package gnu.java.net.protocol.ftp;
40 
41 import java.io.IOException;
42 import java.io.OutputStream;
43 
44 /**
45  * A DTP output stream that implements the FTP compressed transfer mode.
46  *
47  * @author Chris Burdess (dog@gnu.org)
48  */
49 class CompressedOutputStream
50   extends DTPOutputStream
51 {
52 
53   static final byte RECORD = -128;      // 0x80
54   static final byte EOF = 64;   // 0x40
55 
CompressedOutputStream(DTP dtp, OutputStream out)56   CompressedOutputStream(DTP dtp, OutputStream out)
57   {
58     super(dtp, out);
59   }
60 
61   /**
62    * Just one byte cannot be compressed.
63    * It takes 5 bytes to transmit - hardly very compressed!
64    */
write(int c)65   public void write(int c)
66     throws IOException
67   {
68     if (transferComplete)
69       {
70         return;
71       }
72     byte[] buf = new byte[]
73       {
74         RECORD,                   /* record descriptor */
75         0x00, 0x01,             /* one byte */
76         0x01,                   /* one uncompressed byte */
77         (byte) c                /* the byte */
78       };
79     out.write(buf, 0, 5);
80   }
81 
write(byte[] b)82   public void write(byte[] b)
83     throws IOException
84   {
85     write(b, 0, b.length);
86   }
87 
88   /**
89    * The larger len is, the better.
90    */
write(byte[] b, int off, int len)91   public void write(byte[] b, int off, int len)
92     throws IOException
93   {
94     if (transferComplete)
95       {
96         return;
97       }
98     byte[] buf = compress(b, off, len);
99     len = buf.length;
100     buf[0] = RECORD;            /* record descriptor */
101     buf[1] = (byte) ((len & 0x00ff) >> 8);      /* high byte of bytecount */
102     buf[2] = (byte) (len & 0xff00);     /* low byte of bytecount */
103     out.write(buf, 0, len);
104   }
105 
106   /**
107    * Returns the compressed form of the given byte array.
108    * The first 3 bytes are left free for header information.
109    */
compress(byte[] b, int off, int len)110   byte[] compress(byte[] b, int off, int len)
111   {
112     byte[] buf = new byte[len];
113     byte last = 0;
114     int pos = 0, raw_count = 0, rep_count = 1;
115     for (int i = off; i < len; i++)
116       {
117         byte c = b[i];
118         if (i > off && c == last) // compress
119           {
120             if (raw_count > 0)      // flush raw bytes to buf
121               {
122                 // need to add raw_count+1 bytes
123                 if (pos + (raw_count + 1) > buf.length)
124                   {
125                     buf = realloc(buf, len);
126                   }
127                 pos = flush_raw(buf, pos, b, (i - raw_count) - 1,
128                                 raw_count);
129                 raw_count = 0;
130               }
131             rep_count++;            // keep looking for same byte
132           }
133         else
134           {
135             if (rep_count > 1)      // flush compressed bytes to buf
136               {
137                 // need to add 2 bytes
138                 if (pos + 2 > buf.length)
139                   {
140                     buf = realloc(buf, len);
141                   }
142                 pos = flush_compressed(buf, pos, rep_count, last);
143                 rep_count = 1;
144               }
145             raw_count++;            // keep looking for raw bytes
146           }
147         if (rep_count == 127)     // flush compressed bytes
148           {
149             // need to add 2 bytes
150             if (pos + 2 > buf.length)
151               {
152                 buf = realloc(buf, len);
153               }
154             pos = flush_compressed(buf, pos, rep_count, last);
155             rep_count = 1;
156           }
157         if (raw_count == 127)     // flush raw bytes
158           {
159             // need to add raw_count+1 bytes
160             if (pos + (raw_count + 1) > buf.length)
161               {
162                 buf = realloc(buf, len);
163               }
164             pos = flush_raw(buf, pos, b, (i - raw_count), raw_count);
165             raw_count = 0;
166           }
167         last = c;
168       }
169     if (rep_count > 1)          // flush compressed bytes
170       {
171         // need to add 2 bytes
172         if (pos + 2 > buf.length)
173           {
174             buf = realloc(buf, len);
175           }
176         pos = flush_compressed(buf, pos, rep_count, last);
177         rep_count = 1;
178       }
179     if (raw_count > 0)          // flush raw bytes
180       {
181         // need to add raw_count+1 bytes
182         if (pos + (raw_count + 1) > buf.length)
183           {
184             buf = realloc(buf, len);
185           }
186         pos = flush_raw(buf, pos, b, (len - raw_count), raw_count);
187         raw_count = 0;
188       }
189     byte[] ret = new byte[pos + 3];
190     System.arraycopy(buf, 0, ret, 3, pos);
191     return ret;
192   }
193 
flush_compressed(byte[] buf, int pos, int count, byte c)194   int flush_compressed(byte[] buf, int pos, int count, byte c)
195   {
196     buf[pos++] = (byte) (0x80 | count);
197     buf[pos++] = c;
198     return pos;
199   }
200 
flush_raw(byte[] buf, int pos, byte[] src, int off, int len)201   int flush_raw(byte[] buf, int pos, byte[] src, int off, int len)
202   {
203     buf[pos++] = (byte) len;
204     System.arraycopy(src, off, buf, pos, len);
205     return pos + len;
206   }
207 
realloc(byte[] buf, int len)208   byte[] realloc(byte[] buf, int len)
209   {
210     byte[] ret = new byte[buf.length + len];
211     System.arraycopy(buf, 0, ret, 0, buf.length);
212     return ret;
213   }
214 
close()215   public void close()
216     throws IOException
217   {
218     byte[] buf = new byte[]
219       {
220         EOF,                      /* eof descriptor */
221         0x00, 0x00              /* no bytes */
222       };
223     out.write(buf, 0, 3);
224     out.close();
225   }
226 
227 }
228