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 */
020package org.apache.directory.api.asn1.ber.tlv;
021
022
023import org.apache.directory.api.i18n.I18n;
024import org.apache.directory.api.util.Strings;
025
026
027/**
028 * Parse and decode an Integer value.
029 *
030 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
031 */
032public final class IntegerDecoder
033{
034    /** A mask used to get only the necessary bytes */
035    private static final int[] MASK = new int[]
036        { 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF };
037
038
039    private IntegerDecoder()
040    {
041    }
042
043
044    /**
045     * Parse a byte buffer and send back an integer, controlling that this number
046     * is in a specified interval.
047     *
048     * @param value The Value containing the byte[] to parse
049     * @param min Lowest value allowed, included
050     * @param max Highest value allowed, included
051     * @return An integer
052     * @throws IntegerDecoderException Thrown if the byte[] does not contains an integer
053     */
054    public static int parse( BerValue value, int min, int max ) throws IntegerDecoderException
055    {
056        int result = parseInt( value );
057
058        if ( ( result >= min ) && ( result <= max ) )
059        {
060            return result;
061        }
062        else
063        {
064            throw new IntegerDecoderException( I18n.err( I18n.ERR_01306_VALUE_NOT_IN_RANGE, min, max ) );
065        }
066    }
067
068
069    /**
070     * Parse a byte buffer and send back an integer
071     *
072     * @param value The byte buffer to parse
073     * @return An integer
074     * @throws IntegerDecoderException Thrown if the byte stream does not contains an integer
075     */
076    public static int parse( BerValue value ) throws IntegerDecoderException
077    {
078        return parseInt( value );
079    }
080
081
082    /**
083     * Helper method used to parse the integer. We don't check any minimal or maximal
084     * bound.
085     * An BER encoded int can be either positive or negative. It uses the minimum
086     * number of byts necessary to encode the value. The high order bit gives the
087     * sign of the integer : if it's 1, then it's a negative value, otherwise it's
088     * a positive value. Integer with a high order bit set to 1 but prefixed by a 0x00
089     * are positive. If the integer is negative, then the 2 complement value is
090     * stored<br>
091     * Here are a few samples :
092     * <ul>
093     * <li>0x02 0x01 0x00 : integer 0</li>
094     * <li>0x02 0x01 0x01 : integer 1</li>
095     * <li>0x02 0x01 0x7F : integer 127</li>
096     * <li>0x02 0x01 0x80 : integer -128</li>
097     * <li>0x02 0x01 0x81 : integer -127</li>
098     * <li>0x02 0x01 0xFF : integer -1</li>
099     * <li>0x02 0x02 0x00 0x80 : integer 128</li>
100     * <li>0x02 0x02 0x00 0x81 : integer 129</li>
101     * <li>0x02 0x02 0x00 0xFF : integer 255</li>
102     * </ul>
103     * and so on...
104     * 
105     * @param value the BER PDU to parse
106     * @return The decoded value
107     * @exception IntegerDecoderException If the BER contains an invalid integer value 
108     */
109    private static int parseInt( BerValue value ) throws IntegerDecoderException
110    {
111        int result = 0;
112
113        byte[] bytes = value.getData();
114
115        if ( Strings.isEmpty( bytes ) )
116        {
117            throw new IntegerDecoderException( I18n.err( I18n.ERR_01304_0_BYTES_LONG_INTEGER ) );
118        }
119
120        boolean positive = true;
121
122        switch ( bytes.length )
123        {
124            case 5:
125                if ( bytes[0] == 0x00 )
126                {
127                    if ( ( bytes[1] & ( byte ) 0x80 ) != ( byte ) 0x80 )
128                    {
129                        throw new IntegerDecoderException( I18n.err( I18n.ERR_01304_0_BYTES_LONG_INTEGER ) );
130                    }
131
132                    result = bytes[1] & 0x00FF;
133                    result = ( result << 8 ) | ( bytes[2] & 0x00FF );
134                    result = ( result << 8 ) | ( bytes[3] & 0x00FF );
135                    result = ( result << 8 ) | ( bytes[4] & 0x00FF );
136                }
137                else
138                {
139                    throw new IntegerDecoderException( I18n.err( I18n.ERR_01304_0_BYTES_LONG_INTEGER ) );
140                }
141
142                break;
143
144            case 4:
145                if ( bytes[0] == 0x00 )
146                {
147                    result = bytes[1] & 0x00FF;
148                }
149                else
150                {
151                    result = bytes[0] & 0x00FF;
152
153                    if ( ( bytes[0] & ( byte ) 0x80 ) == ( byte ) 0x80 )
154                    {
155                        positive = false;
156                    }
157
158                    result = ( result << 8 ) | ( bytes[1] & 0x00FF );
159                }
160
161                result = ( result << 8 ) | ( bytes[2] & 0x00FF );
162                result = ( result << 8 ) | ( bytes[3] & 0x00FF );
163
164                break;
165
166            case 3:
167                if ( bytes[0] == 0x00 )
168                {
169                    result = bytes[1] & 0x00FF;
170                }
171                else
172                {
173                    result = bytes[0] & 0x00FF;
174
175                    if ( ( bytes[0] & ( byte ) 0x80 ) == ( byte ) 0x80 )
176                    {
177                        positive = false;
178                    }
179
180                    result = ( result << 8 ) | ( bytes[1] & 0x00FF );
181                }
182
183                result = ( result << 8 ) | ( bytes[2] & 0x00FF );
184
185                break;
186
187            case 2:
188                if ( bytes[0] == 0x00 )
189                {
190                    result = bytes[1] & 0x00FF;
191                }
192                else
193                {
194                    result = bytes[0] & 0x00FF;
195
196                    if ( ( bytes[0] & ( byte ) 0x80 ) == ( byte ) 0x80 )
197                    {
198                        positive = false;
199                    }
200
201                    result = ( result << 8 ) | ( bytes[1] & 0x00FF );
202                }
203
204                break;
205
206            case 1:
207                result = ( result << 8 ) | ( bytes[0] & 0x00FF );
208
209                if ( ( bytes[0] & ( byte ) 0x80 ) == ( byte ) 0x80 )
210                {
211                    positive = false;
212                }
213
214                break;
215
216            default:
217                throw new IntegerDecoderException( I18n.err( I18n.ERR_01305_ABOVE_4_BYTES_INTEGER ) );
218        }
219
220        if ( !positive )
221        {
222            result = -( ( ( ~result ) + 1 ) & MASK[bytes.length - 1] );
223        }
224
225        return result;
226    }
227}