001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.cldchi.tools.memoryprofiler.jdwp;
028:
029: import java.io.*;
030:
031: /**
032: * This class encapsulates dynamic linear byte array that is handled
033: * as stream. This class is a superclass for <code>Packet</code> that
034: * encapsulates JDWP packet. <code>ByteBuffer</code> class contains
035: * all the generic (not JDWP specific) methods to work with JDWP packet-kind
036: * structures.
037: *
038: * @see jdwp.Packet
039: * @see jdwp.Command
040: * @see jdwp.Reply
041: */
042: class ByteBuffer {
043:
044: /**
045: * The size that buffer increases if necessary
046: */
047: private int Delta;
048:
049: /**
050: * The current size of filled part of the buffer
051: */
052: private int CurrentSize;
053:
054: /**
055: * The index of the byte that will be read next
056: */
057: int parseOffset;
058:
059: /**
060: * The contents of the buffer
061: */
062: protected byte[] bytes;
063:
064: /**
065: * Constructs a new <code>ByteBuffer</code> object with default
066: * initial size and delta.
067: *
068: * @see ByteBuffer#Delta
069: */
070: public ByteBuffer() {
071: this (128, 128);
072: }
073:
074: /**
075: * Constructs a new <code>ByteBuffer</code> object with specified
076: * initial size and delta.
077: *
078: * @param InitialSize initial size of the buffer
079: * @param Delta the size that buffer increases if necessary
080: *
081: * @see ByteBuffer#Delta
082: */
083: public ByteBuffer(int InitialSize, int Delta) {
084: if (Delta <= 0)
085: Delta = 16;
086: this .Delta = Delta;
087: CurrentSize = 0;
088: bytes = new byte[InitialSize];
089: parseOffset = 0;
090: }
091:
092: /**
093: * Returns length of the filled part of the buffer
094: *
095: * @return length of the filled part of the buffer
096: */
097: public int length() {
098: return CurrentSize;
099: }
100:
101: /**
102: * Checks if the buffer is large enough to place the
103: * specified number of bytes. If no, the buffer is
104: * automatically increased to necessary size.
105: *
106: * @param Space a number of bytes that it's necessary to place
107: */
108: private void checkSpace(int Space) {
109: if (bytes.length >= CurrentSize + Space)
110: return;
111:
112: int NewSize = bytes.length;
113: while (NewSize < CurrentSize + Space)
114: NewSize = NewSize + Delta;
115:
116: byte[] NewData = new byte[NewSize];
117:
118: for (int i = 0; i < CurrentSize; i++)
119: NewData[i] = bytes[i];
120:
121: bytes = NewData;
122: }
123:
124: /**
125: * Stores byte at the specified index in the buffer. If specified index
126: * is outside the filled area of the buffer <code>BoundException</code>
127: * is raised.
128: *
129: * @param off index where byte should be placed
130: * @param b byte that should be placed
131: * @throws BoundException if specified index is outside the filled area of
132: * the buffer
133: */
134: public void putByte(int off, int b) throws BoundException {
135: if ((off < 0) || (off >= CurrentSize))
136: throw new BoundException();
137:
138: bytes[off] = (byte) (b & 0xFF);
139: }
140:
141: /**
142: * Stores a number of bytes at the specified location of the buffer. If
143: * at least one byte is to be stored outside the already filled area of the
144: * buffer <code>BoundException</code> is raised.
145: *
146: * @param off index in the buffer where the first byte should be stored
147: * @param b array of bytes whose values should be stored in the buffer
148: * @param start the first index in the array of bytes that should be stored
149: * @param len the number of bytes that should be stored in the buffer
150: *
151: * @throws BoundException if at least one byte should be stored outside the
152: * already filled area of the buffer
153: */
154: public void putBytes(int off, byte[] b, int start, int len)
155: throws BoundException {
156: for (int i = 0; i < len; i++)
157: putByte(off++, b[start++]);
158: }
159:
160: /**
161: * Stores an integer value (short, int, long) in the buffer. The value is
162: * stored in Java (little endian) format. If at least one byte of the value
163: * is to be placed outside the alrady filled area of the buffer
164: * <code>BoundException</code> is raised
165: *
166: * @param off index where the first byte of the value should be stored
167: * @param l a value that should be stored
168: * @param count length of the value (2 for short, 4 for int, 8 for long)
169: *
170: * @throws BoundException if at least one byte of the value should be
171: * stored outside the already filled area of the buffer
172: */
173: public void putID(int off, long l, int count) throws BoundException {
174:
175: if ((count <= 0) || (count > 8))
176: throw new BoundException();
177:
178: int shift = (count - 1) * 8;
179:
180: for (int i = 0; i < count; i++) {
181: putByte(off++, (int) ((l >>> shift) & 0xFF));
182: shift = shift - 8;
183: }
184: }
185:
186: /**
187: * Stores <code>int</code> value in the buffer starting from specified
188: * index. The value is stored in little endian format.
189: * If at least one byte of the value
190: * is to be placed outside the alrady filled area of the buffer
191: * <code>BoundException</code> is raised.
192: *
193: * @param off index where the first byte of the value should be stored
194: * @param b the value that should be stored
195: * @throws BoundException if at least one byte of the value should be
196: * stored outside the already filled area of the buffer
197: */
198: public void putInt(int off, int b) throws BoundException {
199: putID(off, b, 4);
200: }
201:
202: /**
203: * Stores <code>short</code> value in the buffer starting from specified
204: * index. The value is stored in little endian format.
205: * If at least one byte of the value
206: * is to be placed outside the alrady filled area of the buffer
207: * <code>BoundException</code> is raised.
208: *
209: * @param off index where the first byte of the value should be stored
210: * @param b the value that should be stored
211: * @throws BoundException if at least one byte of the value should be
212: * stored outside the already filled area of the buffer
213: */
214: public void putShort(int off, int b) throws BoundException {
215: putID(off, b, 2);
216: }
217:
218: /**
219: * Stores <code>long</code> value in the buffer starting from specified
220: * index. The value is stored in little endian format.
221: * If at least one byte of the value
222: * is to be placed outside the alrady filled area of the buffer
223: * <code>BoundException</code> is raised.
224: *
225: * @param off index where the first byte of the value should be stored
226: * @param b the value that should be stored
227: * @throws BoundException if at least one byte of the value should be
228: * stored outside the already filled area of the buffer
229: */
230: public void putLong(int off, long l) throws BoundException {
231: putID(off, l, 8);
232: }
233:
234: /**
235: * Adds byte to the end of the filled part of the buffer.
236: * If there is no place to
237: * store this data the buffer is automatically increased.
238: *
239: * @param b a byte to be appended
240: */
241: public void addByte(int b) {
242: checkSpace(1);
243:
244: int where = CurrentSize;
245: CurrentSize++;
246:
247: try {
248: putByte(where, b);
249: } catch (BoundException e) {
250: }
251: ;
252: }
253:
254: /**
255: * Adds a number of bytes to the end of the filled part of the buffer.
256: * If there is no place to
257: * store this data the buffer is automatically increased.
258: *
259: * @param b a byte array the data should be appended from
260: * @param start the index in the byte array of the first byte that
261: * should be appended
262: * @param len a number of bytes that should be added to the buffer
263: */
264: public void addBytes(byte[] b, int start, int len) {
265: checkSpace(len);
266:
267: int where = CurrentSize;
268: CurrentSize = CurrentSize + len;
269:
270: try {
271: putBytes(where, b, start, len);
272: } catch (BoundException e) {
273: }
274: ;
275: }
276:
277: /**
278: * Adds an integer value (int, short, long)
279: * to the end of the filled part of the buffer.
280: * If there is no place to
281: * store this data the buffer is automatically increased.
282: * The value is stored in the little endian format.
283: *
284: * @param l a value to be appended
285: * @param count a size of the value in bytes (2 for <code>short</code>,
286: * 4 for <code>int</code>, 8 for <code>long</code>)
287: */
288: public void addID(long l, int count) {
289: checkSpace(count);
290:
291: int where = CurrentSize;
292: CurrentSize = CurrentSize + count;
293:
294: try {
295: putID(where, l, count);
296: } catch (BoundException e) {
297: }
298: ;
299: }
300:
301: /**
302: * Adds <code>int</code> to the end of the filled part of the buffer.
303: * If there is no place to
304: * store this data the buffer is automatically increased.
305: * The value is stored in little endian format.
306: *
307: * @param b a value to be appended
308: */
309: public void addInt(int b) {
310: addID(b, 4);
311: }
312:
313: /**
314: * Adds <code>short</code> to the end of the filled part of the buffer.
315: * If there is no place to
316: * store this data the buffer is automatically increased.
317: * The value is stored in little endian format.
318: *
319: * @param b a value to be appended
320: */
321: public void addShort(int b) {
322: addID(b, 2);
323: }
324:
325: /**
326: * Adds <code>long</code> to the end of the filled part of the buffer.
327: * If there is no place to
328: * store this data the buffer is automatically increased.
329: * The value is stored in little endian format.
330: *
331: * @param b a value to be appended
332: */
333: public void addLong(long l) {
334: addID(l, 8);
335: }
336:
337: /**
338: * Adds <code>String</code> to the end of the filled part of the buffer.
339: * If there is no place to
340: * store this data the buffer is automatically increased.
341: * The value is stored in UTF-8 format.
342: *
343: * @param s a string to be appended
344: */
345: public void addString(String s) {
346: ByteArrayOutputStream baos = new ByteArrayOutputStream();
347: DataOutputStream dos = new DataOutputStream(baos);
348: try {
349: dos.writeUTF(s);
350: } catch (IOException e) {
351: System.err.println("Error creating the UTF-8 sring");
352: return;
353: }
354: byte[] buf = baos.toByteArray();
355: int len = ((buf[0] << 8) & 0xFF00) | (buf[1] & 0xFF);
356: addByte(0);
357: addByte(0);
358: addBytes(buf, 0, len + 2);
359: }
360:
361: /**
362: * Moves the reading marker to the beginning of the buffer.
363: * Typically the process of reading data from the buffer is
364: * organized as follows: at first move the reading marker to the
365: * some start position (for example, in the beginning of the buffer)
366: * and then consequently read the data using get methods (for example,
367: * <code>getInt()</code>). So the process of reading data from the byte
368: * buffer is looks like reading data from generic stream (for example,
369: * from file).
370: */
371: public void resetParser() {
372: parseOffset = 0;
373: }
374:
375: /**
376: * Move the reading marker to the specified position.
377: *
378: * @param i index of the buffer where reading marker should point
379: */
380: public void resetParser(int i) {
381: parseOffset = i;
382: }
383:
384: /**
385: * Checks if reading marker points to the end of filled data of the
386: * buffer. This method is analogue of <code>eof</code> method for the
387: * files.
388: *
389: * @return <code>true</code> if reading marker points to the end of
390: * filled data, <code>false</code> otherwise
391: */
392: public boolean isParsed() {
393: return (parseOffset == CurrentSize);
394: }
395:
396: /**
397: * Tries to read next byte from the buffer. Byte is to read is one
398: * that is pointed by reading marker. After completing the operation
399: * the reading marker is incremented.
400: *
401: * @return current byte from the buffer
402: * @throws BoundException if byte to be read is outside the filled area
403: */
404: public int getByte() throws BoundException {
405: if (parseOffset >= CurrentSize)
406: throw new BoundException();
407:
408: return (int) (bytes[parseOffset++] & 0xFF);
409: }
410:
411: /**
412: * Tries to read next integer value (short, int, long) from the buffer.
413: * Value is read is one
414: * that is pointed by reading marker. After completing the operation
415: * the reading marker is incremented. The value is stored in little endian
416: * format.
417: *
418: * @param count a size in bytes of integer value (2 for <code>short</code>,
419: * 4 for <code>int</code>, 8 for <code>long</code>)
420: * @return current integer value from the buffer
421: * @throws BoundException if value to be read is outside the filled area
422: */
423: public long getID(int count) throws BoundException {
424:
425: if ((count <= 0) || (count > 8))
426: throw new BoundException();
427:
428: long l = 0;
429: for (int i = 0; i < count; i++)
430: l = (l * 0x100) + getByte();
431: return l;
432: }
433:
434: /**
435: * Tries to read next <code>int</code> from the buffer.
436: * Value is read is one
437: * that is pointed by reading marker. After completing the operation
438: * the reading marker is incremented. The value is stored in little endian
439: * format.
440: *
441: * @return current <code>int</code> value from the buffer
442: * @throws BoundException if value to be read is outside the filled area
443: */
444: public int getInt() throws BoundException {
445: return (int) getID(4);
446: }
447:
448: /**
449: * Tries to read next <code>short</code> from the buffer.
450: * Value is read is one
451: * that is pointed by reading marker. After completing the operation
452: * the reading marker is incremented. The value is stored in little endian
453: * format.
454: *
455: * @return current <code>short</code> value from the buffer
456: * @throws BoundException if value to be read is outside the filled area
457: */
458: public int getShort() throws BoundException {
459: return (int) getID(2);
460: }
461:
462: /**
463: * Tries to read next <code>long</code> from the buffer.
464: * Value is read is one
465: * that is pointed by reading marker. After completing the operation
466: * the reading marker is incremented. The value is stored in little endian
467: * format.
468: *
469: * @return current <code>long</code> value from the buffer
470: * @throws BoundException if value to be read is outside the filled area
471: */
472: public long getLong() throws BoundException {
473: return getID(8);
474: }
475:
476: /**
477: * Tries to read next string from the buffer.
478: * Value is read is one
479: * that is pointed by reading marker. After completing the operation
480: * the reading marker is incremented. The value is stored in UTF-8
481: * format.
482: *
483: * @return current UTF-8 string from the buffer
484: * @throws BoundException if value to be read is outside the filled area
485: */
486: public String getString() throws BoundException {
487: int l = getInt();
488: if (l < 0)
489: throw new BoundException();
490: if (l == 0)
491: return "";
492:
493: byte[] d = new byte[l + 2];
494: d[0] = (byte) ((l >> 8) & 0xFF);
495: d[1] = (byte) (l & 0xFF);
496: for (int i = 0; i < l; i++) {
497: d[i + 2] = (byte) getByte();
498: }
499:
500: ByteArrayInputStream bais = new ByteArrayInputStream(d);
501: DataInputStream dis = new DataInputStream(bais);
502:
503: try {
504: String res = dis.readUTF();
505: return res;
506: } catch (IOException e) {
507: throw new BoundException(e.getMessage());
508: }
509: }
510:
511: /**
512: * Tries to read a single byte from the buffer. Byte is to read is
513: * located at specified index.
514: *
515: * @param off the index of the buffer where the required byte is located
516: * @return a required byte from the buffer
517: * @throws BoundException if byte to be read is outside the filled area
518: */
519: public int getByte(int off) throws BoundException {
520: int old_offset = parseOffset;
521: parseOffset = off;
522:
523: int r = getByte();
524: parseOffset = old_offset;
525: return r;
526: }
527:
528: /**
529: * Tries to read an integer value (short, int, long) from the buffer.
530: * Value is to read is located at specified index. The value is stored in
531: * little endian format.
532: *
533: * @param off the index of the buffer where the required value is located
534: * @param count a size in bytes of integer value (2 for <code>short</code>,
535: * 4 for <code>int</code>, 8 for <code>long</code>)
536: * @return a required value from the buffer
537: * @throws BoundException if value to be read is outside the filled area
538: */
539: public long getID(int off, int count) throws BoundException {
540: int old_offset = parseOffset;
541: parseOffset = off;
542:
543: long l = getID(count);
544: parseOffset = old_offset;
545: return l;
546: }
547:
548: /**
549: * Tries to read an <code>int</code> value from the buffer.
550: * Value is to read is located at specified index. The value is stored in
551: * little endian format.
552: *
553: * @param off the index of the buffer where the required value is located
554: * @return a required value from the buffer
555: * @throws BoundException if value to be read is outside the filled area
556: */
557: public int getInt(int off) throws BoundException {
558: return (int) getID(off, 4);
559: }
560:
561: /**
562: * Tries to read an <code>short</code> value from the buffer.
563: * Value is to read is located at specified index. The value is stored in
564: * little endian format.
565: *
566: * @param off the index of the buffer where the required value is located
567: * @return a required value from the buffer
568: * @throws BoundException if value to be read is outside the filled area
569: */
570: public int getShort(int off) throws BoundException {
571: return (int) getID(off, 2);
572: }
573:
574: /**
575: * Tries to read an <code>long</code> value from the buffer.
576: * Value is to read is located at specified index. The value is stored in
577: * little endian format.
578: *
579: * @param off the index of the buffer where the required value is located
580: * @return a required value from the buffer
581: * @throws BoundException if value to be read is outside the filled area
582: */
583: public long getLong(int off) throws BoundException {
584: return getID(off, 8);
585: }
586:
587: /**
588: * Tries to read a string from the buffer.
589: * The string is to read is located at specified index. The value is stored
590: * in UTF-8 format.
591: *
592: * @param off the index of the buffer where the required string is located
593: * @return a required string from the buffer
594: * @throws BoundException if string to be read is outside the filled area
595: */
596: public String getString(int off) throws BoundException {
597:
598: int old_offset = parseOffset;
599: parseOffset = off;
600:
601: String s = getString();
602:
603: parseOffset = old_offset;
604:
605: return s;
606: }
607:
608: /**
609: * Deletes a few bytes from the beginning of the buffer and copies the
610: * last part to the beginning of the buffer.
611: *
612: * @param count a number of bytes to be deleted from the head of the buffer
613: */
614: public void deleteBytes(int count) {
615: int j = 0;
616: while (count < CurrentSize)
617: bytes[j++] = bytes[count++];
618: CurrentSize = j;
619: }
620:
621: /**
622: * Clears the buffer by setting the size of the filled area to zero.
623: * The reading marker's position is not affected by this method so if
624: * the reading marker was not positioned to the beginning of the buffer
625: * it'll become to be in invalid position.
626: */
627: public void resetBuffer() {
628: CurrentSize = 0;
629: }
630:
631: /**
632: * Returns string representation of the contents of the buffer from
633: * the specified index. This method is invoked by KJDB when JDWP reply
634: * packet is not received (usually it's a fatal error)and this information
635: * is useful for localizing the problem.
636: *
637: * @param start the first index that should be printed
638: * @return string representation of a part of the byte buffer
639: *
640: * @see jdwp.BackEndTest#printReplies
641: */
642: public String toString(int start) {
643:
644: String Result = "", HexLine = "", DisplayLine = "";
645:
646: int j = 0;
647:
648: for (int i = start; i < length(); i++) {
649:
650: HexLine = HexLine + Tools.Hex(bytes[i], 2) + " ";
651:
652: if (bytes[i] >= 0 && bytes[i] < 32)
653: DisplayLine = DisplayLine + ".";
654: else
655: DisplayLine = DisplayLine + new String(bytes, i, 1);
656:
657: if ((i == length() - 1) || (((i - start) & 0x0F) == 0x0F)) {
658: Result = Result + Tools.Hex(j, 4) + ": "
659: + Tools.PadR(HexLine, 48) + " " + DisplayLine
660: + "\n";
661: HexLine = "";
662: DisplayLine = "";
663: j = j + 16;
664: }
665: }
666: return Result;
667: }
668:
669: /**
670: * Returns string representation of the object. Currently this method is
671: * not used by KJDB but it may be useful for debugging purposes.
672: *
673: * @return string representation of the object
674: */
675: public String toString() {
676: return toString(0);
677: }
678:
679: }
|