1 | /* |
2 | |
3 | Derby - Class org.apache.derby.iapi.services.io.CompressedNumber |
4 | |
5 | Copyright 1998, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.iapi.services.io; |
22 | |
23 | import org.apache.derby.iapi.services.sanity.SanityManager; |
24 | |
25 | import java.io.*; |
26 | |
27 | /** |
28 | Static methods to write and read compressed forms of numbers |
29 | to DataOut and DataIn interfaces. Format written is platform |
30 | independent like the Data* interfaces and must remain fixed |
31 | once a product is shipped. If a different format is required |
32 | then write a new set of methods, e.g. writeInt2. The formats |
33 | defined by stored format identifiers are implicitly dependent |
34 | on these formats not changing. |
35 | */ |
36 | |
37 | public abstract class CompressedNumber { |
38 | |
39 | // the maximum number of bytes written out for an int |
40 | public static final int MAX_INT_STORED_SIZE = 4; |
41 | |
42 | // the maximum number of bytes written out for a long |
43 | public static final int MAX_LONG_STORED_SIZE = 8; |
44 | |
45 | // largest int stored compressed in 1 byte |
46 | public static final int MAX_COMPRESSED_INT_ONE_BYTE = 0x3f; |
47 | |
48 | // largest int stored compressed in 2 bytes |
49 | public static final int MAX_COMPRESSED_INT_TWO_BYTES = 0x3fff; |
50 | |
51 | |
52 | /** |
53 | Write a compressed integer only supporting signed values. |
54 | Formats are (with x representing value bits): |
55 | <PRE> |
56 | 1 Byte - 00xxxxxx Represents the value <= 63 (0x3f) |
57 | 2 Byte - 01xxxxxx xxxxxxxx Represents the value > 63 && <= 16383 (0x3fff) |
58 | 4 byte - 1xxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx Represents the value > 16383 && <= MAX_INT |
59 | </PRE> |
60 | |
61 | |
62 | @exception IOException value is negative or an exception was thrown by a method on out. |
63 | */ |
64 | public static final int writeInt(DataOutput out, int value) throws IOException { |
65 | |
66 | if (value < 0) |
67 | throw new IOException(); |
68 | |
69 | if (value <= 0x3f) { |
70 | |
71 | out.writeByte(value); |
72 | return 1; |
73 | } |
74 | |
75 | if (value <= 0x3fff) { |
76 | |
77 | out.writeByte(0x40 | (value >>> 8)); |
78 | out.writeByte(value & 0xff); |
79 | return 2; |
80 | } |
81 | |
82 | out.writeByte(((value >>> 24) | 0x80) & 0xff); |
83 | out.writeByte((value >>> 16) & 0xff); |
84 | out.writeByte((value >>> 8) & 0xff); |
85 | out.writeByte((value) & 0xff); |
86 | return 4; |
87 | } |
88 | |
89 | /** |
90 | Write a compressed integer directly to an OutputStream. |
91 | @exception IOException an exception was thrown by a method on in. |
92 | */ |
93 | public static final int writeInt(OutputStream out, int value) throws IOException { |
94 | |
95 | if (value < 0) |
96 | throw new IOException(); |
97 | |
98 | if (value <= 0x3f) { |
99 | |
100 | out.write(value); |
101 | return 1; |
102 | } |
103 | |
104 | if (value <= 0x3fff) { |
105 | |
106 | out.write(0x40 | (value >>> 8)); |
107 | out.write(value & 0xff); |
108 | return 2; |
109 | } |
110 | |
111 | out.write(((value >>> 24) | 0x80) & 0xff); |
112 | out.write((value >>> 16) & 0xff); |
113 | out.write((value >>> 8) & 0xff); |
114 | out.write((value) & 0xff); |
115 | return 4; |
116 | } |
117 | |
118 | |
119 | /** |
120 | Read an integer previously written by writeInt(). |
121 | |
122 | @exception IOException an exception was thrown by a method on in. |
123 | */ |
124 | public static final int readInt(DataInput in) throws IOException { |
125 | |
126 | int value = in.readUnsignedByte(); |
127 | |
128 | if ((value & ~0x3f) == 0) |
129 | { |
130 | // length is stored in this byte, we also know that the 0x80 bit |
131 | // was not set, so no need to mask off the sign extension from |
132 | // the byte to int conversion. |
133 | |
134 | // account for 1 byte stored length of field + 1 for all returns |
135 | return(value); |
136 | } |
137 | else if ((value & 0x80) == 0) |
138 | { |
139 | // length is stored in 2 bytes. only use low 6 bits from 1st byte. |
140 | |
141 | if (SanityManager.DEBUG) |
142 | { |
143 | SanityManager.ASSERT((value & 0x40) == 0x40); |
144 | } |
145 | |
146 | // top 8 bits of 2 byte length is stored in this byte, we also |
147 | // know that the 0x80 bit was not set, so no need to mask off the |
148 | // sign extension from the 1st byte to int conversion. Need to |
149 | // mask the byte in data[offset + 1] to account for possible sign |
150 | // extension. |
151 | |
152 | return(((value & 0x3f) << 8) | in.readUnsignedByte()); |
153 | } |
154 | else |
155 | { |
156 | // length is stored in 4 bytes. only use low 7 bits from 1st byte. |
157 | |
158 | if (SanityManager.DEBUG) |
159 | { |
160 | SanityManager.ASSERT((value & 0x80) == 0x80); |
161 | } |
162 | |
163 | // top 8 bits of 4 byte length is stored in this byte, we also |
164 | // know that the 0x80 bit was set, so need to mask off the |
165 | // sign extension from the 1st byte to int conversion. Need to |
166 | // mask the bytes from the next 3 bytes data[offset + 1,2,3] to |
167 | // account for possible sign extension. |
168 | // |
169 | return( |
170 | ((value & 0x7f) << 24) | |
171 | (in.readUnsignedByte() << 16) | |
172 | (in.readUnsignedByte() << 8) | |
173 | (in.readUnsignedByte() )); |
174 | } |
175 | } |
176 | |
177 | /** |
178 | Read an integer previously written by writeInt(). |
179 | |
180 | @exception IOException an exception was thrown by a method on in. |
181 | */ |
182 | public static final int readInt(InputStream in) throws IOException { |
183 | |
184 | int value = InputStreamUtil.readUnsignedByte(in); |
185 | |
186 | if ((value & ~0x3f) == 0) |
187 | { |
188 | return(value); |
189 | } |
190 | else if ((value & 0x80) == 0) |
191 | { |
192 | return( |
193 | ((value & 0x3f) << 8) | InputStreamUtil.readUnsignedByte(in)); |
194 | } |
195 | else |
196 | { |
197 | return( |
198 | ((value & 0x7f) << 24) | |
199 | (InputStreamUtil.readUnsignedByte(in) << 16) | |
200 | (InputStreamUtil.readUnsignedByte(in) << 8) | |
201 | (InputStreamUtil.readUnsignedByte(in) )); |
202 | } |
203 | } |
204 | |
205 | public static final int readInt( |
206 | byte[] data, |
207 | int offset) |
208 | { |
209 | int value = data[offset++]; |
210 | |
211 | if ((value & ~0x3f) == 0) |
212 | { |
213 | // length is stored in this byte, we also know that the 0x80 bit |
214 | // was not set, so no need to mask off the sign extension from |
215 | // the byte to int conversion. |
216 | |
217 | return(value); |
218 | } |
219 | else if ((value & 0x80) == 0) |
220 | { |
221 | // length is stored in 2 bytes. only use low 6 bits from 1st byte. |
222 | |
223 | if (SanityManager.DEBUG) |
224 | { |
225 | SanityManager.ASSERT((value & 0x40) == 0x40); |
226 | } |
227 | |
228 | // top 8 bits of 2 byte length is stored in this byte, we also |
229 | // know that the 0x80 bit was not set, so no need to mask off the |
230 | // sign extension from the 1st byte to int conversion. Need to |
231 | // mask the byte in data[offset + 1] to account for possible sign |
232 | // extension. |
233 | |
234 | return(((value & 0x3f) << 8) | (data[offset] & 0xff)); |
235 | } |
236 | else |
237 | { |
238 | // length is stored in 4 bytes. only use low 7 bits from 1st byte. |
239 | |
240 | if (SanityManager.DEBUG) |
241 | { |
242 | SanityManager.ASSERT((value & 0x80) == 0x80); |
243 | } |
244 | |
245 | // top 8 bits of 4 byte length is stored in this byte, we also |
246 | // know that the 0x80 bit was set, so need to mask off the |
247 | // sign extension from the 1st byte to int conversion. Need to |
248 | // mask the bytes from the next 3 bytes data[offset + 1,2,3] to |
249 | // account for possible sign extension. |
250 | // |
251 | return( |
252 | ((value & 0x7f) << 24) | |
253 | ((data[offset++] & 0xff) << 16) | |
254 | ((data[offset++] & 0xff) << 8) | |
255 | ((data[offset] & 0xff) )); |
256 | } |
257 | } |
258 | |
259 | /** |
260 | * Return the compressed Int value + stored size of the length + 1 |
261 | * <p> |
262 | * Given offset in array to beginning of compressed int, return the |
263 | * value of the compressed int + the number of bytes used to store the |
264 | * length. |
265 | * <p> |
266 | * So 1 byte lengths will return: length + 1 + 1 |
267 | * So 2 byte lengths will return: length + 2 + 1 |
268 | * So 4 byte lengths will return: length + 4 + 1 |
269 | * <p> |
270 | * Note that this routine will not work for lengths MAX_INT - (MAX_INT - 5). |
271 | * <p> |
272 | * This routine is currently used by the StorePage code to skip fields |
273 | * as efficiently as possible. Since the page size is less than |
274 | * (MAX_INT - 5) it is all right to use this routine. |
275 | * |
276 | * @return compressed int value + length used to store the length. |
277 | * |
278 | * @param data byte array containing the field. |
279 | * @param offset offset to beginning of field, ie. data[offset] contains |
280 | * 1st byte of the compressed int. |
281 | **/ |
282 | public static final int readIntAndReturnIntPlusOverhead( |
283 | byte[] data, |
284 | int offset) |
285 | { |
286 | int value = data[offset]; |
287 | |
288 | if ((value & ~0x3f) == 0) |
289 | { |
290 | // length is stored in this byte, we also know that the 0x80 bit |
291 | // was not set, so no need to mask off the sign extension from |
292 | // the byte to int conversion. |
293 | |
294 | // account for 1 byte stored length of field + 1 for all returns |
295 | return(value + 2); |
296 | } |
297 | else if ((value & 0x80) == 0) |
298 | { |
299 | // length is stored in 2 bytes. only use low 6 bits from 1st byte. |
300 | |
301 | if (SanityManager.DEBUG) |
302 | { |
303 | SanityManager.ASSERT((value & 0x40) == 0x40); |
304 | } |
305 | |
306 | // top 8 bits of 2 byte length is stored in this byte, we also |
307 | // know that the 0x80 bit was not set, so no need to mask off the |
308 | // sign extension from the 1st byte to int conversion. Need to |
309 | // mask the byte in data[offset + 1] to account for possible sign |
310 | // extension. |
311 | |
312 | // add 3 to account for 2 byte length + 1 added to all returns |
313 | |
314 | return((((value & 0x3f) << 8) | (data[offset + 1] & 0xff)) + 3); |
315 | } |
316 | else |
317 | { |
318 | // length is stored in 4 bytes. only use low 7 bits from 1st byte. |
319 | |
320 | if (SanityManager.DEBUG) |
321 | { |
322 | SanityManager.ASSERT((value & 0x80) == 0x80); |
323 | } |
324 | |
325 | // top 8 bits of 4 byte length is stored in this byte, we also |
326 | // know that the 0x80 bit was set, so need to mask off the |
327 | // sign extension from the 1st byte to int conversion. Need to |
328 | // mask the bytes from the next 3 bytes data[offset + 1,2,3] to |
329 | // account for possible sign extension. |
330 | |
331 | |
332 | // add 5 to account for 4 byte length + 1 added to all returns |
333 | return( |
334 | (((value & 0x7f) << 24) | |
335 | ((data[offset + 1] & 0xff) << 16) | |
336 | ((data[offset + 2] & 0xff) << 8) | |
337 | (data[offset + 3] & 0xff)) + 5); |
338 | } |
339 | } |
340 | |
341 | |
342 | /** |
343 | Skip an integer previously written by writeInt(). |
344 | |
345 | @exception IOException an exception was thrown by a method on in. |
346 | */ |
347 | public static final int skipInt(DataInput in) throws IOException { |
348 | |
349 | int value = in.readUnsignedByte(); |
350 | |
351 | if ((value & 0x80) == 0x80) { |
352 | in.skipBytes(3); |
353 | return 4; |
354 | } |
355 | |
356 | if ((value & 0x40) == 0x40) { |
357 | in.skipBytes(1); |
358 | return 2; |
359 | } |
360 | |
361 | return 1; |
362 | } |
363 | |
364 | /** |
365 | Skip an integer previously written by writeInt(). |
366 | |
367 | @exception IOException an exception was thrown by a method on in. |
368 | */ |
369 | public static final int skipInt(InputStream in) throws IOException { |
370 | |
371 | int value = InputStreamUtil.readUnsignedByte(in); |
372 | |
373 | int skipBytes = 0; |
374 | |
375 | if ((value & 0x80) == 0x80) { |
376 | skipBytes = 3; |
377 | } |
378 | else if ((value & 0x40) == 0x40) { |
379 | skipBytes = 1; |
380 | } |
381 | |
382 | if (skipBytes != 0) { |
383 | if (in.skip(skipBytes) != skipBytes) |
384 | throw new EOFException(); |
385 | } |
386 | |
387 | |
388 | return skipBytes + 1; |
389 | } |
390 | |
391 | /** |
392 | Return the number of bytes that would be written by a writeInt call |
393 | */ |
394 | public static final int sizeInt(int value) { |
395 | if (value <= 0x3f) { |
396 | return 1; |
397 | } |
398 | if (value <= 0x3fff) { |
399 | return 2; |
400 | } |
401 | return 4; |
402 | } |
403 | |
404 | /** |
405 | Write a compressed long only supporting signed values. |
406 | |
407 | Formats are (with x representing value bits): |
408 | <PRE> |
409 | 2 byte - 00xxxxxx xxxxxxxx Represents the value <= 16383 (0x3fff) |
410 | 4 byte - 01xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx Represents the value > 16383 && <= 0x3fffffff |
411 | 8 byte - 1xxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx Represents the value > 0x3fffffff && <= MAX_LONG |
412 | </PRE> |
413 | |
414 | |
415 | @exception IOException value is negative or an exception was thrown by a method on out. |
416 | */ |
417 | public static final int writeLong(DataOutput out, long value) throws IOException { |
418 | |
419 | if (value < 0) |
420 | throw new IOException(); |
421 | |
422 | if (value <= 0x3fff) { |
423 | |
424 | out.writeByte((int) ((value >>> 8) & 0xff)); |
425 | out.writeByte((int) ((value ) & 0xff)); |
426 | return 2; |
427 | } |
428 | |
429 | if (value <= 0x3fffffff) { |
430 | |
431 | out.writeByte((int) (((value >>> 24) | 0x40) & 0xff)); |
432 | out.writeByte((int) ( (value >>> 16) & 0xff)); |
433 | out.writeByte((int) ( (value >>> 8) & 0xff)); |
434 | out.writeByte((int) ( (value ) & 0xff)); |
435 | return 4; |
436 | } |
437 | |
438 | out.writeByte((int) (((value >>> 56) | 0x80) & 0xff)); |
439 | out.writeByte((int) ( (value >>> 48) & 0xff)); |
440 | out.writeByte((int) ( (value >>> 40) & 0xff)); |
441 | out.writeByte((int) ( (value >>> 32) & 0xff)); |
442 | out.writeByte((int) ( (value >>> 24) & 0xff)); |
443 | out.writeByte((int) ( (value >>> 16) & 0xff)); |
444 | out.writeByte((int) ( (value >>> 8) & 0xff)); |
445 | out.writeByte((int) ( (value ) & 0xff)); |
446 | return 8; |
447 | } |
448 | /** |
449 | Write a compressed integer only supporting signed values. |
450 | |
451 | @exception IOException value is negative or an exception was thrown by a method on out. |
452 | */ |
453 | public static final int writeLong(OutputStream out, long value) throws IOException { |
454 | |
455 | if (value < 0) |
456 | throw new IOException(); |
457 | |
458 | if (value <= 0x3fff) { |
459 | |
460 | out.write((int) ((value >>> 8) & 0xff)); |
461 | out.write((int) ((value ) & 0xff)); |
462 | return 2; |
463 | } |
464 | |
465 | if (value <= 0x3fffffff) { |
466 | |
467 | out.write((int) (((value >>> 24) | 0x40) & 0xff)); |
468 | out.write((int) ( (value >>> 16) & 0xff)); |
469 | out.write((int) ( (value >>> 8) & 0xff)); |
470 | out.write((int) ( (value ) & 0xff)); |
471 | return 4; |
472 | } |
473 | |
474 | out.write((int) (((value >>> 56) | 0x80) & 0xff)); |
475 | out.write((int) ( (value >>> 48) & 0xff)); |
476 | out.write((int) ( (value >>> 40) & 0xff)); |
477 | out.write((int) ( (value >>> 32) & 0xff)); |
478 | out.write((int) ( (value >>> 24) & 0xff)); |
479 | out.write((int) ( (value >>> 16) & 0xff)); |
480 | out.write((int) ( (value >>> 8) & 0xff)); |
481 | out.write((int) ( (value ) & 0xff)); |
482 | return 8; |
483 | } |
484 | |
485 | /** |
486 | Read a long previously written by writeLong(). |
487 | |
488 | @exception IOException an exception was thrown by a method on in. |
489 | */ |
490 | public static final long readLong(DataInput in) throws IOException { |
491 | |
492 | int int_value = in.readUnsignedByte(); |
493 | |
494 | if ((int_value & ~0x3f) == 0) |
495 | { |
496 | // test for small case first - assuming this is usual case. |
497 | // this is stored in 2 bytes. |
498 | |
499 | return((int_value << 8) | in.readUnsignedByte()); |
500 | } |
501 | else if ((int_value & 0x80) == 0) |
502 | { |
503 | // value is stored in 4 bytes. only use low 6 bits from 1st byte. |
504 | |
505 | return( |
506 | ((int_value & 0x3f) << 24) | |
507 | (in.readUnsignedByte() << 16) | |
508 | (in.readUnsignedByte() << 8) | |
509 | (in.readUnsignedByte())); |
510 | } |
511 | else |
512 | { |
513 | // value is stored in 8 bytes. only use low 7 bits from 1st byte. |
514 | return( |
515 | (((long) (int_value & 0x7f) ) << 56) | |
516 | (((long) in.readUnsignedByte()) << 48) | |
517 | (((long) in.readUnsignedByte()) << 40) | |
518 | (((long) in.readUnsignedByte()) << 32) | |
519 | (((long) in.readUnsignedByte()) << 24) | |
520 | (((long) in.readUnsignedByte()) << 16) | |
521 | (((long) in.readUnsignedByte()) << 8) | |
522 | (((long) in.readUnsignedByte()) )); |
523 | } |
524 | } |
525 | |
526 | /** |
527 | Read a long previously written by writeLong(). |
528 | |
529 | @exception IOException an exception was thrown by a method on in. |
530 | */ |
531 | public static final long readLong(InputStream in) throws IOException { |
532 | |
533 | int int_value = InputStreamUtil.readUnsignedByte(in); |
534 | |
535 | if ((int_value & ~0x3f) == 0) |
536 | { |
537 | // test for small case first - assuming this is usual case. |
538 | // this is stored in 2 bytes. |
539 | |
540 | return((int_value << 8) | InputStreamUtil.readUnsignedByte(in)); |
541 | } |
542 | else if ((int_value & 0x80) == 0) |
543 | { |
544 | // value is stored in 4 bytes. only use low 6 bits from 1st byte. |
545 | |
546 | return( |
547 | ((int_value & 0x3f) << 24) | |
548 | (InputStreamUtil.readUnsignedByte(in) << 16) | |
549 | (InputStreamUtil.readUnsignedByte(in) << 8) | |
550 | (InputStreamUtil.readUnsignedByte(in) )); |
551 | |
552 | } |
553 | else |
554 | { |
555 | // value is stored in 8 bytes. only use low 7 bits from 1st byte. |
556 | long value = int_value; |
557 | |
558 | return( |
559 | (((long) (value & 0x7f) ) << 56) | |
560 | (((long) InputStreamUtil.readUnsignedByte(in)) << 48) | |
561 | (((long) InputStreamUtil.readUnsignedByte(in)) << 40) | |
562 | (((long) InputStreamUtil.readUnsignedByte(in)) << 32) | |
563 | (((long) InputStreamUtil.readUnsignedByte(in)) << 24) | |
564 | (((long) InputStreamUtil.readUnsignedByte(in)) << 16) | |
565 | (((long) InputStreamUtil.readUnsignedByte(in)) << 8) | |
566 | (((long) InputStreamUtil.readUnsignedByte(in)) )); |
567 | } |
568 | } |
569 | |
570 | public static final long readLong( |
571 | byte[] data, |
572 | int offset) |
573 | { |
574 | int int_value = data[offset++]; |
575 | |
576 | if ((int_value & ~0x3f) == 0) |
577 | { |
578 | // test for small case first - assuming this is usual case. |
579 | // this is stored in 2 bytes. |
580 | |
581 | return((int_value << 8) | (data[offset] & 0xff)); |
582 | } |
583 | else if ((int_value & 0x80) == 0) |
584 | { |
585 | // value is stored in 4 bytes. only use low 6 bits from 1st byte. |
586 | |
587 | return( |
588 | ((int_value & 0x3f) << 24) | |
589 | ((data[offset++] & 0xff) << 16) | |
590 | ((data[offset++] & 0xff) << 8) | |
591 | ((data[offset] & 0xff) )); |
592 | |
593 | } |
594 | else |
595 | { |
596 | // value is stored in 8 bytes. only use low 6 bits from 1st byte. |
597 | return( |
598 | (((long) (int_value & 0x7f)) << 56) | |
599 | (((long) (data[offset++] & 0xff)) << 48) | |
600 | (((long) (data[offset++] & 0xff)) << 40) | |
601 | (((long) (data[offset++] & 0xff)) << 32) | |
602 | (((long) (data[offset++] & 0xff)) << 24) | |
603 | (((long) (data[offset++] & 0xff)) << 16) | |
604 | (((long) (data[offset++] & 0xff)) << 8) | |
605 | (((long) (data[offset] & 0xff)) )); |
606 | |
607 | } |
608 | } |
609 | |
610 | /** |
611 | Skip a long previously written by writeLong(). |
612 | |
613 | @exception IOException an exception was thrown by a method on in. |
614 | */ |
615 | public static final int skipLong(DataInput in) throws IOException { |
616 | |
617 | long value = in.readUnsignedByte(); |
618 | |
619 | if ((value & 0x80) == 0x80) |
620 | { |
621 | in.skipBytes(7); |
622 | return 8; |
623 | } |
624 | |
625 | |
626 | if ((value & 0x40) == 0x40) |
627 | { |
628 | in.skipBytes(3); |
629 | return 4; |
630 | |
631 | } |
632 | |
633 | in.skipBytes(1); |
634 | return 2; |
635 | } |
636 | |
637 | /** |
638 | Skip a long previously written by writeLong(). |
639 | |
640 | @exception IOException an exception was thrown by a method on in. |
641 | */ |
642 | public static final int skipLong(InputStream in) throws IOException { |
643 | |
644 | int value = InputStreamUtil.readUnsignedByte(in); |
645 | |
646 | int skipBytes; |
647 | |
648 | if ((value & 0x80) == 0x80) { |
649 | skipBytes = 7; |
650 | } |
651 | else if ((value & 0x40) == 0x40) { |
652 | skipBytes = 3; |
653 | } |
654 | else |
655 | skipBytes = 1; |
656 | |
657 | if (in.skip(skipBytes) != skipBytes) |
658 | throw new EOFException(); |
659 | |
660 | return skipBytes + 1; |
661 | } |
662 | |
663 | public static final int sizeLong(long value) { |
664 | |
665 | if (value <= 0x3fff) { |
666 | |
667 | return 2; |
668 | } |
669 | |
670 | if (value <= 0x3fffffff) { |
671 | return 4; |
672 | } |
673 | |
674 | return 8; |
675 | } |
676 | |
677 | // /* FOR TESTING |
678 | // ***************************************************** |
679 | |
680 | private static byte[] holder = new byte[8]; |
681 | private static ArrayOutputStream aos = new ArrayOutputStream(holder); |
682 | private static DataOutput out = new DataOutputStream(aos); |
683 | |
684 | private static ArrayInputStream ais = new ArrayInputStream(holder); |
685 | private static DataInput in = new DataInputStream(ais); |
686 | private static InputStream in_stream = ais; |
687 | |
688 | |
689 | private static short checkInt(int i, short oldLength) throws IOException { |
690 | |
691 | aos.setPosition(0); |
692 | int length = CompressedNumber.writeInt(out, i); |
693 | if (length != oldLength) { |
694 | System.out.println("changing length to " + length + " at value " + i + " 0x" + Integer.toHexString(i)); |
695 | |
696 | oldLength = (short) length; |
697 | } |
698 | |
699 | int writtenBytes = aos.getPosition(); |
700 | if (writtenBytes != length) { |
701 | System.out.println("MISMATCH written bytes expected " + length + " got " + writtenBytes); |
702 | System.exit(1); |
703 | } |
704 | |
705 | if (length != CompressedNumber.sizeInt(i)) { |
706 | System.out.println("MISMATCH sizeInt() bytes expected " + length + " got " + CompressedNumber.sizeInt(i)); |
707 | System.exit(1); |
708 | } |
709 | |
710 | ais.setPosition(0); |
711 | int value = CompressedNumber.readInt(in); |
712 | if (value != i) { |
713 | System.out.println("MISMATCH value readInt(DataInput) expected " + i + " got " + value); |
714 | System.exit(1); |
715 | } |
716 | |
717 | ais.setPosition(0); |
718 | value = ais.readCompressedInt(); |
719 | if (value != i) { |
720 | System.out.println("MISMATCH value readInt(DataInput) expected " + i + " got " + value); |
721 | System.exit(1); |
722 | } |
723 | |
724 | ais.setPosition(0); |
725 | value = CompressedNumber.readInt(in_stream); |
726 | if (value != i) { |
727 | System.out.println("MISMATCH value in readInt(InputStream) expected " + i + " got " + value); |
728 | System.exit(1); |
729 | } |
730 | |
731 | |
732 | value = CompressedNumber.readInt(holder, 0); |
733 | if (value != i) { |
734 | System.out.println( |
735 | "MISMATCH frome readInt(byte[], offset) value expected " + |
736 | i + " got " + value); |
737 | System.exit(1); |
738 | } |
739 | |
740 | ais.setPosition(0); |
741 | int skipLength = CompressedNumber.skipInt(in); |
742 | if (skipLength != length) { |
743 | System.out.println("MISMATCH skip length expected " + length + " got " + skipLength); |
744 | System.exit(1); |
745 | } |
746 | |
747 | int value_plus_int_length = readIntAndReturnIntPlusOverhead(holder, 0); |
748 | if (value_plus_int_length != (length + i + 1)) { |
749 | System.out.println("MISMATCH readIntAndReturnIntPlusOverhead() return expected " + (length + i) + " got " + value_plus_int_length); |
750 | System.exit(1); |
751 | } |
752 | |
753 | int skipPosition = ais.getPosition(); |
754 | if (skipPosition != length) { |
755 | System.out.println("MISMATCH skip position expected " + length + " got " + skipPosition); |
756 | System.exit(1); |
757 | } |
758 | |
759 | return oldLength; |
760 | } |
761 | |
762 | private static short checkLong(long i, short oldLength) throws IOException { |
763 | |
764 | aos.setPosition(0); |
765 | int length = CompressedNumber.writeLong(out, i); |
766 | if (length != oldLength) { |
767 | System.out.println("changing length to " + length + " at value " + i + " 0x" + Long.toHexString(i)); |
768 | oldLength = (short) length; |
769 | } |
770 | |
771 | int writtenBytes = aos.getPosition(); |
772 | if (writtenBytes != length) { |
773 | System.out.println("MISMATCH written bytes expected " + length + " got " + writtenBytes); |
774 | System.exit(1); |
775 | } |
776 | |
777 | if (length != CompressedNumber.sizeLong(i)) { |
778 | System.out.println("MISMATCH sizeLong() bytes expected " + length + " got " + CompressedNumber.sizeLong(i)); |
779 | System.exit(1); |
780 | } |
781 | |
782 | long value = CompressedNumber.readLong(holder, 0); |
783 | if (value != i) { |
784 | for (int j = 0; j < 8; j++) { |
785 | |
786 | System.out.println(Integer.toHexString((int) holder[j])); |
787 | } |
788 | |
789 | System.out.println( |
790 | "MISMATCH in readLong(byte[], offset) value expected " + |
791 | Long.toHexString(i) + " got " + value); |
792 | System.exit(1); |
793 | } |
794 | |
795 | ais.setPosition(0); |
796 | value = CompressedNumber.readLong(in_stream); |
797 | if (value != i) { |
798 | for (int j = 0; j < 8; j++) { |
799 | |
800 | System.out.println(Integer.toHexString((int) holder[j])); |
801 | } |
802 | System.out.println("MISMATCH value in readLong(InputStream) expected " + Long.toHexString(i) + " got " + value); |
803 | System.exit(1); |
804 | } |
805 | |
806 | ais.setPosition(0); |
807 | value = ais.readCompressedLong(); |
808 | if (value != i) { |
809 | for (int j = 0; j < 8; j++) { |
810 | |
811 | System.out.println(Integer.toHexString((int) holder[j])); |
812 | } |
813 | System.out.println("MISMATCH value in readLong(InputStream) expected " + Long.toHexString(i) + " got " + value); |
814 | System.exit(1); |
815 | } |
816 | |
817 | |
818 | ais.setPosition(0); |
819 | value = CompressedNumber.readLong(in); |
820 | if (value != i) { |
821 | for (int j = 0; j < 8; j++) { |
822 | |
823 | System.out.println(Integer.toHexString((int) holder[j])); |
824 | } |
825 | System.out.println("MISMATCH value in readLong(DataInput) expected " + Long.toHexString(i) + " got " + value); |
826 | System.exit(1); |
827 | } |
828 | |
829 | ais.setPosition(0); |
830 | int skipLength = CompressedNumber.skipLong(in); |
831 | if (skipLength != length) { |
832 | System.out.println("MISMATCH skip length expected " + length + " got " + skipLength); |
833 | System.exit(1); |
834 | } |
835 | |
836 | int skipPosition = ais.getPosition(); |
837 | if (skipPosition != length) { |
838 | System.out.println("MISMATCH skip position expected " + length + " got " + skipPosition); |
839 | System.exit(1); |
840 | } |
841 | |
842 | return oldLength; |
843 | } |
844 | |
845 | public static void main(String[] args) throws IOException { |
846 | |
847 | short oldLength = -1; |
848 | |
849 | System.out.println("** Testing Int"); |
850 | |
851 | oldLength = checkInt(0, oldLength); |
852 | oldLength = checkInt(1, oldLength); |
853 | oldLength = checkInt(2, oldLength); |
854 | |
855 | oldLength = checkInt(0x3f - 4, oldLength); |
856 | oldLength = checkInt(0x3f - 3, oldLength); |
857 | oldLength = checkInt(0x3f - 2, oldLength); |
858 | oldLength = checkInt(0x3f - 1, oldLength); |
859 | oldLength = checkInt(0x3f , oldLength); |
860 | oldLength = checkInt(0x3f + 1, oldLength); |
861 | oldLength = checkInt(0x3f + 2, oldLength); |
862 | oldLength = checkInt(0x3f + 3, oldLength); |
863 | oldLength = checkInt(0x3f + 4, oldLength); |
864 | |
865 | oldLength = checkInt(0x3f80 - 4, oldLength); |
866 | oldLength = checkInt(0x3f80 - 3, oldLength); |
867 | oldLength = checkInt(0x3f80 - 2, oldLength); |
868 | oldLength = checkInt(0x3f80 - 1, oldLength); |
869 | oldLength = checkInt(0x3f80 , oldLength); |
870 | oldLength = checkInt(0x3f80 + 1, oldLength); |
871 | oldLength = checkInt(0x3f80 + 2, oldLength); |
872 | oldLength = checkInt(0x3f80 + 3, oldLength); |
873 | oldLength = checkInt(0x3f80 + 4, oldLength); |
874 | |
875 | oldLength = checkInt(0x3fff - 4, oldLength); |
876 | oldLength = checkInt(0x3fff - 3, oldLength); |
877 | oldLength = checkInt(0x3fff - 2, oldLength); |
878 | oldLength = checkInt(0x3fff - 1, oldLength); |
879 | oldLength = checkInt(0x3fff , oldLength); |
880 | oldLength = checkInt(0x3fff + 1, oldLength); |
881 | oldLength = checkInt(0x3fff + 2, oldLength); |
882 | oldLength = checkInt(0x3fff + 3, oldLength); |
883 | oldLength = checkInt(0x3fff + 4, oldLength); |
884 | |
885 | oldLength = checkInt(Integer.MAX_VALUE - 4, oldLength); |
886 | oldLength = checkInt(Integer.MAX_VALUE - 3, oldLength); |
887 | oldLength = checkInt(Integer.MAX_VALUE - 2, oldLength); |
888 | oldLength = checkInt(Integer.MAX_VALUE - 1, oldLength); |
889 | oldLength = checkInt(Integer.MAX_VALUE , oldLength); |
890 | |
891 | oldLength = -1; |
892 | for (int i = 0; i < 0xf0000; i++) |
893 | { |
894 | oldLength = checkInt(i, oldLength); |
895 | } |
896 | |
897 | // takes 30 minutes to run. |
898 | // |
899 | // for (int i = 0; i < Integer.MAX_VALUE; i++) |
900 | // { |
901 | // if (i % 0x00800000 == 0) |
902 | // System.out.println("checking: " + i); |
903 | // |
904 | // oldLength = checkInt(i, oldLength); |
905 | // } |
906 | |
907 | |
908 | System.out.println("** Testing Long"); |
909 | |
910 | oldLength = -1; |
911 | for (int i = 0; i < 0xf0000; i++) |
912 | { |
913 | oldLength = checkLong(i, oldLength); |
914 | } |
915 | |
916 | oldLength = -1; |
917 | |
918 | oldLength = checkLong(0, oldLength); |
919 | oldLength = checkLong(1, oldLength); |
920 | oldLength = checkLong(2, oldLength); |
921 | |
922 | oldLength = checkLong(0x3fff - 2, oldLength); |
923 | oldLength = checkLong(0x3fff - 1, oldLength); |
924 | oldLength = checkLong(0x3fff , oldLength); |
925 | oldLength = checkLong(0x3fff + 1, oldLength); |
926 | oldLength = checkLong(0x3fff + 2, oldLength); |
927 | |
928 | oldLength = checkLong(0x3fffffff - 4, oldLength); |
929 | oldLength = checkLong(0x3fffffff - 3, oldLength); |
930 | oldLength = checkLong(0x3fffffff - 2, oldLength); |
931 | oldLength = checkLong(0x3fffffff - 1, oldLength); |
932 | oldLength = checkLong(0x3fffffff , oldLength); |
933 | oldLength = checkLong(0x3fffffff + 1, oldLength); |
934 | oldLength = checkLong(0x3fffffff + 2, oldLength); |
935 | oldLength = checkLong(0x3fffffff + 3, oldLength); |
936 | oldLength = checkLong(0x3fffffff + 4, oldLength); |
937 | |
938 | oldLength = checkLong(0x70000000 - 2, oldLength); |
939 | oldLength = checkLong(0x70000000 - 1, oldLength); |
940 | oldLength = checkLong(0x70000000 , oldLength); |
941 | oldLength = checkLong(0x70000000 + 1, oldLength); |
942 | oldLength = checkLong(0x70000000 + 2, oldLength); |
943 | |
944 | |
945 | oldLength = checkLong(Long.MAX_VALUE - 2, oldLength); |
946 | oldLength = checkLong(Long.MAX_VALUE - 1, oldLength); |
947 | oldLength = checkLong(Long.MAX_VALUE , oldLength); |
948 | |
949 | |
950 | } |
951 | // ********************************************************/ |
952 | } |