Source code: com/voytechs/jnetstream/io/ProtocolDataInputStream.java
1 /*
2 * File: ProtocolDataInputStream.java
3 * Auth: Mark Bednarczyk
4 * Date: 2003-06-30
5 * Id: $Id: ProtocolDataInputStream.java,v 1.1.1.1 2003/09/22 16:32:07 voytechs Exp $
6 ********************************************
7 Copyright (C) 2003 Mark Bednarczyk
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 ********************************************
23 * $Log: ProtocolDataInputStream.java,v $
24 * Revision 1.1.1.1 2003/09/22 16:32:07 voytechs
25 * Initial import.
26 *
27 */
28 package com.voytechs.jnetstream.io;
29
30 import java.lang.*;
31 import java.util.*;
32 import java.io.*;
33
34 /**
35 * This stream object extends the DataInputStream which defines how
36 * to read basic data types from a binary stream. This stream in addition
37 * to the Java native primitives that DataInput defines, add ability to
38 * read on a variable bit boundry. Any subset of bits can be read and returned
39 * in 'int' data type.
40 */
41 abstract public class ProtocolDataInputStream
42 extends InputStream {
43
44 /* Internal attributes */
45 private static final boolean debug = false;
46
47
48 /**
49 * Keep the current byte value out of which we are reading various bits.
50 * Only have to keep track of the last byte.
51 */
52 private int cacheByte = 0;
53
54 /**
55 * This is the bit offset wihin the bytes that we have read so far.
56 * If bitsLeft == 0 then there is no real data in the chachedByte variable
57 * if bitsLeft > 1 and bitOffset < 7 then we have real data in the
58 * cached byte and should read from upto the remainder of the cache.
59 * It should always have values between 0 and 7;
60 */
61 protected int bitsLeft = 0;
62
63
64 /**
65 * Our source DataInputStream since we require to provide similar
66 * interface as it is.
67 */
68 protected DataInputStream in;
69
70
71 /**
72 * Initialize with DataInputStream. We will besides the baseclasses
73 * functions to read native data types from the stream we will define
74 * methods for accessing on variouable bit boundries.
75 * @param inputStream Stream to read underlying data from.
76 */
77 public ProtocolDataInputStream(DataInputStream inputStream) {
78 this.in = inputStream;
79 }
80
81 public ProtocolDataInputStream(InputStream inputStream) {
82 this.in = new DataInputStream(inputStream);
83 }
84
85 /**
86 * Any normal read() operation invalidates our bit cache since we only
87 * allow reads to happen at the byte boudary.
88 * @return reads the next byte of the stream and returns it as an int.
89 * Invalidates any bit positioning within the stream and aligns the next
90 * bit read at the start of the next byte.
91 *
92 * @exception IOException any exceptions thrown by source input stream
93 */
94 public int read()
95 throws IOException {
96
97 if(bitsLeft != 0) {
98 return(readBits(8));
99 }
100
101 bitsLeft = 0;
102
103
104 int b = in.read();
105 if(b == -1)
106 throw new EOFException();
107
108 return(b);
109 }
110
111
112 /**
113 * Read from the stream number of bits and return them as a 'int' data type
114 * while advancing the bit-based position in the stream.
115 *
116 * @param numOfBits Number of bits (0 to 32) to read from the stream
117 * starting at the current byte and bit position. The maximum number of
118 * bits that can be read is 32. 0 count of bits will always return
119 * value 0 and will not advance the position in the stream.
120 * Less then 0 will generate an IvalidArgumentException.
121 *
122 * @exception InvalidArgumentExcpeption is trown if numOfBits is outside
123 * the 0 to 32 range.
124 * @exception IOException any IOException thrown by source input stream.
125 */
126 public int readBits(int numOfBits)
127 throws IOException {
128
129 if(numOfBits == 0)
130 return(0);
131
132 if(numOfBits > 32)
133 throw
134 new IndexOutOfBoundsException("0 >= range <= 32bits");
135
136 int returnValue = 0;;
137
138 /**
139 * Loop through and copy 1 bit at a time numOfBits amount of bits
140 * into returnValue integer.
141 */
142 while(--numOfBits >= 0) {
143
144
145 /**
146 * Check to see if we have any unprocessed bits in the cache.
147 * if not fetch the next byte and set the bitsLeft counter to 8.
148 * 8 is the number of bits in the byte since we just fetched a single
149 * byte.
150 */
151 if(bitsLeft == 0) // empty cache
152 loadChacheByte();
153
154 bitsLeft --;
155
156 /**
157 * Read the bit at the current offset position out of the cache
158 * and advance the pointer but reducing number of bits left.
159 */
160 int bit = cacheByte & (0x1 << bitsLeft);
161 if(bit != 0)
162 returnValue |= 1 << numOfBits ;
163
164 if(debug)
165 System.out.println("[" + numOfBits
166 + ":" + (0x1 << bitsLeft)
167 + "=" + bit + "]");
168
169 /**
170 * Copy the saved bit into our return value at the specified
171 * offset within the new integer.
172 */
173
174 }
175
176 return(returnValue);
177 }
178
179 /**
180 * Read from the stream number of bits and return them as a 'long' data type
181 * while advancing the bit-based position in the stream.
182 *
183 * @param numOfBits Number of bits (0 to 64) to read from the stream
184 * starting at the current byte and bit position. The maximum number of
185 * bits that can be read is 64. 0 count of bits will always return
186 * value 0 and will not advance the position in the stream.
187 * Less then 0 will generate an IvalidArgumentException.
188 *
189 * @exception InvalidArgumentExcpeption is trown if numOfBits is outside
190 * the 0 to 64 range.
191 * @exception IOException any IOException thrown by source input stream.
192 */
193 public long readBitsLong(int numOfBits)
194 throws IOException {
195
196 if(numOfBits == 0)
197 return(0);
198
199 if(numOfBits > 64)
200 throw
201 new IndexOutOfBoundsException("0 >= range <= 64bits");
202
203 long returnValue = 0;;
204
205 /**
206 * Loop through and copy 1 bit at a time numOfBits amount of bits
207 * into returnValue integer.
208 */
209 while(--numOfBits >= 0) {
210
211
212 /**
213 * Check to see if we have any unprocessed bits in the cache.
214 * if not fetch the next byte and set the bitsLeft counter to 8.
215 * 8 is the number of bits in the byte since we just fetched a single
216 * byte.
217 */
218 if(bitsLeft == 0) // empty cache
219 loadChacheByte();
220
221 bitsLeft --;
222
223 /**
224 * Read the bit at the current offset position out of the cache
225 * and advance the pointer but reducing number of bits left.
226 */
227 int bit = cacheByte & (0x1 << bitsLeft);
228 if(bit != 0)
229 bit = 1;
230 else
231 bit = 0;
232
233 if(debug)
234 System.out.println("[" + numOfBits
235 + ":" + (0x1 << bitsLeft)
236 + "=" + bit + "]");
237
238 /**
239 * Copy the saved bit into our return value at the specified
240 * offset within the new integer.
241 */
242 returnValue |= bit << numOfBits ;
243
244 }
245
246 return(returnValue);
247 }
248
249 private void loadChacheByte()
250 throws IOException {
251
252 cacheByte = in.read();
253
254 if(cacheByte == -1)
255 throw new EOFException();
256
257 bitsLeft = 8;
258 }
259
260
261 /*
262 *
263 ******************************************************
264 ******** Redo java.io.DataInputStream Interface ****
265 ******************************************************
266 *
267 */
268
269 abstract protected boolean isReady(int len) throws EOPacket;
270 abstract protected boolean isReady() throws EOPacket;
271
272 /**
273 * Read one byte from the stream.
274 */
275 public byte readByte()
276 throws IOException, EOPacket, EOPacketStream {
277
278 isReady(1);
279
280 try {
281 return(in.readByte());
282 }
283 catch(IOException ioe) {
284 reThrow(ioe);
285 throw ioe; // Never reached
286 }
287 }
288
289 /**
290 * Read one input byte and returns true if that byte is nonzero,
291 * false if that bye is zero.
292 */
293 public boolean readBoolean()
294 throws IOException, EOPacket, EOPacketStream {
295
296 isReady(1);
297
298 try {
299 return(in.readBoolean());
300 }
301 catch(IOException ioe) {
302 reThrow(ioe);
303 throw ioe; // Never reached
304 }
305 }
306
307 /**
308 * Reads a 32bit int using Big Endian encoding from stream.
309 */
310 public int readInt()
311 throws IOException, EOPacket, EOPacketStream {
312
313 isReady(4);
314
315 try {
316 return(in.readInt());
317 }
318 catch(IOException ioe) {
319 reThrow(ioe);
320 throw ioe; // Never reached
321 }
322 }
323
324 /**
325 * Reads a 32bit int using Big Endian encoding from stream.
326 */
327 public int readShort()
328 throws IOException, EOPacket, EOPacketStream {
329
330 isReady(2);
331
332 try {
333 return(in.readShort());
334 }
335 catch(IOException ioe) {
336 reThrow(ioe);
337 throw ioe; // Never reached
338 }
339 }
340
341 /**
342 * Reads a 32bit unsigned int using Big Endian encoding from stream.
343 */
344 public long readUnsignedInt()
345 throws IOException, EOPacket, EOPacketStream {
346
347 isReady(4);
348
349 try {
350 long returnValue = 0;
351
352 returnValue = (((long)read()) & 0xFF) << 24;
353 returnValue |= (((long)read()) & 0xFF) << 16;
354 returnValue |= (((long)read()) & 0xFF) << 8;
355 returnValue |= (((long)read()) & 0xFF) << 0;
356
357 return(returnValue);
358 }
359 catch(IOException ioe) {
360 reThrow(ioe);
361 throw ioe; // Never reached
362 }
363 }
364
365 /**
366 * Reads a 16bit unsigned int using Big Endian encoding from stream.
367 */
368 public int readUnsignedShort()
369 throws IOException, EOPacket, EOPacketStream {
370
371 isReady(2);
372
373 try {
374 return(in.readUnsignedShort());
375 }
376 catch(IOException ioe) {
377 reThrow(ioe);
378 throw ioe; // Never reached
379 }
380 }
381
382 /**
383 * Reads a 64bit unsigned int using Big Endian encoding from stream.
384 */
385 public long readLong()
386 throws IOException, EOPacket, EOPacketStream {
387
388 isReady(8);
389
390 try {
391 return(in.readLong());
392 }
393 catch(IOException ioe) {
394 reThrow(ioe);
395 throw ioe; // Never reached
396 }
397 }
398
399 /**
400 * Reads a 16bit short using Little Endian encoding from stream.
401 */
402 public short readShortLittleEndian()
403 throws IOException, EOPacket, EOPacketStream {
404
405 isReady(2);
406
407 try {
408 short returnValue = 0;
409
410 returnValue = (short)((read() & 0xFF) << 0);
411 returnValue |= (short)((read() & 0xFF) << 8);
412
413 return(returnValue);
414 }
415 catch(IOException ioe) {
416 reThrow(ioe);
417 throw ioe; // Never reached
418 }
419 }
420
421 /**
422 * Reads a 32bit int using Little Endian encoding from stream.
423 */
424 public int readIntLittleEndian()
425 throws IOException, EOPacket, EOPacketStream {
426
427 isReady(4);
428
429 try {
430 int returnValue = 0;
431
432 returnValue = (read() & 0xFF) << 0;
433 returnValue |= (read() & 0xFF) << 8;
434 returnValue |= (read() & 0xFF) << 16;
435 returnValue |= (read() & 0xFF) << 24;
436
437 return(returnValue);
438 }
439 catch(IOException ioe) {
440 reThrow(ioe);
441 throw ioe; // Never reached
442 }
443 }
444
445 /**
446 * Reads a 32bit int using Little Endian encoding from stream.
447 */
448 public long readUnsignedIntLittleEndian()
449 throws IOException, EOPacket, EOPacketStream {
450
451 isReady(4);
452
453 try {
454 long returnValue = 0;
455 int b;
456
457 returnValue = (((b = read()) < 0)?(long)b + 256:b) << 0;
458 returnValue |= (((b = read()) < 0)?(long)b + 256:b) << 8;
459 returnValue |= (((b = read()) < 0)?(long)b + 256:b) << 16;
460 returnValue |= (((b = read()) < 0)?(long)b + 256:b) << 24;
461
462 return(returnValue);
463 }
464 catch(IOException ioe) {
465 reThrow(ioe);
466 throw ioe; // Never reached
467 }
468 }
469
470 /**
471 * Reads a 64bit long int using Little Endian encoding from stream.
472 */
473 public long readLongLittleEndian()
474 throws IOException, EOPacket, EOPacketStream {
475
476 isReady(8);
477
478 try {
479 long returnValue = 0;
480
481 int b;
482 returnValue = (((b = read()) < 0)?(long)b + 256:b) << 0;
483 returnValue |= (((b = read()) < 0)?(long)b + 256:b) << 8;
484 returnValue |= (((b = read()) < 0)?(long)b + 256:b) << 16;
485 returnValue |= (((b = read()) < 0)?(long)b + 256:b) << 24;
486 returnValue |= (((b = read()) < 0)?(long)b + 256:b) << 32;
487 returnValue |= (((b = read()) < 0)?(long)b + 256:b) << 48;
488 returnValue |= (((b = read()) < 0)?(long)b + 256:b) << 56;
489
490 return(returnValue);
491 }
492 catch(IOException ioe) {
493 reThrow(ioe);
494 throw ioe; // Never reached
495 }
496 }
497
498 /**
499 * Reads a 16bit unsigned short using Little Endian encoding from stream.
500 */
501 public int readUnsignedShortLittleEndian()
502 throws IOException, EOPacket, EOPacketStream {
503
504 isReady(2);
505
506 try {
507 int returnValue = 0;
508
509 int b;
510 returnValue = (((b = read()) < 0)?(int)b + 256:b) << 0;
511 returnValue |= (((b = read()) < 0)?(int)b + 256:b) << 8;
512
513
514 return(returnValue);
515 }
516 catch(IOException ioe) {
517 reThrow(ioe);
518
519 throw ioe; // Never reached
520 }
521 }
522
523
524 /**
525 * Reads a 1 byte from stream but returns it as an int.
526 * This is replaces the normal read() method but implements
527 * ProtocolStream specific exception reporting.
528 */
529 public int readFromPacket()
530 throws IOException, EOPacket, EOPacketStream {
531
532 isReady(1);
533
534 try {
535 return(read());
536 }
537 catch(IOException ioe) {
538 reThrow(ioe);
539
540 throw ioe; // Never reached
541 }
542 }
543
544 /**
545 * Reads n bytes from stream but returns it as an int.
546 * This is replaces the normal read() method but implements
547 * ProtocolStream specific exception reporting.
548 */
549 public int readFromPacket(byte [] array)
550 throws IOException, EOPacket, EOPacketStream {
551
552 isReady(array.length);
553
554 try {
555 return(read(array));
556 }
557 catch(IOException ioe) {
558 reThrow(ioe);
559
560 throw ioe; // Never reached
561 }
562 }
563
564 /**
565 * Reads a 1 byte from stream but returns it as an int.
566 * This is replaces the normal read() method but implements
567 * ProtocolStream specific exception reporting.
568 */
569 public int readUnsignedByte()
570 throws IOException, EOPacket, EOPacketStream {
571
572 return(readFromPacket());
573 }
574
575
576
577
578 /**
579 * Conveniece method that checks if the stream has thrown
580 * EOFException. If yes then EOPacketStream exception is thrown
581 * instead otherwise the original exception is re-thrown.
582 *
583 * This method always throws an exception. Either original or new
584 * EOPacketStream.
585 *
586 * @param ioe IOException to check if its EOFException.
587 * @exception IOException original exception re-thrown again.
588 * @exception EOPacketStream a replacement exception is thrown which
589 * indicates that end of packet stream has been reached.
590 */
591 protected void reThrow (IOException ioe)
592 throws IOException, EOPacketStream {
593
594 if(ioe instanceof EOFException)
595 throw new EOPacketStream();
596 else
597 throw ioe;
598 }
599
600
601
602 /**
603 * Test function for ProtocolDataInputStream
604 * @param args command line arguments
605 */
606 public static void main(String [] args) {
607 }
608
609 } /* END OF: ProtocolDataInputStream */