1 /*
2 * Copyright (c) 1998-2002 The Jgroup Team.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 */
18
19 package jgroup.util;
20
21 import java.io.EOFException;
22 import java.io.Externalizable;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.ObjectInput;
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutput;
28 import java.io.PushbackInputStream;
29 import java.io.UTFDataFormatException;
30
31 import jgroup.core.ConfigManager;
32
33 /**
34 * Input stream message optimized to avoid message copying.
35 *
36 * @author Alberto Montresor
37 * @author Hein Meling
38 * @since Jgroup 0.8
39 */
40 public final class InMessage
41 extends InputStream
42 implements ObjectInput, Externalizable
43 {
44
45 ////////////////////////////////////////////////////////////////////////////////////////////
46 // Constants
47 ////////////////////////////////////////////////////////////////////////////////////////////
48
49 private static final long serialVersionUID = 9133407439996893111L;
50
51 /** Initial number of fragments composing a message; can grow */
52 private static final int NUM_BUFFER = 16;
53
54
55 ////////////////////////////////////////////////////////////////////////////////////////////
56 // Static section
57 ////////////////////////////////////////////////////////////////////////////////////////////
58
59 /** Temporary buffer used in writeTemp */
60 private static byte[] temp = new byte[8];
61
62
63 ////////////////////////////////////////////////////////////////////////////////////////////
64 // Fields
65 ////////////////////////////////////////////////////////////////////////////////////////////
66
67 private int payload; // Payload size in bytes
68 private int header; // Header size in bytes
69 private int trailer; // Trailer size in bytes
70 private int totlen; // Total length of a fragment
71 private int endpayload; // Last byte used for payload
72
73 private int position; // Current position in the stream [0..byteCount]
74 private int fragment; // Current fragment number
75 private int offset; // Current offset in the fragment
76
77 private int fragmentCount; // Total number of fragments used
78 private int byteCount; // Total number of bytes written
79
80 private byte[] buffer; // Current buffer
81 private byte[][] fragments; // Byte array fragments
82
83 private int marked; // Mark position
84
85
86 ////////////////////////////////////////////////////////////////////////////////////////////
87 // Constructors
88 ////////////////////////////////////////////////////////////////////////////////////////////
89
90 /**
91 * This constructor builds an empty message subdivided in fragments.
92 * Each fragment has a length of <code> header + payload + trailer</code>.
93 * <p>
94 * A <tt>IndexOutOfBoundsException</tt> is thrown if: <tt>payload</tt> is
95 * negative or equal to zero; <tt>header</tt> is negative; <tt>trailer</tt>
96 * is negative; <tt>size</tt> is negative or equal to zero.
97 */
98 public InMessage(int payload, int header, int trailer)
99 {
100 if (payload <= 0 || header < 0 || trailer < 0)
101 throw new IndexOutOfBoundsException();
102
103 // Store and compute fixed limits
104 this.payload = payload;
105 this.header = header;
106 this.trailer = trailer;
107 endpayload = payload + header;
108 totlen = endpayload + trailer;
109
110 // Initialize variables regarding message size
111 byteCount = 0;
112 fragmentCount = 0;
113
114 // Initialize variables regarding message position in the stream
115 position = 0;
116 fragment = 0;
117 offset = header;
118
119 // Create fragments
120 fragments = new byte[NUM_BUFFER][];
121 }
122
123
124 /**
125 * This constructor builds a message input stream subdivided in
126 * fragments with length <code> header + payload + trailer</code>.
127 *
128 * @exception IndexOutOfBoundsException
129 * Raised if <code>payload</code> is negative or equal to zero;
130 * <code>header</code> is negative; <code>trailer</code> is
131 * negative; <code>size</code> is negative or equal to zero.
132 */
133 public InMessage(int payload, int header, int trailer, int size)
134 {
135 if (payload <= 0 || header < 0 || trailer < 0 || size <= 0)
136 throw new IndexOutOfBoundsException();
137
138 // Store and compute fixed limits
139 this.payload = payload;
140 this.header = header;
141 this.trailer = trailer;
142 endpayload = payload + header;
143 totlen = endpayload + trailer;
144
145 // Initialize variables regarding message size
146 byteCount = 0;
147 fragmentCount = 0;
148
149 // Initialize variables regarding message position in the stream
150 position = 0;
151 fragment = 0;
152 offset = header;
153
154 // Create fragments
155 fragments = new byte[size % payload + 1][];
156 }
157
158
159 /**
160 * Construct a InMessage starting from an OutMessage. In this way, it possible
161 * to local deliver messages without copying them.
162 */
163 public InMessage(OutMessage msg)
164 {
165 // Store and compute fixed limits
166 this.payload = msg.getPayload();
167 this.header = msg.getHeader();
168 this.trailer = msg.getTrailer();
169 endpayload = payload + header;
170 totlen = endpayload + trailer;
171
172 // Initialize variables regarding message size
173 byteCount = msg.getByteCount();
174 fragmentCount = msg.getFragmentCount();
175
176 // Initialize variables regarding message position in the stream
177 position = 0;
178 fragment = 0;
179 offset = header;
180
181 // Create fragments
182 fragments = msg.getFragments();
183 buffer = fragments[0];
184 }
185
186
187 ////////////////////////////////////////////////////////////////////////////////////////////
188 // Methods from Externalizable
189 ////////////////////////////////////////////////////////////////////////////////////////////
190
191 /**
192 * Default constructor for externalization.
193 */
194 public InMessage() { }
195
196
197 /**
198 * Restores the content of this object from the marshalled data contained
199 * in the specified input stream.
200 *
201 * @param in the stream to be read
202 */
203 public void readExternal(ObjectInput in)
204 throws IOException
205 {
206 payload = in.readInt();
207 header = in.readInt();
208 trailer = in.readInt();
209 endpayload = payload + header;
210 totlen = endpayload + trailer;
211 fragmentCount = in.readInt();
212 byteCount = in.readInt();
213 fragment = in.readInt();
214 position = in.readInt();
215 marked = in.readInt();
216
217 // Initialize the offset variable based on the parameters from the stream
218 offset = header + position;
219
220 // Reconstruct fragments from stream
221 fragments = new byte[fragmentCount][];
222 int fcount = fragmentCount - 1;
223 for (int i = 0; i < fcount; i++) {
224 fragments[i] = new byte[totlen];
225 in.readFully(fragments[i], 0, totlen);
226 }
227 fragments[fcount] = new byte[totlen];
228 int lastFragLen = getLastPayload() + header + trailer;
229 in.readFully(fragments[fcount], 0, lastFragLen);
230 buffer = fragments[0];
231
232 // System.out.println("readExternal: " + this.toString(true));
233 }
234
235 /**
236 * Marshals the content of this object to the specified output stream.
237 *
238 * @param out the stream to be written
239 */
240 public void writeExternal(ObjectOutput out)
241 throws IOException
242 {
243 out.writeInt(payload);
244 out.writeInt(header);
245 out.writeInt(trailer);
246 out.writeInt(fragmentCount);
247 out.writeInt(byteCount);
248 out.writeInt(fragment);
249 out.writeInt(position);
250 out.writeInt(marked);
251
252 int fcount = fragmentCount - 1;
253 for (int i = 0; i < fcount; i++) {
254 out.write(fragments[i]);
255 }
256 int lastFragLen = getLastPayload() + header + trailer;
257 out.write(fragments[fcount], 0, lastFragLen);
258
259 // System.out.println("writeExternal: " + this.toString(true));
260 }
261
262
263 ////////////////////////////////////////////////////////////////////////////////////////////
264 // Methods inherited from InputStream
265 ////////////////////////////////////////////////////////////////////////////////////////////
266
267 /**
268 * Reads the next byte of data from the input stream. The value byte is
269 * returned as an <code>int</code> in the range <code>0</code> to
270 * <code>255</code>. If no byte is available because the end of the stream
271 * has been reached, the value <code>-1</code> is returned. This method
272 * blocks until input data is available, the end of the stream is detected,
273 * or an exception is thrown.
274 *
275 * @return the next byte of data, or <code>-1</code> if the end of the
276 * stream is reached.
277 * @exception IOException if an I/O error occurs.
278 */
279 public int read()
280 {
281 if (position == byteCount) {
282 /*
283 * The end of the stream has been reached
284 */
285 return -1;
286 }
287
288 if (offset == endpayload) {
289 /*
290 * The end of the current fragment has been reached; we move to the next
291 * fragment and we reset the offset.
292 */
293 fragment++;
294 buffer = fragments[fragment];
295 offset = header;
296 }
297 position++;
298 return buffer[offset++] & 0xFF;
299 }
300
301
302 /**
303 * Reads some number of bytes from the input stream and stores them into
304 * the buffer array <code>b</code>. The number of bytes actually read is
305 * returned as an integer. This method blocks until input data is
306 * available, end of file is detected, or an exception is thrown.
307 *
308 * <p> If <code>b</code> is <code>null</code>, a
309 * <code>NullPointerException</code> is thrown. If the length of
310 * <code>b</code> is zero, then no bytes are read and <code>0</code> is
311 * returned; otherwise, there is an attempt to read at least one byte. If
312 * no byte is available because the stream is at end of file, the value
313 * <code>-1</code> is returned; otherwise, at least one byte is read and
314 * stored into <code>b</code>.
315 *
316 * <p> The first byte read is stored into element <code>b[0]</code>, the
317 * next one into <code>b[1]</code>, and so on. The number of bytes read is,
318 * at most, equal to the length of <code>b</code>. Let <i>k</i> be the
319 * number of bytes actually read; these bytes will be stored in elements
320 * <code>b[0]</code> through <code>b[</code><i>k</i><code>-1]</code>,
321 * leaving elements <code>b[</code><i>k</i><code>]</code> through
322 * <code>b[b.length-1]</code> unaffected.
323 *
324 * <p> The <code>read(b)</code> method for class <code>InputStream</code>
325 * has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
326 *
327 * @param b the buffer into which the data is read.
328 * @return the total number of bytes read into the buffer, or
329 * <code>-1</code> is there is no more data because the end of
330 * the stream has been reached.
331 */
332 public int read(byte b[])
333 {
334 return read(b, 0, b.length);
335 }
336
337 /**
338 * Reads up to <code>len</code> bytes of data from the input stream into
339 * an array of bytes. An attempt is made to read as many as
340 * <code>len</code> bytes, but a smaller number may be read, possibly
341 * zero. The number of bytes actually read is returned as an integer.
342 *
343 * <p> If <code>b</code> is <code>null</code>, a
344 * <code>NullPointerException</code> is thrown.
345 *
346 * <p> If <code>off</code> is negative, or <code>len</code> is negative, or
347 * <code>off+len</code> is greater than the length of the array
348 * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
349 * thrown.
350 *
351 * <p> If <code>len</code> is zero, then no bytes are read and
352 * <code>0</code> is returned; otherwise, there is an attempt to read at
353 * least one byte. If no byte is available because the stream is at end of
354 * file, the value <code>-1</code> is returned; otherwise, at least one
355 * byte is read and stored into <code>b</code>.
356 *
357 * <p> The first byte read is stored into element <code>b[off]</code>, the
358 * next one into <code>b[off+1]</code>, and so on. The number of bytes read
359 * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
360 * bytes actually read; these bytes will be stored in elements
361 * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
362 * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
363 * <code>b[off+len-1]</code> unaffected.
364 *
365 * <p> In every case, elements <code>b[0]</code> through
366 * <code>b[off]</code> and elements <code>b[off+len]</code> through
367 * <code>b[b.length-1]</code> are unaffected.
368 *
369 * @param b the buffer into which the data is read.
370 * @param off the start offset in array <code>b</code>
371 * at which the data is written.
372 * @param len the maximum number of bytes to read.
373 * @return the total number of bytes read into the buffer, or
374 * <code>-1</code> if there is no more data because the end of
375 * the stream has been reached.
376 */
377 public int read(byte b[], int off, int len)
378 {
379 int tobecopied;
380 int remained;
381
382 if (b == null)
383 throw new NullPointerException();
384 if (off < 0 || off > b.length || len < 0 || (off + len > b.length) || (off + len < 0))
385 throw new IndexOutOfBoundsException();
386 if (len == 0)
387 return 0;
388 if (position == byteCount)
389 return -1;
390
391 if (position + len > byteCount)
392 len = byteCount - position;
393 if (b.length < len)
394 len = b.length;
395 position += len;
396 remained = len;
397 tobecopied = endpayload - offset;
398 while (remained > tobecopied) {
399 System.arraycopy(fragments[fragment], offset, b, off, tobecopied);
400 off += tobecopied;
401 remained -= tobecopied;
402 fragment++;
403 offset = header;
404 tobecopied = payload;
405 }
406 buffer = fragments[fragment];
407 System.arraycopy(buffer, offset, b, off, remained);
408 offset += remained;
409
410 return len;
411 }
412
413 /**
414 * Skips over and discards <code>n</code> bytes of data from this input
415 * stream. The <code>skip</code> method may, for a variety of reasons, end
416 * up skipping over some smaller number of bytes, possibly <code>0</code>.
417 * This may result from any of a number of conditions; reaching end of file
418 * before <code>n</code> bytes have been skipped is only one possibility.
419 * The actual number of bytes skipped is returned. If <code>n</code> is
420 * negative, no bytes are skipped.
421 *
422 * <p> The <code>skip</code> method of <code>InputStream</code> creates a
423 * byte array and then repeatedly reads into it until <code>n</code> bytes
424 * have been read or the end of the stream has been reached. Subclasses are
425 * encouraged to provide a more efficient implementation of this method.
426 *
427 * @param n the number of bytes to be skipped.
428 * @return the actual number of bytes skipped.
429 * @exception IOException if an I/O error occurs.
430 */
431 public long skip(long n)
432 {
433 long value = n;
434 position += n;
435 if (position > byteCount) {
436 value -= (position - byteCount);
437 position = byteCount;
438 }
439 return value;
440 }
441
442 /**
443 * Marks the current position in this input stream. A subsequent call to
444 * the <code>reset</code> method repositions this stream at the last marked
445 * position so that subsequent reads re-read the same bytes.
446 *
447 * <p> The <code>readlimit</code> arguments tells this input stream to
448 * allow that many bytes to be read before the mark position gets
449 * invalidated.
450 *
451 * <p> The general contract of <code>mark</code> is that, if the method
452 * <code>markSupported</code> returns <code>true</code>, the stream somehow
453 * remembers all the bytes read after the call to <code>mark</code> and
454 * stands ready to supply those same bytes again if and whenever the method
455 * <code>reset</code> is called. However, the stream is not required to
456 * remember any data at all if more than <code>readlimit</code> bytes are
457 * read from the stream before <code>reset</code> is called.
458 *
459 *
460 * @param readlimit not used
461 */
462 public synchronized void mark(int readlimit)
463 {
464 marked = position;
465 }
466
467
468 /**
469 * Repositions this stream to the position at the time the
470 * <code>mark</code> method was last called on this input stream.
471 *
472 * <p> The general contract of <code>reset</code> is:
473 *
474 * <p><ul>
475 *
476 * <li> If the method <code>markSupported</code> returns
477 * <code>true</code>, then:
478 *
479 * <ul><li> If the method <code>mark</code> has not been called since
480 * the stream was created, or the number of bytes read from the stream
481 * since <code>mark</code> was last called is larger than the argument
482 * to <code>mark</code> at that last call, then an
483 * <code>IOException</code> might be thrown.
484 *
485 * <li> If such an <code>IOException</code> is not thrown, then the
486 * stream is reset to a state such that all the bytes read since the
487 * most recent call to <code>mark</code> (or since the start of the
488 * file, if <code>mark</code> has not been called) will be resupplied
489 * to subsequent callers of the <code>read</code> method, followed by
490 * any bytes that otherwise would have been the next input data as of
491 * the time of the call to <code>reset</code>. </ul>
492 *
493 * <li> If the method <code>markSupported</code> returns
494 * <code>false</code>, then:
495 *
496 * <ul><li> The call to <code>reset</code> may throw an
497 * <code>IOException</code>.
498 *
499 * <li> If an <code>IOException</code> is not thrown, then the stream
500 * is reset to a fixed state that depends on the particular type of the
501 * input stream and how it was created. The bytes that will be supplied
502 * to subsequent callers of the <code>read</code> method depend on the
503 * particular type of the input stream. </ul></ul>
504 *
505 * <p> The method <code>reset</code> for class <code>InputStream</code>
506 * does nothing and always throws an <code>IOException</code>.
507 *
508 * @exception IOException if this stream has not been marked or if the
509 * mark has been invalidated.
510 */
511 public synchronized void reset()
512 throws IOException
513 {
514 fragment = marked / payload;
515 offset = header + marked % payload;
516 position = marked;
517 buffer = fragments[fragment];
518 }
519
520 /**
521 * Tests if this input stream supports the <code>mark</code> and
522 * <code>reset</code> methods. The <code>markSupported</code> method of
523 * <code>InputStream</code> returns <code>false</code>.
524 *
525 * @return <code>true</code> if this true type supports the mark and reset
526 * method; <code>false</code> otherwise.
527 */
528 public boolean markSupported()
529 {
530 return true;
531 }
532
533 ////////////////////////////////////////////////////////////////////////////////////////////
534 // Methods from DataInput
535 ////////////////////////////////////////////////////////////////////////////////////////////
536
537 /**
538 * Reads some bytes from an input stream and stores them into the
539 * buffer array <code>b</code>. The number of bytes read is equal to
540 * the length of <code>b</code>. <p>
541 *
542 * This method blocks until one of the following conditions occurs:
543 * <p>
544 *
545 * <ul>
546 *
547 * <li><code>b.length</code> bytes of input data are available, in
548 * which case a normal return is made.
549 *
550 * <li>End of file is detected, in which case an
551 * <code>EOFException</code> is thrown.
552 *
553 * <li>An I/O error occurs, in which case an <code>IOException</code>
554 * other than <code>EOFException</code> is thrown.
555 *
556 * </ul>
557 * <p>
558 *
559 * If <code>b.length</code> is zero, then no bytes are read.
560 * Otherwise, the first byte read is stored into element
561 * <code>b[0]</code>, the next one into <code>b[1]</code>, and so on.
562 * If an exception is thrown from this method, then it may be that
563 * some but not all bytes of <code>b</code> have been updated with
564 * data from the input stream.
565 *
566 * @param b
567 * The buffer into which the data is read.
568 * @exception EOFException
569 * If this stream reaches the end before reading all the bytes.
570 * @exception IOException
571 * If an I/O error occurs.
572 * @exception NullPointerException
573 * Thrown if <code>b</code> is <code>null</code>.
574 */
575 public void readFully(byte b[]) throws IOException
576 {
577 read(b);
578 }
579
580 /**
581 *
582 * Reads <code>len</code>
583 * bytes from
584 * an input stream.
585 * <p>
586 * This method
587 * blocks until one of the following conditions
588 * occurs:<p>
589 * <ul>
590 * <li><code>len</code> bytes
591 * of input data are available, in which case
592 * a normal return is made.
593 *
594 * <li>End of file
595 * is detected, in which case an <code>EOFException</code>
596 * is thrown.
597 *
598 * <li>An I/O error occurs, in
599 * which case an <code>IOException</code> other
600 * than <code>EOFException</code> is thrown.
601 * </ul>
602 * <p>
603 * If <code>b</code> is <code>null</code>,
604 * a <code>NullPointerException</code> is thrown.
605 * If <code>off</code> is negative, or <code>len</code>
606 * is negative, or <code>off+len</code> is
607 * greater than the length of the array <code>b</code>,
608 * then an <code>IndexOutOfBoundsException</code>
609 * is thrown.
610 * If <code>len</code> is zero,
611 * then no bytes are read. Otherwise, the first
612 * byte read is stored into element <code>b[off]</code>,
613 * the next one into <code>b[off+1]</code>,
614 * and so on. The number of bytes read is,
615 * at most, equal to <code>len</code>.
616 *
617 * @param b the buffer into which the data is read.
618 * @exception EOFException if this stream reaches the end before reading
619 * all the bytes.
620 * @exception IOException if an I/O error occurs.
621 */
622 public void readFully(byte b[], int off, int len) throws IOException
623 {
624 read(b, off, len);
625 }
626
627 /**
628 * Makes an attempt to skip over
629 * <code>n</code> bytes
630 * of data from the input
631 * stream, discarding the skipped bytes. However,
632 * it may skip
633 * over some smaller number of
634 * bytes, possibly zero. This may result from
635 * any of a
636 * number of conditions; reaching
637 * end of file before <code>n</code> bytes
638 * have been skipped is
639 * only one possibility.
640 * This method never throws an <code>EOFException</code>.
641 * The actual
642 * number of bytes skipped is returned.
643 *
644 * @param n the number of bytes to be skipped.
645 * @return the number of bytes skipped, which is always <code>n</code>.
646 * @exception EOFException if this stream reaches the end before skipping
647 * all the bytes.
648 * @exception IOException if an I/O error occurs.
649 */
650 public int skipBytes(int n) throws IOException
651 {
652 return (int) skip(n);
653 }
654
655 /**
656 * Reads one input byte and returns
657 * <code>true</code> if that byte is nonzero,
658 * <code>false</code> if that byte is zero.
659 * This method is suitable for reading
660 * the byte written by the <code>writeBoolean</code>
661 * method of interface <code>DataOutput</code>.
662 *
663 * @return the <code>boolean</code> value read.
664 * @exception EOFException if this stream reaches the end before reading
665 * all the bytes.
666 * @exception IOException if an I/O error occurs.
667 */
668 public boolean readBoolean() throws IOException
669 {
670 int ch = read();
671 if (ch < 0)
672 throw new EOFException();
673 return (ch != 0);
674 }
675
676 /**
677 * Reads and returns one input byte.
678 * The byte is treated as a signed value in
679 * the range <code>-128</code> through <code>127</code>,
680 * inclusive.
681 * This method is suitable for
682 * reading the byte written by the <code>writeByte</code>
683 * method of interface <code>DataOutput</code>.
684 *
685 * @return the 8-bit value read.
686 * @exception EOFException if this stream reaches the end before reading
687 * all the bytes.
688 * @exception IOException if an I/O error occurs.
689 */
690 public byte readByte() throws IOException
691 {
692 int ch = read();
693 if (ch < 0)
694 throw new EOFException();
695 return (byte)(ch);
696 }
697
698 /**
699 * Reads one input byte, zero-extends
700 * it to type <code>int</code>, and returns
701 * the result, which is therefore in the range
702 * <code>0</code>
703 * through <code>255</code>.
704 * This method is suitable for reading
705 * the byte written by the <code>writeByte</code>
706 * method of interface <code>DataOutput</code>
707 * if the argument to <code>writeByte</code>
708 * was intended to be a value in the range
709 * <code>0</code> through <code>255</code>.
710 *
711 * @return the unsigned 8-bit value read.
712 * @exception EOFException if this stream reaches the end before reading
713 * all the bytes.
714 * @exception IOException if an I/O error occurs.
715 */
716 public int readUnsignedByte() throws IOException
717 {
718 int ch = read();
719 if (ch < 0)
720 throw new EOFException();
721 return ch;
722 }
723
724 /**
725 * Reads two input bytes and returns
726 * a <code>short</code> value. Let <code>a</code>
727 * be the first byte read and <code>b</code>
728 * be the second byte. The value
729 * returned
730 * is:
731 * <p><pre><code>(short)((a << 8) * | (b & 0xff))
732 * </code></pre>
733 * This method
734 * is suitable for reading the bytes written
735 * by the <code>writeShort</code> method of
736 * interface <code>DataOutput</code>.
737 *
738 * @return the 16-bit value read.
739 * @exception EOFException if this stream reaches the end before reading
740 * all the bytes.
741 * @exception IOException if an I/O error occurs.
742 */
743 public short readShort() throws IOException
744 {
745 return (short) read2();
746 }
747
748 /**
749 * Reads two input bytes and returns
750 * an <code>int</code> value in the range <code>0</code>
751 * through <code>65535</code>. Let <code>a</code>
752 * be the first byte read and
753 * <code>b</code>
754 * be the second byte. The value returned is:
755 * <p><pre><code>(((a & 0xff) << 8) | (b & 0xff))
756 * </code></pre>
757 * This method is suitable for reading the bytes
758 * written by the <code>writeShort</code> method
759 * of interface <code>DataOutput</code> if
760 * the argument to <code>writeShort</code>
761 * was intended to be a value in the range
762 * <code>0</code> through <code>65535</code>.
763 *
764 * @return the unsigned 16-bit value read.
765 * @exception EOFException if this stream reaches the end before reading
766 * all the bytes.
767 * @exception IOException if an I/O error occurs.
768 */
769 public int readUnsignedShort() throws IOException
770 {
771 return read2();
772 }
773
774 /**
775 * Reads an input <code>char</code> and returns the <code>char</code> value.
776 * A Unicode <code>char</code> is made up of two bytes.
777 * Let <code>a</code>
778 * be the first byte read and <code>b</code>
779 * be the second byte. The value
780 * returned is:
781 * <p><pre><code>(char)((a << 8) | (b & 0xff))
782 * </code></pre>
783 * This method
784 * is suitable for reading bytes written by
785 * the <code>writeChar</code> method of interface
786 * <code>DataOutput</code>.
787 *
788 * @return the Unicode <code>char</code> read.
789 * @exception EOFException if this stream reaches the end before reading
790 * all the bytes.
791 * @exception IOException if an I/O error occurs.
792 */
793 public char readChar() throws IOException
794 {
795 return (char) read2();
796 }
797
798 /**
799 * Reads four input bytes and returns an
800 * <code>int</code> value. Let <code>a</code>
801 * be the first byte read, <code>b</code> be
802 * the second byte, <code>c</code> be the third
803 * byte,
804 * and <code>d</code> be the fourth
805 * byte. The value returned is:
806 * <p><pre>
807 * <code>
808 * (((a & 0xff) << 24) | ((b & 0xff) << 16) |
809 *  ((c & 0xff) << 8) | (d & 0xff))
810 * </code></pre>
811 * This method is suitable
812 * for reading bytes written by the <code>writeInt</code>
813 * method of interface <code>DataOutput</code>.
814 *
815 * @return the <code>int</code> value read.
816 * @exception EOFException if this stream reaches the end before reading
817 * all the bytes.
818 * @exception IOException if an I/O error occurs.
819 */
820 public int readInt() throws IOException
821 {
822 return read4();
823 }
824
825 /**
826 * Reads eight input bytes and returns
827 * a <code>long</code> value. Let <code>a</code>
828 * be the first byte read, <code>b</code> be
829 * the second byte, <code>c</code> be the third
830 * byte, <code>d</code>
831 * be the fourth byte,
832 * <code>e</code> be the fifth byte, <code>f</code>
833 * be the sixth byte, <code>g</code> be the
834 * seventh byte,
835 * and <code>h</code> be the
836 * eighth byte. The value returned is:
837 * <p><pre> <code>
838 * (((long)(a & 0xff) << 56) |
839 * ((long)(b & 0xff) << 48) |
840 * ((long)(c & 0xff) << 40) |
841 * ((long)(d & 0xff) << 32) |
842 * ((long)(e & 0xff) << 24) |
843 * ((long)(f & 0xff) << 16) |
844 * ((long)(g & 0xff) << 8) |
845 * ((long)(h & 0xff)))
846 * </code></pre>
847 * <p>
848 * This method is suitable
849 * for reading bytes written by the <code>writeLong</code>
850 * method of interface <code>DataOutput</code>.
851 *
852 * @return the <code>long</code> value read.
853 * @exception EOFException if this stream reaches the end before reading
854 * all the bytes.
855 * @exception IOException if an I/O error occurs.
856 */
857 public long readLong() throws IOException
858 {
859 return read8();
860 }
861
862 /**
863 * Reads four input bytes and returns
864 * a <code>float</code> value. It does this
865 * by first constructing an <code>int</code>
866 * value in exactly the manner
867 * of the <code>readInt</code>
868 * method, then converting this <code>int</code>
869 * value to a <code>float</code> in
870 * exactly the manner of the method <code>Float.intBitsToFloat</code>.
871 * This method is suitable for reading
872 * bytes written by the <code>writeFloat</code>
873 * method of interface <code>DataOutput</code>.
874 *
875 * @return the <code>float</code> value read.
876 * @exception EOFException if this stream reaches the end before reading
877 * all the bytes.
878 * @exception IOException if an I/O error occurs.
879 */
880 public float readFloat() throws IOException
881 {
882 return Float.intBitsToFloat(read4());
883 }
884
885 /**
886 * Reads eight input bytes and returns
887 * a <code>double</code> value. It does this
888 * by first constructing a <code>long</code>
889 * value in exactly the manner
890 * of the <code>readlong</code>
891 * method, then converting this <code>long</code>
892 * value to a <code>double</code> in exactly
893 * the manner of the method <code>Double.longBitsToDouble</code>.
894 * This method is suitable for reading
895 * bytes written by the <code>writeDouble</code>
896 * method of interface <code>DataOutput</code>.
897 *
898 * @return the <code>double</code> value read.
899 * @exception EOFException if this stream reaches the end before reading
900 * all the bytes.
901 * @exception IOException if an I/O error occurs.
902 */
903 public double readDouble() throws IOException
904 {
905 return Double.longBitsToDouble(read8());
906 }
907
908 private char lineBuffer[];
909
910 /**
911 * Reads the next line of text from the input stream.
912 * It reads successive bytes, converting
913 * each byte separately into a character,
914 * until it encounters a line terminator or
915 * end of
916 * file; the characters read are then
917 * returned as a <code>String</code>. Note
918 * that because this
919 * method processes bytes,
920 * it does not support input of the full Unicode
921 * character set.
922 * <p>
923 * If end of file is encountered
924 * before even one byte can be read, then <code>null</code>
925 * is returned. Otherwise, each byte that is
926 * read is converted to type <code>char</code>
927 * by zero-extension. If the character <code>'\n'</code>
928 * is encountered, it is discarded and reading
929 * ceases. If the character <code>'\r'</code>
930 * is encountered, it is discarded and, if
931 * the following byte converts  to the
932 * character <code>'\n'</code>, then that is
933 * discarded also; reading then ceases. If
934 * end of file is encountered before either
935 * of the characters <code>'\n'</code> and
936 * <code>'\r'</code> is encountered, reading
937 * ceases. Once reading has ceased, a <code>String</code>
938 * is returned that contains all the characters
939 * read and not discarded, taken in order.
940 * Note that every character in this string
941 * will have a value less than <code>\u0100</code>,
942 * that is, <code>(char)256</code>.
943 *
944 * @return if this stream reaches the end before reading all the bytes.
945 * @exception IOException if an I/O error occurs.
946 */
947 public String readLine() throws IOException
948 {
949 PushbackInputStream in = new PushbackInputStream(this);
950
951 char buf[] = lineBuffer;
952
953 if (buf == null) {
954 buf = lineBuffer = new char[128];
955 }
956
957 int room = buf.length;
958 int offset = 0;
959 int c;
960
961 loop: while (true) {
962 switch (c = in.read()) {
963 case -1:
964 case '\n':
965 break loop;
966
967 case '\r':
968 int c2 = in.read();
969 if ((c2 != '\n') && (c2 != -1)) {
970 in.unread(c2);
971 }
972 break loop;
973
974 default:
975 if (--room < 0) {
976 buf = new char[offset + 128];
977 room = buf.length - offset - 1;
978 System.arraycopy(lineBuffer, 0, buf, 0, offset);
979 lineBuffer = buf;
980 }
981 buf[offset++] = (char) c;
982 break;
983 }
984 }
985 if ((c == -1) && (offset == 0)) {
986 return null;
987 }
988 return String.copyValueOf(buf, 0, offset);
989 }
990
991 /**
992 * Reads in a string that has been encoded using a modified UTF-8 format.
993 * The general contract of <code>readUTF</code>
994 * is that it reads a representation of a Unicode
995 * character string encoded in Java modified
996 * UTF-8 format; this string of characters
997 * is then returned as a <code>String</code>.
998 * <p>
999 * First, two bytes are read and used to
1000 * construct an unsigned 16-bit integer in
1001 * exactly the manner of the <code>readUnsignedShort</code>
1002 * method . This integer value is called the
1003 * <i>UTF length</i> and specifies the number
1004 * of additional bytes to be read. These bytes
1005 * are then converted to characters by considering
1006 * them in groups. The length of each group
1007 * is computed from the value of the first
1008 * byte of the group. The byte following a
1009 * group, if any, is the first byte of the
1010 * next group.
1011 * <p>
1012 * If the first byte of a group
1013 * matches the bit pattern <code>0xxxxxxx</code>
1014 * (where <code>x</code> means "may be <code>0</code>
1015 * or <code>1</code>"), then the group consists
1016 * of just that byte. The byte is zero-extended
1017 * to form a character.
1018 * <p>
1019 * If the first byte
1020 * of a group matches the bit pattern <code>110xxxxx</code>,
1021 * then the group consists of that byte <code>a</code>
1022 * and a second byte <code>b</code>. If there
1023 * is no byte <code>b</code> (because byte
1024 * <code>a</code> was the last of the bytes
1025 * to be read), or if byte <code>b</code> does
1026 * not match the bit pattern <code>10xxxxxx</code>,
1027 * then a <code>UTFDataFormatException</code>
1028 * is thrown. Otherwise, the group is converted
1029 * to the character:<p>
1030 * <pre><code>(char)(((a& 0x1F) << 6) | (b & 0x3F))
1031 * </code></pre>
1032 * If the first byte of a group
1033 * matches the bit pattern <code>1110xxxx</code>,
1034 * then the group consists of that byte <code>a</code>
1035 * and two more bytes <code>b</code> and <code>c</code>.
1036 * If there is no byte <code>c</code> (because
1037 * byte <code>a</code> was one of the last
1038 * two of the bytes to be read), or either
1039 * byte <code>b</code> or byte <code>c</code>
1040 * does not match the bit pattern <code>10xxxxxx</code>,
1041 * then a <code>UTFDataFormatException</code>
1042 * is thrown. Otherwise, the group is converted
1043 * to the character:<p>
1044 * <pre><code>
1045 * (char)(((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F))
1046 * </code></pre>
1047 * If the first byte of a group matches the
1048 * pattern <code>1111xxxx</code> or the pattern
1049 * <code>10xxxxxx</code>, then a <code>UTFDataFormatException</code>
1050 * is thrown.
1051 * <p>
1052 * If end of file is encountered
1053 * at any time during this entire process,
1054 * then an <code>EOFException</code> is thrown.
1055 * <p>
1056 * After every group has been converted to
1057 * a character by this process, the characters
1058 * are gathered, in the same order in which
1059 * their corresponding groups were read from
1060 * the input stream, to form a <code>String</code>,
1061 * which is returned.
1062 * <p>
1063 * The <code>writeUTF</code>
1064 * method of interface <code>DataOutput</code>
1065 * may be used to write data that is suitable
1066 * for reading by this method.
1067 * @return a Unicode string.
1068 * @exception EOFException if this stream reaches the end
1069 * before reading all the bytes.
1070 * @exception IOException if an I/O error occurs.
1071 * @exception UTFDataFormatException if the bytes do not represent a
1072 * valid UTF-8 encoding of a string.
1073 */
1074 public String readUTF() throws IOException
1075 {
1076 int utflen = readUnsignedShort();
1077 StringBuilder str = new StringBuilder(utflen);
1078 byte bytearr [] = new byte[utflen];
1079 int c, char2, char3;
1080 int count = 0;
1081
1082 readFully(bytearr, 0, utflen);
1083
1084 while (count < utflen) {
1085 c = bytearr[count] & 0xff;
1086 switch (c >> 4) {
1087 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
1088 /* 0xxxxxxx*/
1089 count++;
1090 str.append((char)c);
1091 break;
1092 case 12: case 13:
1093 /* 110x xxxx 10xx xxxx*/
1094 count += 2;
1095 if (count > utflen)
1096 throw new UTFDataFormatException();
1097 char2 = bytearr[count-1];
1098 if ((char2 & 0xC0) != 0x80)
1099 throw new UTFDataFormatException();
1100 str.append((char)(((c & 0x1F) << 6) | (char2 & 0x3F)));
1101 break;
1102 case 14:
1103 /* 1110 xxxx 10xx xxxx 10xx xxxx */
1104 count += 3;
1105 if (count > utflen)
1106 throw new UTFDataFormatException();
1107 char2 = bytearr[count-2];
1108 char3 = bytearr[count-1];
1109 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
1110 throw new UTFDataFormatException();
1111 str.append((char)(((c & 0x0F) << 12) |
1112 ((char2 & 0x3F) << 6) |
1113 ((char3 & 0x3F) << 0)));
1114 break;
1115 default:
1116 /* 10xx xxxx, 1111 xxxx */
1117 throw new UTFDataFormatException();
1118 }
1119 }
1120 // The number of chars produced may be less than utflen
1121 return new String(str);
1122 }
1123
1124
1125 ////////////////////////////////////////////////////////////////////////////////////////////
1126 // Methods from ObjectInput
1127 ////////////////////////////////////////////////////////////////////////////////////////////
1128
1129
1130 /**
1131 * Read and return an object. The class that implements this interface
1132 * defines where the object is "read" from.
1133 *
1134 * @exception java.lang.ClassNotFoundException If the class of a serialized
1135 * object cannot be found.
1136 * @exception IOException If any of the usual Input/Output
1137 * related exceptions occur.
1138 */
1139 public Object readObject()
1140 throws ClassNotFoundException, IOException
1141 {
1142 // workaround since InMessage don't provide native support for readObject()
1143 ObjectInputStream ois = new ObjectInputStream(this);
1144 return ois.readObject();
1145 }
1146
1147 ////////////////////////////////////////////////////////////////////////////////////////////
1148 // Public methods
1149 ////////////////////////////////////////////////////////////////////////////////////////////
1150
1151 /**
1152 * Move the current position in the stream to the specified
1153 * <code>position</code>. <p>
1154 *
1155 * @exception IndexOutOfBoundsException
1156 * Raised if the new position in the message exceeds the maximum
1157 * length.
1158 */
1159 public void seek(int newpos)
1160 {
1161 if (newpos > byteCount) {
1162 throw new IndexOutOfBoundsException("New position (" + newpos
1163 + ") exceeds maximum length:" + byteCount);
1164 }
1165 fragment = newpos / payload;
1166 offset = header + newpos % payload;
1167 position = newpos;
1168 buffer = fragments[fragment];
1169 }
1170
1171 /**
1172 * Read a short integer from the stream at the current offset.
1173 * <p>
1174 * If the message ends before having the possibility to read the
1175 * value, a <tt>ArrayIndexOutOfBoundsException</tt> is thrown.
1176 */
1177 private int read2()
1178 throws IOException
1179 {
1180 position += 2;
1181 if (position > byteCount) {
1182 position = byteCount;
1183 throw new EOFException("pos="+position+" > byteCount="+byteCount);
1184 }
1185 if (offset + 2 > endpayload) {
1186 readTemp(2);
1187 return ((temp[0] << 8) & 0xFF00) | (temp[1] & 0xFF);
1188 } else {
1189 return ((buffer[offset++] << 8) & 0xFF00) | (buffer[offset++] & 0xFF);
1190 }
1191 }
1192
1193
1194 /**
1195 * Read an integer from the stream at the current offset.
1196 * <p>
1197 *
1198 * If the message ends before all four bytes of the <code>int</code>
1199 * value can be read, a <code>EOFException</code> is thrown.
1200 */
1201 private int read4()
1202 throws IOException
1203 {
1204 position += 4;
1205 if (position > byteCount) {
1206 position = byteCount;
1207 throw new EOFException("pos: "+ position + ", byteCount: " + byteCount + ", offset: " + offset);
1208 }
1209 if (offset + 4 > endpayload) {
1210 readTemp(4);
1211 return ((temp[0] << 24) & 0xFF000000) | ((temp[1] << 16)
1212 & 0xFF0000) | ((temp[2] << 8) & 0xFF00) | (temp[3] & 0xFF);
1213 } else {
1214 return ((buffer[offset++] << 24) & 0xFF000000) | ((buffer[offset++] << 16)
1215 & 0xFF0000) | ((buffer[offset++] << 8) & 0xFF00) | (buffer[offset++] & 0xFF);
1216 }
1217 }
1218
1219
1220 /**
1221 * Read a long integer from the stream at the current offset.
1222 * <p>
1223 * If the message ends before having the possibility to read the
1224 * value, a <tt>ArrayIndexOutOfBoundsException</tt> is thrown.
1225 */
1226 private long read8()
1227 throws IOException
1228 {
1229 int v1, v2;
1230
1231 position += 8;
1232 if (position > byteCount) {
1233 position = byteCount;
1234 throw new EOFException();
1235 }
1236 if (offset + 8 > endpayload) {
1237 readTemp(8);
1238 v1 = ((temp[0] << 24) & 0xFF000000) | ((temp[1] << 16) & 0xFF0000) |
1239 ((temp[2] << 8) & 0xFF00) | (temp[3] & 0xFF);
1240 v2 = ((temp[4] << 24) & 0xFF000000) | ((temp[5] << 16) & 0xFF0000) |
1241 ((temp[6] << 8) & 0xFF00) | (temp[7] & 0xFF);
1242 } else {
1243 v1 = ((buffer[offset++] << 24) & 0xFF000000) | ((buffer[offset++] << 16)
1244 & 0xFF0000) | ((buffer[offset++] << 8) & 0xFF00) | (buffer[offset++] & 0xFF);
1245 v2 = ((buffer[offset++] << 24) & 0xFF000000) | ((buffer[offset++] << 16)
1246 & 0xFF0000) | ((buffer[offset++] << 8) & 0xFF00) | (buffer[offset++] & 0xFF);
1247 }
1248 return ((long)(v1) << 32) | (v2 & 0xFFFFFFFFL);
1249 }
1250
1251
1252 /**
1253 * Compares the bytes contained in this InMessage with those
1254 * contained in another InMessage. Starting from position
1255 * <t>pos1</t> in this message and from position <t>pos2</t>
1256 * from the other message, compares <t>len</t> bytes and
1257 * returns false if they differ.
1258 *
1259 * @param pos1 starting position in this message
1260 * @param msg the meessage to be compared
1261 * @param pos2 starting position in <t>msg</t>
1262 * @param len the number of bytes to be compared
1263 */
1264 public boolean compare(int pos1, InMessage msg, int pos2, int len)
1265 {
1266 /*
1267 * Verify that comparison arguments are regular
1268 * FIX 16/11/2000: Fixed the check by removing unnecessary (and wrong)
1269 * comparison between pos2+len and byteCount
1270 */
1271 if (pos1 >= byteCount || pos2 >= msg.byteCount)
1272 throw new IllegalArgumentException("Pos1: " + pos1 + "Pos2: " + pos2 + " byteCount: " + byteCount + " len " + len);
1273
1274 /*
1275 * Compute start positions for comparison
1276 */
1277 int frag1 = pos1 / payload;
1278 int frag2 = pos2 / msg.payload;
1279 int index1 = header + pos1 % payload;
1280 int index2 = msg.header + pos2 % msg.payload;
1281
1282 for (int i = 0; i < len; i++) {
1283 /*
1284 * Stop comparison if bytes in byte arrays differ
1285 */
1286 if (fragments[frag1][index1] != msg.fragments[frag2][index2])
1287 return false;
1288
1289 /*
1290 * Go to the next byte, moving to the next fragment if necessary
1291 */
1292 index1++;
1293 index2++;
1294 if (index1 == endpayload) {
1295 index1 = header;
1296 frag1++;
1297 }
1298 if (index2 == msg.endpayload) {
1299 index2 = header;
1300 frag2++;
1301 }
1302 }
1303
1304 /*
1305 * The byte arrays are equal.
1306 */
1307 return true;
1308 }
1309
1310
1311 ////////////////////////////////////////////////////////////////////////////////////////////
1312 // Package methods
1313 ////////////////////////////////////////////////////////////////////////////////////////////
1314
1315 /**
1316 * Insert a new fragment in the message whose payload field is
1317 * <tt>blen</tt> bytes long.
1318 */
1319 public void insert(byte[] data, int blen)
1320 {
1321 byteCount += blen;
1322 if (fragmentCount == fragments.length) {
1323 // Double the space
1324 byte[][] btemp = new byte[fragments.length*2][];
1325 System.arraycopy(fragments, 0, btemp, 0, fragments.length);
1326 fragments = btemp;
1327 }
1328 fragments[fragmentCount] = data;
1329 if (fragmentCount == 0)
1330 buffer = fragments[0];
1331 fragmentCount++;
1332 }
1333
1334
1335 /**
1336 * Returns the total number of bytes stored in this message output stream.
1337 */
1338 public int getByteCount()
1339 {
1340 return byteCount;
1341 }
1342
1343
1344 /**
1345 * Returns the current position in the message.
1346 */
1347 public int getPosition()
1348 {
1349 return position;
1350 }
1351
1352
1353 /**
1354 * Returns the number of fragments composing the message.
1355 */
1356 public int getFragmentCount()
1357 {
1358 return fragmentCount;
1359 }
1360
1361
1362 public byte[][] getFragments()
1363 {
1364 return fragments;
1365 }
1366
1367
1368 /**
1369 * Returns the length of the payload field for this message.
1370 */
1371 public int getPayload()
1372 {
1373 return payload;
1374 }
1375
1376 /**
1377 * Returns the length of the payload of the last fragment composing
1378 * the message.
1379 */
1380 public int getLastPayload()
1381 {
1382 if (byteCount % payload == 0)
1383 return payload;
1384 else
1385 return (byteCount % payload);
1386 }
1387
1388 /**
1389 *
1390 */
1391 public int getHeader()
1392 {
1393 return header;
1394 }
1395
1396 /**
1397 *
1398 */
1399 public int getTrailer()
1400 {
1401 return trailer;
1402 }
1403
1404
1405 ////////////////////////////////////////////////////////////////////////////////////////////
1406 // Private methods
1407 ////////////////////////////////////////////////////////////////////////////////////////////
1408
1409 /**
1410 * Copy <tt>size</tt> bytes in the temp array; this function is invoked when
1411 * the bytes read are part in fragment and part in the following one.
1412 */
1413 private void readTemp(int size)
1414 {
1415 for (int i = 0; i < size; i++) {
1416 if (offset == endpayload) {
1417 fragment++;
1418 buffer = fragments[fragment];
1419 offset = header;
1420 }
1421 temp[i] = buffer[offset++];
1422 }
1423 }
1424
1425
1426 ////////////////////////////////////////////////////////////////////////////////////////////
1427 // Object methods
1428 ////////////////////////////////////////////////////////////////////////////////////////////
1429
1430 /**
1431 * Returns a string representation of this object
1432 */
1433 public String toString()
1434 {
1435 StringBuilder buf = new StringBuilder();
1436 buf.append("[InMessage: byteCnt=");
1437 buf.append(byteCount);
1438 buf.append(", payload=");
1439 buf.append(payload);
1440 buf.append(", header=");
1441 buf.append(header);
1442 buf.append(", trailer=");
1443 buf.append(trailer);
1444 buf.append(", totlen=");
1445 buf.append(totlen);
1446 buf.append(", endpayload=");
1447 buf.append(endpayload);
1448 buf.append(", position=");
1449 buf.append(position);
1450 buf.append(", fragment=");
1451 buf.append(fragment);
1452 buf.append(", offset=");
1453 buf.append(offset);
1454 buf.append(", fragmentCount=");
1455 buf.append(fragmentCount);
1456 buf.append(", marked=");
1457 buf.append(marked);
1458 buf.append("]");
1459
1460 if (ConfigManager.logMsgContent) {
1461 buf.append(": ");
1462 } else {
1463 // Return without logging message content
1464 return buf.toString();
1465 }
1466
1467 for (int i = 0; i < fragmentCount-1; i++) {
1468 for (int j = header; j < endpayload; j++) {
1469 buf.append(" ");
1470 if (j-header+1 == position) {
1471 buf.append(">> ");
1472 }
1473 buf.append(Util.byte2str(fragments[i][j]));
1474 if (j-header+1 == position) {
1475 buf.append(" << ");
1476 }
1477 }
1478 }
1479
1480 if (fragmentCount > 0) {
1481 /*
1482 * Print the content of the fragments, assuming that the InMessage
1483 * object has been initialized; that is fragmentCount > 0.
1484 */
1485 int stop = byteCount - (fragmentCount-1)*payload + header;
1486 int lastFragStop = getLastPayload() + header;
1487
1488 for (int j = header; j < stop; j++) {
1489 buf.append(" ");
1490 if (j-header+1 == position) {
1491 buf.append(">> ");
1492 }
1493 buf.append(Util.byte2str(fragments[fragmentCount-1][j]));
1494 if (j-header+1 == position) {
1495 buf.append(" << ");
1496 }
1497 }
1498 buf.append(" --- stop=");
1499 buf.append(stop);
1500 buf.append(", lastFragStop=");
1501 buf.append(lastFragStop);
1502 }
1503
1504 return buf.toString();
1505 }
1506
1507 } // END InMessage