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 }