BinaryCodec.java

  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. package org.apache.commons.codec.binary;

  18. import org.apache.commons.codec.BinaryDecoder;
  19. import org.apache.commons.codec.BinaryEncoder;
  20. import org.apache.commons.codec.DecoderException;
  21. import org.apache.commons.codec.EncoderException;

  22. /**
  23.  * Converts between byte arrays and strings of "0"s and "1"s.
  24.  *
  25.  * <p>This class is immutable and thread-safe.</p>
  26.  *
  27.  * TODO: may want to add more bit vector functions like and/or/xor/nand
  28.  * TODO: also might be good to generate boolean[] from byte[] et cetera.
  29.  *
  30.  * @since 1.3
  31.  * @version $Id: BinaryCodec.java 1619948 2014-08-22 22:53:55Z ggregory $
  32.  */
  33. public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
  34.     /*
  35.      * tried to avoid using ArrayUtils to minimize dependencies while using these empty arrays - dep is just not worth
  36.      * it.
  37.      */
  38.     /** Empty char array. */
  39.     private static final char[] EMPTY_CHAR_ARRAY = new char[0];

  40.     /** Empty byte array. */
  41.     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

  42.     /** Mask for bit 0 of a byte. */
  43.     private static final int BIT_0 = 1;

  44.     /** Mask for bit 1 of a byte. */
  45.     private static final int BIT_1 = 0x02;

  46.     /** Mask for bit 2 of a byte. */
  47.     private static final int BIT_2 = 0x04;

  48.     /** Mask for bit 3 of a byte. */
  49.     private static final int BIT_3 = 0x08;

  50.     /** Mask for bit 4 of a byte. */
  51.     private static final int BIT_4 = 0x10;

  52.     /** Mask for bit 5 of a byte. */
  53.     private static final int BIT_5 = 0x20;

  54.     /** Mask for bit 6 of a byte. */
  55.     private static final int BIT_6 = 0x40;

  56.     /** Mask for bit 7 of a byte. */
  57.     private static final int BIT_7 = 0x80;

  58.     private static final int[] BITS = {BIT_0, BIT_1, BIT_2, BIT_3, BIT_4, BIT_5, BIT_6, BIT_7};

  59.     /**
  60.      * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
  61.      *
  62.      * @param raw
  63.      *                  the raw binary data to convert
  64.      * @return 0 and 1 ASCII character bytes one for each bit of the argument
  65.      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
  66.      */
  67.     @Override
  68.     public byte[] encode(final byte[] raw) {
  69.         return toAsciiBytes(raw);
  70.     }

  71.     /**
  72.      * Converts an array of raw binary data into an array of ASCII 0 and 1 chars.
  73.      *
  74.      * @param raw
  75.      *                  the raw binary data to convert
  76.      * @return 0 and 1 ASCII character chars one for each bit of the argument
  77.      * @throws EncoderException
  78.      *                  if the argument is not a byte[]
  79.      * @see org.apache.commons.codec.Encoder#encode(Object)
  80.      */
  81.     @Override
  82.     public Object encode(final Object raw) throws EncoderException {
  83.         if (!(raw instanceof byte[])) {
  84.             throw new EncoderException("argument not a byte array");
  85.         }
  86.         return toAsciiChars((byte[]) raw);
  87.     }

  88.     /**
  89.      * Decodes a byte array where each byte represents an ASCII '0' or '1'.
  90.      *
  91.      * @param ascii
  92.      *                  each byte represents an ASCII '0' or '1'
  93.      * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
  94.      * @throws DecoderException
  95.      *                  if argument is not a byte[], char[] or String
  96.      * @see org.apache.commons.codec.Decoder#decode(Object)
  97.      */
  98.     @Override
  99.     public Object decode(final Object ascii) throws DecoderException {
  100.         if (ascii == null) {
  101.             return EMPTY_BYTE_ARRAY;
  102.         }
  103.         if (ascii instanceof byte[]) {
  104.             return fromAscii((byte[]) ascii);
  105.         }
  106.         if (ascii instanceof char[]) {
  107.             return fromAscii((char[]) ascii);
  108.         }
  109.         if (ascii instanceof String) {
  110.             return fromAscii(((String) ascii).toCharArray());
  111.         }
  112.         throw new DecoderException("argument not a byte array");
  113.     }

  114.     /**
  115.      * Decodes a byte array where each byte represents an ASCII '0' or '1'.
  116.      *
  117.      * @param ascii
  118.      *                  each byte represents an ASCII '0' or '1'
  119.      * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
  120.      * @see org.apache.commons.codec.Decoder#decode(Object)
  121.      */
  122.     @Override
  123.     public byte[] decode(final byte[] ascii) {
  124.         return fromAscii(ascii);
  125.     }

  126.     /**
  127.      * Decodes a String where each char of the String represents an ASCII '0' or '1'.
  128.      *
  129.      * @param ascii
  130.      *                  String of '0' and '1' characters
  131.      * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
  132.      * @see org.apache.commons.codec.Decoder#decode(Object)
  133.      */
  134.     public byte[] toByteArray(final String ascii) {
  135.         if (ascii == null) {
  136.             return EMPTY_BYTE_ARRAY;
  137.         }
  138.         return fromAscii(ascii.toCharArray());
  139.     }

  140.     // ------------------------------------------------------------------------
  141.     //
  142.     // static codec operations
  143.     //
  144.     // ------------------------------------------------------------------------
  145.     /**
  146.      * Decodes a char array where each char represents an ASCII '0' or '1'.
  147.      *
  148.      * @param ascii
  149.      *                  each char represents an ASCII '0' or '1'
  150.      * @return the raw encoded binary where each bit corresponds to a char in the char array argument
  151.      */
  152.     public static byte[] fromAscii(final char[] ascii) {
  153.         if (ascii == null || ascii.length == 0) {
  154.             return EMPTY_BYTE_ARRAY;
  155.         }
  156.         // get length/8 times bytes with 3 bit shifts to the right of the length
  157.         final byte[] l_raw = new byte[ascii.length >> 3];
  158.         /*
  159.          * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
  160.          * loop.
  161.          */
  162.         for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) {
  163.             for (int bits = 0; bits < BITS.length; ++bits) {
  164.                 if (ascii[jj - bits] == '1') {
  165.                     l_raw[ii] |= BITS[bits];
  166.                 }
  167.             }
  168.         }
  169.         return l_raw;
  170.     }

  171.     /**
  172.      * Decodes a byte array where each byte represents an ASCII '0' or '1'.
  173.      *
  174.      * @param ascii
  175.      *                  each byte represents an ASCII '0' or '1'
  176.      * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
  177.      */
  178.     public static byte[] fromAscii(final byte[] ascii) {
  179.         if (isEmpty(ascii)) {
  180.             return EMPTY_BYTE_ARRAY;
  181.         }
  182.         // get length/8 times bytes with 3 bit shifts to the right of the length
  183.         final byte[] l_raw = new byte[ascii.length >> 3];
  184.         /*
  185.          * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
  186.          * loop.
  187.          */
  188.         for (int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8) {
  189.             for (int bits = 0; bits < BITS.length; ++bits) {
  190.                 if (ascii[jj - bits] == '1') {
  191.                     l_raw[ii] |= BITS[bits];
  192.                 }
  193.             }
  194.         }
  195.         return l_raw;
  196.     }

  197.     /**
  198.      * Returns <code>true</code> if the given array is <code>null</code> or empty (size 0.)
  199.      *
  200.      * @param array
  201.      *            the source array
  202.      * @return <code>true</code> if the given array is <code>null</code> or empty (size 0.)
  203.      */
  204.     private static boolean isEmpty(final byte[] array) {
  205.         return array == null || array.length == 0;
  206.     }

  207.     /**
  208.      * Converts an array of raw binary data into an array of ASCII 0 and 1 character bytes - each byte is a truncated
  209.      * char.
  210.      *
  211.      * @param raw
  212.      *                  the raw binary data to convert
  213.      * @return an array of 0 and 1 character bytes for each bit of the argument
  214.      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
  215.      */
  216.     public static byte[] toAsciiBytes(final byte[] raw) {
  217.         if (isEmpty(raw)) {
  218.             return EMPTY_BYTE_ARRAY;
  219.         }
  220.         // get 8 times the bytes with 3 bit shifts to the left of the length
  221.         final byte[] l_ascii = new byte[raw.length << 3];
  222.         /*
  223.          * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
  224.          * loop.
  225.          */
  226.         for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
  227.             for (int bits = 0; bits < BITS.length; ++bits) {
  228.                 if ((raw[ii] & BITS[bits]) == 0) {
  229.                     l_ascii[jj - bits] = '0';
  230.                 } else {
  231.                     l_ascii[jj - bits] = '1';
  232.                 }
  233.             }
  234.         }
  235.         return l_ascii;
  236.     }

  237.     /**
  238.      * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
  239.      *
  240.      * @param raw
  241.      *                  the raw binary data to convert
  242.      * @return an array of 0 and 1 characters for each bit of the argument
  243.      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
  244.      */
  245.     public static char[] toAsciiChars(final byte[] raw) {
  246.         if (isEmpty(raw)) {
  247.             return EMPTY_CHAR_ARRAY;
  248.         }
  249.         // get 8 times the bytes with 3 bit shifts to the left of the length
  250.         final char[] l_ascii = new char[raw.length << 3];
  251.         /*
  252.          * We decr index jj by 8 as we go along to not recompute indices using multiplication every time inside the
  253.          * loop.
  254.          */
  255.         for (int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8) {
  256.             for (int bits = 0; bits < BITS.length; ++bits) {
  257.                 if ((raw[ii] & BITS[bits]) == 0) {
  258.                     l_ascii[jj - bits] = '0';
  259.                 } else {
  260.                     l_ascii[jj - bits] = '1';
  261.                 }
  262.             }
  263.         }
  264.         return l_ascii;
  265.     }

  266.     /**
  267.      * Converts an array of raw binary data into a String of ASCII 0 and 1 characters.
  268.      *
  269.      * @param raw
  270.      *                  the raw binary data to convert
  271.      * @return a String of 0 and 1 characters representing the binary data
  272.      * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
  273.      */
  274.     public static String toAsciiString(final byte[] raw) {
  275.         return new String(toAsciiChars(raw));
  276.     }
  277. }