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