001/*
002 * Copyright 2007-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.asn1;
022
023
024
025import com.unboundid.util.Debug;
026import com.unboundid.util.NotMutable;
027import com.unboundid.util.ThreadSafety;
028import com.unboundid.util.ThreadSafetyLevel;
029
030import static com.unboundid.asn1.ASN1Messages.*;
031
032
033
034/**
035 * This class provides an ASN.1 integer element that is backed by a Java
036 * {@code int}, which is a signed 32-bit value and can represent any integer
037 * between -2147483648 and 2147483647.  If you need support for integer values
038 * in the signed 64-bit range, see the {@link ASN1Long} class as an alternative.
039 * If you need support for integer values of arbitrary size, see
040 * {@link ASN1BigInteger}.
041 */
042@NotMutable()
043@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
044public final class ASN1Integer
045       extends ASN1Element
046{
047  /**
048   * The serial version UID for this serializable class.
049   */
050  private static final long serialVersionUID = -733929804601994372L;
051
052
053
054  // The int value for this element.
055  private final int intValue;
056
057
058
059  /**
060   * Creates a new ASN.1 integer element with the default BER type and the
061   * provided int value.
062   *
063   * @param  intValue  The int value to use for this element.
064   */
065  public ASN1Integer(final int intValue)
066  {
067    super(ASN1Constants.UNIVERSAL_INTEGER_TYPE, encodeIntValue(intValue));
068
069    this.intValue = intValue;
070  }
071
072
073
074  /**
075   * Creates a new ASN.1 integer element with the specified BER type and the
076   * provided int value.
077   *
078   * @param  type      The BER type to use for this element.
079   * @param  intValue  The int value to use for this element.
080   */
081  public ASN1Integer(final byte type, final int intValue)
082  {
083    super(type, encodeIntValue(intValue));
084
085    this.intValue = intValue;
086  }
087
088
089
090  /**
091   * Creates a new ASN.1 integer element with the specified BER type and the
092   * provided int and pre-encoded values.
093   *
094   * @param  type      The BER type to use for this element.
095   * @param  intValue  The int value to use for this element.
096   * @param  value     The pre-encoded value to use for this element.
097   */
098  private ASN1Integer(final byte type, final int intValue, final byte[] value)
099  {
100    super(type, value);
101
102    this.intValue = intValue;
103  }
104
105
106
107  /**
108   * Encodes the provided int value to a byte array suitable for use as the
109   * value of an integer element.
110   *
111   * @param  intValue  The int value to be encoded.
112   *
113   * @return  A byte array containing the encoded value.
114   */
115  static byte[] encodeIntValue(final int intValue)
116  {
117    if (intValue < 0)
118    {
119      if ((intValue & 0xFFFF_FF80) == 0xFFFF_FF80)
120      {
121        return new byte[]
122        {
123          (byte) (intValue & 0xFF)
124        };
125      }
126      else if ((intValue & 0xFFFF_8000) == 0xFFFF_8000)
127      {
128        return new byte[]
129        {
130          (byte) ((intValue >> 8) & 0xFF),
131          (byte) (intValue & 0xFF)
132        };
133      }
134      else if ((intValue & 0xFF80_0000) == 0xFF80_0000)
135      {
136        return new byte[]
137        {
138          (byte) ((intValue >> 16) & 0xFF),
139          (byte) ((intValue >> 8) & 0xFF),
140          (byte) (intValue & 0xFF)
141        };
142      }
143      else
144      {
145        return new byte[]
146        {
147          (byte) ((intValue >> 24) & 0xFF),
148          (byte) ((intValue >> 16) & 0xFF),
149          (byte) ((intValue >> 8) & 0xFF),
150          (byte) (intValue & 0xFF)
151        };
152      }
153    }
154    else
155    {
156      if ((intValue & 0x0000_007F) == intValue)
157      {
158        return new byte[]
159        {
160          (byte) (intValue & 0x7F)
161        };
162      }
163      else if ((intValue & 0x0000_7FFF) == intValue)
164      {
165        return new byte[]
166        {
167          (byte) ((intValue >> 8) & 0x7F),
168          (byte) (intValue & 0xFF)
169        };
170      }
171      else if ((intValue & 0x007F_FFFF) == intValue)
172      {
173        return new byte[]
174        {
175          (byte) ((intValue >> 16) & 0x7F),
176          (byte) ((intValue >> 8) & 0xFF),
177          (byte) (intValue & 0xFF)
178        };
179      }
180      else
181      {
182        return new byte[]
183        {
184          (byte) ((intValue >> 24) & 0x7F),
185          (byte) ((intValue >> 16) & 0xFF),
186          (byte) ((intValue >> 8) & 0xFF),
187          (byte) (intValue & 0xFF)
188        };
189      }
190    }
191  }
192
193
194
195  /**
196   * Retrieves the int value for this element.
197   *
198   * @return  The int value for this element.
199   */
200  public int intValue()
201  {
202    return intValue;
203  }
204
205
206
207  /**
208   * Decodes the contents of the provided byte array as an integer element.
209   *
210   * @param  elementBytes  The byte array to decode as an ASN.1 integer element.
211   *
212   * @return  The decoded ASN.1 integer element.
213   *
214   * @throws  ASN1Exception  If the provided array cannot be decoded as an
215   *                         integer element.
216   */
217  public static ASN1Integer decodeAsInteger(final byte[] elementBytes)
218         throws ASN1Exception
219  {
220    try
221    {
222      int valueStartPos = 2;
223      int length = (elementBytes[1] & 0x7F);
224      if (length != elementBytes[1])
225      {
226        final int numLengthBytes = length;
227
228        length = 0;
229        for (int i=0; i < numLengthBytes; i++)
230        {
231          length <<= 8;
232          length |= (elementBytes[valueStartPos++] & 0xFF);
233        }
234      }
235
236      if ((elementBytes.length - valueStartPos) != length)
237      {
238        throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
239                                     (elementBytes.length - valueStartPos)));
240      }
241
242      final byte[] value = new byte[length];
243      System.arraycopy(elementBytes, valueStartPos, value, 0, length);
244
245      int intValue;
246      switch (value.length)
247      {
248        case 1:
249          intValue = (value[0] & 0xFF);
250          if ((value[0] & 0x80) != 0x00)
251          {
252            intValue |= 0xFFFF_FF00;
253          }
254          break;
255
256        case 2:
257          intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF);
258          if ((value[0] & 0x80) != 0x00)
259          {
260            intValue |= 0xFFFF_0000;
261          }
262          break;
263
264        case 3:
265          intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) |
266                     (value[2] & 0xFF);
267          if ((value[0] & 0x80) != 0x00)
268          {
269            intValue |= 0xFF00_0000;
270          }
271          break;
272
273        case 4:
274          intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) |
275                     ((value[2] & 0xFF) << 8) | (value[3] & 0xFF);
276          break;
277
278        default:
279          throw new ASN1Exception(ERR_ENUMERATED_INVALID_LENGTH.get(
280                                       value.length));
281      }
282
283      return new ASN1Integer(elementBytes[0], intValue, value);
284    }
285    catch (final ASN1Exception ae)
286    {
287      Debug.debugException(ae);
288      throw ae;
289    }
290    catch (final Exception e)
291    {
292      Debug.debugException(e);
293      throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
294    }
295  }
296
297
298
299  /**
300   * Decodes the provided ASN.1 element as an integer element.
301   *
302   * @param  element  The ASN.1 element to be decoded.
303   *
304   * @return  The decoded ASN.1 integer element.
305   *
306   * @throws  ASN1Exception  If the provided element cannot be decoded as an
307   *                         integer element.
308   */
309  public static ASN1Integer decodeAsInteger(final ASN1Element element)
310         throws ASN1Exception
311  {
312    int intValue;
313    final byte[] value = element.getValue();
314    switch (value.length)
315    {
316      case 1:
317        intValue = (value[0] & 0xFF);
318        if ((value[0] & 0x80) != 0x00)
319        {
320          intValue |= 0xFFFF_FF00;
321        }
322        break;
323
324      case 2:
325        intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF);
326        if ((value[0] & 0x80) != 0x00)
327        {
328          intValue |= 0xFFFF_0000;
329        }
330        break;
331
332      case 3:
333        intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) |
334                   (value[2] & 0xFF);
335        if ((value[0] & 0x80) != 0x00)
336        {
337          intValue |= 0xFF00_0000;
338        }
339        break;
340
341      case 4:
342        intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) |
343                   ((value[2] & 0xFF) << 8) | (value[3] & 0xFF);
344        break;
345
346      default:
347        throw new ASN1Exception(ERR_INTEGER_INVALID_LENGTH.get(value.length));
348    }
349
350    return new ASN1Integer(element.getType(), intValue, value);
351  }
352
353
354
355  /**
356   * {@inheritDoc}
357   */
358  @Override()
359  public void toString(final StringBuilder buffer)
360  {
361    buffer.append(intValue);
362  }
363}