001/*
002 * Copyright 2007-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2024 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) 2007-2024 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.NotNull;
043import com.unboundid.util.ThreadSafety;
044import com.unboundid.util.ThreadSafetyLevel;
045
046import static com.unboundid.asn1.ASN1Messages.*;
047
048
049
050/**
051 * This class provides an ASN.1 integer element that is backed by a Java
052 * {@code int}, which is a signed 32-bit value and can represent any integer
053 * between -2147483648 and 2147483647.  If you need support for integer values
054 * in the signed 64-bit range, see the {@link ASN1Long} class as an alternative.
055 * If you need support for integer values of arbitrary size, see
056 * {@link ASN1BigInteger}.
057 */
058@NotMutable()
059@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
060public final class ASN1Integer
061       extends ASN1Element
062{
063  /**
064   * The serial version UID for this serializable class.
065   */
066  private static final long serialVersionUID = -733929804601994372L;
067
068
069
070  // The int value for this element.
071  private final int intValue;
072
073
074
075  /**
076   * Creates a new ASN.1 integer element with the default BER type and the
077   * provided int value.
078   *
079   * @param  intValue  The int value to use for this element.
080   */
081  public ASN1Integer(final int intValue)
082  {
083    super(ASN1Constants.UNIVERSAL_INTEGER_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 value.
093   *
094   * @param  type      The BER type to use for this element.
095   * @param  intValue  The int value to use for this element.
096   */
097  public ASN1Integer(final byte type, final int intValue)
098  {
099    super(type, encodeIntValue(intValue));
100
101    this.intValue = intValue;
102  }
103
104
105
106  /**
107   * Creates a new ASN.1 integer element with the specified BER type and the
108   * provided int and pre-encoded values.
109   *
110   * @param  type      The BER type to use for this element.
111   * @param  intValue  The int value to use for this element.
112   * @param  value     The pre-encoded value to use for this element.
113   */
114  private ASN1Integer(final byte type, final int intValue,
115                      @NotNull final byte[] value)
116  {
117    super(type, value);
118
119    this.intValue = intValue;
120  }
121
122
123
124  /**
125   * Encodes the provided int value to a byte array suitable for use as the
126   * value of an integer element.
127   *
128   * @param  intValue  The int value to be encoded.
129   *
130   * @return  A byte array containing the encoded value.
131   */
132  @NotNull()
133  static byte[] encodeIntValue(final int intValue)
134  {
135    if (intValue < 0)
136    {
137      if ((intValue & 0xFFFF_FF80) == 0xFFFF_FF80)
138      {
139        return new byte[]
140        {
141          (byte) (intValue & 0xFF)
142        };
143      }
144      else if ((intValue & 0xFFFF_8000) == 0xFFFF_8000)
145      {
146        return new byte[]
147        {
148          (byte) ((intValue >> 8) & 0xFF),
149          (byte) (intValue & 0xFF)
150        };
151      }
152      else if ((intValue & 0xFF80_0000) == 0xFF80_0000)
153      {
154        return new byte[]
155        {
156          (byte) ((intValue >> 16) & 0xFF),
157          (byte) ((intValue >> 8) & 0xFF),
158          (byte) (intValue & 0xFF)
159        };
160      }
161      else
162      {
163        return new byte[]
164        {
165          (byte) ((intValue >> 24) & 0xFF),
166          (byte) ((intValue >> 16) & 0xFF),
167          (byte) ((intValue >> 8) & 0xFF),
168          (byte) (intValue & 0xFF)
169        };
170      }
171    }
172    else
173    {
174      if ((intValue & 0x0000_007F) == intValue)
175      {
176        return new byte[]
177        {
178          (byte) (intValue & 0x7F)
179        };
180      }
181      else if ((intValue & 0x0000_7FFF) == intValue)
182      {
183        return new byte[]
184        {
185          (byte) ((intValue >> 8) & 0x7F),
186          (byte) (intValue & 0xFF)
187        };
188      }
189      else if ((intValue & 0x007F_FFFF) == intValue)
190      {
191        return new byte[]
192        {
193          (byte) ((intValue >> 16) & 0x7F),
194          (byte) ((intValue >> 8) & 0xFF),
195          (byte) (intValue & 0xFF)
196        };
197      }
198      else
199      {
200        return new byte[]
201        {
202          (byte) ((intValue >> 24) & 0x7F),
203          (byte) ((intValue >> 16) & 0xFF),
204          (byte) ((intValue >> 8) & 0xFF),
205          (byte) (intValue & 0xFF)
206        };
207      }
208    }
209  }
210
211
212
213  /**
214   * Retrieves the int value for this element.
215   *
216   * @return  The int value for this element.
217   */
218  public int intValue()
219  {
220    return intValue;
221  }
222
223
224
225  /**
226   * Decodes the contents of the provided byte array as an integer element.
227   *
228   * @param  elementBytes  The byte array to decode as an ASN.1 integer element.
229   *
230   * @return  The decoded ASN.1 integer element.
231   *
232   * @throws  ASN1Exception  If the provided array cannot be decoded as an
233   *                         integer element.
234   */
235  @NotNull()
236  public static ASN1Integer decodeAsInteger(@NotNull final byte[] elementBytes)
237         throws ASN1Exception
238  {
239    try
240    {
241      int valueStartPos = 2;
242      int length = (elementBytes[1] & 0x7F);
243      if (length != elementBytes[1])
244      {
245        final int numLengthBytes = length;
246
247        length = 0;
248        for (int i=0; i < numLengthBytes; i++)
249        {
250          length <<= 8;
251          length |= (elementBytes[valueStartPos++] & 0xFF);
252        }
253      }
254
255      if ((elementBytes.length - valueStartPos) != length)
256      {
257        throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
258                                     (elementBytes.length - valueStartPos)));
259      }
260
261      final byte[] value = new byte[length];
262      System.arraycopy(elementBytes, valueStartPos, value, 0, length);
263
264      int intValue;
265      switch (value.length)
266      {
267        case 1:
268          intValue = (value[0] & 0xFF);
269          if ((value[0] & 0x80) != 0x00)
270          {
271            intValue |= 0xFFFF_FF00;
272          }
273          break;
274
275        case 2:
276          intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF);
277          if ((value[0] & 0x80) != 0x00)
278          {
279            intValue |= 0xFFFF_0000;
280          }
281          break;
282
283        case 3:
284          intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) |
285                     (value[2] & 0xFF);
286          if ((value[0] & 0x80) != 0x00)
287          {
288            intValue |= 0xFF00_0000;
289          }
290          break;
291
292        case 4:
293          intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) |
294                     ((value[2] & 0xFF) << 8) | (value[3] & 0xFF);
295          break;
296
297        default:
298          throw new ASN1Exception(ERR_ENUMERATED_INVALID_LENGTH.get(
299                                       value.length));
300      }
301
302      return new ASN1Integer(elementBytes[0], intValue, value);
303    }
304    catch (final ASN1Exception ae)
305    {
306      Debug.debugException(ae);
307      throw ae;
308    }
309    catch (final Exception e)
310    {
311      Debug.debugException(e);
312      throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
313    }
314  }
315
316
317
318  /**
319   * Decodes the provided ASN.1 element as an integer element.
320   *
321   * @param  element  The ASN.1 element to be decoded.
322   *
323   * @return  The decoded ASN.1 integer element.
324   *
325   * @throws  ASN1Exception  If the provided element cannot be decoded as an
326   *                         integer element.
327   */
328  @NotNull()
329  public static ASN1Integer decodeAsInteger(@NotNull final ASN1Element element)
330         throws ASN1Exception
331  {
332    int intValue;
333    final byte[] value = element.getValue();
334    switch (value.length)
335    {
336      case 1:
337        intValue = (value[0] & 0xFF);
338        if ((value[0] & 0x80) != 0x00)
339        {
340          intValue |= 0xFFFF_FF00;
341        }
342        break;
343
344      case 2:
345        intValue = ((value[0] & 0xFF) << 8) | (value[1] & 0xFF);
346        if ((value[0] & 0x80) != 0x00)
347        {
348          intValue |= 0xFFFF_0000;
349        }
350        break;
351
352      case 3:
353        intValue = ((value[0] & 0xFF) << 16) | ((value[1] & 0xFF) << 8) |
354                   (value[2] & 0xFF);
355        if ((value[0] & 0x80) != 0x00)
356        {
357          intValue |= 0xFF00_0000;
358        }
359        break;
360
361      case 4:
362        intValue = ((value[0] & 0xFF) << 24) | ((value[1] & 0xFF) << 16) |
363                   ((value[2] & 0xFF) << 8) | (value[3] & 0xFF);
364        break;
365
366      default:
367        throw new ASN1Exception(ERR_INTEGER_INVALID_LENGTH.get(value.length));
368    }
369
370    return new ASN1Integer(element.getType(), intValue, value);
371  }
372
373
374
375  /**
376   * {@inheritDoc}
377   */
378  @Override()
379  public void toString(@NotNull final StringBuilder buffer)
380  {
381    buffer.append(intValue);
382  }
383}