1 /* 2 * Copyright 2001-2004 The Apache Software Foundation. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.apache.commons.codec.binary; 18 19 import org.apache.commons.codec.BinaryDecoder; 20 import org.apache.commons.codec.BinaryEncoder; 21 import org.apache.commons.codec.DecoderException; 22 import org.apache.commons.codec.EncoderException; 23 24 /** 25 * Translates between byte arrays and strings of "0"s and "1"s. 26 * 27 * <b>TODO:</b> may want to add more bit vector functions like and/or/xor/nand. 28 * <B>TODO:</b> also might be good to generate boolean[] 29 * from byte[] et. cetera. 30 * 31 * @author Apache Software Foundation 32 * @since 1.3 33 * @version $Id $ 34 */ 35 public class BinaryCodec implements BinaryDecoder, BinaryEncoder { 36 /* 37 * tried to avoid using ArrayUtils to minimize dependencies while using these empty arrays - dep is just not worth 38 * it. 39 */ 40 /** Empty char array. */ 41 private static final char[] EMPTY_CHAR_ARRAY = new char[0]; 42 43 /** Empty byte array. */ 44 private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; 45 46 /** Mask for bit 0 of a byte. */ 47 private static final int BIT_0 = 1; 48 49 /** Mask for bit 1 of a byte. */ 50 private static final int BIT_1 = 0x02; 51 52 /** Mask for bit 2 of a byte. */ 53 private static final int BIT_2 = 0x04; 54 55 /** Mask for bit 3 of a byte. */ 56 private static final int BIT_3 = 0x08; 57 58 /** Mask for bit 4 of a byte. */ 59 private static final int BIT_4 = 0x10; 60 61 /** Mask for bit 5 of a byte. */ 62 private static final int BIT_5 = 0x20; 63 64 /** Mask for bit 6 of a byte. */ 65 private static final int BIT_6 = 0x40; 66 67 /** Mask for bit 7 of a byte. */ 68 private static final int BIT_7 = 0x80; 69 70 private static final int[] BITS = {BIT_0, BIT_1, BIT_2, BIT_3, BIT_4, BIT_5, BIT_6, BIT_7}; 71 72 /** 73 * Converts an array of raw binary data into an array of ascii 0 and 1 characters. 74 * 75 * @param raw 76 * the raw binary data to convert 77 * @return 0 and 1 ascii character bytes one for each bit of the argument 78 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 79 */ encode(byte[] raw)80 public byte[] encode(byte[] raw) { 81 return toAsciiBytes(raw); 82 } 83 84 /** 85 * Converts an array of raw binary data into an array of ascii 0 and 1 chars. 86 * 87 * @param raw 88 * the raw binary data to convert 89 * @return 0 and 1 ascii character chars one for each bit of the argument 90 * @throws EncoderException 91 * if the argument is not a byte[] 92 * @see org.apache.commons.codec.Encoder#encode(java.lang.Object) 93 */ encode(Object raw)94 public Object encode(Object raw) throws EncoderException { 95 if (!(raw instanceof byte[])) { 96 throw new EncoderException("argument not a byte array"); 97 } 98 return toAsciiChars((byte[]) raw); 99 } 100 101 /** 102 * Decodes a byte array where each byte represents an ascii '0' or '1'. 103 * 104 * @param ascii 105 * each byte represents an ascii '0' or '1' 106 * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument 107 * @throws DecoderException 108 * if argument is not a byte[], char[] or String 109 * @see org.apache.commons.codec.Decoder#decode(java.lang.Object) 110 */ decode(Object ascii)111 public Object decode(Object ascii) throws DecoderException { 112 if (ascii == null) { 113 return EMPTY_BYTE_ARRAY; 114 } 115 if (ascii instanceof byte[]) { 116 return fromAscii((byte[]) ascii); 117 } 118 if (ascii instanceof char[]) { 119 return fromAscii((char[]) ascii); 120 } 121 if (ascii instanceof String) { 122 return fromAscii(((String) ascii).toCharArray()); 123 } 124 throw new DecoderException("argument not a byte array"); 125 } 126 127 /** 128 * Decodes a byte array where each byte represents an ascii '0' or '1'. 129 * 130 * @param ascii 131 * each byte represents an ascii '0' or '1' 132 * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument 133 * @see org.apache.commons.codec.Decoder#decode(Object) 134 */ decode(byte[] ascii)135 public byte[] decode(byte[] ascii) { 136 return fromAscii(ascii); 137 } 138 139 /** 140 * Decodes a String where each char of the String represents an ascii '0' or '1'. 141 * 142 * @param ascii 143 * String of '0' and '1' characters 144 * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument 145 * @see org.apache.commons.codec.Decoder#decode(Object) 146 */ toByteArray(String ascii)147 public byte[] toByteArray(String ascii) { 148 if (ascii == null) { 149 return EMPTY_BYTE_ARRAY; 150 } 151 return fromAscii(ascii.toCharArray()); 152 } 153 154 // ------------------------------------------------------------------------ 155 // 156 // static codec operations 157 // 158 // ------------------------------------------------------------------------ 159 /** 160 * Decodes a byte array where each char represents an ascii '0' or '1'. 161 * 162 * @param ascii 163 * each char represents an ascii '0' or '1' 164 * @return the raw encoded binary where each bit corresponds to a char in the char array argument 165 */ fromAscii(char[] ascii)166 public static byte[] fromAscii(char[] ascii) { 167 if (ascii == null || ascii.length == 0) { 168 return EMPTY_BYTE_ARRAY; 169 } 170 // get length/8 times bytes with 3 bit shifts to the right of the length 171 byte[] l_raw = new byte[ascii.length >> 3]; 172 /* 173 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 174 * loop. 175 */ 176 for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) { 177 for (int bits = 0; bits < BITS.length; ++bits) { 178 if (ascii[jj - bits] == '1') { 179 l_raw[ii] |= BITS[bits]; 180 } 181 } 182 } 183 return l_raw; 184 } 185 186 /** 187 * Decodes a byte array where each byte represents an ascii '0' or '1'. 188 * 189 * @param ascii 190 * each byte represents an ascii '0' or '1' 191 * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument 192 */ fromAscii(byte[] ascii)193 public static byte[] fromAscii(byte[] ascii) { 194 if (ascii == null || ascii.length == 0) { 195 return EMPTY_BYTE_ARRAY; 196 } 197 // get length/8 times bytes with 3 bit shifts to the right of the length 198 byte[] l_raw = new byte[ascii.length >> 3]; 199 /* 200 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 201 * loop. 202 */ 203 for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) { 204 for (int bits = 0; bits < BITS.length; ++bits) { 205 if (ascii[jj - bits] == '1') { 206 l_raw[ii] |= BITS[bits]; 207 } 208 } 209 } 210 return l_raw; 211 } 212 213 /** 214 * Converts an array of raw binary data into an array of ascii 0 and 1 character bytes - each byte is a truncated 215 * char. 216 * 217 * @param raw 218 * the raw binary data to convert 219 * @return an array of 0 and 1 character bytes for each bit of the argument 220 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 221 */ toAsciiBytes(byte[] raw)222 public static byte[] toAsciiBytes(byte[] raw) { 223 if (raw == null || raw.length == 0) { 224 return EMPTY_BYTE_ARRAY; 225 } 226 // get 8 times the bytes with 3 bit shifts to the left of the length 227 byte[] l_ascii = new byte[raw.length << 3]; 228 /* 229 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 230 * loop. 231 */ 232 for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) { 233 for (int bits = 0; bits < BITS.length; ++bits) { 234 if ((raw[ii] & BITS[bits]) == 0) { 235 l_ascii[jj - bits] = '0'; 236 } else { 237 l_ascii[jj - bits] = '1'; 238 } 239 } 240 } 241 return l_ascii; 242 } 243 244 /** 245 * Converts an array of raw binary data into an array of ascii 0 and 1 characters. 246 * 247 * @param raw 248 * the raw binary data to convert 249 * @return an array of 0 and 1 characters for each bit of the argument 250 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 251 */ toAsciiChars(byte[] raw)252 public static char[] toAsciiChars(byte[] raw) { 253 if (raw == null || raw.length == 0) { 254 return EMPTY_CHAR_ARRAY; 255 } 256 // get 8 times the bytes with 3 bit shifts to the left of the length 257 char[] l_ascii = new char[raw.length << 3]; 258 /* 259 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 260 * loop. 261 */ 262 for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) { 263 for (int bits = 0; bits < BITS.length; ++bits) { 264 if ((raw[ii] & BITS[bits]) == 0) { 265 l_ascii[jj - bits] = '0'; 266 } else { 267 l_ascii[jj - bits] = '1'; 268 } 269 } 270 } 271 return l_ascii; 272 } 273 274 /** 275 * Converts an array of raw binary data into a String of ascii 0 and 1 characters. 276 * 277 * @param raw 278 * the raw binary data to convert 279 * @return a String of 0 and 1 characters representing the binary data 280 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 281 */ toAsciiString(byte[] raw)282 public static String toAsciiString(byte[] raw) { 283 return new String(toAsciiChars(raw)); 284 } 285 } 286