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