001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    
021    package org.apache.directory.shared.asn1.codec.binary;
022    
023    
024    import org.apache.directory.shared.asn1.codec.BinaryDecoder;
025    import org.apache.directory.shared.asn1.codec.BinaryEncoder;
026    import org.apache.directory.shared.asn1.codec.DecoderException;
027    import org.apache.directory.shared.asn1.codec.EncoderException;
028    import org.apache.directory.shared.i18n.I18n;
029    
030    
031    /**
032     * Translates between byte arrays and strings of "0"s and "1"s.
033     * 
034     * @todo may want to add more bit vector functions like and/or/xor/nand
035     * @todo also might be good to generate boolean[] from byte[] et. cetera.
036     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
037     * @since 1.3
038     * @version $Id $
039     */
040    public class BinaryCodec implements BinaryDecoder, BinaryEncoder
041    {
042        /*
043         * tried to avoid using ArrayUtils to minimize dependencies while using
044         * these empty arrays - dep is just not worth it.
045         */
046        /** Empty char array. */
047        private static final char[] EMPTY_CHAR_ARRAY = new char[0];
048    
049        /** Empty byte array. */
050        private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
051    
052        /** Mask for bit 0 of a byte. */
053        private static final int BIT_0 = 1;
054    
055        /** Mask for bit 1 of a byte. */
056        private static final int BIT_1 = 0x02;
057    
058        /** Mask for bit 2 of a byte. */
059        private static final int BIT_2 = 0x04;
060    
061        /** Mask for bit 3 of a byte. */
062        private static final int BIT_3 = 0x08;
063    
064        /** Mask for bit 4 of a byte. */
065        private static final int BIT_4 = 0x10;
066    
067        /** Mask for bit 5 of a byte. */
068        private static final int BIT_5 = 0x20;
069    
070        /** Mask for bit 6 of a byte. */
071        private static final int BIT_6 = 0x40;
072    
073        /** Mask for bit 7 of a byte. */
074        private static final int BIT_7 = 0x80;
075    
076        private static final int[] BITS =
077            { BIT_0, BIT_1, BIT_2, BIT_3, BIT_4, BIT_5, BIT_6, BIT_7 };
078    
079    
080        /**
081         * Converts an array of raw binary data into an array of ascii 0 and 1
082         * characters.
083         * 
084         * @param raw
085         *            the raw binary data to convert
086         * @return 0 and 1 ascii character bytes one for each bit of the argument
087         * @see org.apache.directory.shared.asn1.codec.BinaryEncoder#encode(byte[])
088         */
089        public byte[] encode( byte[] raw )
090        {
091            return toAsciiBytes( raw );
092        }
093    
094    
095        /**
096         * Converts an array of raw binary data into an array of ascii 0 and 1
097         * chars.
098         * 
099         * @param raw
100         *            the raw binary data to convert
101         * @return 0 and 1 ascii character chars one for each bit of the argument
102         * @throws EncoderException
103         *             if the argument is not a byte[]
104         * @see org.apache.directory.shared.asn1.codec.Encoder#encode(java.lang.Object)
105         */
106        public Object encode( Object raw ) throws EncoderException
107        {
108            if ( !( raw instanceof byte[] ) )
109            {
110                throw new EncoderException( I18n.err( I18n.ERR_00012 ) );
111            }
112            return toAsciiChars( ( byte[] ) raw );
113        }
114    
115    
116        /**
117         * Decodes a byte array where each byte represents an ascii '0' or '1'.
118         * 
119         * @param ascii
120         *            each byte represents an ascii '0' or '1'
121         * @return the raw encoded binary where each bit corresponds to a byte in
122         *         the byte array argument
123         * @throws DecoderException
124         *             if argument is not a byte[], char[] or String
125         * @see org.apache.directory.shared.asn1.codec.Decoder#decode(java.lang.Object)
126         */
127        public Object decode( Object ascii ) throws DecoderException
128        {
129            if ( ascii == null )
130            {
131                return EMPTY_BYTE_ARRAY;
132            }
133            if ( ascii instanceof byte[] )
134            {
135                return fromAscii( ( byte[] ) ascii );
136            }
137            if ( ascii instanceof char[] )
138            {
139                return fromAscii( ( char[] ) ascii );
140            }
141            if ( ascii instanceof String )
142            {
143                return fromAscii( ( ( String ) ascii ).toCharArray() );
144            }
145            throw new DecoderException( I18n.err( I18n.ERR_00012 ) );
146        }
147    
148    
149        /**
150         * Decodes a byte array where each byte represents an ascii '0' or '1'.
151         * 
152         * @param ascii
153         *            each byte represents an ascii '0' or '1'
154         * @return the raw encoded binary where each bit corresponds to a byte in
155         *         the byte array argument
156         * @see org.apache.directory.shared.asn1.codec.Decoder#decode(Object)
157         */
158        public byte[] decode( byte[] ascii )
159        {
160            return fromAscii( ascii );
161        }
162    
163    
164        /**
165         * Decodes a String where each char of the String represents an ascii '0' or
166         * '1'.
167         * 
168         * @param ascii
169         *            String of '0' and '1' characters
170         * @return the raw encoded binary where each bit corresponds to a byte in
171         *         the byte array argument
172         * @see org.apache.directory.shared.asn1.codec.Decoder#decode(Object)
173         */
174        public byte[] toByteArray( String ascii )
175        {
176            if ( ascii == null )
177            {
178                return EMPTY_BYTE_ARRAY;
179            }
180            return fromAscii( ascii.toCharArray() );
181        }
182    
183    
184        // ------------------------------------------------------------------------
185        //
186        // static codec operations
187        //
188        // ------------------------------------------------------------------------
189        /**
190         * Decodes a byte array where each char represents an ascii '0' or '1'.
191         * 
192         * @param ascii
193         *            each char represents an ascii '0' or '1'
194         * @return the raw encoded binary where each bit corresponds to a char in
195         *         the char array argument
196         */
197        public static byte[] fromAscii( char[] ascii )
198        {
199            if ( ascii == null || ascii.length == 0 )
200            {
201                return EMPTY_BYTE_ARRAY;
202            }
203            // get length/8 times bytes with 3 bit shifts to the right of the length
204            byte[] l_raw = new byte[ascii.length >> 3];
205            /*
206             * We decr index jj by 8 as we go along to not recompute indices using
207             * multiplication every time inside the loop.
208             */
209            for ( int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8 )
210            {
211                for ( int bits = 0; bits < BITS.length; ++bits )
212                {
213                    if ( ascii[jj - bits] == '1' )
214                    {
215                        l_raw[ii] |= BITS[bits];
216                    }
217                }
218            }
219            return l_raw;
220        }
221    
222    
223        /**
224         * Decodes a byte array where each byte represents an ascii '0' or '1'.
225         * 
226         * @param ascii
227         *            each byte represents an ascii '0' or '1'
228         * @return the raw encoded binary where each bit corresponds to a byte in
229         *         the byte array argument
230         */
231        public static byte[] fromAscii( byte[] ascii )
232        {
233            if ( ascii == null || ascii.length == 0 )
234            {
235                return EMPTY_BYTE_ARRAY;
236            }
237            // get length/8 times bytes with 3 bit shifts to the right of the length
238            byte[] l_raw = new byte[ascii.length >> 3];
239            /*
240             * We decr index jj by 8 as we go along to not recompute indices using
241             * multiplication every time inside the loop.
242             */
243            for ( int ii = 0, jj = ascii.length - 1; ii < l_raw.length; ii++, jj -= 8 )
244            {
245                for ( int bits = 0; bits < BITS.length; ++bits )
246                {
247                    if ( ascii[jj - bits] == '1' )
248                    {
249                        l_raw[ii] |= BITS[bits];
250                    }
251                }
252            }
253            return l_raw;
254        }
255    
256    
257        /**
258         * Converts an array of raw binary data into an array of ascii 0 and 1
259         * character bytes - each byte is a truncated char.
260         * 
261         * @param raw
262         *            the raw binary data to convert
263         * @return an array of 0 and 1 character bytes for each bit of the argument
264         * @see org.apache.directory.shared.asn1.codec.BinaryEncoder#encode(byte[])
265         */
266        public static byte[] toAsciiBytes( byte[] raw )
267        {
268            if ( raw == null || raw.length == 0 )
269            {
270                return EMPTY_BYTE_ARRAY;
271            }
272            // get 8 times the bytes with 3 bit shifts to the left of the length
273            byte[] l_ascii = new byte[raw.length << 3];
274            /*
275             * We decr index jj by 8 as we go along to not recompute indices using
276             * multiplication every time inside the loop.
277             */
278            for ( int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8 )
279            {
280                for ( int bits = 0; bits < BITS.length; ++bits )
281                {
282                    if ( ( raw[ii] & BITS[bits] ) == 0 )
283                    {
284                        l_ascii[jj - bits] = '0';
285                    }
286                    else
287                    {
288                        l_ascii[jj - bits] = '1';
289                    }
290                }
291            }
292            return l_ascii;
293        }
294    
295    
296        /**
297         * Converts an array of raw binary data into an array of ascii 0 and 1
298         * characters.
299         * 
300         * @param raw
301         *            the raw binary data to convert
302         * @return an array of 0 and 1 characters for each bit of the argument
303         * @see org.apache.directory.shared.asn1.codec.BinaryEncoder#encode(byte[])
304         */
305        public static char[] toAsciiChars( byte[] raw )
306        {
307            if ( raw == null || raw.length == 0 )
308            {
309                return EMPTY_CHAR_ARRAY;
310            }
311            // get 8 times the bytes with 3 bit shifts to the left of the length
312            char[] l_ascii = new char[raw.length << 3];
313            /*
314             * We decr index jj by 8 as we go along to not recompute indices using
315             * multiplication every time inside the loop.
316             */
317            for ( int ii = 0, jj = l_ascii.length - 1; ii < raw.length; ii++, jj -= 8 )
318            {
319                for ( int bits = 0; bits < BITS.length; ++bits )
320                {
321                    if ( ( raw[ii] & BITS[bits] ) == 0 )
322                    {
323                        l_ascii[jj - bits] = '0';
324                    }
325                    else
326                    {
327                        l_ascii[jj - bits] = '1';
328                    }
329                }
330            }
331            return l_ascii;
332        }
333    
334    
335        /**
336         * Converts an array of raw binary data into a String of ascii 0 and 1
337         * characters.
338         * 
339         * @param raw
340         *            the raw binary data to convert
341         * @return a String of 0 and 1 characters representing the binary data
342         * @see org.apache.directory.shared.asn1.codec.BinaryEncoder#encode(byte[])
343         */
344        public static String toAsciiString( byte[] raw )
345        {
346            return new String( toAsciiChars( raw ) );
347        }
348    }