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