1 | /* |
2 | |
3 | Derby - Class org.apache.derby.iapi.services.io.FormatableBitSet |
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.InputStream; |
26 | import java.io.ObjectOutput; |
27 | import java.io.ObjectInput; |
28 | import java.io.IOException; |
29 | |
30 | /** |
31 | * FormatableBitSet is implemented as a packed array of bytes. |
32 | * |
33 | * @author Jamie -- originally coded by Jeff |
34 | */ |
35 | |
36 | public final class FormatableBitSet implements Formatable, Cloneable |
37 | { |
38 | |
39 | /******************************************************** |
40 | ** |
41 | ** This class implements Formatable. That means that it |
42 | ** can write itself to and from a formatted stream. If |
43 | ** you add more fields to this class, make sure that you |
44 | ** also write/read them with the writeExternal()/readExternal() |
45 | ** methods. |
46 | ** |
47 | ** If, inbetween releases, you add more fields to this class, |
48 | ** then you should bump the version number emitted by the getTypeFormatId() |
49 | ** method. |
50 | ** |
51 | ********************************************************/ |
52 | |
53 | /** |
54 | ** Bits are stored as an array of bytes. |
55 | ** Bits are numbered starting at 0. Bits |
56 | ** 0..7 go in byte[0], 8..15 in byte[1] and so on. |
57 | ** The number of bytes is tracked as part |
58 | ** of the byte array. The number of bits |
59 | ** being used is derived by the number of |
60 | ** bytes being used and the number of bits |
61 | ** being used by the last byte. The partially |
62 | ** unused byte is always byte[byte.length] with the |
63 | ** lowest bits being unused. |
64 | ** |
65 | ** Zero length bits are stored using a |
66 | ** zero length byte array, with all bits |
67 | ** marked as unused. |
68 | */ |
69 | private byte[] value; |
70 | private short bitsInLastByte; |
71 | |
72 | private transient int lengthAsBits; |
73 | |
74 | /** |
75 | * Niladic Constructor |
76 | */ |
77 | public FormatableBitSet() |
78 | { |
79 | } |
80 | |
81 | /** |
82 | * Constructs a Bit with the initial number of bits |
83 | */ |
84 | public FormatableBitSet(int numBits) |
85 | { |
86 | initializeBits(numBits); |
87 | } |
88 | |
89 | private void initializeBits(int numBits) |
90 | { |
91 | int numBytes = numBytesFromBits(numBits); |
92 | |
93 | // the byte array is zero'ed out by the new operator |
94 | value = new byte[numBytes]; |
95 | bitsInLastByte = numBitsInLastByte(numBits); |
96 | lengthAsBits = numBits; |
97 | } |
98 | |
99 | /** |
100 | * Constructs a Bit from an array of bytes. Assume |
101 | * bytes are all being used. |
102 | * |
103 | * @param newValue The array of bytes to make up the new Bit |
104 | */ |
105 | public FormatableBitSet(byte[] newValue) |
106 | { |
107 | value = newValue; |
108 | bitsInLastByte = 8; |
109 | lengthAsBits = calculateLength(newValue.length); |
110 | } |
111 | |
112 | /** |
113 | * Constructs a Bit from an array of bytes. |
114 | * |
115 | * @param newValue The array of bytes to make up the new Bit |
116 | * @param numBits The number of bits |
117 | */ |
118 | public FormatableBitSet(byte[] newValue, int numBits) |
119 | { |
120 | bitsInLastByte = numBitsInLastByte(numBits); |
121 | lengthAsBits = numBits; |
122 | |
123 | int lenInBytes = numBytesFromBits(numBits); |
124 | |
125 | if (lenInBytes == newValue.length) { |
126 | value = newValue; |
127 | } else { |
128 | value = new byte[lenInBytes]; |
129 | System.arraycopy(newValue, 0, value, 0, newValue.length); |
130 | } |
131 | } |
132 | |
133 | /** |
134 | * Copy constructor |
135 | * |
136 | * @param original the FormatableBitSet to make a copy from |
137 | */ |
138 | public FormatableBitSet (FormatableBitSet original) |
139 | { |
140 | if (SanityManager.DEBUG) |
141 | SanityManager.ASSERT( |
142 | original != null, "cannot make copy from a null FormatableBitSet"); |
143 | |
144 | bitsInLastByte = original.bitsInLastByte; |
145 | lengthAsBits = original.lengthAsBits; |
146 | |
147 | int lenInBytes = FormatableBitSet.numBytesFromBits(original.lengthAsBits); |
148 | value = new byte[lenInBytes]; |
149 | if (lenInBytes > 0) |
150 | System.arraycopy(original.value, 0, value, 0, lenInBytes); |
151 | } |
152 | |
153 | /* |
154 | * Cloneable |
155 | */ |
156 | public Object clone() |
157 | { |
158 | return new FormatableBitSet(this); |
159 | } |
160 | |
161 | |
162 | /** |
163 | * Get the length in bytes of a Bit value |
164 | * |
165 | * @return The length in bytes of this value |
166 | */ |
167 | public int getLengthInBytes() |
168 | { |
169 | if (value == null) |
170 | { |
171 | return 0; |
172 | } |
173 | |
174 | return FormatableBitSet.numBytesFromBits(lengthAsBits); |
175 | } |
176 | |
177 | /** |
178 | ** Get the length in bits |
179 | ** |
180 | ** @return The length in bits for this value |
181 | ** |
182 | ** NOTE: could possibly be changed to a long. As is |
183 | ** we are restricted to 2^(31-3) -> 256meg instead |
184 | ** of 2^31 (Integer.MAX_VALUE) like other datatypes |
185 | ** (or 2 gig). If it is ever changed to a long |
186 | ** be sure to change read/writeExternal which write |
187 | ** out the length in bits. |
188 | */ |
189 | public int getLength() { |
190 | return lengthAsBits; |
191 | } |
192 | |
193 | private int calculateLength(int realByteLength) |
194 | { |
195 | if (realByteLength == 0) |
196 | { |
197 | return 0; |
198 | } |
199 | |
200 | return ((realByteLength - 1) * 8) + bitsInLastByte; |
201 | } |
202 | |
203 | /** |
204 | * Get the length in bits -- alias for getLength() |
205 | * |
206 | * @return The length in bits for this value |
207 | */ |
208 | public int size() |
209 | { |
210 | return getLength(); |
211 | } |
212 | |
213 | /** |
214 | * Get the value of the byte array |
215 | * |
216 | * @return The value of the byte array |
217 | */ |
218 | |
219 | public byte[] getByteArray() |
220 | { |
221 | if (value == null) |
222 | return null; |
223 | |
224 | // In some cases the array is bigger than the actual number |
225 | // of valid bytes. |
226 | int realByteLength = getLengthInBytes(); |
227 | |
228 | // Currently the case is that the return from this |
229 | // call only includes the valid bytes. |
230 | if (value.length != realByteLength) { |
231 | byte[] data = new byte[realByteLength]; |
232 | System.arraycopy(value, 0, data, 0, realByteLength); |
233 | |
234 | value = data; |
235 | } |
236 | |
237 | return value; |
238 | } |
239 | |
240 | /** |
241 | * Set the value of the byte array |
242 | * |
243 | * @return The value of the byte array |
244 | */ |
245 | public boolean isNull() |
246 | { |
247 | return this.value == null; |
248 | } |
249 | |
250 | /** |
251 | * Grow (widen) a FormatableBitSet to N bis |
252 | * |
253 | * @param n The number of bits you want. The bits are |
254 | * always added as 0 and are appended to the |
255 | * least significant end of the bit array. |
256 | * |
257 | * ASSUMPTIONS: that all extra bits in the last byte |
258 | * are zero. |
259 | */ |
260 | public void grow(int n) |
261 | { |
262 | if (n <= this.getLength()) |
263 | return; |
264 | |
265 | if (value == null) |
266 | { |
267 | initializeBits(n); |
268 | return; |
269 | } |
270 | |
271 | int delta = n - this.getLength(); |
272 | |
273 | |
274 | int oldNumBytes = getLengthInBytes(); |
275 | |
276 | /* |
277 | ** If we have enough space in the left over bits, |
278 | ** then all we need to do is change the modulo. |
279 | */ |
280 | if ((oldNumBytes != 0) && |
281 | (8 - this.bitsInLastByte) >= delta) |
282 | { |
283 | this.bitsInLastByte += delta; |
284 | lengthAsBits = n; |
285 | return; |
286 | } |
287 | |
288 | int newNumBytes = FormatableBitSet.numBytesFromBits(n); |
289 | |
290 | // is there enough room in the existing array |
291 | if (newNumBytes <= value.length) { |
292 | // ensure the bits are zeroed |
293 | for (int i = oldNumBytes; i < newNumBytes; i++) |
294 | value[i] = 0; |
295 | } else { |
296 | |
297 | |
298 | /* |
299 | ** We didn't have enough bytes in value, so we need |
300 | ** to create a bigger byte array and use that. |
301 | */ |
302 | byte[] newValue = new byte[newNumBytes]; |
303 | |
304 | System.arraycopy(value, 0, newValue, 0, oldNumBytes); |
305 | |
306 | value = newValue; |
307 | } |
308 | bitsInLastByte = numBitsInLastByte(n); |
309 | lengthAsBits = n; |
310 | } |
311 | |
312 | /** |
313 | * Shrink (narrow) a FormatableBitSet to N bits |
314 | * |
315 | * @param n The number of bits the caller wants. The |
316 | * bits are always removed from the |
317 | * least significant end of the bit array. |
318 | */ |
319 | public FormatableBitSet shrink(int n) |
320 | { |
321 | int numBytes; |
322 | int lastByteNum; |
323 | |
324 | /* |
325 | ** Sanity check: we shouldn't shrink down to |
326 | ** nothing. |
327 | */ |
328 | if (SanityManager.DEBUG) |
329 | { |
330 | if (value == null) |
331 | { |
332 | SanityManager.THROWASSERT("Attempt to shrink a null Bit"+ |
333 | " -- caller should have known better probably"); |
334 | return null; |
335 | } |
336 | } |
337 | |
338 | if (n >= this.getLength()) |
339 | { |
340 | return this; |
341 | } |
342 | |
343 | |
344 | lastByteNum = numBytesFromBits(n) - 1; |
345 | bitsInLastByte = numBitsInLastByte(n); |
346 | lengthAsBits = n; |
347 | |
348 | /* |
349 | ** Mask out any left over bits in the |
350 | ** last byte. Retain the highest bits. |
351 | */ |
352 | if (bitsInLastByte != 8) |
353 | { |
354 | value[lastByteNum] &= 0xFF00 >> bitsInLastByte; |
355 | } |
356 | |
357 | return this; |
358 | } |
359 | |
360 | /* |
361 | ** Some of the operators required by SQL. These could alternatively |
362 | ** be in SQLBit, but since they are so tightly bound to the implementation |
363 | ** rather than return something that undermines the encapsulation |
364 | ** of this type, i have chosen to put them in here. |
365 | */ |
366 | |
367 | /** |
368 | * Bit equivalence. Compare this with other. |
369 | * If the length is different, then cannot be |
370 | * equal so short circuit. Otherwise, rely on |
371 | * compare(). Note that two zero length bits are |
372 | * considered equal. |
373 | * |
374 | * @param other the other bit to compare to |
375 | * |
376 | * @return TRUE|FALSE |
377 | */ |
378 | public boolean equals(FormatableBitSet other) |
379 | { |
380 | if (this.getLength() != other.getLength()) |
381 | { |
382 | return false; |
383 | } |
384 | |
385 | return (this.compare(other) == 0); |
386 | } |
387 | |
388 | /** |
389 | * Bit comparison. Compare this with other. |
390 | * Will always do a byte by byte compare. |
391 | * |
392 | * Given 2 similar bits of unequal lengths (x and y), |
393 | * where x.getLength() < y.getLength() but where: |
394 | * |
395 | * x[0..x.getLength()] == y[0..x.getLength()] |
396 | * |
397 | * then x < y. |
398 | * |
399 | * |
400 | * @param other the other bit to compare to |
401 | * |
402 | * @return -1 - if other < this |
403 | * 0 - if other == this |
404 | * 1 - if other > this |
405 | * |
406 | */ |
407 | public int compare(FormatableBitSet other) |
408 | { |
409 | |
410 | int otherCount, thisCount; |
411 | int otherLen, thisLen; |
412 | byte[] otherb; |
413 | |
414 | otherb = other.value; |
415 | /* |
416 | ** By convention, nulls sort low, and null == null |
417 | */ |
418 | if (this.value == null || otherb == null) |
419 | { |
420 | if (this.value != null) // otherb == null |
421 | return 1; |
422 | if (otherb != null) // this.value == null |
423 | return -1; |
424 | return 0; // both null |
425 | } |
426 | |
427 | otherLen = other.getLengthInBytes(); |
428 | thisLen = getLengthInBytes(); |
429 | for (otherCount = 0, thisCount = 0; |
430 | otherCount < otherLen && thisCount < thisLen; |
431 | otherCount++, thisCount++) |
432 | { |
433 | if (otherb[otherCount] != this.value[thisCount]) |
434 | break; |
435 | } |
436 | |
437 | /* |
438 | ** '==' if byte by byte comparison is identical and |
439 | ** exact same length in bits (not bytes). |
440 | */ |
441 | if ((otherCount == otherLen) && (thisCount == thisLen)) |
442 | { |
443 | if (this.getLength() == other.getLength()) |
444 | { |
445 | return 0; |
446 | } |
447 | |
448 | /* |
449 | ** If subset of bits is identical, return 1 |
450 | ** if other.getLength() > this.getLength(); otherwise, |
451 | ** -1 |
452 | */ |
453 | return (other.getLength() < this.getLength()) ? 1 : -1; |
454 | } |
455 | |
456 | if (otherCount == otherLen) |
457 | { |
458 | return 1; |
459 | } |
460 | else if (thisCount == thisLen) |
461 | { |
462 | return -1; |
463 | } |
464 | else |
465 | { |
466 | /* |
467 | ** Ok, we have a difference somewhere. Now |
468 | ** we have to go to the trouble of converting |
469 | ** to a int and masking out the sign to get |
470 | ** a valid comparision because bytes are signed. |
471 | */ |
472 | int otherInt, thisInt; |
473 | |
474 | otherInt = (int)otherb[otherCount]; |
475 | otherInt &= (0x100 - 1); |
476 | |
477 | thisInt = (int)this.value[thisCount]; |
478 | thisInt &= (0x100 - 1); |
479 | |
480 | return (thisInt > otherInt) ? 1 : -1; |
481 | |
482 | } |
483 | } |
484 | |
485 | /** |
486 | * Bit concatenation. |
487 | * |
488 | * @param other the other bit to append to this |
489 | * |
490 | * @return Bit -- the newly concatenated bit |
491 | * |
492 | */ |
493 | public FormatableBitSet concatenate(FormatableBitSet other) |
494 | { |
495 | int newLen; |
496 | int otherLen; |
497 | int prevLen; |
498 | int prevLenBytes; |
499 | int newLenBytes; |
500 | int otherLenBytes; |
501 | int i, j; |
502 | byte[] newValue; |
503 | byte[] otherValue; |
504 | int shiftBits; |
505 | int inByte; |
506 | |
507 | |
508 | prevLen = this.getLength(); |
509 | prevLenBytes = this.getLengthInBytes(); |
510 | otherLen = other.getLength(); |
511 | otherValue = other.getByteArray(); |
512 | otherLenBytes = other.getLengthInBytes(); |
513 | newLen = prevLen + otherLen; |
514 | newLenBytes = numBytesFromBits(newLen); |
515 | newValue = new byte[newLenBytes]; |
516 | |
517 | |
518 | /* |
519 | ** Copy over the entire array in this.value |
520 | ** to newLenBytes. |
521 | */ |
522 | for (i = 0; i < prevLenBytes; i++) |
523 | { |
524 | newValue[i] = this.value[i]; |
525 | } |
526 | |
527 | /* |
528 | ** Now if we have any bits left over |
529 | ** we need to shift them, and keep |
530 | ** shifting everything down. Be careful |
531 | ** to handle the case where the bit |
532 | ** used to have length 0. |
533 | */ |
534 | shiftBits = (prevLen == 0) ? 8 : this.bitsInLastByte; |
535 | for (j = 0; j < otherLenBytes; j++, i++) |
536 | { |
537 | if (shiftBits == 8) |
538 | { |
539 | newValue[i] = otherValue[j]; |
540 | } |
541 | else |
542 | { |
543 | /* |
544 | ** Convert to an int because it will get converted |
545 | ** on the shift anyway. |
546 | */ |
547 | inByte = (int)otherValue[j]; |
548 | |
549 | /* |
550 | ** Mask off the high bits in case they are now |
551 | ** turned on if we had the sign bit on. |
552 | */ |
553 | inByte &= (0x100 - 1); |
554 | |
555 | /* |
556 | ** Use the high order bits to finish off |
557 | ** the last byte |
558 | */ |
559 | newValue[i-1] |= (inByte >>> shiftBits); |
560 | |
561 | /* |
562 | ** Start the next one with whatever is left, unless |
563 | ** there is nothing left. |
564 | */ |
565 | if (i < newLenBytes) |
566 | { |
567 | newValue[i] |= (inByte << (8 - shiftBits)); |
568 | } |
569 | } |
570 | } |
571 | |
572 | return new FormatableBitSet(newValue, newLen); |
573 | } |
574 | |
575 | /** |
576 | * Produce a hash code by putting the value bytes into an int, exclusive OR'ing |
577 | * if there are more than 4 bytes. |
578 | * |
579 | * @return the hash code |
580 | */ |
581 | public int hashCode() |
582 | { |
583 | if( null == value) |
584 | return 0; |
585 | |
586 | int code = 0; |
587 | int i; |
588 | int shift = 0; |
589 | |
590 | int byteLength = getLengthInBytes(); |
591 | for( i = 0; i < byteLength; i++) |
592 | { |
593 | code ^= (value[i] & 0xff)<<shift; |
594 | shift += 8; |
595 | if( 32 <= shift) |
596 | shift = 0; |
597 | } |
598 | return code; |
599 | } |
600 | |
601 | /** |
602 | * Bit isSet |
603 | * |
604 | * @param position the bit to check |
605 | * |
606 | */ |
607 | public final boolean isSet(int position) |
608 | { |
609 | if (SanityManager.DEBUG) |
610 | { |
611 | if (position >= this.getLength()) |
612 | { |
613 | SanityManager.THROWASSERT( |
614 | "Attempt to get a bit position (" + position + |
615 | ")" + |
616 | "that exceeds the max length (" + this.getLength() + ")"); |
617 | } |
618 | } |
619 | |
620 | try { |
621 | |
622 | int bytepos = position / 8; |
623 | int bitpos = 7 - (position % 8); |
624 | |
625 | return ((value[bytepos] & (1 << bitpos)) != 0); |
626 | |
627 | } catch (ArrayIndexOutOfBoundsException e) { |
628 | // Should not happen, handle it just in case not all cases are tested |
629 | // by insane server. |
630 | return false; |
631 | } |
632 | } |
633 | |
634 | /** |
635 | * Bit get -- alias for isSet() |
636 | * |
637 | * @param position the bit to check |
638 | * |
639 | */ |
640 | public final boolean get(int position) |
641 | { |
642 | return isSet(position); |
643 | } |
644 | |
645 | /** |
646 | * Bit set |
647 | * |
648 | * @param position the bit to set |
649 | * |
650 | */ |
651 | public void set(int position) |
652 | { |
653 | |
654 | if (SanityManager.DEBUG) |
655 | { |
656 | if (position >= this.getLength()) |
657 | { |
658 | SanityManager.THROWASSERT( |
659 | "Attempt to set a bit position that exceeds the max length (" |
660 | + this.getLength() + ")"); |
661 | } |
662 | } |
663 | |
664 | // Should not happen, handle it just in case not all cases are tested |
665 | // by insane server. |
666 | if (position >= getLength()) |
667 | grow(position); |
668 | |
669 | int bytepos = position / 8; |
670 | int bitpos = 7 - (position % 8); |
671 | |
672 | value[bytepos] |= (1 << bitpos); |
673 | } |
674 | |
675 | /** |
676 | * Bit clear |
677 | * |
678 | * @param position the bit to clear |
679 | * |
680 | */ |
681 | public void clear(int position) |
682 | { |
683 | int bytepos; |
684 | int bitpos; |
685 | |
686 | if (SanityManager.DEBUG) |
687 | { |
688 | if (position >= this.getLength()) |
689 | { |
690 | SanityManager.THROWASSERT( |
691 | "Attempt to set a bit position that exceeds the max length (" |
692 | + this.getLength() + ")"); |
693 | } |
694 | } |
695 | |
696 | // Should not happen, handle it just in case not all cases are tested |
697 | // by insane server. |
698 | if (position >= getLength()) |
699 | grow(position); |
700 | |
701 | bytepos = position / 8; |
702 | bitpos = 7 - (position % 8); |
703 | |
704 | value[bytepos] &= ~(1 << bitpos); |
705 | } |
706 | |
707 | /** |
708 | Clear all the bits in this FormatableBitSet |
709 | */ |
710 | public void clear() |
711 | { |
712 | if (value == null) |
713 | return; |
714 | |
715 | int byteLength = getLengthInBytes(); |
716 | for (int ix=0; ix < byteLength; ix++) |
717 | value[ix] = 0; |
718 | } |
719 | |
720 | |
721 | /** |
722 | * Figure out how many bytes are needed to |
723 | * store the input number of bits. |
724 | * |
725 | * @param bits bits |
726 | * |
727 | * @return the number of bytes |
728 | */ |
729 | protected static int |
730 | numBytesFromBits(int bits) |
731 | { |
732 | return (bits == 0) ? 0 : ((bits - 1) / 8) + 1; |
733 | } |
734 | |
735 | /** |
736 | * Figure out how many bits are in the last |
737 | * byte from the total number of bits. |
738 | * |
739 | * @param bits bits |
740 | * |
741 | * @return the number of bits |
742 | */ |
743 | private static short |
744 | numBitsInLastByte(int bits) |
745 | { |
746 | int modulo = bits % 8; |
747 | return (short)((modulo == 0) ? |
748 | ((bits == 0) ? 0 : 8) : |
749 | modulo); |
750 | } |
751 | |
752 | /** |
753 | * Translate a hex character to a byte. |
754 | * |
755 | * @param hexChar A character with the value [0-9a-fA-F]. |
756 | * |
757 | * @return A byte with the numeric value corresponding to the hex character |
758 | */ |
759 | private static byte |
760 | hexCharToByte(char hexChar) |
761 | { |
762 | byte byteValue; |
763 | |
764 | switch (hexChar) |
765 | { |
766 | case '0': |
767 | byteValue = 0; |
768 | break; |
769 | |
770 | case '1': |
771 | byteValue = 1; |
772 | break; |
773 | |
774 | case '2': |
775 | byteValue = 2; |
776 | break; |
777 | |
778 | case '3': |
779 | byteValue = 3; |
780 | break; |
781 | |
782 | case '4': |
783 | byteValue = 4; |
784 | break; |
785 | |
786 | case '5': |
787 | byteValue = 5; |
788 | break; |
789 | |
790 | case '6': |
791 | byteValue = 6; |
792 | break; |
793 | |
794 | case '7': |
795 | byteValue = 7; |
796 | break; |
797 | |
798 | case '8': |
799 | byteValue = 8; |
800 | break; |
801 | |
802 | case '9': |
803 | byteValue = 9; |
804 | break; |
805 | |
806 | case 'a': |
807 | case 'A': |
808 | byteValue = 0xA; |
809 | break; |
810 | |
811 | case 'b': |
812 | case 'B': |
813 | byteValue = 0xB; |
814 | break; |
815 | |
816 | case 'c': |
817 | case 'C': |
818 | byteValue = 0xC; |
819 | break; |
820 | |
821 | case 'd': |
822 | case 'D': |
823 | byteValue = 0xD; |
824 | break; |
825 | |
826 | case 'e': |
827 | case 'E': |
828 | byteValue = 0xE; |
829 | break; |
830 | |
831 | case 'f': |
832 | case 'F': |
833 | byteValue = 0xF; |
834 | break; |
835 | |
836 | default: |
837 | if (SanityManager.DEBUG) |
838 | { |
839 | SanityManager.THROWASSERT("illegal char = " + hexChar); |
840 | } |
841 | throw new IllegalArgumentException(); |
842 | } |
843 | |
844 | return byteValue; |
845 | } |
846 | |
847 | private static char[] decodeArray = {'0', '1', '2', '3', '4', '5', '6', '7', |
848 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; |
849 | |
850 | /** |
851 | * Format the string into BitSet format: {0, 2, 4, 8} if bits 0, 2, 4, 8 |
852 | * are set. |
853 | * |
854 | * @return A new String containing the formatted Bit value |
855 | */ |
856 | public String toString() |
857 | { |
858 | char[] outChars; |
859 | int inPosition; |
860 | int outPosition; |
861 | int inByte; |
862 | |
863 | if (value == null) |
864 | { |
865 | return null; |
866 | } |
867 | { |
868 | // give it a reasonable size |
869 | StringBuffer str = new StringBuffer(getLength()*8*3); |
870 | str.append("{"); |
871 | boolean first = true; |
872 | for (inPosition = 0; inPosition < getLength(); inPosition++) |
873 | { |
874 | if (isSet(inPosition)) |
875 | { |
876 | if (!first) |
877 | str.append(", "); |
878 | first = false; |
879 | str.append(inPosition); |
880 | } |
881 | } |
882 | str.append("}"); |
883 | return new String(str); |
884 | } |
885 | } |
886 | |
887 | |
888 | |
889 | /** |
890 | * Statically calculates how many bits can fit into the number of |
891 | * bytes if this Bit object is externalized. Only valid for this |
892 | * implementation of Bit. |
893 | */ |
894 | public static int maxBitsForSpace(int numBytes) |
895 | { |
896 | return (numBytes - 4)*8; |
897 | |
898 | } |
899 | |
900 | /** |
901 | * If any bit is set, return the bit number of a bit that is set. |
902 | * If no bit is set, return -1; |
903 | * |
904 | * @return the bit number of a bit that is set, or -1 if no bit is set |
905 | */ |
906 | public int anySetBit() |
907 | { |
908 | int numbytes = getLengthInBytes(); |
909 | int bitpos; |
910 | |
911 | for (int i = 0; i < numbytes-1; i++) |
912 | { |
913 | if (value[i] != 0) |
914 | { |
915 | for (int j = 0; j < 8; j++) |
916 | { |
917 | bitpos = 7-j; |
918 | if (((1 << bitpos) & value[i]) != 0) |
919 | return ((i*8)+j); |
920 | } |
921 | } |
922 | } |
923 | |
924 | |
925 | // only the top part of the last byte is relevant |
926 | byte mask = (byte)(0xFF << (8-bitsInLastByte)); |
927 | if ((value[numbytes-1] & mask) != 0) |
928 | { |
929 | for (int j = 0; j < bitsInLastByte; j++) |
930 | { |
931 | bitpos = 7-j; |
932 | if (((1 << bitpos) & value[numbytes-1]) != 0) |
933 | return ((numbytes-1)*8)+j; |
934 | } |
935 | } |
936 | |
937 | return -1; |
938 | } |
939 | |
940 | /** |
941 | * Like anySetBit(), but return any set bit whose number is bigger than |
942 | * beyondBit. If no bit is set after beyondBit, -1 is returned. |
943 | * By using anySetBit() and anySetBit(beyondBit), one can quickly go |
944 | * thru the entire bit array to return all set bit. |
945 | * |
946 | * @param beyondBit only look at bit that is greater than this bit number |
947 | * @return the bit number of a bit that is set, or -1 if no bit after |
948 | * beyondBit is set |
949 | */ |
950 | public int anySetBit(int beyondBit) |
951 | { |
952 | if (SanityManager.DEBUG) |
953 | { |
954 | if (beyondBit >= this.getLength()) |
955 | SanityManager.THROWASSERT( |
956 | "Attempt to access bit position that exceeds the max length (" |
957 | + this.getLength() + ")"); |
958 | } |
959 | |
960 | int startingBit = (beyondBit+1); |
961 | |
962 | // we have seen the last bit. |
963 | if (startingBit >= this.getLength()) |
964 | return -1; |
965 | |
966 | int numbytes = getLengthInBytes(); |
967 | int startingByte = startingBit / 8; |
968 | int startingBitpos = startingBit % 8; |
969 | int bitpos; |
970 | byte mask; |
971 | |
972 | // see if any bits in this byte is set, only the bottom part of the |
973 | // first byte is relevant |
974 | mask = (byte)(0xFF >> startingBitpos); |
975 | |
976 | if (startingByte == numbytes-1) // starting byte == last byte |
977 | mask &= (byte)(0xFF << (8-bitsInLastByte)); |
978 | |
979 | if ((value[startingByte] & mask ) != 0) |
980 | { |
981 | // I know we will see the bit before bitsInLastByte even if we are |
982 | // at the last byte, no harm in going up to 8 in the loop |
983 | for (int j = startingBitpos; j < 8; j++) |
984 | { |
985 | if (SanityManager.DEBUG) |
986 | { |
987 | if (startingByte == numbytes-1) |
988 | SanityManager.ASSERT(j < bitsInLastByte, |
989 | "going beyond the last bit"); |
990 | } |
991 | bitpos = 7-j; |
992 | if (((1 << bitpos) & value[startingByte]) != 0) |
993 | { |
994 | return (startingByte*8+j); |
995 | } |
996 | } |
997 | } |
998 | |
999 | for (int i = (startingByte+1); i < numbytes-1; i++) |
1000 | { |
1001 | if (value[i] != 0) |
1002 | { |
1003 | for (int j = 0; j < 8; j++) |
1004 | { |
1005 | bitpos = 7-j; |
1006 | if (((1 << bitpos) & value[i]) != 0) |
1007 | { |
1008 | return ((i*8)+j); |
1009 | } |
1010 | } |
1011 | } |
1012 | } |
1013 | |
1014 | // Last byte if there are more than one bytes. Only the top part of |
1015 | // the last byte is relevant |
1016 | if (startingByte != numbytes-1) |
1017 | { |
1018 | mask = (byte)(0xFF << (8-bitsInLastByte)); |
1019 | |
1020 | if ((value[numbytes-1] & mask) != 0) |
1021 | { |
1022 | for (int j = 0; j < bitsInLastByte; j++) |
1023 | { |
1024 | bitpos = 7-j; |
1025 | if (((1 << bitpos) & value[numbytes-1]) != 0) |
1026 | { |
1027 | return ((numbytes-1)*8)+j; |
1028 | } |
1029 | } |
1030 | } |
1031 | } |
1032 | |
1033 | return -1; |
1034 | |
1035 | } |
1036 | |
1037 | /** |
1038 | * Bitwise OR this Bit with another Bit. |
1039 | * |
1040 | * @param otherBit the other Bit |
1041 | */ |
1042 | public void or(FormatableBitSet otherBit) |
1043 | { |
1044 | if (otherBit == null || otherBit.getLength() == 0) |
1045 | return; |
1046 | |
1047 | int otherLength = otherBit.getLength(); |
1048 | |
1049 | if (otherLength > getLength()) |
1050 | grow(otherLength); // expand this bit |
1051 | |
1052 | if (otherBit instanceof FormatableBitSet) |
1053 | { |
1054 | // we know the bit ordering, optimize this |
1055 | FormatableBitSet ob = (FormatableBitSet)otherBit; |
1056 | int obByteLen = ob.getLengthInBytes(); |
1057 | for (int i = 0; i < obByteLen-1; i++) |
1058 | value[i] |= ob.value[i]; |
1059 | |
1060 | // do the last byte bit by bit |
1061 | for (int i = (obByteLen-1)*8; i < otherLength; i++) |
1062 | if (otherBit.isSet(i)) |
1063 | set(i); |
1064 | } |
1065 | else |
1066 | { |
1067 | // we don't know the bit ordering, call thru the interface and go |
1068 | // thru bit by bit |
1069 | // this bit impl's length >= other bit's length |
1070 | |
1071 | for (int i = 0; i < otherLength; i++) |
1072 | { |
1073 | if (otherBit.isSet(i)) |
1074 | set(i); |
1075 | } |
1076 | } |
1077 | } |
1078 | |
1079 | /** |
1080 | * Bitwise AND this Bit with another Bit. |
1081 | * |
1082 | * @param otherBit the other Bit |
1083 | */ |
1084 | public void and(FormatableBitSet otherBit) |
1085 | { |
1086 | if (SanityManager.DEBUG) |
1087 | SanityManager.ASSERT(otherBit != null, "cannot AND null with a FormatableBitSet"); |
1088 | |
1089 | int otherLength = otherBit.getLength(); |
1090 | |
1091 | // Supposedly cannot happen, but handle it just in case. |
1092 | if (otherLength > getLength()) |
1093 | grow(otherLength); // expand this bit |
1094 | |
1095 | if (otherLength < getLength()) |
1096 | { |
1097 | // clear all bits that are not in the other bit |
1098 | int startingByte = (otherLength * 8) + 1; |
1099 | int len = getLengthInBytes(); |
1100 | for (int i = startingByte; i < len; i++) |
1101 | value[i] = 0; |
1102 | |
1103 | for (int i = otherLength; i < startingByte*8; i++) |
1104 | { |
1105 | if (i < getLength()) |
1106 | clear(i); |
1107 | else |
1108 | break; |
1109 | } |
1110 | } |
1111 | |
1112 | if (otherLength == 0) |
1113 | return; |
1114 | |
1115 | int length = otherBit.getLengthInBytes() < getLengthInBytes() ? |
1116 | otherBit.getLengthInBytes() : getLengthInBytes(); |
1117 | |
1118 | for (int i = 0; i < length; i++) |
1119 | value[i] &= otherBit.value[i]; |
1120 | } |
1121 | |
1122 | /** |
1123 | * Logically XORs this FormatableBitSet with the specified FormatableBitSet. |
1124 | * @param set The FormatableBitSet to be XORed with. |
1125 | */ |
1126 | public void xor(FormatableBitSet set) |
1127 | { |
1128 | if (SanityManager.DEBUG) |
1129 | { |
1130 | if (getLength() != set.getLength()) |
1131 | { |
1132 | SanityManager.THROWASSERT("getLength() (" + getLength() + |
1133 | ") and set.getLength() (" + |
1134 | set.getLength() + |
1135 | ") expected to be the same"); |
1136 | } |
1137 | } |
1138 | |
1139 | int setLength = set.getLength(); |
1140 | for (int i = setLength; i-- > 0; ) |
1141 | { |
1142 | if (isSet(i) && set.isSet(i)) |
1143 | { |
1144 | clear(i); |
1145 | } |
1146 | else if (isSet(i) || set.isSet(i)) |
1147 | { |
1148 | set(i); |
1149 | } |
1150 | } |
1151 | } |
1152 | |
1153 | /** |
1154 | * Get a count of the number of bits that are set. |
1155 | * |
1156 | * @return The number of bits that are set. |
1157 | */ |
1158 | public int getNumBitsSet() |
1159 | { |
1160 | int count = 0; |
1161 | |
1162 | for (int index = getLength() - 1; index >= 0; index--) |
1163 | { |
1164 | if (isSet(index)) |
1165 | { |
1166 | count++; |
1167 | } |
1168 | } |
1169 | |
1170 | return count; |
1171 | } |
1172 | |
1173 | ///////////////////////////////////////////////////////// |
1174 | // |
1175 | // EXTERNALIZABLE |
1176 | // |
1177 | ///////////////////////////////////////////////////////// |
1178 | /** |
1179 | * Format: <UL> |
1180 | * <LI>int length in bits </LI> |
1181 | * <LI>byte[] </LI></UL> |
1182 | * |
1183 | * @see java.io.Externalizable#writeExternal |
1184 | */ |
1185 | public void writeExternal(ObjectOutput out) throws IOException |
1186 | { |
1187 | // never called when value is null |
1188 | if (SanityManager.DEBUG) |
1189 | SanityManager.ASSERT(value != null); |
1190 | |
1191 | out.writeInt(getLength()); |
1192 | int byteLen = getLengthInBytes(); |
1193 | if (byteLen > 0) |
1194 | { |
1195 | out.write(value, 0, byteLen); |
1196 | } |
1197 | } |
1198 | |
1199 | /** |
1200 | * Note: gracefully handles zero length |
1201 | * bits -- will create a zero length array |
1202 | * with no bits being used. Fortunately |
1203 | * in.read() is ok with a zero length array |
1204 | * so no special code. |
1205 | * <p> |
1206 | * WARNING: this method cannot be changed w/o |
1207 | * changing SQLBit because SQLBit calls this |
1208 | * directly w/o calling read/writeObject(), so |
1209 | * the format id is not stored in that case. |
1210 | * |
1211 | * @see java.io.Externalizable#readExternal |
1212 | */ |
1213 | public void readExternal(ObjectInput in) throws IOException |
1214 | { |
1215 | int lenInBits; |
1216 | int lenInBytes; |
1217 | |
1218 | lenInBits = in.readInt(); |
1219 | |
1220 | lenInBytes = FormatableBitSet.numBytesFromBits(lenInBits); |
1221 | |
1222 | |
1223 | /* |
1224 | ** How can lenInBytes be zero? The implication is |
1225 | ** that lenInBits is zero. Well, the reason this can |
1226 | ** happen is that the store will reset our stream |
1227 | ** out from underneath us if we are a Bit column that |
1228 | ** overflows onto another page because it assumes that |
1229 | ** we want to stream it in specially. Because of this warped |
1230 | ** API, our readInt() will return 0 even though our |
1231 | ** writeExternal() did a writeInt(xxx). The upshot |
1232 | ** is that you should leave the following alone. |
1233 | */ |
1234 | |
1235 | value = new byte[lenInBytes]; |
1236 | |
1237 | in.readFully(value); |
1238 | |
1239 | bitsInLastByte = numBitsInLastByte(lenInBits); |
1240 | |
1241 | lengthAsBits = lenInBits; |
1242 | } |
1243 | |
1244 | public void readExternalFromArray(ArrayInputStream in) throws IOException |
1245 | { |
1246 | int lenInBits = in.readInt(); |
1247 | |
1248 | int lenInBytes = FormatableBitSet.numBytesFromBits(lenInBits); |
1249 | |
1250 | value = new byte[lenInBytes]; |
1251 | |
1252 | in.readFully(value); |
1253 | |
1254 | bitsInLastByte = numBitsInLastByte(lenInBits); |
1255 | |
1256 | lengthAsBits = lenInBits; |
1257 | } |
1258 | |
1259 | /** |
1260 | * Get the formatID which corresponds to this class. |
1261 | * |
1262 | * @return the formatID of this class |
1263 | */ |
1264 | public int getTypeFormatId() { return StoredFormatIds.BITIMPL_V01_ID; } |
1265 | } |