001/* 002 * Copyright 2007-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.asn1; 022 023 024 025import java.util.ArrayList; 026import java.util.Collection; 027 028import com.unboundid.util.ByteStringBuffer; 029import com.unboundid.util.NotMutable; 030import com.unboundid.util.ThreadSafety; 031import com.unboundid.util.ThreadSafetyLevel; 032 033import static com.unboundid.asn1.ASN1Constants.*; 034import static com.unboundid.asn1.ASN1Messages.*; 035import static com.unboundid.util.Debug.*; 036 037 038 039/** 040 * This class provides an ASN.1 set element, which is used to hold a set of 041 * zero or more other elements (potentially including additional "envelope" 042 * element types like other sequences and/or sets) in which the order of those 043 * elements should not be considered significant. 044 */ 045@NotMutable() 046@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 047public final class ASN1Set 048 extends ASN1Element 049{ 050 /** 051 * The serial version UID for this serializable class. 052 */ 053 private static final long serialVersionUID = -523497075310394409L; 054 055 056 057 /* 058 * NOTE: This class uses lazy initialization for the encoded value. The 059 * encoded value should only be needed by the getValue() method, which is used 060 * by ASN1Element.encode(). Even though this class is externally immutable, 061 * that does not by itself make it completely threadsafe, because weirdness in 062 * the Java memory model could allow the assignment to be performed out of 063 * order. By passing the value through a volatile variable any time the value 064 * is set other than in the constructor (which will always be safe) we ensure 065 * that this reordering cannot happen. 066 * 067 * In the majority of cases, passing the value through assignments to 068 * valueBytes through a volatile variable is much faster than declaring 069 * valueBytes itself to be volatile because a volatile variable cannot be held 070 * in CPU caches or registers and must only be accessed from memory visible to 071 * all threads. Since the value may be read much more often than it is 072 * written, passing it through a volatile variable rather than making it 073 * volatile directly can help avoid that penalty when possible. 074 */ 075 076 077 078 // The set of ASN.1 elements contained in this set. 079 private final ASN1Element[] elements; 080 081 // The encoded representation of the value, if available. 082 private byte[] encodedValue; 083 084 // A volatile variable used to guard publishing the encodedValue array. See 085 // the note above to explain why this is needed. 086 private volatile byte[] encodedValueGuard; 087 088 089 090 /** 091 * Creates a new ASN.1 set with the default BER type and no encapsulated 092 * elements. 093 */ 094 public ASN1Set() 095 { 096 super(UNIVERSAL_SET_TYPE); 097 098 elements = NO_ELEMENTS; 099 encodedValue = NO_VALUE; 100 } 101 102 103 104 /** 105 * Creates a new ASN.1 set with the specified BER type and no encapsulated 106 * elements. 107 * 108 * @param type The BER type to use for this element. 109 */ 110 public ASN1Set(final byte type) 111 { 112 super(type); 113 114 elements = NO_ELEMENTS; 115 encodedValue = NO_VALUE; 116 } 117 118 119 120 /** 121 * Creates a new ASN.1 set with the default BER type and the provided set of 122 * elements. 123 * 124 * @param elements The set of elements to include in this set. 125 */ 126 public ASN1Set(final ASN1Element... elements) 127 { 128 super(UNIVERSAL_SET_TYPE); 129 130 if (elements == null) 131 { 132 this.elements = NO_ELEMENTS; 133 } 134 else 135 { 136 this.elements = elements; 137 } 138 139 encodedValue = null; 140 } 141 142 143 144 /** 145 * Creates a new ASN.1 set with the default BER type and the provided set of 146 * elements. 147 * 148 * @param elements The set of elements to include in this set. 149 */ 150 public ASN1Set(final Collection<? extends ASN1Element> elements) 151 { 152 super(UNIVERSAL_SET_TYPE); 153 154 if ((elements == null) || elements.isEmpty()) 155 { 156 this.elements = NO_ELEMENTS; 157 } 158 else 159 { 160 this.elements = new ASN1Element[elements.size()]; 161 elements.toArray(this.elements); 162 } 163 164 encodedValue = null; 165 } 166 167 168 169 /** 170 * Creates a new ASN.1 set with the specified BER type and the provided set of 171 * elements. 172 * 173 * @param type The BER type to use for this element. 174 * @param elements The set of elements to include in this set. 175 */ 176 public ASN1Set(final byte type, final ASN1Element... elements) 177 { 178 super(type); 179 180 if (elements == null) 181 { 182 this.elements = NO_ELEMENTS; 183 } 184 else 185 { 186 this.elements = elements; 187 } 188 189 encodedValue = null; 190 } 191 192 193 194 /** 195 * Creates a new ASN.1 set with the specified BER type and the provided set of 196 * elements. 197 * 198 * @param type The BER type to use for this element. 199 * @param elements The set of elements to include in this set. 200 */ 201 public ASN1Set(final byte type, 202 final Collection<? extends ASN1Element> elements) 203 { 204 super(type); 205 206 if ((elements == null) || elements.isEmpty()) 207 { 208 this.elements = NO_ELEMENTS; 209 } 210 else 211 { 212 this.elements = new ASN1Element[elements.size()]; 213 elements.toArray(this.elements); 214 } 215 216 encodedValue = null; 217 } 218 219 220 221 /** 222 * Creates a new ASN.1 set with the specified type, set of elements, and 223 * encoded value. 224 * 225 * @param type The BER type to use for this element. 226 * @param elements The set of elements to include in this set. 227 * @param value The pre-encoded value for this element. 228 */ 229 private ASN1Set(final byte type, final ASN1Element[] elements, 230 final byte[] value) 231 { 232 super(type); 233 234 this.elements = elements; 235 encodedValue = value; 236 } 237 238 239 240 /** 241 * {@inheritDoc} 242 */ 243 @Override() 244 byte[] getValueArray() 245 { 246 return getValue(); 247 } 248 249 250 251 /** 252 * {@inheritDoc} 253 */ 254 @Override() 255 int getValueOffset() 256 { 257 return 0; 258 } 259 260 261 262 /** 263 * {@inheritDoc} 264 */ 265 @Override() 266 public int getValueLength() 267 { 268 return getValue().length; 269 } 270 271 272 273 /** 274 * {@inheritDoc} 275 */ 276 @Override() 277 public byte[] getValue() 278 { 279 if (encodedValue == null) 280 { 281 encodedValueGuard = ASN1Sequence.encodeElements(elements); 282 encodedValue = encodedValueGuard; 283 } 284 285 return encodedValue; 286 } 287 288 289 290 /** 291 * {@inheritDoc} 292 */ 293 @Override() 294 public void encodeTo(final ByteStringBuffer buffer) 295 { 296 buffer.append(getType()); 297 298 if (elements.length == 0) 299 { 300 buffer.append((byte) 0x00); 301 return; 302 } 303 304 // In this case, it will likely be faster to just go ahead and append 305 // encoded representations of all of the elements and insert the length 306 // later once we know it. 307 final int originalLength = buffer.length(); 308 for (final ASN1Element e : elements) 309 { 310 e.encodeTo(buffer); 311 } 312 313 buffer.insert(originalLength, 314 encodeLength(buffer.length() - originalLength)); 315 } 316 317 318 319 /** 320 * Retrieves the set of encapsulated elements held in this set. 321 * 322 * @return The set of encapsulated elements held in this set. 323 */ 324 public ASN1Element[] elements() 325 { 326 return elements; 327 } 328 329 330 331 /** 332 * Decodes the contents of the provided byte array as a set element. 333 * 334 * @param elementBytes The byte array to decode as an ASN.1 set element. 335 * 336 * @return The decoded ASN.1 set element. 337 * 338 * @throws ASN1Exception If the provided array cannot be decoded as a set 339 * element. 340 */ 341 public static ASN1Set decodeAsSet(final byte[] elementBytes) 342 throws ASN1Exception 343 { 344 try 345 { 346 int valueStartPos = 2; 347 int length = (elementBytes[1] & 0x7F); 348 if (length != elementBytes[1]) 349 { 350 final int numLengthBytes = length; 351 352 length = 0; 353 for (int i=0; i < numLengthBytes; i++) 354 { 355 length <<= 8; 356 length |= (elementBytes[valueStartPos++] & 0xFF); 357 } 358 } 359 360 if ((elementBytes.length - valueStartPos) != length) 361 { 362 throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length, 363 (elementBytes.length - valueStartPos))); 364 } 365 366 final byte[] value = new byte[length]; 367 System.arraycopy(elementBytes, valueStartPos, value, 0, length); 368 369 int numElements = 0; 370 final ArrayList<ASN1Element> elementList = new ArrayList<ASN1Element>(5); 371 try 372 { 373 int pos = 0; 374 while (pos < value.length) 375 { 376 final byte type = value[pos++]; 377 378 final byte firstLengthByte = value[pos++]; 379 int l = (firstLengthByte & 0x7F); 380 if (l != firstLengthByte) 381 { 382 final int numLengthBytes = l; 383 l = 0; 384 for (int i=0; i < numLengthBytes; i++) 385 { 386 l <<= 8; 387 l |= (value[pos++] & 0xFF); 388 } 389 } 390 391 final int posPlusLength = pos + l; 392 if ((l < 0) || (posPlusLength < 0) || (posPlusLength > value.length)) 393 { 394 throw new ASN1Exception( 395 ERR_SET_BYTES_DECODE_LENGTH_EXCEEDS_AVAILABLE.get()); 396 } 397 398 elementList.add(new ASN1Element(type, value, pos, l)); 399 pos += l; 400 numElements++; 401 } 402 } 403 catch (final ASN1Exception ae) 404 { 405 throw ae; 406 } 407 catch (final Exception e) 408 { 409 debugException(e); 410 throw new ASN1Exception(ERR_SET_BYTES_DECODE_EXCEPTION.get(e), e); 411 } 412 413 int i = 0; 414 final ASN1Element[] elements = new ASN1Element[numElements]; 415 for (final ASN1Element e : elementList) 416 { 417 elements[i++] = e; 418 } 419 420 return new ASN1Set(elementBytes[0], elements, value); 421 } 422 catch (final ASN1Exception ae) 423 { 424 debugException(ae); 425 throw ae; 426 } 427 catch (final Exception e) 428 { 429 debugException(e); 430 throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e); 431 } 432 } 433 434 435 436 /** 437 * Decodes the provided ASN.1 element as a set element. 438 * 439 * @param element The ASN.1 element to be decoded. 440 * 441 * @return The decoded ASN.1 set element. 442 * 443 * @throws ASN1Exception If the provided element cannot be decoded as a set 444 * element. 445 */ 446 public static ASN1Set decodeAsSet(final ASN1Element element) 447 throws ASN1Exception 448 { 449 int numElements = 0; 450 final ArrayList<ASN1Element> elementList = new ArrayList<ASN1Element>(5); 451 final byte[] value = element.getValue(); 452 453 try 454 { 455 int pos = 0; 456 while (pos < value.length) 457 { 458 final byte type = value[pos++]; 459 460 final byte firstLengthByte = value[pos++]; 461 int length = (firstLengthByte & 0x7F); 462 if (length != firstLengthByte) 463 { 464 final int numLengthBytes = length; 465 length = 0; 466 for (int i=0; i < numLengthBytes; i++) 467 { 468 length <<= 8; 469 length |= (value[pos++] & 0xFF); 470 } 471 } 472 473 final int posPlusLength = pos + length; 474 if ((length < 0) || (posPlusLength < 0) || 475 (posPlusLength > value.length)) 476 { 477 throw new ASN1Exception( 478 ERR_SET_DECODE_LENGTH_EXCEEDS_AVAILABLE.get( 479 String.valueOf(element))); 480 } 481 482 elementList.add(new ASN1Element(type, value, pos, length)); 483 pos += length; 484 numElements++; 485 } 486 } 487 catch (final ASN1Exception ae) 488 { 489 throw ae; 490 } 491 catch (final Exception e) 492 { 493 debugException(e); 494 throw new ASN1Exception( 495 ERR_SET_DECODE_EXCEPTION.get(String.valueOf(element), e), e); 496 } 497 498 int i = 0; 499 final ASN1Element[] elements = new ASN1Element[numElements]; 500 for (final ASN1Element e : elementList) 501 { 502 elements[i++] = e; 503 } 504 505 return new ASN1Set(element.getType(), elements, value); 506 } 507 508 509 510 /** 511 * Appends a string representation of this ASN.1 element to the provided 512 * buffer. 513 * 514 * @param buffer The buffer to which to append the information. 515 */ 516 @Override() 517 public void toString(final StringBuilder buffer) 518 { 519 buffer.append('['); 520 for (int i=0; i < elements.length; i++) 521 { 522 if (i > 0) 523 { 524 buffer.append(','); 525 } 526 elements[i].toString(buffer); 527 } 528 buffer.append(']'); 529 } 530}