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   * 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 }