Source code: com/anotherbigidea/io/OutStream.java
1 /****************************************************************
2 * Copyright (c) 2001, David N. Main, All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the
6 * following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 *
17 * 3. The name of the author may not be used to endorse or
18 * promote products derived from this software without specific
19 * prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 ****************************************************************/
34 package com.anotherbigidea.io;
35
36 import java.io.ByteArrayOutputStream;
37 import java.io.DataOutputStream;
38 import java.io.IOException;
39 import java.io.OutputStream;
40 import java.util.zip.Deflater;
41 import java.util.zip.DeflaterOutputStream;
42
43 /**
44 * Output Stream Wrapper
45 */
46 public class OutStream extends OutputStreamWrapper
47 {
48 //--Bit buffer..
49 private int mBitBuf;
50 private int mBitPos;
51
52 public OutStream( OutputStream out )
53 {
54 super( out );
55
56 initBits();
57 }
58
59 protected OutStream() {
60 super(null);
61 }
62
63 /**
64 * Write a signed value to the output stream in the given number of bits.
65 * The value must actually fit in that number of bits or it will be garbled
66 */
67 public void writeSBits( int numBits, int value ) throws IOException
68 {
69 //--Mask out any sign bit
70 long lval = value & 0x7FFFFFFF;
71
72 if( value < 0 ) //add the sign bit
73 {
74 lval |= 1L << (numBits-1);
75 }
76
77 //--Write the bits as if unsigned
78 writeUBits( numBits, lval );
79 }
80
81 public void flush() throws IOException
82 {
83 flushBits();
84 super.flush();
85 }
86
87 /**
88 * Compress all subsequent data
89 */
90 public void writeCompressed() {
91 setOutputStream( new DeflaterOutputStream( getOutputStream(),
92 new Deflater( Deflater.BEST_COMPRESSION )));
93 }
94
95 /**
96 * Flush the bit buffer to the output stream and reset values
97 */
98 public void flushBits() throws IOException
99 {
100 if( mBitPos == 0 ) return; //nothing to flush
101
102 super.write( mBitBuf );
103 mBitBuf = 0;
104 mBitPos = 0;
105 }
106
107 /**
108 * Write an unsigned value to the output stream in the given number of bits
109 */
110 public void writeUBits( int numBits, long value ) throws IOException
111 {
112 if( numBits == 0 ) return;
113
114 if( mBitPos == 0 ) mBitPos = 8; //bitBuf was empty
115
116 int bitNum = numBits;
117
118 while( bitNum > 0 ) //write all bits
119 {
120 while( mBitPos > 0 && bitNum > 0 ) //write into all position of the bit buffer
121 {
122 if( getBit( bitNum, value ) ) mBitBuf = setBit( mBitPos, mBitBuf );
123
124 bitNum--;
125 mBitPos--;
126 }
127
128 if( mBitPos == 0 ) //bit buffer is full - write it
129 {
130 writeUI8( mBitBuf );
131 mBitBuf = 0;
132 if( bitNum > 0 ) mBitPos = 8; //prepare for more bits
133 }
134 }
135 }
136
137
138 /**
139 * Get the given bit (where lowest bit is numbered 1)
140 */
141 public static boolean getBit( int bitNum, long value )
142 {
143 return (value & (1L << (bitNum - 1))) != 0;
144 }
145
146 /**
147 * Set the given bit (where lowest bit is numbered 1)
148 */
149 public static int setBit( int bitNum, int value )
150 {
151 return value | ( 1 << (bitNum-1) );
152 }
153
154 /**
155 * Write the given bytes to the output stream
156 */
157 public void write( byte[] bytes ) throws IOException
158 {
159 flushBits();
160
161 if( bytes != null && bytes.length > 0 )
162 {
163 super.write( bytes );
164 }
165 }
166
167 /**
168 * Write the given bytes to the output stream
169 */
170 public void write( byte[] bytes, int start, int length ) throws IOException
171 {
172 flushBits();
173
174 if( bytes != null && length > 0 )
175 {
176 super.write( bytes, start, length );
177 }
178 }
179
180 /**
181 * Write an 8 bit unsigned value to the out stream
182 */
183 public void writeUI8( int value ) throws IOException
184 {
185 flushBits();
186
187 super.write( value );
188 }
189
190 /**
191 * Write a 16 bit unsigned value to the out stream
192 */
193 public void writeUI16( int value ) throws IOException
194 {
195 flushBits();
196
197 super.write( value & 0xff );
198 super.write( value >> 8 );
199 }
200
201 /**
202 * Write a 16 bit signed value to the out stream
203 */
204 public void writeSI16( short value ) throws IOException
205 {
206 flushBits();
207
208 super.write( value & 0xff );
209 super.write( value >> 8 );
210 }
211
212
213 /**
214 * Write a 32 bit unsigned value to the out stream
215 */
216 public void writeUI32( long value ) throws IOException
217 {
218 flushBits();
219
220 super.write( (int)( value & 0xff ) );
221 super.write( (int)( value >> 8 ) );
222 super.write( (int)( value >> 16 ) );
223 super.write( (int)( value >> 24 ) );
224 }
225
226 /**
227 * Write a string to the output stream using the default encoding and add terminating null
228 */
229 public void writeString( String s, String encoding ) throws IOException
230 {
231 if( s == null ) s = "";
232 writeString( s.getBytes( encoding ) );
233 }
234
235 /**
236 * Write a string to the output stream and add terminating null
237 */
238 public void writeString( byte[] string ) throws IOException
239 {
240 flushBits();
241
242 if( string != null ) super.write( string );
243 super.write( 0 ); //terminate string
244 }
245
246 /**
247 * Calculate the byte length of a string as it would be written to the
248 * output stream
249 */
250 public static int getStringLength( byte[] string )
251 {
252 if( string == null ) return 1;
253 return string.length + 1; //to include the terminating null
254 }
255
256 /**
257 * Calculate the byte length of a string as it would be written to the
258 * output stream using the default character encoding
259 */
260 public static int getStringLength( String string )
261 {
262 if( string == null ) return 1;
263 byte[] bytes = string.getBytes();
264
265 return bytes.length + 1; //to include the terminating null
266 }
267
268 /**
269 * Reset the bit buffer
270 */
271 private void initBits()
272 {
273 mBitBuf = 0;
274 mBitPos = 0;
275 }
276
277 /**
278 * Determine the minimum number of bits required to hold the given
279 * signed value
280 */
281 public static int determineSignedBitSize( int value )
282 {
283 if( value >= 0 ) return determineUnsignedBitSize( value ) + 1;
284
285 //--This is probably a really bad way of doing this...
286 int topBit = 31;
287 long mask = 0x40000000L;
288
289 while( topBit > 0 )
290 {
291 if( (value & mask) == 0 ) break;
292
293 mask >>= 1;
294 topBit--;
295 }
296
297 if( topBit == 0 ) return 2; //must have been -1
298
299 //HACK: Flash represents -16 as 110000 rather than 10000 etc..
300 int val2 = value & (( 1 << topBit) - 1 );
301 if( val2 == 0 )
302 {
303 topBit++;
304 }
305
306 return topBit + 1;
307 }
308
309 /**
310 * Determine the minimum number of bits required to hold the given
311 * unsigned value (may be zero)
312 */
313 public static int determineUnsignedBitSize( long value )
314 {
315 //--This is probably a really bad way of doing this...
316 int topBit = 32;
317 long mask = 0x80000000L;
318
319 while( topBit > 0 )
320 {
321 if( (value & mask) != 0 ) return topBit;
322
323 mask >>= 1;
324 topBit--;
325 }
326
327 return 0;
328 }
329
330 /**
331 * Write a float value
332 */
333 public void writeFloat( float value ) throws IOException
334 {
335 writeSI32( Float.floatToIntBits( value ) );
336 }
337
338 /**
339 * Write a double value
340 */
341 public void writeDouble( double value ) throws IOException
342 {
343 ByteArrayOutputStream baos = new ByteArrayOutputStream();
344 DataOutputStream dout = new DataOutputStream( baos );
345
346 dout.writeDouble( value );
347
348 dout.flush();
349
350 byte[] bytes = baos.toByteArray();
351 byte[] bytes2 = new byte[8];
352
353 bytes2[0] = bytes[3];
354 bytes2[1] = bytes[2];
355 bytes2[2] = bytes[1];
356 bytes2[3] = bytes[0];
357 bytes2[4] = bytes[7];
358 bytes2[5] = bytes[6];
359 bytes2[6] = bytes[5];
360 bytes2[7] = bytes[4];
361
362 write( bytes2 );
363 }
364
365 /**
366 * Write a 32 bit signed value
367 */
368 public void writeSI32( int value ) throws IOException
369 {
370 flushBits();
371
372 super.write( value & 0xff );
373 super.write( value >> 8 );
374 super.write( value >> 16 );
375 super.write( value >> 24 );
376 }
377
378 /**
379 * Util to convert a signed int to 2 bytes
380 */
381 public static byte[] sintTo2Bytes( int value )
382 {
383 return new byte[]
384 {
385 uintToByte( value & 0xff ),
386 uintToByte( value >> 8 )
387 };
388 }
389
390 /**
391 * Util to convert an unsigned int to 2 bytes
392 */
393 public static byte[] uintTo2Bytes( int value )
394 {
395 return new byte[]
396 {
397 uintToByte( value & 0xff ),
398 uintToByte( value >> 8 )
399 };
400 }
401
402 /**
403 * Util to convert an unsigned int to 4 bytes
404 */
405 public static byte[] uintTo4Bytes( int value )
406 {
407 return new byte[]
408 {
409 uintToByte( value & 0xff ),
410 uintToByte( value >> 8 ),
411 uintToByte( value >> 16 ),
412 uintToByte( value >> 24 )
413 };
414 }
415
416 /**
417 * Util to convert an unsigned int to an unsigned byte
418 */
419 public static byte uintToByte( int value )
420 {
421 int lowbit = value & 1;
422 value >>= 1;
423
424 byte b = (byte)value;
425 b <<= 1;
426 b |= (byte)lowbit;
427
428 return b;
429 }
430
431 /**
432 * @see java.io.OutputStream#write(int)
433 */
434 public void write( int b ) throws IOException {
435 flushBits();
436 super.write( b );
437 }
438
439 /**
440 * Override to ensure that bits are flushed
441 */
442 public void close() throws IOException {
443 flush();
444 super.close();
445 }
446
447 }