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