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