001: /*
002: Copyright (C) 2002-2005 MySQL AB
003:
004: This program is free software; you can redistribute it and/or modify
005: it under the terms of version 2 of the GNU General Public License as
006: published by the Free Software Foundation.
007:
008: There are special exceptions to the terms and conditions of the GPL
009: as it is applied to this software. View the full text of the
010: exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
011: software distribution.
012:
013: This program is distributed in the hope that it will be useful,
014: but WITHOUT ANY WARRANTY; without even the implied warranty of
015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: GNU General Public License for more details.
017:
018: You should have received a copy of the GNU General Public License
019: along with this program; if not, write to the Free Software
020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021:
022:
023:
024: */
025: package com.mysql.jdbc;
026:
027: import java.io.UnsupportedEncodingException;
028:
029: import java.nio.ByteBuffer;
030:
031: import java.sql.SQLException;
032:
033: /**
034: * Buffer contains code to read and write packets from/to the MySQL server.
035: *
036: * @version $Id: Buffer.java 6445 2007-06-07 15:25:59Z mmatthews $
037: * @author Mark Matthews
038: */
039: class Buffer {
040: static final int MAX_BYTES_TO_DUMP = 512;
041:
042: static final int NO_LENGTH_LIMIT = -1;
043:
044: static final long NULL_LENGTH = -1;
045:
046: private int bufLength = 0;
047:
048: private byte[] byteBuffer;
049:
050: private int position = 0;
051:
052: protected boolean wasMultiPacket = false;
053:
054: Buffer(byte[] buf) {
055: this .byteBuffer = buf;
056: setBufLength(buf.length);
057: }
058:
059: Buffer(int size) {
060: this .byteBuffer = new byte[size];
061: setBufLength(this .byteBuffer.length);
062: this .position = MysqlIO.HEADER_LENGTH;
063: }
064:
065: final void clear() {
066: this .position = MysqlIO.HEADER_LENGTH;
067: }
068:
069: final void dump() {
070: dump(getBufLength());
071: }
072:
073: final String dump(int numBytes) {
074: return StringUtils.dumpAsHex(getBytes(0,
075: numBytes > getBufLength() ? getBufLength() : numBytes),
076: numBytes > getBufLength() ? getBufLength() : numBytes);
077: }
078:
079: final String dumpClampedBytes(int numBytes) {
080: int numBytesToDump = numBytes < MAX_BYTES_TO_DUMP ? numBytes
081: : MAX_BYTES_TO_DUMP;
082:
083: String dumped = StringUtils.dumpAsHex(getBytes(0,
084: numBytesToDump > getBufLength() ? getBufLength()
085: : numBytesToDump),
086: numBytesToDump > getBufLength() ? getBufLength()
087: : numBytesToDump);
088:
089: if (numBytesToDump < numBytes) {
090: return dumped + " ....(packet exceeds max. dump length)";
091: }
092:
093: return dumped;
094: }
095:
096: final void dumpHeader() {
097: for (int i = 0; i < MysqlIO.HEADER_LENGTH; i++) {
098: String hexVal = Integer.toHexString(readByte(i) & 0xff);
099:
100: if (hexVal.length() == 1) {
101: hexVal = "0" + hexVal; //$NON-NLS-1$
102: }
103:
104: System.out.print(hexVal + " "); //$NON-NLS-1$
105: }
106: }
107:
108: final void dumpNBytes(int start, int nBytes) {
109: StringBuffer asciiBuf = new StringBuffer();
110:
111: for (int i = start; (i < (start + nBytes))
112: && (i < getBufLength()); i++) {
113: String hexVal = Integer.toHexString(readByte(i) & 0xff);
114:
115: if (hexVal.length() == 1) {
116: hexVal = "0" + hexVal; //$NON-NLS-1$
117: }
118:
119: System.out.print(hexVal + " "); //$NON-NLS-1$
120:
121: if ((readByte(i) > 32) && (readByte(i) < 127)) {
122: asciiBuf.append((char) readByte(i));
123: } else {
124: asciiBuf.append("."); //$NON-NLS-1$
125: }
126:
127: asciiBuf.append(" "); //$NON-NLS-1$
128: }
129:
130: System.out.println(" " + asciiBuf.toString()); //$NON-NLS-1$
131: }
132:
133: final void ensureCapacity(int additionalData) throws SQLException {
134: if ((this .position + additionalData) > getBufLength()) {
135: if ((this .position + additionalData) < this .byteBuffer.length) {
136: // byteBuffer.length is != getBufLength() all of the time
137: // due to re-using of packets (we don't shrink them)
138: //
139: // If we can, don't re-alloc, just set buffer length
140: // to size of current buffer
141: setBufLength(this .byteBuffer.length);
142: } else {
143: //
144: // Otherwise, re-size, and pad so we can avoid
145: // allocing again in the near future
146: //
147: int newLength = (int) (this .byteBuffer.length * 1.25);
148:
149: if (newLength < (this .byteBuffer.length + additionalData)) {
150: newLength = this .byteBuffer.length
151: + (int) (additionalData * 1.25);
152: }
153:
154: if (newLength < this .byteBuffer.length) {
155: newLength = this .byteBuffer.length + additionalData;
156: }
157:
158: byte[] newBytes = new byte[newLength];
159:
160: System.arraycopy(this .byteBuffer, 0, newBytes, 0,
161: this .byteBuffer.length);
162: this .byteBuffer = newBytes;
163: setBufLength(this .byteBuffer.length);
164: }
165: }
166: }
167:
168: /**
169: * Skip over a length-encoded string
170: *
171: * @return The position past the end of the string
172: */
173: public int fastSkipLenString() {
174: long len = this .readFieldLength();
175:
176: this .position += len;
177:
178: return (int) len;
179: }
180:
181: public void fastSkipLenByteArray() {
182: long len = this .readFieldLength();
183:
184: if (len == NULL_LENGTH || len == 0) {
185: return;
186: }
187:
188: this .position += len;
189: }
190:
191: protected final byte[] getBufferSource() {
192: return this .byteBuffer;
193: }
194:
195: int getBufLength() {
196: return this .bufLength;
197: }
198:
199: /**
200: * Returns the array of bytes this Buffer is using to read from.
201: *
202: * @return byte array being read from
203: */
204: public byte[] getByteBuffer() {
205: return this .byteBuffer;
206: }
207:
208: final byte[] getBytes(int len) {
209: byte[] b = new byte[len];
210: System.arraycopy(this .byteBuffer, this .position, b, 0, len);
211: this .position += len; // update cursor
212:
213: return b;
214: }
215:
216: /*
217: * (non-Javadoc)
218: *
219: * @see com.mysql.jdbc.Buffer#getBytes(int, int)
220: */
221: byte[] getBytes(int offset, int len) {
222: byte[] dest = new byte[len];
223: System.arraycopy(this .byteBuffer, offset, dest, 0, len);
224:
225: return dest;
226: }
227:
228: int getCapacity() {
229: return this .byteBuffer.length;
230: }
231:
232: public ByteBuffer getNioBuffer() {
233: throw new IllegalArgumentException(Messages
234: .getString("ByteArrayBuffer.0")); //$NON-NLS-1$
235: }
236:
237: /**
238: * Returns the current position to write to/ read from
239: *
240: * @return the current position to write to/ read from
241: */
242: public int getPosition() {
243: return this .position;
244: }
245:
246: // 2000-06-05 Changed
247: final boolean isLastDataPacket() {
248: return ((getBufLength() < 9) && ((this .byteBuffer[0] & 0xff) == 254));
249: }
250:
251: final long newReadLength() {
252: int sw = this .byteBuffer[this .position++] & 0xff;
253:
254: switch (sw) {
255: case 251:
256: return 0;
257:
258: case 252:
259: return readInt();
260:
261: case 253:
262: return readLongInt();
263:
264: case 254: // changed for 64 bit lengths
265: return readLongLong();
266:
267: default:
268: return sw;
269: }
270: }
271:
272: final byte readByte() {
273: return this .byteBuffer[this .position++];
274: }
275:
276: final byte readByte(int readAt) {
277: return this .byteBuffer[readAt];
278: }
279:
280: final long readFieldLength() {
281: int sw = this .byteBuffer[this .position++] & 0xff;
282:
283: switch (sw) {
284: case 251:
285: return NULL_LENGTH;
286:
287: case 252:
288: return readInt();
289:
290: case 253:
291: return readLongInt();
292:
293: case 254:
294: return readLongLong();
295:
296: default:
297: return sw;
298: }
299: }
300:
301: // 2000-06-05 Changed
302: final int readInt() {
303: byte[] b = this .byteBuffer; // a little bit optimization
304:
305: return (b[this .position++] & 0xff)
306: | ((b[this .position++] & 0xff) << 8);
307: }
308:
309: final int readIntAsLong() {
310: byte[] b = this .byteBuffer;
311:
312: return (b[this .position++] & 0xff)
313: | ((b[this .position++] & 0xff) << 8)
314: | ((b[this .position++] & 0xff) << 16)
315: | ((b[this .position++] & 0xff) << 24);
316: }
317:
318: final byte[] readLenByteArray(int offset) {
319: long len = this .readFieldLength();
320:
321: if (len == NULL_LENGTH) {
322: return null;
323: }
324:
325: if (len == 0) {
326: return Constants.EMPTY_BYTE_ARRAY;
327: }
328:
329: this .position += offset;
330:
331: return getBytes((int) len);
332: }
333:
334: final long readLength() {
335: int sw = this .byteBuffer[this .position++] & 0xff;
336:
337: switch (sw) {
338: case 251:
339: return 0;
340:
341: case 252:
342: return readInt();
343:
344: case 253:
345: return readLongInt();
346:
347: case 254:
348: return readLong();
349:
350: default:
351: return sw;
352: }
353: }
354:
355: // 2000-06-05 Fixed
356: final long readLong() {
357: byte[] b = this .byteBuffer;
358:
359: return ((long) b[this .position++] & 0xff)
360: | (((long) b[this .position++] & 0xff) << 8)
361: | ((long) (b[this .position++] & 0xff) << 16)
362: | ((long) (b[this .position++] & 0xff) << 24);
363: }
364:
365: // 2000-06-05 Changed
366: final int readLongInt() {
367: byte[] b = this .byteBuffer;
368:
369: return (b[this .position++] & 0xff)
370: | ((b[this .position++] & 0xff) << 8)
371: | ((b[this .position++] & 0xff) << 16);
372: }
373:
374: // 2000-06-05 Fixed
375: final long readLongLong() {
376: byte[] b = this .byteBuffer;
377:
378: return (b[this .position++] & 0xff)
379: | ((long) (b[this .position++] & 0xff) << 8)
380: | ((long) (b[this .position++] & 0xff) << 16)
381: | ((long) (b[this .position++] & 0xff) << 24)
382: | ((long) (b[this .position++] & 0xff) << 32)
383: | ((long) (b[this .position++] & 0xff) << 40)
384: | ((long) (b[this .position++] & 0xff) << 48)
385: | ((long) (b[this .position++] & 0xff) << 56);
386: }
387:
388: final int readnBytes() {
389: int sw = this .byteBuffer[this .position++] & 0xff;
390:
391: switch (sw) {
392: case 1:
393: return this .byteBuffer[this .position++] & 0xff;
394:
395: case 2:
396: return this .readInt();
397:
398: case 3:
399: return this .readLongInt();
400:
401: case 4:
402: return (int) this .readLong();
403:
404: default:
405: return 255;
406: }
407: }
408:
409: //
410: // Read a null-terminated string
411: //
412: // To avoid alloc'ing a new byte array, we
413: // do this by hand, rather than calling getNullTerminatedBytes()
414: //
415: final String readString() {
416: int i = this .position;
417: int len = 0;
418: int maxLen = getBufLength();
419:
420: while ((i < maxLen) && (this .byteBuffer[i] != 0)) {
421: len++;
422: i++;
423: }
424:
425: String s = new String(this .byteBuffer, this .position, len);
426: this .position += (len + 1); // update cursor
427:
428: return s;
429: }
430:
431: final String readString(String encoding) throws SQLException {
432: int i = this .position;
433: int len = 0;
434: int maxLen = getBufLength();
435:
436: while ((i < maxLen) && (this .byteBuffer[i] != 0)) {
437: len++;
438: i++;
439: }
440:
441: try {
442: return new String(this .byteBuffer, this .position, len,
443: encoding);
444: } catch (UnsupportedEncodingException uEE) {
445: throw SQLError
446: .createSQLException(
447: Messages.getString("ByteArrayBuffer.1") //$NON-NLS-1$
448: + encoding + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
449: } finally {
450: this .position += (len + 1); // update cursor
451: }
452: }
453:
454: void setBufLength(int bufLengthToSet) {
455: this .bufLength = bufLengthToSet;
456: }
457:
458: /**
459: * Sets the array of bytes to use as a buffer to read from.
460: *
461: * @param byteBuffer
462: * the array of bytes to use as a buffer
463: */
464: public void setByteBuffer(byte[] byteBufferToSet) {
465: this .byteBuffer = byteBufferToSet;
466: }
467:
468: /**
469: * Set the current position to write to/ read from
470: *
471: * @param position
472: * the position (0-based index)
473: */
474: public void setPosition(int positionToSet) {
475: this .position = positionToSet;
476: }
477:
478: /**
479: * Sets whether this packet was part of a multipacket
480: *
481: * @param flag
482: * was this packet part of a multipacket?
483: */
484: public void setWasMultiPacket(boolean flag) {
485: this .wasMultiPacket = flag;
486: }
487:
488: public String toString() {
489: return dumpClampedBytes(getPosition());
490: }
491:
492: public String toSuperString() {
493: return super .toString();
494: }
495:
496: /**
497: * Was this packet part of a multipacket?
498: *
499: * @return was this packet part of a multipacket?
500: */
501: public boolean wasMultiPacket() {
502: return this .wasMultiPacket;
503: }
504:
505: final void writeByte(byte b) throws SQLException {
506: ensureCapacity(1);
507:
508: this .byteBuffer[this .position++] = b;
509: }
510:
511: // Write a byte array
512: final void writeBytesNoNull(byte[] bytes) throws SQLException {
513: int len = bytes.length;
514: ensureCapacity(len);
515: System.arraycopy(bytes, 0, this .byteBuffer, this .position, len);
516: this .position += len;
517: }
518:
519: // Write a byte array with the given offset and length
520: final void writeBytesNoNull(byte[] bytes, int offset, int length)
521: throws SQLException {
522: ensureCapacity(length);
523: System.arraycopy(bytes, offset, this .byteBuffer, this .position,
524: length);
525: this .position += length;
526: }
527:
528: final void writeDouble(double d) throws SQLException {
529: long l = Double.doubleToLongBits(d);
530: writeLongLong(l);
531: }
532:
533: final void writeFieldLength(long length) throws SQLException {
534: if (length < 251) {
535: writeByte((byte) length);
536: } else if (length < 65536L) {
537: ensureCapacity(3);
538: writeByte((byte) 252);
539: writeInt((int) length);
540: } else if (length < 16777216L) {
541: ensureCapacity(4);
542: writeByte((byte) 253);
543: writeLongInt((int) length);
544: } else {
545: ensureCapacity(9);
546: writeByte((byte) 254);
547: writeLongLong(length);
548: }
549: }
550:
551: final void writeFloat(float f) throws SQLException {
552: ensureCapacity(4);
553:
554: int i = Float.floatToIntBits(f);
555: byte[] b = this .byteBuffer;
556: b[this .position++] = (byte) (i & 0xff);
557: b[this .position++] = (byte) (i >>> 8);
558: b[this .position++] = (byte) (i >>> 16);
559: b[this .position++] = (byte) (i >>> 24);
560: }
561:
562: // 2000-06-05 Changed
563: final void writeInt(int i) throws SQLException {
564: ensureCapacity(2);
565:
566: byte[] b = this .byteBuffer;
567: b[this .position++] = (byte) (i & 0xff);
568: b[this .position++] = (byte) (i >>> 8);
569: }
570:
571: // Write a String using the specified character
572: // encoding
573: final void writeLenBytes(byte[] b) throws SQLException {
574: int len = b.length;
575: ensureCapacity(len + 9);
576: writeFieldLength(len);
577: System.arraycopy(b, 0, this .byteBuffer, this .position, len);
578: this .position += len;
579: }
580:
581: // Write a String using the specified character
582: // encoding
583: final void writeLenString(String s, String encoding,
584: String serverEncoding,
585: SingleByteCharsetConverter converter,
586: boolean parserKnowsUnicode, ConnectionImpl conn)
587: throws UnsupportedEncodingException, SQLException {
588: byte[] b = null;
589:
590: if (converter != null) {
591: b = converter.toBytes(s);
592: } else {
593: b = StringUtils.getBytes(s, encoding, serverEncoding,
594: parserKnowsUnicode, conn);
595: }
596:
597: int len = b.length;
598: ensureCapacity(len + 9);
599: writeFieldLength(len);
600: System.arraycopy(b, 0, this .byteBuffer, this .position, len);
601: this .position += len;
602: }
603:
604: // 2000-06-05 Changed
605: final void writeLong(long i) throws SQLException {
606: ensureCapacity(4);
607:
608: byte[] b = this .byteBuffer;
609: b[this .position++] = (byte) (i & 0xff);
610: b[this .position++] = (byte) (i >>> 8);
611: b[this .position++] = (byte) (i >>> 16);
612: b[this .position++] = (byte) (i >>> 24);
613: }
614:
615: // 2000-06-05 Changed
616: final void writeLongInt(int i) throws SQLException {
617: ensureCapacity(3);
618: byte[] b = this .byteBuffer;
619: b[this .position++] = (byte) (i & 0xff);
620: b[this .position++] = (byte) (i >>> 8);
621: b[this .position++] = (byte) (i >>> 16);
622: }
623:
624: final void writeLongLong(long i) throws SQLException {
625: ensureCapacity(8);
626: byte[] b = this .byteBuffer;
627: b[this .position++] = (byte) (i & 0xff);
628: b[this .position++] = (byte) (i >>> 8);
629: b[this .position++] = (byte) (i >>> 16);
630: b[this .position++] = (byte) (i >>> 24);
631: b[this .position++] = (byte) (i >>> 32);
632: b[this .position++] = (byte) (i >>> 40);
633: b[this .position++] = (byte) (i >>> 48);
634: b[this .position++] = (byte) (i >>> 56);
635: }
636:
637: // Write null-terminated string
638: final void writeString(String s) throws SQLException {
639: ensureCapacity((s.length() * 2) + 1);
640: writeStringNoNull(s);
641: this .byteBuffer[this .position++] = 0;
642: }
643:
644: // Write null-terminated string in the given encoding
645: final void writeString(String s, String encoding,
646: ConnectionImpl conn) throws SQLException {
647: ensureCapacity((s.length() * 2) + 1);
648: try {
649: writeStringNoNull(s, encoding, encoding, false, conn);
650: } catch (UnsupportedEncodingException ue) {
651: throw new SQLException(ue.toString(),
652: SQLError.SQL_STATE_GENERAL_ERROR);
653: }
654:
655: this .byteBuffer[this .position++] = 0;
656: }
657:
658: // Write string, with no termination
659: final void writeStringNoNull(String s) throws SQLException {
660: int len = s.length();
661: ensureCapacity(len * 2);
662: System.arraycopy(s.getBytes(), 0, this .byteBuffer,
663: this .position, len);
664: this .position += len;
665:
666: // for (int i = 0; i < len; i++)
667: // {
668: // this.byteBuffer[this.position++] = (byte)s.charAt(i);
669: // }
670: }
671:
672: // Write a String using the specified character
673: // encoding
674: final void writeStringNoNull(String s, String encoding,
675: String serverEncoding, boolean parserKnowsUnicode,
676: ConnectionImpl conn) throws UnsupportedEncodingException,
677: SQLException {
678: byte[] b = StringUtils.getBytes(s, encoding, serverEncoding,
679: parserKnowsUnicode, conn);
680:
681: int len = b.length;
682: ensureCapacity(len);
683: System.arraycopy(b, 0, this.byteBuffer, this.position, len);
684: this.position += len;
685: }
686: }
|