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 * Translates between byte arrays and strings of "0"s and "1"s.
27 *
28 * @todo may want to add more bit vector functions like and/or/xor/nand
29 * @todo also might be good to generate boolean[]
30 * from byte[] et. cetera.
31 *
32 * @author Apache Software Foundation
33 * @since 1.3
34 * @version $Id: BinaryCodec.java 480406 2006-11-29 04:56:58Z bayard $
35 */
36 public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
37 /*
38 * tried to avoid using ArrayUtils to minimize dependencies while using these empty arrays - dep is just not worth
39 * it.
40 */
41 /** Empty char array. */
42 private static final char[] EMPTY_CHAR_ARRAY = new char[0];
43
44 /** Empty byte array. */
45 private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
46
47 /** Mask for bit 0 of a byte. */
48 private static final int BIT_0 = 1;
49
50 /** Mask for bit 1 of a byte. */
51 private static final int BIT_1 = 0x02;
52
53 /** Mask for bit 2 of a byte. */
54 private static final int BIT_2 = 0x04;
55
56 /** Mask for bit 3 of a byte. */
57 private static final int BIT_3 = 0x08;
58
59 /** Mask for bit 4 of a byte. */
60 private static final int BIT_4 = 0x10;
61
62 /** Mask for bit 5 of a byte. */
63 private static final int BIT_5 = 0x20;
64
65 /** Mask for bit 6 of a byte. */
66 private static final int BIT_6 = 0x40;
67
68 /** Mask for bit 7 of a byte. */
69 private static final int BIT_7 = 0x80;
70
71 private static final int[] BITS = {BIT_0, BIT_1, BIT_2, BIT_3, BIT_4, BIT_5, BIT_6, BIT_7};
72
73 /**
74 * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
75 *
76 * @param raw
77 * the raw binary data to convert
78 * @return 0 and 1 ASCII character bytes one for each bit of the argument
79 * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
80 */
81 public byte[] encode(byte[] raw) {
82 return toAsciiBytes(raw);
83 }
84
85 /**
86 * Converts an array of raw binary data into an array of ASCII 0 and 1 chars.
87 *
88 * @param raw
89 * the raw binary data to convert
90 * @return 0 and 1 ASCII character chars one for each bit of the argument
91 * @throws EncoderException
92 * if the argument is not a byte[]
93 * @see org.apache.commons.codec.Encoder#encode(java.lang.Object)
94 */
95 public Object encode(Object raw) throws EncoderException {
96 if (!(raw instanceof byte[])) {
97 throw new EncoderException("argument not a byte array");
98 }
99 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 }