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_00038_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    private static int parseInt( BerValue value ) throws IntegerDecoderException
106    {
107        int result = 0;
108
109        byte[] bytes = value.getData();
110
111        if ( Strings.isEmpty( bytes ) )
112        {
113            throw new IntegerDecoderException( I18n.err( I18n.ERR_00036_0_BYTES_LONG_INTEGER ) );
114        }
115
116        boolean positive = true;
117
118        switch ( bytes.length )
119        {
120            case 5:
121                if ( bytes[0] == 0x00 )
122                {
123                    if ( ( bytes[1] & ( byte ) 0x80 ) != ( byte ) 0x80 )
124                    {
125                        throw new IntegerDecoderException( I18n.err( I18n.ERR_00036_0_BYTES_LONG_INTEGER ) );
126                    }
127
128                    result = bytes[1] & 0x00FF;
129                    result = ( result << 8 ) | ( bytes[2] & 0x00FF );
130                    result = ( result << 8 ) | ( bytes[3] & 0x00FF );
131                    result = ( result << 8 ) | ( bytes[4] & 0x00FF );
132                }
133                else
134                {
135                    throw new IntegerDecoderException( I18n.err( I18n.ERR_00036_0_BYTES_LONG_INTEGER ) );
136                }
137
138                break;
139
140            case 4:
141                if ( bytes[0] == 0x00 )
142                {
143                    result = bytes[1] & 0x00FF;
144                }
145                else
146                {
147                    result = bytes[0] & 0x00FF;
148
149                    if ( ( bytes[0] & ( byte ) 0x80 ) == ( byte ) 0x80 )
150                    {
151                        positive = false;
152                    }
153
154                    result = ( result << 8 ) | ( bytes[1] & 0x00FF );
155                }
156
157                result = ( result << 8 ) | ( bytes[2] & 0x00FF );
158                result = ( result << 8 ) | ( bytes[3] & 0x00FF );
159
160                break;
161
162            case 3:
163                if ( bytes[0] == 0x00 )
164                {
165                    result = bytes[1] & 0x00FF;
166                }
167                else
168                {
169                    result = bytes[0] & 0x00FF;
170
171                    if ( ( bytes[0] & ( byte ) 0x80 ) == ( byte ) 0x80 )
172                    {
173                        positive = false;
174                    }
175
176                    result = ( result << 8 ) | ( bytes[1] & 0x00FF );
177                }
178
179                result = ( result << 8 ) | ( bytes[2] & 0x00FF );
180
181                break;
182
183            case 2:
184                if ( bytes[0] == 0x00 )
185                {
186                    result = bytes[1] & 0x00FF;
187                }
188                else
189                {
190                    result = bytes[0] & 0x00FF;
191
192                    if ( ( bytes[0] & ( byte ) 0x80 ) == ( byte ) 0x80 )
193                    {
194                        positive = false;
195                    }
196
197                    result = ( result << 8 ) | ( bytes[1] & 0x00FF );
198                }
199
200                break;
201
202            case 1:
203                result = ( result << 8 ) | ( bytes[0] & 0x00FF );
204
205                if ( ( bytes[0] & ( byte ) 0x80 ) == ( byte ) 0x80 )
206                {
207                    positive = false;
208                }
209
210                break;
211
212            default:
213                throw new IntegerDecoderException( I18n.err( I18n.ERR_00037_ABOVE_4_BYTES_INTEGER ) );
214        }
215
216        if ( !positive )
217        {
218            result = -( ( ( ~result ) + 1 ) & MASK[bytes.length - 1] );
219        }
220
221        return result;
222    }
223}