com.unboundid.asn1.ASN1OctetString Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unboundid-ldapsdk Show documentation
Show all versions of unboundid-ldapsdk Show documentation
The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use
Java API for communicating with LDAP directory servers and performing
related tasks like reading and writing LDIF, encoding and decoding data
using base64 and ASN.1 BER, and performing secure communication. This
package contains the Standard Edition of the LDAP SDK, which is a
complete, general-purpose library for communicating with LDAPv3 directory
servers.
/*
* Copyright 2007-2018 Ping Identity Corporation
* All Rights Reserved.
*/
/*
* Copyright (C) 2008-2018 Ping Identity Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPLv2 only)
* or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
package com.unboundid.asn1;
import com.unboundid.util.ByteString;
import com.unboundid.util.ByteStringBuffer;
import com.unboundid.util.Debug;
import com.unboundid.util.NotMutable;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.Validator;
import static com.unboundid.asn1.ASN1Messages.*;
/**
* This class provides an ASN.1 octet string element, whose value is simply
* comprised of zero or more bytes. Octet string elements are frequently used
* to represent string values as well.
*/
@NotMutable()
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class ASN1OctetString
extends ASN1Element
implements ByteString
{
/**
* The serial version UID for this serializable class.
*/
private static final long serialVersionUID = -7857753188341295516L;
/*
* NOTE: This class uses lazy initialization for the value. The value may
* be initially specified as either a string or a byte array, and if the value
* is provided as a string, then the byte array version of that value will be
* computed on-demand later. Even though this class is externally immutable,
* that does not by itself make it completely threadsafe, because weirdness in
* the Java memory model could allow the assignment to be performed out of
* order. By passing the value through a volatile variable any time the value
* is set other than in the constructor (which will always be safe) we ensure
* that this reordering cannot happen. This is only needed for the valueBytes
* array because it is not required for primitives (like length and offset) or
* for objects with only final fields (like stringValue).
*
* In the majority of cases, passing the value through a volatile variable is
* much faster than declaring valueBytes itself to be volatile because a
* volatile variable cannot be held in CPU caches or registers and must only
* be accessed from memory visible to all threads. Since the value may be
* read much more often than it is written, passing it through a volatile
* variable rather than making it volatile directly can help avoid that
* penalty when possible.
*/
// The binary representation of the value for this element.
private byte[] valueBytes;
// A volatile variable used to guard publishing the valueBytes array. See the
// note above to explain why this is needed.
private volatile byte[] valueBytesGuard;
// The length of the value in the byte array, if applicable.
private int length;
// The offset in the byte array at which the value begins, if applicable.
private int offset;
// The string representation of the value for this element.
private String stringValue;
/**
* Creates a new ASN.1 octet string element with the default BER type and
* no value.
*/
public ASN1OctetString()
{
super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
valueBytes = StaticUtils.NO_BYTES;
stringValue = "";
offset = 0;
length = 0;
}
/**
* Creates a new ASN.1 octet string element with the specified type and no
* value.
*
* @param type The BER type to use for this element.
*/
public ASN1OctetString(final byte type)
{
super(type);
valueBytes = StaticUtils.NO_BYTES;
stringValue = "";
offset = 0;
length = 0;
}
/**
* Creates a new ASN.1 octet string element with the default BER type and the
* provided value.
*
* @param value The value to use for this element.
*/
public ASN1OctetString(final byte[] value)
{
super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
if (value == null)
{
valueBytes = StaticUtils.NO_BYTES;
stringValue = "";
offset = 0;
length = 0;
}
else
{
valueBytes = value;
stringValue = null;
offset = 0;
length = value.length;
}
}
/**
* Creates a new ASN.1 octet string element with the default BER type and the
* provided value.
*
* @param value The byte array containing the value to use for this
* element It must not be {@code null}.
* @param offset The offset within the array at which the value begins. It
* must be greater than or equal to zero and less than or
* equal to the length of the array.
* @param length The length in bytes of the value. It must be greater than
* or equal to zero, and it must not extend beyond the end of
* the array.
*/
public ASN1OctetString(final byte[] value, final int offset, final int length)
{
super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
Validator.ensureNotNull(value);
Validator.ensureTrue((offset >= 0) && (length >= 0) &&
(offset+length <= value.length));
valueBytes = value;
stringValue = null;
this.offset = offset;
this.length = length;
}
/**
* Creates a new ASN.1 octet string element with the specified type and the
* provided value.
*
* @param type The BER type to use for this element.
* @param value The value to use for this element.
*/
public ASN1OctetString(final byte type, final byte[] value)
{
super(type);
if (value == null)
{
valueBytes = StaticUtils.NO_BYTES;
stringValue = "";
offset = 0;
length = 0;
}
else
{
valueBytes = value;
stringValue = null;
offset = 0;
length = value.length;
}
}
/**
* Creates a new ASN.1 octet string element with the specified type and the
* provided value.
*
* @param type The BER type to use for this element.
* @param value The byte array containing the value to use for this
* element. It must not be {@code null}.
* @param offset The offset within the array at which the value begins. It
* must be greater than or equal to zero and less than or
* equal to the length of the array..
* @param length The length in bytes of the value. It must be greater than
* or equal to zero, and it must not extend beyond the end of
* the array.
*/
public ASN1OctetString(final byte type, final byte[] value, final int offset,
final int length)
{
super(type);
Validator.ensureTrue((offset >= 0) && (length >= 0) &&
(offset+length <= value.length));
valueBytes = value;
stringValue = null;
this.offset = offset;
this.length = length;
}
/**
* Creates a new ASN.1 octet string element with the default BER type and the
* provided value.
*
* @param value The value to use for this element.
*/
public ASN1OctetString(final String value)
{
super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE);
if (value == null)
{
valueBytes = StaticUtils.NO_BYTES;
stringValue = "";
offset = 0;
length = 0;
}
else
{
valueBytes = null;
stringValue = value;
offset = -1;
length = -1;
}
}
/**
* Creates a new ASN.1 octet string element with the specified type and the
* provided value.
*
* @param type The BER type to use for this element.
* @param value The value to use for this element.
*/
public ASN1OctetString(final byte type, final String value)
{
super(type);
if (value == null)
{
valueBytes = StaticUtils.NO_BYTES;
stringValue = "";
offset = 0;
length = 0;
}
else
{
valueBytes = null;
stringValue = value;
offset = -1;
length = -1;
}
}
/**
* {@inheritDoc}
*/
@Override()
byte[] getValueArray()
{
return getValue();
}
/**
* {@inheritDoc}
*/
@Override()
int getValueOffset()
{
return 0;
}
/**
* {@inheritDoc}
*/
@Override()
public int getValueLength()
{
return getValue().length;
}
/**
* {@inheritDoc}
*/
@Override()
public byte[] getValue()
{
if (valueBytes == null)
{
valueBytesGuard = StaticUtils.getBytes(stringValue);
offset = 0;
length = valueBytesGuard.length;
valueBytes = valueBytesGuard;
}
else if ((offset != 0) || (length != valueBytes.length))
{
final byte[] newArray = new byte[length];
System.arraycopy(valueBytes, offset, newArray, 0, length);
offset = 0;
valueBytesGuard = newArray;
valueBytes = valueBytesGuard;
}
return valueBytes;
}
/**
* {@inheritDoc}
*/
@Override()
public void encodeTo(final ByteStringBuffer buffer)
{
buffer.append(getType());
if (valueBytes == null)
{
// Assume that the string contains only ASCII characters. That will be
// true most of the time and we can optimize for it. If it's not true,
// then we'll fix it later.
final int stringLength = stringValue.length();
final int lengthStartPos = buffer.length();
encodeLengthTo(stringLength, buffer);
final int valueStartPos = buffer.length();
buffer.append(stringValue);
final int stringBytesLength = buffer.length() - valueStartPos;
if (stringBytesLength != stringLength)
{
// This must mean that the string had non-ASCII characters in it, so
// fix the encoded representation.
final byte[] newLengthBytes = encodeLength(stringBytesLength);
if (newLengthBytes.length == (valueStartPos - lengthStartPos))
{
// It takes the same number of bytes to encode the new length as
// the length we previously expected, so we can just overwrite the
// length bytes in the backing array.
System.arraycopy(newLengthBytes, 0, buffer.getBackingArray(),
lengthStartPos, newLengthBytes.length);
}
else
{
buffer.setLength(lengthStartPos);
buffer.append(newLengthBytes);
buffer.append(stringValue);
}
}
}
else
{
encodeLengthTo(length, buffer);
buffer.append(valueBytes, offset, length);
}
}
/**
* Retrieves the string value for this element.
*
* @return The String value for this element.
*/
@Override()
public String stringValue()
{
if (stringValue == null)
{
if (length == 0)
{
stringValue = "";
}
else
{
stringValue = StaticUtils.toUTF8String(valueBytes, offset, length);
}
}
return stringValue;
}
/**
* Decodes the contents of the provided byte array as an octet string element.
*
* @param elementBytes The byte array to decode as an ASN.1 octet string
* element.
*
* @return The decoded ASN.1 octet string element.
*
* @throws ASN1Exception If the provided array cannot be decoded as an
* octet string element.
*/
public static ASN1OctetString decodeAsOctetString(final byte[] elementBytes)
throws ASN1Exception
{
try
{
int valueStartPos = 2;
int length = (elementBytes[1] & 0x7F);
if (length != elementBytes[1])
{
final int numLengthBytes = length;
length = 0;
for (int i=0; i < numLengthBytes; i++)
{
length <<= 8;
length |= (elementBytes[valueStartPos++] & 0xFF);
}
}
if ((elementBytes.length - valueStartPos) != length)
{
throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length,
(elementBytes.length - valueStartPos)));
}
return new ASN1OctetString(elementBytes[0], elementBytes, valueStartPos,
length);
}
catch (final ASN1Exception ae)
{
Debug.debugException(ae);
throw ae;
}
catch (final Exception e)
{
Debug.debugException(e);
throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e);
}
}
/**
* Decodes the provided ASN.1 element as an octet string element.
*
* @param element The ASN.1 element to be decoded.
*
* @return The decoded ASN.1 octet string element.
*/
public static ASN1OctetString decodeAsOctetString(final ASN1Element element)
{
return new ASN1OctetString(element.getType(), element.getValue());
}
/**
* Appends the value of this ASN.1 octet string to the provided buffer.
*
* @param buffer The buffer to which the value is to be appended.
*/
@Override()
public void appendValueTo(final ByteStringBuffer buffer)
{
if (valueBytes == null)
{
buffer.append(stringValue);
}
else
{
buffer.append(valueBytes, offset, length);
}
}
/**
* Converts this byte string to an ASN.1 octet string.
*
* @return An ASN.1 octet string with the value of this byte string.
*/
@Override()
public ASN1OctetString toASN1OctetString()
{
return this;
}
/**
* {@inheritDoc}
*/
@Override()
public void toString(final StringBuilder buffer)
{
buffer.append(stringValue());
}
}