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.miloss.fgsms.common.codec; 19 20 21 /** 22 * Converts between byte arrays and strings of "0"s and "1"s. 23 * 24 * <p>This class is immutable and thread-safe.</p> 25 * 26 * TODO: may want to add more bit vector functions like and/or/xor/nand 27 * TODO: also might be good to generate boolean[] from byte[] et cetera. 28 * 29 * @since 1.3 30 * @version $Id$ 31 */ 32 public class BinaryCodec implements BinaryDecoder, BinaryEncoder { 33 /* 34 * tried to avoid using ArrayUtils to minimize dependencies while using these empty arrays - dep is just not worth 35 * it. 36 */ 37 /** Empty char array. */ 38 private static final char[] EMPTY_CHAR_ARRAY = new char[0]; 39 40 /** Empty byte array. */ 41 private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; 42 43 /** Mask for bit 0 of a byte. */ 44 private static final int BIT_0 = 1; 45 46 /** Mask for bit 1 of a byte. */ 47 private static final int BIT_1 = 0x02; 48 49 /** Mask for bit 2 of a byte. */ 50 private static final int BIT_2 = 0x04; 51 52 /** Mask for bit 3 of a byte. */ 53 private static final int BIT_3 = 0x08; 54 55 /** Mask for bit 4 of a byte. */ 56 private static final int BIT_4 = 0x10; 57 58 /** Mask for bit 5 of a byte. */ 59 private static final int BIT_5 = 0x20; 60 61 /** Mask for bit 6 of a byte. */ 62 private static final int BIT_6 = 0x40; 63 64 /** Mask for bit 7 of a byte. */ 65 private static final int BIT_7 = 0x80; 66 67 private static final int[] BITS = {BIT_0, BIT_1, BIT_2, BIT_3, BIT_4, BIT_5, BIT_6, BIT_7}; 68 69 /** 70 * Converts an array of raw binary data into an array of ASCII 0 and 1 characters. 71 * 72 * @param raw 73 * the raw binary data to convert 74 * @return 0 and 1 ASCII character bytes one for each bit of the argument 75 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 76 */ 77 @Override 78 public byte[] encode(final byte[] raw) { 79 return toAsciiBytes(raw); 80 } 81 82 /** 83 * Converts an array of raw binary data into an array of ASCII 0 and 1 chars. 84 * 85 * @param raw 86 * the raw binary data to convert 87 * @return 0 and 1 ASCII character chars one for each bit of the argument 88 * @throws EncoderException 89 * if the argument is not a byte[] 90 * @see org.apache.commons.codec.Encoder#encode(Object) 91 */ 92 @Override 93 public Object encode(final Object raw) throws EncoderException { 94 if (!(raw instanceof byte[])) { 95 throw new EncoderException("argument not a byte array"); 96 } 97 return toAsciiChars((byte[]) raw); 98 } 99 100 /** 101 * Decodes a byte array where each byte represents an ASCII '0' or '1'. 102 * 103 * @param ascii 104 * each byte represents an ASCII '0' or '1' 105 * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument 106 * @throws DecoderException 107 * if argument is not a byte[], char[] or String 108 * @see org.apache.commons.codec.Decoder#decode(Object) 109 */ 110 @Override 111 public Object decode(final 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 */ 135 @Override 136 public byte[] decode(final 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(final 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 char 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(final 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 final 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(final byte[] ascii) { 195 if (isEmpty(ascii)) { 196 return EMPTY_BYTE_ARRAY; 197 } 198 // get length/8 times bytes with 3 bit shifts to the right of the length 199 final 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 * Returns <code>true</code> if the given array is <code>null</code> or empty (size 0.) 216 * 217 * @param array 218 * the source array 219 * @return <code>true</code> if the given array is <code>null</code> or empty (size 0.) 220 */ 221 private static boolean isEmpty(final byte[] array) { 222 return array == null || array.length == 0; 223 } 224 225 /** 226 * Converts an array of raw binary data into an array of ASCII 0 and 1 character bytes - each byte is a truncated 227 * char. 228 * 229 * @param raw 230 * the raw binary data to convert 231 * @return an array of 0 and 1 character bytes for each bit of the argument 232 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 233 */ 234 public static byte[] toAsciiBytes(final byte[] raw) { 235 if (isEmpty(raw)) { 236 return EMPTY_BYTE_ARRAY; 237 } 238 // get 8 times the bytes with 3 bit shifts to the left of the length 239 final byte[] l_ascii = new byte[raw.length << 3]; 240 /* 241 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 242 * loop. 243 */ 244 for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) { 245 for (int bits = 0; bits < BITS.length; ++bits) { 246 if ((raw[ii] & BITS[bits]) == 0) { 247 l_ascii[jj - bits] = '0'; 248 } else { 249 l_ascii[jj - bits] = '1'; 250 } 251 } 252 } 253 return l_ascii; 254 } 255 256 /** 257 * Converts an array of raw binary data into an array of ASCII 0 and 1 characters. 258 * 259 * @param raw 260 * the raw binary data to convert 261 * @return an array of 0 and 1 characters for each bit of the argument 262 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 263 */ 264 public static char[] toAsciiChars(final byte[] raw) { 265 if (isEmpty(raw)) { 266 return EMPTY_CHAR_ARRAY; 267 } 268 // get 8 times the bytes with 3 bit shifts to the left of the length 269 final char[] l_ascii = new char[raw.length << 3]; 270 /* 271 * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the 272 * loop. 273 */ 274 for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) { 275 for (int bits = 0; bits < BITS.length; ++bits) { 276 if ((raw[ii] & BITS[bits]) == 0) { 277 l_ascii[jj - bits] = '0'; 278 } else { 279 l_ascii[jj - bits] = '1'; 280 } 281 } 282 } 283 return l_ascii; 284 } 285 286 /** 287 * Converts an array of raw binary data into a String of ASCII 0 and 1 characters. 288 * 289 * @param raw 290 * the raw binary data to convert 291 * @return a String of 0 and 1 characters representing the binary data 292 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[]) 293 */ 294 public static String toAsciiString(final byte[] raw) { 295 return new String(toAsciiChars(raw)); 296 } 297 }