1 |
/* |
/* |
2 |
* $Id$ |
* CompressedOutputStream.java |
3 |
* Copyright (C) 2003 The Free Software Foundation |
* Copyright (C) 2003 The Free Software Foundation |
4 |
* |
* |
5 |
* This file is part of GNU inetlib, a library. |
* This file is part of GNU inetlib, a library. |
46 |
* A DTP output stream that implements the FTP compressed transfer mode. |
* A DTP output stream that implements the FTP compressed transfer mode. |
47 |
* |
* |
48 |
* @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> |
* @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> |
|
* @version $Revision$ $Date$ |
|
49 |
*/ |
*/ |
50 |
class CompressedOutputStream extends DTPOutputStream |
class CompressedOutputStream |
51 |
|
extends DTPOutputStream |
52 |
{ |
{ |
53 |
|
|
54 |
static final byte RECORD = -128; // 0x80 |
static final byte RECORD = -128; // 0x80 |
55 |
static final byte EOF = 64; // 0x40 |
static final byte EOF = 64; // 0x40 |
56 |
|
|
57 |
CompressedOutputStream (DTP dtp, OutputStream out) |
CompressedOutputStream(DTP dtp, OutputStream out) |
58 |
{ |
{ |
59 |
super (dtp, out); |
super(dtp, out); |
60 |
} |
} |
61 |
|
|
62 |
/** |
/** |
63 |
* Just one byte cannot be compressed. |
* Just one byte cannot be compressed. |
64 |
* It takes 5 bytes to transmit - hardly very compressed! |
* It takes 5 bytes to transmit - hardly very compressed! |
65 |
*/ |
*/ |
66 |
public void write (int c) throws IOException |
public void write(int c) |
67 |
{ |
throws IOException |
68 |
if (transferComplete) |
{ |
69 |
{ |
if (transferComplete) |
70 |
return; |
{ |
71 |
} |
return; |
72 |
byte[] buf = new byte[] |
} |
73 |
{ |
byte[] buf = new byte[] |
74 |
RECORD, /* record descriptor */ |
{ |
75 |
0x00, 0x01, /* one byte */ |
RECORD, /* record descriptor */ |
76 |
0x01, /* one uncompressed byte */ |
0x00, 0x01, /* one byte */ |
77 |
(byte) c /* the byte */ |
0x01, /* one uncompressed byte */ |
78 |
}; |
(byte) c /* the byte */ |
79 |
out.write (buf, 0, 5); |
}; |
80 |
} |
out.write(buf, 0, 5); |
81 |
|
} |
82 |
public void write (byte[] b) throws IOException |
|
83 |
{ |
public void write(byte[] b) |
84 |
write (b, 0, b.length); |
throws IOException |
85 |
} |
{ |
86 |
|
write(b, 0, b.length); |
87 |
|
} |
88 |
|
|
89 |
/** |
/** |
90 |
* The larger len is, the better. |
* The larger len is, the better. |
91 |
*/ |
*/ |
92 |
public void write (byte[] b, int off, int len) throws IOException |
public void write(byte[] b, int off, int len) |
93 |
{ |
throws IOException |
94 |
if (transferComplete) |
{ |
95 |
{ |
if (transferComplete) |
96 |
return; |
{ |
97 |
} |
return; |
98 |
byte[] buf = compress (b, off, len); |
} |
99 |
len = buf.length; |
byte[] buf = compress(b, off, len); |
100 |
buf[0] = RECORD; /* record descriptor */ |
len = buf.length; |
101 |
buf[1] = (byte) ((len & 0x00ff) >> 8); /* high byte of bytecount */ |
buf[0] = RECORD; /* record descriptor */ |
102 |
buf[2] = (byte) (len & 0xff00); /* low byte of bytecount */ |
buf[1] = (byte) ((len & 0x00ff) >> 8); /* high byte of bytecount */ |
103 |
out.write (buf, 0, len); |
buf[2] = (byte) (len & 0xff00); /* low byte of bytecount */ |
104 |
} |
out.write(buf, 0, len); |
105 |
|
} |
106 |
|
|
107 |
/** |
/** |
108 |
* Returns the compressed form of the given byte array. |
* Returns the compressed form of the given byte array. |
109 |
* The first 3 bytes are left free for header information. |
* The first 3 bytes are left free for header information. |
110 |
*/ |
*/ |
111 |
byte[] compress (byte[] b, int off, int len) |
byte[] compress(byte[] b, int off, int len) |
112 |
{ |
{ |
113 |
byte[] buf = new byte[len]; |
byte[] buf = new byte[len]; |
114 |
byte last = 0; |
byte last = 0; |
115 |
int pos = 0, raw_count = 0, rep_count = 1; |
int pos = 0, raw_count = 0, rep_count = 1; |
116 |
for (int i = off; i < len; i++) |
for (int i = off; i < len; i++) |
117 |
{ |
{ |
118 |
byte c = b[i]; |
byte c = b[i]; |
119 |
if (i > off && c == last) // compress |
if (i > off && c == last) // compress |
120 |
{ |
{ |
121 |
if (raw_count > 0) // flush raw bytes to buf |
if (raw_count > 0) // flush raw bytes to buf |
122 |
{ |
{ |
123 |
// need to add raw_count+1 bytes |
// need to add raw_count+1 bytes |
124 |
if (pos + (raw_count + 1) > buf.length) |
if (pos + (raw_count + 1) > buf.length) |
125 |
{ |
{ |
126 |
buf = realloc (buf, len); |
buf = realloc(buf, len); |
127 |
} |
} |
128 |
pos = flush_raw (buf, pos, b, (i - raw_count) - 1, |
pos = flush_raw(buf, pos, b, (i - raw_count) - 1, |
129 |
raw_count); |
raw_count); |
130 |
raw_count = 0; |
raw_count = 0; |
131 |
} |
} |
132 |
rep_count++; // keep looking for same byte |
rep_count++; // keep looking for same byte |
133 |
} |
} |
134 |
else |
else |
135 |
{ |
{ |
136 |
if (rep_count > 1) // flush compressed bytes to buf |
if (rep_count > 1) // flush compressed bytes to buf |
137 |
{ |
{ |
138 |
// need to add 2 bytes |
// need to add 2 bytes |
139 |
if (pos + 2 > buf.length) |
if (pos + 2 > buf.length) |
140 |
{ |
{ |
141 |
buf = realloc (buf, len); |
buf = realloc(buf, len); |
142 |
} |
} |
143 |
pos = flush_compressed (buf, pos, rep_count, last); |
pos = flush_compressed(buf, pos, rep_count, last); |
144 |
rep_count = 1; |
rep_count = 1; |
145 |
} |
} |
146 |
raw_count++; // keep looking for raw bytes |
raw_count++; // keep looking for raw bytes |
147 |
} |
} |
148 |
if (rep_count == 127) // flush compressed bytes |
if (rep_count == 127) // flush compressed bytes |
149 |
{ |
{ |
150 |
// need to add 2 bytes |
// need to add 2 bytes |
151 |
if (pos + 2 > buf.length) |
if (pos + 2 > buf.length) |
152 |
{ |
{ |
153 |
buf = realloc (buf, len); |
buf = realloc(buf, len); |
154 |
} |
} |
155 |
pos = flush_compressed (buf, pos, rep_count, last); |
pos = flush_compressed(buf, pos, rep_count, last); |
156 |
rep_count = 1; |
rep_count = 1; |
157 |
} |
} |
158 |
if (raw_count == 127) // flush raw bytes |
if (raw_count == 127) // flush raw bytes |
159 |
{ |
{ |
160 |
// need to add raw_count+1 bytes |
// need to add raw_count+1 bytes |
161 |
if (pos + (raw_count + 1) > buf.length) |
if (pos + (raw_count + 1) > buf.length) |
162 |
{ |
{ |
163 |
buf = realloc (buf, len); |
buf = realloc(buf, len); |
164 |
} |
} |
165 |
pos = flush_raw (buf, pos, b, (i - raw_count), raw_count); |
pos = flush_raw(buf, pos, b, (i - raw_count), raw_count); |
166 |
raw_count = 0; |
raw_count = 0; |
167 |
} |
} |
168 |
last = c; |
last = c; |
169 |
} |
} |
170 |
if (rep_count > 1) // flush compressed bytes |
if (rep_count > 1) // flush compressed bytes |
171 |
{ |
{ |
172 |
// need to add 2 bytes |
// need to add 2 bytes |
173 |
if (pos + 2 > buf.length) |
if (pos + 2 > buf.length) |
174 |
{ |
{ |
175 |
buf = realloc (buf, len); |
buf = realloc(buf, len); |
176 |
} |
} |
177 |
pos = flush_compressed (buf, pos, rep_count, last); |
pos = flush_compressed(buf, pos, rep_count, last); |
178 |
rep_count = 1; |
rep_count = 1; |
179 |
} |
} |
180 |
if (raw_count > 0) // flush raw bytes |
if (raw_count > 0) // flush raw bytes |
181 |
{ |
{ |
182 |
// need to add raw_count+1 bytes |
// need to add raw_count+1 bytes |
183 |
if (pos + (raw_count + 1) > buf.length) |
if (pos + (raw_count + 1) > buf.length) |
184 |
{ |
{ |
185 |
buf = realloc (buf, len); |
buf = realloc(buf, len); |
186 |
} |
} |
187 |
pos = flush_raw (buf, pos, b, (len - raw_count), raw_count); |
pos = flush_raw(buf, pos, b, (len - raw_count), raw_count); |
188 |
raw_count = 0; |
raw_count = 0; |
189 |
} |
} |
190 |
byte[] ret = new byte[pos + 3]; |
byte[] ret = new byte[pos + 3]; |
191 |
System.arraycopy (buf, 0, ret, 3, pos); |
System.arraycopy(buf, 0, ret, 3, pos); |
192 |
return ret; |
return ret; |
193 |
} |
} |
194 |
|
|
195 |
int flush_compressed (byte[] buf, int pos, int count, byte c) |
int flush_compressed(byte[] buf, int pos, int count, byte c) |
196 |
{ |
{ |
197 |
buf[pos++] = (byte) (0x80 | count); |
buf[pos++] = (byte) (0x80 | count); |
198 |
buf[pos++] = c; |
buf[pos++] = c; |
199 |
return pos; |
return pos; |
200 |
} |
} |
201 |
|
|
202 |
int flush_raw (byte[] buf, int pos, byte[] src, int off, int len) |
int flush_raw(byte[] buf, int pos, byte[] src, int off, int len) |
203 |
{ |
{ |
204 |
buf[pos++] = (byte) len; |
buf[pos++] = (byte) len; |
205 |
System.arraycopy (src, off, buf, pos, len); |
System.arraycopy(src, off, buf, pos, len); |
206 |
return pos + len; |
return pos + len; |
207 |
} |
} |
208 |
|
|
209 |
byte[] realloc (byte[] buf, int len) |
byte[] realloc(byte[] buf, int len) |
210 |
{ |
{ |
211 |
byte[] ret = new byte[buf.length + len]; |
byte[] ret = new byte[buf.length + len]; |
212 |
System.arraycopy (buf, 0, ret, 0, buf.length); |
System.arraycopy(buf, 0, ret, 0, buf.length); |
213 |
return ret; |
return ret; |
214 |
} |
} |
215 |
|
|
216 |
public void close () throws IOException |
public void close() |
217 |
{ |
throws IOException |
218 |
byte[] buf = new byte[] |
{ |
219 |
{ |
byte[] buf = new byte[] |
220 |
EOF, /* eof descriptor */ |
{ |
221 |
0x00, 0x00 /* no bytes */ |
EOF, /* eof descriptor */ |
222 |
}; |
0x00, 0x00 /* no bytes */ |
223 |
out.write (buf, 0, 3); |
}; |
224 |
out.close (); |
out.write(buf, 0, 3); |
225 |
} |
out.close(); |
226 |
|
} |
227 |
|
|
228 |
} |
} |
229 |
|
|