View Javadoc

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   * TODO: may want to add more bit vector functions like and/or/xor/nand 
29   * TODO: also might be good to generate boolean[] from byte[] et cetera.
30   * 
31   * @author Apache Software Foundation
32   * @since 1.3
33   * @version $Id: BinaryCodec.java 1157192 2011-08-12 17:27:38Z ggregory $
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       */
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(Object)
93       */
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(Object)
110      */
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      */
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      */
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 char 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      */
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      */
193     public static byte[] fromAscii(byte[] ascii) {
194         if (isEmpty(ascii)) {
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      * Returns <code>true</code> if the given array is <code>null</code> or empty (size 0.)
215      * 
216      * @param array
217      *            the source array
218      * @return <code>true</code> if the given array is <code>null</code> or empty (size 0.)
219      */
220     private static boolean isEmpty(byte[] array) {
221         return array == null || array.length == 0;
222     }
223 
224     /**
225      * Converts an array of raw binary data into an array of ASCII 0 and 1 character bytes - each byte is a truncated
226      * char.
227      * 
228      * @param raw
229      *                  the raw binary data to convert
230      * @return an array of 0 and 1 character bytes for each bit of the argument
231      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
232      */
233     public static byte[] toAsciiBytes(byte[] raw) {
234         if (isEmpty(raw)) {
235             return EMPTY_BYTE_ARRAY;
236         }
237         // get 8 times the bytes with 3 bit shifts to the left of the length
238         byte[] l_ascii = new byte[raw.length << 3];
239         /*
240          * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
241          * loop.
242          */
243         for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
244             for (int bits = 0; bits < BITS.length; ++bits) {
245                 if ((raw[ii] & BITS[bits]) == 0) {
246                     l_ascii[jj - bits] = '0';
247                 } else {
248                     l_ascii[jj - bits] = '1';
249                 }
250             }
251         }
252         return l_ascii;
253     }
254 
255     /**
256      * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
257      * 
258      * @param raw
259      *                  the raw binary data to convert
260      * @return an array of 0 and 1 characters for each bit of the argument
261      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
262      */
263     public static char[] toAsciiChars(byte[] raw) {
264         if (isEmpty(raw)) {
265             return EMPTY_CHAR_ARRAY;
266         }
267         // get 8 times the bytes with 3 bit shifts to the left of the length
268         char[] l_ascii = new char[raw.length << 3];
269         /*
270          * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
271          * loop.
272          */
273         for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
274             for (int bits = 0; bits < BITS.length; ++bits) {
275                 if ((raw[ii] & BITS[bits]) == 0) {
276                     l_ascii[jj - bits] = '0';
277                 } else {
278                     l_ascii[jj - bits] = '1';
279                 }
280             }
281         }
282         return l_ascii;
283     }
284 
285     /**
286      * Converts an array of raw binary data into a String of ASCII 0 and 1 characters.
287      * 
288      * @param raw
289      *                  the raw binary data to convert
290      * @return a String of 0 and 1 characters representing the binary data
291      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
292      */
293     public static String toAsciiString(byte[] raw) {
294         return new String(toAsciiChars(raw));
295     }
296 }