Home | History | Annotate | Download | only in binary
      1 /*
      2  * Copyright 2001-2004 The Apache Software Foundation.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.apache.commons.codec.binary;
     18 
     19 import org.apache.commons.codec.BinaryDecoder;
     20 import org.apache.commons.codec.BinaryEncoder;
     21 import org.apache.commons.codec.DecoderException;
     22 import org.apache.commons.codec.EncoderException;
     23 
     24 /**
     25  * Translates between byte arrays and strings of "0"s and "1"s.
     26  *
     27  * <b>TODO:</b> may want to add more bit vector functions like and/or/xor/nand.
     28  * <B>TODO:</b> also might be good to generate boolean[]
     29  * from byte[] et. cetera.
     30  *
     31  * @author Apache Software Foundation
     32  * @since 1.3
     33  * @version $Id $
     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(java.lang.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(java.lang.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 byte 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 (ascii == null || ascii.length == 0) {
    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      * Converts an array of raw binary data into an array of ascii 0 and 1 character bytes - each byte is a truncated
    215      * char.
    216      *
    217      * @param raw
    218      *                  the raw binary data to convert
    219      * @return an array of 0 and 1 character bytes for each bit of the argument
    220      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
    221      */
    222     public static byte[] toAsciiBytes(byte[] raw) {
    223         if (raw == null || raw.length == 0) {
    224             return EMPTY_BYTE_ARRAY;
    225         }
    226         // get 8 times the bytes with 3 bit shifts to the left of the length
    227         byte[] l_ascii = new byte[raw.length << 3];
    228         /*
    229          * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
    230          * loop.
    231          */
    232         for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
    233             for (int bits = 0; bits < BITS.length; ++bits) {
    234                 if ((raw[ii] & BITS[bits]) == 0) {
    235                     l_ascii[jj - bits] = '0';
    236                 } else {
    237                     l_ascii[jj - bits] = '1';
    238                 }
    239             }
    240         }
    241         return l_ascii;
    242     }
    243 
    244     /**
    245      * Converts an array of raw binary data into an array of ascii 0 and 1 characters.
    246      *
    247      * @param raw
    248      *                  the raw binary data to convert
    249      * @return an array of 0 and 1 characters for each bit of the argument
    250      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
    251      */
    252     public static char[] toAsciiChars(byte[] raw) {
    253         if (raw == null || raw.length == 0) {
    254             return EMPTY_CHAR_ARRAY;
    255         }
    256         // get 8 times the bytes with 3 bit shifts to the left of the length
    257         char[] l_ascii = new char[raw.length << 3];
    258         /*
    259          * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
    260          * loop.
    261          */
    262         for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
    263             for (int bits = 0; bits < BITS.length; ++bits) {
    264                 if ((raw[ii] & BITS[bits]) == 0) {
    265                     l_ascii[jj - bits] = '0';
    266                 } else {
    267                     l_ascii[jj - bits] = '1';
    268                 }
    269             }
    270         }
    271         return l_ascii;
    272     }
    273 
    274     /**
    275      * Converts an array of raw binary data into a String of ascii 0 and 1 characters.
    276      *
    277      * @param raw
    278      *                  the raw binary data to convert
    279      * @return a String of 0 and 1 characters representing the binary data
    280      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
    281      */
    282     public static String toAsciiString(byte[] raw) {
    283         return new String(toAsciiChars(raw));
    284     }
    285 }
    286