1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.codec.binary; 19 20 import org.apache.commons.codec.BinaryDecoder; 21 import org.apache.commons.codec.BinaryEncoder; 22 import org.apache.commons.codec.DecoderException; 23 import org.apache.commons.codec.EncoderException; 24 25 /** 26 * Converts between byte arrays and strings of "0"s and "1"s. 27 * 28 * <p>This class is immutable and thread-safe.</p> 29 * 30 * TODO: may want to add more bit vector functions like and/or/xor/nand 31 * TODO: also might be good to generate boolean[] from byte[] et cetera. 32 * 33 * @since 1.3 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 = {}; 42 43 /** Empty byte array. */ 44 private static final byte[] EMPTY_BYTE_ARRAY = {}; 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 * Decodes a byte array where each byte represents an ASCII '0' or '1'. 74 * 75 * @param ascii 76 * each byte represents an ASCII '0' or '1' 77 * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument 78 */ 79 public static byte[] fromAscii(final byte[] ascii) { 80 if (isEmpty(ascii)) { 81 return EMPTY_BYTE_ARRAY; 82 } 83 final int asciiLength = ascii.length; 84 // get length/8 times bytes with 3 bit shifts to the right of the length 85 final byte[] raw = new byte[asciiLength >> 3]; 86 /* 87 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 88 * loop. 89 */ 90 for (int ii = 0, jj = asciiLength - 1; ii < raw.length; ii++, jj -= 8) { 91 for (int bits = 0; bits < BITS.length; ++bits) { 92 if (ascii[jj - bits] == '1') { 93 raw[ii] |= BITS[bits]; 94 } 95 } 96 } 97 return raw; 98 } 99 100 /** 101 * Decodes a char array where each char represents an ASCII '0' or '1'. 102 * 103 * @param ascii 104 * each char represents an ASCII '0' or '1' 105 * @return the raw encoded binary where each bit corresponds to a char in the char array argument 106 */ 107 public static byte[] fromAscii(final char[] ascii) { 108 if (ascii == null || ascii.length == 0) { 109 return EMPTY_BYTE_ARRAY; 110 } 111 final int asciiLength = ascii.length; 112 // get length/8 times bytes with 3 bit shifts to the right of the length 113 final byte[] raw = new byte[asciiLength >> 3]; 114 /* 115 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 116 * loop. 117 */ 118 for (int ii = 0, jj = asciiLength - 1; ii < raw.length; ii++, jj -= 8) { 119 for (int bits = 0; bits < BITS.length; ++bits) { 120 if (ascii[jj - bits] == '1') { 121 raw[ii] |= BITS[bits]; 122 } 123 } 124 } 125 return raw; 126 } 127 128 /** 129 * Returns {@code true} if the given array is {@code null} or empty (size 0.) 130 * 131 * @param array 132 * the source array 133 * @return {@code true} if the given array is {@code null} or empty (size 0.) 134 */ 135 static boolean isEmpty(final byte[] array) { 136 return array == null || array.length == 0; 137 } 138 139 /** 140 * Converts an array of raw binary data into an array of ASCII 0 and 1 character bytes - each byte is a truncated 141 * char. 142 * 143 * @param raw 144 * the raw binary data to convert 145 * @return an array of 0 and 1 character bytes for each bit of the argument 146 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 147 */ 148 public static byte[] toAsciiBytes(final byte[] raw) { 149 if (isEmpty(raw)) { 150 return EMPTY_BYTE_ARRAY; 151 } 152 final int rawLength = raw.length; 153 // get 8 times the bytes with 3 bit shifts to the left of the length 154 final byte[] l_ascii = new byte[rawLength << 3]; 155 /* 156 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 157 * loop. 158 */ 159 for (int ii = 0, jj = l_ascii.length - 1; ii < rawLength; ii++, jj -= 8) { 160 for (int bits = 0; bits < BITS.length; ++bits) { 161 if ((raw[ii] & BITS[bits]) == 0) { 162 l_ascii[jj - bits] = '0'; 163 } else { 164 l_ascii[jj - bits] = '1'; 165 } 166 } 167 } 168 return l_ascii; 169 } 170 171 /** 172 * Converts an array of raw binary data into an array of ASCII 0 and 1 characters. 173 * 174 * @param raw 175 * the raw binary data to convert 176 * @return an array of 0 and 1 characters for each bit of the argument 177 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 178 */ 179 public static char[] toAsciiChars(final byte[] raw) { 180 if (isEmpty(raw)) { 181 return EMPTY_CHAR_ARRAY; 182 } 183 final int rawLength = raw.length; 184 // get 8 times the bytes with 3 bit shifts to the left of the length 185 final char[] l_ascii = new char[rawLength << 3]; 186 /* 187 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 188 * loop. 189 */ 190 for (int ii = 0, jj = l_ascii.length - 1; ii < rawLength; ii++, jj -= 8) { 191 for (int bits = 0; bits < BITS.length; ++bits) { 192 if ((raw[ii] & BITS[bits]) == 0) { 193 l_ascii[jj - bits] = '0'; 194 } else { 195 l_ascii[jj - bits] = '1'; 196 } 197 } 198 } 199 return l_ascii; 200 } 201 202 /** 203 * Converts an array of raw binary data into a String of ASCII 0 and 1 characters. 204 * 205 * @param raw 206 * the raw binary data to convert 207 * @return a String of 0 and 1 characters representing the binary data 208 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 209 */ 210 public static String toAsciiString(final byte[] raw) { 211 return new String(toAsciiChars(raw)); 212 } 213 214 /** 215 * Decodes a byte array where each byte represents an ASCII '0' or '1'. 216 * 217 * @param ascii 218 * each byte represents an ASCII '0' or '1' 219 * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument 220 * @see org.apache.commons.codec.Decoder#decode(Object) 221 */ 222 @Override 223 public byte[] decode(final byte[] ascii) { 224 return fromAscii(ascii); 225 } 226 227 /** 228 * Decodes a byte array where each byte represents an ASCII '0' or '1'. 229 * 230 * @param ascii 231 * each byte represents an ASCII '0' or '1' 232 * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument 233 * @throws DecoderException 234 * if argument is not a byte[], char[] or String 235 * @see org.apache.commons.codec.Decoder#decode(Object) 236 */ 237 @Override 238 public Object decode(final Object ascii) throws DecoderException { 239 if (ascii == null) { 240 return EMPTY_BYTE_ARRAY; 241 } 242 if (ascii instanceof byte[]) { 243 return fromAscii((byte[]) ascii); 244 } 245 if (ascii instanceof char[]) { 246 return fromAscii((char[]) ascii); 247 } 248 if (ascii instanceof String) { 249 return fromAscii(((String) ascii).toCharArray()); 250 } 251 throw new DecoderException("argument not a byte array"); 252 } 253 254 /** 255 * Converts an array of raw binary data into an array of ASCII 0 and 1 characters. 256 * 257 * @param raw 258 * the raw binary data to convert 259 * @return 0 and 1 ASCII character bytes one for each bit of the argument 260 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 261 */ 262 @Override 263 public byte[] encode(final byte[] raw) { 264 return toAsciiBytes(raw); 265 } 266 267 /** 268 * Converts an array of raw binary data into an array of ASCII 0 and 1 chars. 269 * 270 * @param raw 271 * the raw binary data to convert 272 * @return 0 and 1 ASCII character chars one for each bit of the argument 273 * @throws EncoderException 274 * if the argument is not a byte[] 275 * @see org.apache.commons.codec.Encoder#encode(Object) 276 */ 277 @Override 278 public Object encode(final Object raw) throws EncoderException { 279 if (!(raw instanceof byte[])) { 280 throw new EncoderException("argument not a byte array"); 281 } 282 return toAsciiChars((byte[]) raw); 283 } 284 285 /** 286 * Decodes a String where each char of the String represents an ASCII '0' or '1'. 287 * 288 * @param ascii 289 * String of '0' and '1' characters 290 * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument 291 * @see org.apache.commons.codec.Decoder#decode(Object) 292 */ 293 public byte[] toByteArray(final String ascii) { 294 if (ascii == null) { 295 return EMPTY_BYTE_ARRAY; 296 } 297 return fromAscii(ascii.toCharArray()); 298 } 299 }