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