001: /*
002:
003: Derby - Class org.apache.derbyTesting.unitTests.util.BitUtil
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyTesting.unitTests.util;
023:
024: /**
025: * This class provides utility methods for
026: * converting byte arrays to hexidecimal Strings and manipulating BIT/BIT VARYING values as a
027: * packed vector of booleans.
028: *
029: * <P> The BIT/BIT VARYING methods are modeled after
030: * some methods in the <I>java.util.BitSet</I> class.
031: * An alternative to using a SQL BIT (VARYING) column
032: * in conjunction with the methods provided herein to
033: * provide bit manipulation would be to use a serialized
034: * <I>java.util.BitSet</I> column instead.
035: * <p>
036: * This class contains the following static methods: <UL>
037: * <LI> void <B>set</B>(byte[] bytes, int position) to set a bit</LI>
038: * <LI> void <B>clear</B>(byte[] bytes, int position) to clear a bit</LI>
039: * <LI> boolean <B>get</B>(byte[] bytes, int position) to get the
040: * bit status </LI> </UL>
041: * <p>
042: * Since these methods effectively allow a SQL BIT to be
043: * considered as an array of booleans, all offsets (position
044: * parameters) are zero based. So if you want to set
045: * the first bit of a BIT type, you would use <I>
046: * set(MyBitColumn, 0) </I>.
047: * <p>
048: * Examples: <UL>
049: * <LI> SELECT BitUtil::get(bitcol, 2) FROM mytab </LI>
050: * <LI> UPDATE mytab SET bitcol = BitUtil::set(bitcol, 2) </LI>
051: * <LI> UPDATE mytab SET bitcol = BitUtil::clear(bitcol, 2) </LI> </UL>
052: *
053: */
054: public class BitUtil {
055: /**
056: * Set the bit at the specified position
057: *
058: * @param bytes the byte array
059: * @param position the bit to set, starting from zero
060: *
061: * @return the byte array with the set bit
062: *
063: * @exception IndexOutOfBoundsException on bad position
064: */
065: public static byte[] set(byte[] bytes, int position) {
066: if (position >= 0) {
067: int bytepos = position >> 3;
068: if (bytepos < bytes.length) {
069: int bitpos = 7 - (position % 8);
070:
071: bytes[bytepos] |= (1 << bitpos);
072: return bytes;
073: }
074: }
075: throw new IndexOutOfBoundsException(Integer.toString(position));
076: }
077:
078: /**
079: * Clear the bit at the specified position
080: *
081: * @param bytes the byte array
082: * @param position the bit to clear, starting from zero
083: *
084: * @return the byte array with the cleared bit
085: *
086: * @exception IndexOutOfBoundsException on bad position
087: */
088: public static byte[] clear(byte[] bytes, int position) {
089: if (position >= 0) {
090: int bytepos = position >> 3;
091: if (bytepos < bytes.length) {
092: int bitpos = 7 - (position % 8);
093: bytes[bytepos] &= ~(1 << bitpos);
094: return bytes;
095: }
096: }
097:
098: throw new IndexOutOfBoundsException(Integer.toString(position));
099: }
100:
101: /**
102: * Check to see if the specified bit is set
103: *
104: * @param bytes the byte array
105: * @param position the bit to check, starting from zero
106: *
107: * @return true/false
108: *
109: * @exception IndexOutOfBoundsException on bad position
110: */
111: public static boolean get(byte[] bytes, int position) {
112: if (position >= 0) {
113: int bytepos = position >> 3;
114: if (bytepos < bytes.length) {
115: int bitpos = 7 - (position % 8);
116: return ((bytes[bytepos] & (1 << bitpos)) != 0);
117: }
118: }
119: throw new IndexOutOfBoundsException(Integer.toString(position));
120: }
121:
122: private static char[] hex_table = { '0', '1', '2', '3', '4', '5',
123: '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
124:
125: /**
126: Convert a byte array to a human-readable String for debugging purposes.
127: */
128: public static String hexDump(byte[] data) {
129: byte byte_value;
130:
131: StringBuffer str = new StringBuffer(data.length * 3);
132:
133: str.append("Hex dump:\n");
134:
135: for (int i = 0; i < data.length; i += 16) {
136: // dump the header: 00000000:
137: String offset = Integer.toHexString(i);
138:
139: // "0" left pad offset field so it is always 8 char's long.
140: for (int offlen = offset.length(); offlen < 8; offlen++)
141: str.append("0");
142: str.append(offset);
143: str.append(":");
144:
145: // dump hex version of 16 bytes per line.
146: for (int j = 0; (j < 16) && ((i + j) < data.length); j++) {
147: byte_value = data[i + j];
148:
149: // add spaces between every 2 bytes.
150: if ((j % 2) == 0)
151: str.append(" ");
152:
153: // dump a single byte.
154: byte high_nibble = (byte) ((byte_value & 0xf0) >>> 4);
155: byte low_nibble = (byte) (byte_value & 0x0f);
156:
157: str.append(hex_table[high_nibble]);
158: str.append(hex_table[low_nibble]);
159: }
160:
161: // dump ascii version of 16 bytes
162: str.append(" ");
163:
164: for (int j = 0; (j < 16) && ((i + j) < data.length); j++) {
165: char char_value = (char) data[i + j];
166:
167: // RESOLVE (really want isAscii() or isPrintable())
168: if (Character.isLetterOrDigit(char_value))
169: str.append(String.valueOf(char_value));
170: else
171: str.append(".");
172: }
173:
174: // new line
175: str.append("\n");
176: }
177: return (str.toString());
178:
179: }
180: }
|