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.ByteString;
041import com.unboundid.util.ByteStringBuffer;
042import com.unboundid.util.Debug;
043import com.unboundid.util.NotMutable;
044import com.unboundid.util.NotNull;
045import com.unboundid.util.Nullable;
046import com.unboundid.util.StaticUtils;
047import com.unboundid.util.ThreadSafety;
048import com.unboundid.util.ThreadSafetyLevel;
049import com.unboundid.util.Validator;
050
051import static com.unboundid.asn1.ASN1Messages.*;
052
053
054
055/**
056 * This class provides an ASN.1 octet string element, whose value is simply
057 * comprised of zero or more bytes.  Octet string elements are frequently used
058 * to represent string values as well.
059 */
060@NotMutable()
061@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
062public final class ASN1OctetString
063       extends ASN1Element
064       implements ByteString
065{
066  /**
067   * The serial version UID for this serializable class.
068   */
069  private static final long serialVersionUID = -7857753188341295516L;
070
071
072
073  /*
074   * NOTE:  This class uses lazy initialization for the value.  The value may
075   * be initially specified as either a string or a byte array, and if the value
076   * is provided as a string, then the byte array version of that value will be
077   * computed on-demand later.  Even though this class is externally immutable,
078   * that does not by itself make it completely threadsafe, because weirdness in
079   * the Java memory model could allow the assignment to be performed out of
080   * order.  By passing the value through a volatile variable any time the value
081   * is set other than in the constructor (which will always be safe) we ensure
082   * that this reordering cannot happen.  This is only needed for the valueBytes
083   * array because it is not required for primitives (like length and offset) or
084   * for objects with only final fields (like stringValue).
085   *
086   * In the majority of cases, passing the value through a volatile variable is
087   * much faster than declaring valueBytes itself to be volatile because a
088   * volatile variable cannot be held in CPU caches or registers and must only
089   * be accessed from memory visible to all threads.  Since the value may be
090   * read much more often than it is written, passing it through a volatile
091   * variable rather than making it volatile directly can help avoid that
092   * penalty when possible.
093   */
094
095
096
097  // The binary representation of the value for this element.
098  @Nullable private byte[] valueBytes;
099
100  // A volatile variable used to guard publishing the valueBytes array.  See the
101  // note above to explain why this is needed.
102  @Nullable private volatile byte[] valueBytesGuard;
103
104  // The length of the value in the byte array, if applicable.
105  private int length;
106
107  // The offset in the byte array at which the value begins, if applicable.
108  private int offset;
109
110  // The string representation of the value for this element.
111  @Nullable private String stringValue;
112
113
114
115  /**
116   * Creates a new ASN.1 octet string element with the default BER type and
117   * no value.
118   */
119  public ASN1OctetString()
120  {
121    super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
122
123    valueBytes  = StaticUtils.NO_BYTES;
124    stringValue = "";
125    offset      = 0;
126    length      = 0;
127  }
128
129
130
131  /**
132   * Creates a new ASN.1 octet string element with the specified type and no
133   * value.
134   *
135   * @param  type  The BER type to use for this element.
136   */
137  public ASN1OctetString(final byte type)
138  {
139    super(type);
140
141    valueBytes  = StaticUtils.NO_BYTES;
142    stringValue = "";
143    offset      = 0;
144    length      = 0;
145  }
146
147
148
149  /**
150   * Creates a new ASN.1 octet string element with the default BER type and the
151   * provided value.
152   *
153   * @param  value  The value to use for this element.
154   */
155  public ASN1OctetString(@Nullable final byte[] value)
156  {
157    super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
158
159    if (value == null)
160    {
161      valueBytes  = StaticUtils.NO_BYTES;
162      stringValue = "";
163      offset      = 0;
164      length      = 0;
165    }
166    else
167    {
168      valueBytes  = value;
169      stringValue = null;
170      offset      = 0;
171      length      = value.length;
172    }
173  }
174
175
176
177  /**
178   * Creates a new ASN.1 octet string element with the default BER type and the
179   * provided value.
180   *
181   * @param  value   The byte array containing the value to use for this
182   *                 element  It must not be {@code null}.
183   * @param  offset  The offset within the array at which the value begins.  It
184   *                 must be greater than or equal to zero and less than or
185   *                 equal to the length of the array.
186   * @param  length  The length in bytes of the value.   It must be greater than
187   *                 or equal to zero, and it must not extend beyond the end of
188   *                 the array.
189   */
190  public ASN1OctetString(@NotNull final byte[] value, final int offset,
191                         final int length)
192  {
193    super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
194
195    Validator.ensureNotNull(value);
196    Validator.ensureTrue((offset >= 0) && (length >= 0) &&
197         (offset+length <= value.length));
198
199    valueBytes  = value;
200    stringValue = null;
201    this.offset = offset;
202    this.length = length;
203  }
204
205
206
207  /**
208   * Creates a new ASN.1 octet string element with the specified type and the
209   * provided value.
210   *
211   * @param  type   The BER type to use for this element.
212   * @param  value  The value to use for this element.
213   */
214  public ASN1OctetString(final byte type, @Nullable final byte[] value)
215  {
216    super(type);
217
218    if (value == null)
219    {
220      valueBytes  = StaticUtils.NO_BYTES;
221      stringValue = "";
222      offset      = 0;
223      length      = 0;
224    }
225    else
226    {
227      valueBytes  = value;
228      stringValue = null;
229      offset      = 0;
230      length      = value.length;
231    }
232  }
233
234
235
236  /**
237   * Creates a new ASN.1 octet string element with the specified type and the
238   * provided value.
239   *
240   * @param  type    The BER type to use for this element.
241   * @param  value   The byte array containing the value to use for this
242   *                 element.  It must not be {@code null}.
243   * @param  offset  The offset within the array at which the value begins.  It
244   *                 must be greater than or equal to zero and less than or
245   *                 equal to the length of the array.
246   * @param  length  The length in bytes of the value.   It must be greater than
247   *                 or equal to zero, and it must not extend beyond the end of
248   *                 the array.
249   */
250  public ASN1OctetString(final byte type, @NotNull final byte[] value,
251                         final int offset, final int length)
252  {
253    super(type);
254
255    Validator.ensureTrue((offset >= 0) && (length >= 0) &&
256         (offset+length <= value.length));
257
258    valueBytes  = value;
259    stringValue = null;
260    this.offset = offset;
261    this.length = length;
262  }
263
264
265
266  /**
267   * Creates a new ASN.1 octet string element with the default BER type and the
268   * provided value.
269   *
270   * @param  value  The value to use for this element.
271   */
272  public ASN1OctetString(@Nullable final String value)
273  {
274    super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
275
276    if (value == null)
277    {
278      valueBytes  = StaticUtils.NO_BYTES;
279      stringValue = "";
280      offset      = 0;
281      length      = 0;
282    }
283    else
284    {
285      valueBytes  = null;
286      stringValue = value;
287      offset      = -1;
288      length      = -1;
289    }
290  }
291
292
293
294  /**
295   * Creates a new ASN.1 octet string element with the specified type and the
296   * provided value.
297   *
298   * @param  type   The BER type to use for this element.
299   * @param  value  The value to use for this element.
300   */
301  public ASN1OctetString(final byte type, @Nullable final String value)
302  {
303    super(type);
304
305    if (value == null)
306    {
307      valueBytes  = StaticUtils.NO_BYTES;
308      stringValue = "";
309      offset      = 0;
310      length      = 0;
311    }
312    else
313    {
314      valueBytes  = null;
315      stringValue = value;
316      offset      = -1;
317      length      = -1;
318    }
319  }
320
321
322
323  /**
324   * {@inheritDoc}
325   */
326  @Override()
327  @NotNull()
328  byte[] getValueArray()
329  {
330    return getValue();
331  }
332
333
334
335  /**
336   * {@inheritDoc}
337   */
338  @Override()
339  int getValueOffset()
340  {
341    return 0;
342  }
343
344
345
346  /**
347   * {@inheritDoc}
348   */
349  @Override()
350  public int getValueLength()
351  {
352    return getValue().length;
353  }
354
355
356
357  /**
358   * {@inheritDoc}
359   */
360  @Override()
361  @NotNull()
362  public byte[] getValue()
363  {
364    if (valueBytes == null)
365    {
366      valueBytesGuard = StaticUtils.getBytes(stringValue);
367      offset          = 0;
368      length          = valueBytesGuard.length;
369      valueBytes      = valueBytesGuard;
370    }
371    else if ((offset != 0) || (length != valueBytes.length))
372    {
373      final byte[] newArray = new byte[length];
374      System.arraycopy(valueBytes, offset, newArray, 0, length);
375      offset = 0;
376      valueBytesGuard = newArray;
377      valueBytes      = valueBytesGuard;
378    }
379
380    return valueBytes;
381  }
382
383
384
385  /**
386   * {@inheritDoc}
387   */
388  @Override()
389  public void encodeTo(@NotNull final ByteStringBuffer buffer)
390  {
391    buffer.append(getType());
392
393    if (valueBytes == null)
394    {
395      // Assume that the string contains only ASCII characters.  That will be
396      // true most of the time and we can optimize for it.  If it's not true,
397      // then we'll fix it later.
398      final int stringLength = stringValue.length();
399      final int lengthStartPos = buffer.length();
400      encodeLengthTo(stringLength, buffer);
401      final int valueStartPos = buffer.length();
402      buffer.append(stringValue);
403      final int stringBytesLength = buffer.length() - valueStartPos;
404      if (stringBytesLength != stringLength)
405      {
406        // This must mean that the string had non-ASCII characters in it, so
407        // fix the encoded representation.
408        final byte[] newLengthBytes = encodeLength(stringBytesLength);
409        if (newLengthBytes.length == (valueStartPos - lengthStartPos))
410        {
411          // It takes the same number of bytes to encode the new length as
412          // the length we previously expected, so we can just overwrite the
413          // length bytes in the backing array.
414          System.arraycopy(newLengthBytes, 0, buffer.getBackingArray(),
415                           lengthStartPos, newLengthBytes.length);
416        }
417        else
418        {
419          buffer.setLength(lengthStartPos);
420          buffer.append(newLengthBytes);
421          buffer.append(stringValue);
422        }
423      }
424    }
425    else
426    {
427      encodeLengthTo(length, buffer);
428      buffer.append(valueBytes, offset, length);
429    }
430  }
431
432
433
434  /**
435   * Retrieves the string value for this element.
436   *
437   * @return  The String value for this element.
438   */
439  @Override()
440  @NotNull()
441  public String stringValue()
442  {
443    if (stringValue == null)
444    {
445      if (length == 0)
446      {
447        stringValue = "";
448      }
449      else
450      {
451        stringValue = StaticUtils.toUTF8String(valueBytes, offset, length);
452      }
453    }
454
455    return stringValue;
456  }
457
458
459
460  /**
461   * Decodes the contents of the provided byte array as an octet string element.
462   *
463   * @param  elementBytes  The byte array to decode as an ASN.1 octet string
464   *                       element.
465   *
466   * @return  The decoded ASN.1 octet string element.
467   *
468   * @throws  ASN1Exception  If the provided array cannot be decoded as an
469   *                         octet string element.
470   */
471  @NotNull()
472  public static ASN1OctetString decodeAsOctetString(
473              @NotNull final byte[] elementBytes)
474         throws ASN1Exception
475  {
476    try
477    {
478      int valueStartPos = 2;
479      int length = (elementBytes[1] & 0x7F);
480      if (length != elementBytes[1])
481      {
482        final int numLengthBytes = length;
483
484        length = 0;
485        for (int i=0; i < numLengthBytes; i++)
486        {
487          length <<= 8;
488          length |= (elementBytes[valueStartPos++] & 0xFF);
489        }
490      }
491
492      if ((elementBytes.length - valueStartPos) != length)
493      {
494        throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
495                                     (elementBytes.length - valueStartPos)));
496      }
497
498      return new ASN1OctetString(elementBytes[0], elementBytes, valueStartPos,
499                                 length);
500    }
501    catch (final ASN1Exception ae)
502    {
503      Debug.debugException(ae);
504      throw ae;
505    }
506    catch (final Exception e)
507    {
508      Debug.debugException(e);
509      throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
510    }
511  }
512
513
514
515  /**
516   * Decodes the provided ASN.1 element as an octet string element.
517   *
518   * @param  element  The ASN.1 element to be decoded.
519   *
520   * @return  The decoded ASN.1 octet string element.
521   */
522  @NotNull()
523  public static ASN1OctetString decodeAsOctetString(
524                     @NotNull final ASN1Element element)
525  {
526    return new ASN1OctetString(element.getType(), element.getValue());
527  }
528
529
530
531  /**
532   * Appends the value of this ASN.1 octet string to the provided buffer.
533   *
534   * @param  buffer  The buffer to which the value is to be appended.
535   */
536  @Override()
537  public void appendValueTo(@NotNull final ByteStringBuffer buffer)
538  {
539    if (valueBytes == null)
540    {
541      buffer.append(stringValue);
542    }
543    else
544    {
545      buffer.append(valueBytes, offset, length);
546    }
547  }
548
549
550
551  /**
552   * Converts this byte string to an ASN.1 octet string.
553   *
554   * @return  An ASN.1 octet string with the value of this byte string.
555   */
556  @Override()
557  @NotNull()
558  public ASN1OctetString toASN1OctetString()
559  {
560    return this;
561  }
562
563
564
565  /**
566   * {@inheritDoc}
567   */
568  @Override()
569  public void toString(@NotNull final StringBuilder buffer)
570  {
571    buffer.append(stringValue());
572  }
573}