Source code: com/mysql/jdbc/Buffer.java
1 /*
2 Copyright (C) 2002-2004 MySQL AB
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of version 2 of the GNU General Public License as
6 published by the Free Software Foundation.
7
8
9 There are special exceptions to the terms and conditions of the GPL
10 as it is applied to this software. View the full text of the
11 exception exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
12 software distribution.
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 */
24 package com.mysql.jdbc;
25
26 import java.io.UnsupportedEncodingException;
27 import java.sql.SQLException;
28
29
30 /**
31 * Buffer contains code to read and write packets from/to the MySQL server.
32 *
33 * @version $Id: Buffer.java,v 1.15.2.18 2004/10/21 19:31:04 mmatthew Exp $
34 * @author Mark Matthews
35 */
36 class Buffer {
37 static final int NO_LENGTH_LIMIT = -1;
38 static final long NULL_LENGTH = -1;
39 private byte[] byteBuffer;
40 private boolean wasMultiPacket = false;
41 private int bufLength = 0;
42 private int position = 0;
43 private int sendLength = 0;
44
45 Buffer(byte[] buf) {
46 this.byteBuffer = buf;
47 setBufLength(buf.length);
48 }
49
50 Buffer(int size) {
51 this.byteBuffer = new byte[size];
52 setBufLength(this.byteBuffer.length);
53 this.position = MysqlIO.HEADER_LENGTH;
54 }
55
56 /**
57 * Sets the array of bytes to use as a buffer to read from.
58 *
59 * @param byteBuffer the array of bytes to use as a buffer
60 */
61 public void setByteBuffer(byte[] byteBuffer) {
62 this.byteBuffer = byteBuffer;
63 }
64
65 /**
66 * Returns the array of bytes this Buffer is using to read from.
67 *
68 * @return byte array being read from
69 */
70 public byte[] getByteBuffer() {
71 return this.byteBuffer;
72 }
73
74 /**
75 * Set the current position to write to/ read from
76 *
77 * @param position the position (0-based index)
78 */
79 public void setPosition(int position) {
80 this.position = position;
81 }
82
83 /**
84 * Returns the current position to write to/ read from
85 *
86 * @return the current position to write to/ read from
87 */
88 public int getPosition() {
89 return this.position;
90 }
91
92 /**
93 * Sets whether this packet was part of a multipacket
94 *
95 * @param flag was this packet part of a multipacket?
96 */
97 public void setWasMultiPacket(boolean flag) {
98 wasMultiPacket = flag;
99 }
100
101 /**
102 * Skip over a length-encoded string
103 *
104 * @return The position past the end of the string
105 */
106 public int fastSkipLenString() {
107 long len = this.readFieldLength();
108
109 position += len;
110
111 return (int) len; // this is safe, as this is only
112 }
113
114 /**
115 * Was this packet part of a multipacket?
116 *
117 * @return was this packet part of a multipacket?
118 */
119 public boolean wasMultiPacket() {
120 return wasMultiPacket;
121 }
122
123 protected final byte[] getBufferSource() {
124 return byteBuffer;
125 }
126
127 final byte[] getBytes(int len) {
128 byte[] b = new byte[len];
129 System.arraycopy(this.byteBuffer, this.position, b, 0, len);
130 this.position += len; // update cursor
131
132 return b;
133 }
134
135 // 2000-06-05 Changed
136 final boolean isLastDataPacket() {
137 return ((getBufLength() < 9) && ((this.byteBuffer[0] & 0xff) == 254));
138 }
139
140 final void clear() {
141 this.position = MysqlIO.HEADER_LENGTH;
142 }
143
144 final void dump() {
145 StringUtils.dumpAsHex(this.byteBuffer, getBufLength());
146 }
147
148 final void dumpHeader() {
149 for (int i = 0; i < MysqlIO.HEADER_LENGTH; i++) {
150 String hexVal = Integer.toHexString((int) this.byteBuffer[i] & 0xff);
151
152 if (hexVal.length() == 1) {
153 hexVal = "0" + hexVal;
154 }
155
156 System.out.print(hexVal + " ");
157 }
158 }
159
160 final void dumpNBytes(int start, int nBytes) {
161 StringBuffer asciiBuf = new StringBuffer();
162
163 for (int i = start;
164 (i < (start + nBytes)) && (i < this.byteBuffer.length); i++) {
165 String hexVal = Integer.toHexString((int) this.byteBuffer[i] & 0xff);
166
167 if (hexVal.length() == 1) {
168 hexVal = "0" + hexVal;
169 }
170
171 System.out.print(hexVal + " ");
172
173 if ((this.byteBuffer[i] > 32) && (this.byteBuffer[i] < 127)) {
174 asciiBuf.append((char) this.byteBuffer[i]);
175 } else {
176 asciiBuf.append(".");
177 }
178
179 asciiBuf.append(" ");
180 }
181
182 System.out.println(" " + asciiBuf.toString());
183 }
184
185 final void ensureCapacity(int additionalData) throws SQLException {
186 if ((this.position + additionalData) > getBufLength()) {
187 if ((this.position + additionalData) < this.byteBuffer.length) {
188 // byteBuffer.length is != getBufLength() all of the time
189 // due to re-using of packets (we don't shrink them)
190 //
191 // If we can, don't re-alloc, just set buffer length
192 // to size of current buffer
193 setBufLength(this.byteBuffer.length);
194 } else {
195 //
196 // Otherwise, re-size, and pad so we can avoid
197 // allocing again in the near future
198 //
199 int newLength = (int) (this.byteBuffer.length * 1.25);
200
201 if (newLength < (this.byteBuffer.length + additionalData)) {
202 newLength = this.byteBuffer.length
203 + (int) (additionalData * 1.25);
204 }
205
206 if (newLength < this.byteBuffer.length) {
207 newLength = this.byteBuffer.length + additionalData;
208 }
209
210 byte[] newBytes = new byte[newLength];
211
212 System.arraycopy(this.byteBuffer, 0, newBytes, 0,
213 this.byteBuffer.length);
214 this.byteBuffer = newBytes;
215 setBufLength(this.byteBuffer.length);
216 }
217 }
218 }
219
220 final long newReadLength() {
221 int sw = this.byteBuffer[this.position++] & 0xff;
222
223 switch (sw) {
224 case 251:
225 return (long) 0;
226
227 case 252:
228 return (long) readInt();
229
230 case 253:
231 return (long) readLongInt();
232
233 case 254: // changed for 64 bit lengths
234 return (long) readLongLong();
235
236 default:
237 return (long) sw;
238 }
239 }
240
241 final byte readByte() {
242 return this.byteBuffer[this.position++];
243 }
244
245 final long readFieldLength() {
246 int sw = this.byteBuffer[this.position++] & 0xff;
247
248 switch (sw) {
249 case 251:
250 return NULL_LENGTH;
251
252 case 252:
253 return (long) readInt();
254
255 case 253:
256 return (long) readLongInt();
257
258 case 254:
259 return readLongLong();
260
261 default:
262 return (long) sw;
263 }
264 }
265
266 // 2000-06-05 Changed
267 final int readInt() {
268 byte[] b = this.byteBuffer; // a little bit optimization
269
270 return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8);
271 }
272
273 final byte[] readLenByteArray(int offset) {
274 long len = this.readFieldLength();
275
276 if (len == NULL_LENGTH) {
277 return null;
278 }
279
280 if (len == 0) {
281 return new byte[0];
282 }
283
284 this.position += offset;
285
286 return getBytes((int) len);
287 }
288
289 final long readLength() {
290 int sw = this.byteBuffer[this.position++] & 0xff;
291
292 switch (sw) {
293 case 251:
294 return (long) 0;
295
296 case 252:
297 return (long) readInt();
298
299 case 253:
300 return (long) readLongInt();
301
302 case 254:
303 return (long) readLong();
304
305 default:
306 return (long) sw;
307 }
308 }
309
310 // 2000-06-05 Fixed
311 final long readLong() {
312 byte[] b = this.byteBuffer;
313
314 return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8)
315 | ((b[this.position++] & 0xff) << 16)
316 | ((b[this.position++] & 0xff) << 24);
317 }
318
319 // 2000-06-05 Changed
320 final int readLongInt() {
321 byte[] b = this.byteBuffer;
322
323 return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8)
324 | ((b[this.position++] & 0xff) << 16);
325 }
326
327 // 2000-06-05 Fixed
328 final long readLongLong() {
329 byte[] b = this.byteBuffer;
330
331 return (long) (b[this.position++] & 0xff)
332 | ((long) (b[this.position++] & 0xff) << 8)
333 | ((long) (b[this.position++] & 0xff) << 16)
334 | ((long) (b[this.position++] & 0xff) << 24)
335 | ((long) (b[this.position++] & 0xff) << 32)
336 | ((long) (b[this.position++] & 0xff) << 40)
337 | ((long) (b[this.position++] & 0xff) << 48)
338 | ((long) (b[this.position++] & 0xff) << 56);
339 }
340
341 //
342 // Read a null-terminated string
343 //
344 // To avoid alloc'ing a new byte array, we
345 // do this by hand, rather than calling getNullTerminatedBytes()
346 //
347 final String readString() {
348 int i = this.position;
349 int len = 0;
350 int maxLen = getBufLength();
351
352 while ((i < maxLen) && (this.byteBuffer[i] != 0)) {
353 len++;
354 i++;
355 }
356
357 String s = new String(this.byteBuffer, this.position, len);
358 this.position += (len + 1); // update cursor
359
360 return s;
361 }
362
363 final String readString(String encoding) throws SQLException {
364 int i = this.position;
365 int len = 0;
366 int maxLen = getBufLength();
367
368 while ((i < maxLen) && (this.byteBuffer[i] != 0)) {
369 len++;
370 i++;
371 }
372
373 try {
374 return new String(this.byteBuffer, this.position, len, encoding);
375
376 } catch (UnsupportedEncodingException uEE) {
377 throw new SQLException("Unsupported character encoding '" + encoding + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
378 } finally {
379 this.position += (len + 1); // update cursor
380 }
381 }
382
383 final int readnBytes() {
384 int sw = this.byteBuffer[this.position++] & 0xff;
385
386 switch (sw) {
387 case 1:
388 return this.byteBuffer[this.position++] & 0xff;
389
390 case 2:
391 return this.readInt();
392
393 case 3:
394 return this.readLongInt();
395
396 case 4:
397 return (int) this.readLong();
398
399 default:
400 return 255;
401 }
402 }
403
404 final void writeByte(byte b) throws SQLException {
405 ensureCapacity(1);
406
407 this.byteBuffer[this.position++] = b;
408 }
409
410 // Write a byte array
411 final void writeBytesNoNull(byte[] bytes) throws SQLException {
412 int len = bytes.length;
413 ensureCapacity(len);
414 System.arraycopy(bytes, 0, this.byteBuffer, this.position, len);
415 this.position += len;
416 }
417
418 // Write a byte array with the given offset and length
419 final void writeBytesNoNull(byte[] bytes, int offset, int length)
420 throws SQLException {
421 ensureCapacity(length);
422 System.arraycopy(bytes, offset, this.byteBuffer, this.position, length);
423 this.position += length;
424 }
425
426 final void writeDouble(double d) {
427 long l = Double.doubleToLongBits(d);
428 writeLongLong(l);
429 }
430
431 final void writeFloat(float f) {
432 int i = Float.floatToIntBits(f);
433 byte[] b = this.byteBuffer;
434 b[this.position++] = (byte) (i & 0xff);
435 b[this.position++] = (byte) (i >>> 8);
436 b[this.position++] = (byte) (i >>> 16);
437 b[this.position++] = (byte) (i >>> 24);
438 }
439
440 // 2000-06-05 Changed
441 final void writeInt(int i) {
442 byte[] b = this.byteBuffer;
443 b[this.position++] = (byte) (i & 0xff);
444 b[this.position++] = (byte) (i >>> 8);
445 }
446
447 // 2000-06-05 Changed
448 final void writeLong(long i) {
449 byte[] b = this.byteBuffer;
450 b[this.position++] = (byte) (i & 0xff);
451 b[this.position++] = (byte) (i >>> 8);
452 b[this.position++] = (byte) (i >>> 16);
453 b[this.position++] = (byte) (i >>> 24);
454 }
455
456 // 2000-06-05 Changed
457 final void writeLongInt(int i) {
458 byte[] b = this.byteBuffer;
459 b[this.position++] = (byte) (i & 0xff);
460 b[this.position++] = (byte) (i >>> 8);
461 b[this.position++] = (byte) (i >>> 16);
462 }
463
464 final void writeLongLong(long i) {
465 byte[] b = this.byteBuffer;
466 b[this.position++] = (byte) (i & 0xff);
467 b[this.position++] = (byte) (i >>> 8);
468 b[this.position++] = (byte) (i >>> 16);
469 b[this.position++] = (byte) (i >>> 24);
470 b[this.position++] = (byte) (i >>> 32);
471 b[this.position++] = (byte) (i >>> 40);
472 b[this.position++] = (byte) (i >>> 48);
473 b[this.position++] = (byte) (i >>> 56);
474 }
475
476 // Write null-terminated string
477 final void writeString(String s) throws SQLException {
478 ensureCapacity(s.length() + 1);
479
480 writeStringNoNull(s);
481 this.byteBuffer[this.position++] = 0;
482 }
483
484 // Write string, with no termination
485 final void writeStringNoNull(String s) throws SQLException {
486 int len = s.length();
487 ensureCapacity(len);
488 System.arraycopy(s.getBytes(), 0, byteBuffer, position, len);
489 position += len;
490
491 // for (int i = 0; i < len; i++)
492 // {
493 // this.byteBuffer[this.position++] = (byte)s.charAt(i);
494 // }
495 }
496
497 // Write a String using the specified character
498 // encoding
499 final void writeStringNoNull(String s, String encoding, String serverEncoding,
500 SingleByteCharsetConverter converter, boolean parserKnowsUnicode)
501 throws UnsupportedEncodingException, SQLException {
502 byte[] b = null;
503
504 if (converter != null) {
505 b = converter.toBytes(s);
506 } else {
507 b = StringUtils.getBytes(s, encoding, serverEncoding, parserKnowsUnicode);
508 }
509
510 int len = b.length;
511 ensureCapacity(len);
512 System.arraycopy(b, 0, this.byteBuffer, this.position, len);
513 this.position += len;
514 }
515
516 void setBufLength(int bufLength) {
517 this.bufLength = bufLength;
518 }
519
520 int getBufLength() {
521 return bufLength;
522 }
523
524 void setSendLength(int sendLength) {
525 this.sendLength = sendLength;
526 }
527 }