001: /*
002: * Copyright 2001-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.commons.codec.binary;
018:
019: import org.apache.commons.codec.BinaryDecoder;
020: import org.apache.commons.codec.BinaryEncoder;
021: import org.apache.commons.codec.DecoderException;
022: import org.apache.commons.codec.EncoderException;
023:
024: /**
025: * Translates between byte arrays and strings of "0"s and "1"s.
026: *
027: * @todo may want to add more bit vector functions like and/or/xor/nand
028: * @todo also might be good to generate boolean[]
029: * from byte[] et. cetera.
030: *
031: * @author Apache Software Foundation
032: * @since 1.3
033: * @version $Id $
034: */
035: public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
036: /*
037: * tried to avoid using ArrayUtils to minimize dependencies while using these empty arrays - dep is just not worth
038: * it.
039: */
040: /** Empty char array. */
041: private static final char[] EMPTY_CHAR_ARRAY = new char[0];
042:
043: /** Empty byte array. */
044: private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
045:
046: /** Mask for bit 0 of a byte. */
047: private static final int BIT_0 = 1;
048:
049: /** Mask for bit 1 of a byte. */
050: private static final int BIT_1 = 0x02;
051:
052: /** Mask for bit 2 of a byte. */
053: private static final int BIT_2 = 0x04;
054:
055: /** Mask for bit 3 of a byte. */
056: private static final int BIT_3 = 0x08;
057:
058: /** Mask for bit 4 of a byte. */
059: private static final int BIT_4 = 0x10;
060:
061: /** Mask for bit 5 of a byte. */
062: private static final int BIT_5 = 0x20;
063:
064: /** Mask for bit 6 of a byte. */
065: private static final int BIT_6 = 0x40;
066:
067: /** Mask for bit 7 of a byte. */
068: private static final int BIT_7 = 0x80;
069:
070: private static final int[] BITS = { BIT_0, BIT_1, BIT_2, BIT_3,
071: BIT_4, BIT_5, BIT_6, BIT_7 };
072:
073: /**
074: * Converts an array of raw binary data into an array of ascii 0 and 1 characters.
075: *
076: * @param raw
077: * the raw binary data to convert
078: * @return 0 and 1 ascii character bytes one for each bit of the argument
079: * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
080: */
081: public byte[] encode(byte[] raw) {
082: return toAsciiBytes(raw);
083: }
084:
085: /**
086: * Converts an array of raw binary data into an array of ascii 0 and 1 chars.
087: *
088: * @param raw
089: * the raw binary data to convert
090: * @return 0 and 1 ascii character chars one for each bit of the argument
091: * @throws EncoderException
092: * if the argument is not a byte[]
093: * @see org.apache.commons.codec.Encoder#encode(java.lang.Object)
094: */
095: public Object encode(Object raw) throws EncoderException {
096: if (!(raw instanceof byte[])) {
097: throw new EncoderException("argument not a byte array");
098: }
099: return toAsciiChars((byte[]) raw);
100: }
101:
102: /**
103: * Decodes a byte array where each byte represents an ascii '0' or '1'.
104: *
105: * @param ascii
106: * each byte represents an ascii '0' or '1'
107: * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
108: * @throws DecoderException
109: * if argument is not a byte[], char[] or String
110: * @see org.apache.commons.codec.Decoder#decode(java.lang.Object)
111: */
112: public Object decode(Object ascii) throws DecoderException {
113: if (ascii == null) {
114: return EMPTY_BYTE_ARRAY;
115: }
116: if (ascii instanceof byte[]) {
117: return fromAscii((byte[]) ascii);
118: }
119: if (ascii instanceof char[]) {
120: return fromAscii((char[]) ascii);
121: }
122: if (ascii instanceof String) {
123: return fromAscii(((String) ascii).toCharArray());
124: }
125: throw new DecoderException("argument not a byte array");
126: }
127:
128: /**
129: * Decodes a byte array where each byte represents an ascii '0' or '1'.
130: *
131: * @param ascii
132: * each byte represents an ascii '0' or '1'
133: * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
134: * @see org.apache.commons.codec.Decoder#decode(Object)
135: */
136: public byte[] decode(byte[] ascii) {
137: return fromAscii(ascii);
138: }
139:
140: /**
141: * Decodes a String where each char of the String represents an ascii '0' or '1'.
142: *
143: * @param ascii
144: * String of '0' and '1' characters
145: * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
146: * @see org.apache.commons.codec.Decoder#decode(Object)
147: */
148: public byte[] toByteArray(String ascii) {
149: if (ascii == null) {
150: return EMPTY_BYTE_ARRAY;
151: }
152: return fromAscii(ascii.toCharArray());
153: }
154:
155: // ------------------------------------------------------------------------
156: //
157: // static codec operations
158: //
159: // ------------------------------------------------------------------------
160: /**
161: * Decodes a byte array where each char represents an ascii '0' or '1'.
162: *
163: * @param ascii
164: * each char represents an ascii '0' or '1'
165: * @return the raw encoded binary where each bit corresponds to a char in the char array argument
166: */
167: public static byte[] fromAscii(char[] ascii) {
168: if (ascii == null || ascii.length == 0) {
169: return EMPTY_BYTE_ARRAY;
170: }
171: // get length/8 times bytes with 3 bit shifts to the right of the length
172: byte[] l_raw = new byte[ascii.length >> 3];
173: /*
174: * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
175: * loop.
176: */
177: for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) {
178: for (int bits = 0; bits < BITS.length; ++bits) {
179: if (ascii[jj - bits] == '1') {
180: l_raw[ii] |= BITS[bits];
181: }
182: }
183: }
184: return l_raw;
185: }
186:
187: /**
188: * Decodes a byte array where each byte represents an ascii '0' or '1'.
189: *
190: * @param ascii
191: * each byte represents an ascii '0' or '1'
192: * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
193: */
194: public static byte[] fromAscii(byte[] ascii) {
195: if (ascii == null || ascii.length == 0) {
196: return EMPTY_BYTE_ARRAY;
197: }
198: // get length/8 times bytes with 3 bit shifts to the right of the length
199: byte[] l_raw = new byte[ascii.length >> 3];
200: /*
201: * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
202: * loop.
203: */
204: for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) {
205: for (int bits = 0; bits < BITS.length; ++bits) {
206: if (ascii[jj - bits] == '1') {
207: l_raw[ii] |= BITS[bits];
208: }
209: }
210: }
211: return l_raw;
212: }
213:
214: /**
215: * Converts an array of raw binary data into an array of ascii 0 and 1 character bytes - each byte is a truncated
216: * char.
217: *
218: * @param raw
219: * the raw binary data to convert
220: * @return an array of 0 and 1 character bytes for each bit of the argument
221: * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
222: */
223: public static byte[] toAsciiBytes(byte[] raw) {
224: if (raw == null || raw.length == 0) {
225: return EMPTY_BYTE_ARRAY;
226: }
227: // get 8 times the bytes with 3 bit shifts to the left of the length
228: byte[] l_ascii = new byte[raw.length << 3];
229: /*
230: * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
231: * loop.
232: */
233: for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
234: for (int bits = 0; bits < BITS.length; ++bits) {
235: if ((raw[ii] & BITS[bits]) == 0) {
236: l_ascii[jj - bits] = '0';
237: } else {
238: l_ascii[jj - bits] = '1';
239: }
240: }
241: }
242: return l_ascii;
243: }
244:
245: /**
246: * Converts an array of raw binary data into an array of ascii 0 and 1 characters.
247: *
248: * @param raw
249: * the raw binary data to convert
250: * @return an array of 0 and 1 characters for each bit of the argument
251: * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
252: */
253: public static char[] toAsciiChars(byte[] raw) {
254: if (raw == null || raw.length == 0) {
255: return EMPTY_CHAR_ARRAY;
256: }
257: // get 8 times the bytes with 3 bit shifts to the left of the length
258: char[] l_ascii = new char[raw.length << 3];
259: /*
260: * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
261: * loop.
262: */
263: for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
264: for (int bits = 0; bits < BITS.length; ++bits) {
265: if ((raw[ii] & BITS[bits]) == 0) {
266: l_ascii[jj - bits] = '0';
267: } else {
268: l_ascii[jj - bits] = '1';
269: }
270: }
271: }
272: return l_ascii;
273: }
274:
275: /**
276: * Converts an array of raw binary data into a String of ascii 0 and 1 characters.
277: *
278: * @param raw
279: * the raw binary data to convert
280: * @return a String of 0 and 1 characters representing the binary data
281: * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
282: */
283: public static String toAsciiString(byte[] raw) {
284: return new String(toAsciiChars(raw));
285: }
286: }
|