001/* 002 * Copyright 2008-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 Ping Identity Corporation 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.io.IOException; 026import java.io.OutputStream; 027import java.nio.BufferOverflowException; 028import java.nio.ByteBuffer; 029 030import com.unboundid.util.ByteStringBuffer; 031import com.unboundid.util.Debug; 032import com.unboundid.util.ThreadSafety; 033import com.unboundid.util.ThreadSafetyLevel; 034 035 036 037/** 038 * This class provides an efficient mechanism for writing ASN.1 elements to 039 * output streams. 040 */ 041@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 042public final class ASN1Writer 043{ 044 /** 045 * The thread-local buffers that will be used for encoding the elements. 046 */ 047 private static final ThreadLocal<ByteStringBuffer> buffers = 048 new ThreadLocal<>(); 049 050 051 052 /** 053 * The maximum amount of memory that will be used for a thread-local buffer. 054 */ 055 private static final int MAX_BUFFER_LENGTH = 524_288; 056 057 058 059 /** 060 * Prevent this class from being instantiated. 061 */ 062 private ASN1Writer() 063 { 064 // No implementation is required. 065 } 066 067 068 069 /** 070 * Writes an encoded representation of the provided ASN.1 element to the 071 * given output stream. 072 * 073 * @param element The ASN.1 element to be written. 074 * @param outputStream The output stream to which the encoded representation 075 * of the element should be written. 076 * 077 * @throws IOException If a problem occurs while writing the element. 078 */ 079 public static void writeElement(final ASN1Element element, 080 final OutputStream outputStream) 081 throws IOException 082 { 083 Debug.debugASN1Write(element); 084 085 ByteStringBuffer buffer = buffers.get(); 086 if (buffer == null) 087 { 088 buffer = new ByteStringBuffer(); 089 buffers.set(buffer); 090 } 091 092 element.encodeTo(buffer); 093 094 try 095 { 096 buffer.write(outputStream); 097 } 098 finally 099 { 100 if (buffer.capacity() > MAX_BUFFER_LENGTH) 101 { 102 buffer.setCapacity(MAX_BUFFER_LENGTH); 103 } 104 buffer.clear(); 105 } 106 } 107 108 109 110 /** 111 * Appends an encoded representation of the provided ASN.1 element to the 112 * given byte buffer. When this method completes, the position will be at the 113 * beginning of the written element, and the limit will be at the end. 114 * 115 * @param element The ASN.1 element to be written. 116 * @param buffer The buffer to which the element should be added. 117 * 118 * @throws BufferOverflowException If the provided buffer does not have 119 * enough space between the position and 120 * the limit to hold the encoded element. 121 */ 122 public static void writeElement(final ASN1Element element, 123 final ByteBuffer buffer) 124 throws BufferOverflowException 125 { 126 Debug.debugASN1Write(element); 127 128 ByteStringBuffer b = buffers.get(); 129 if (b == null) 130 { 131 b = new ByteStringBuffer(); 132 buffers.set(b); 133 } 134 135 element.encodeTo(b); 136 137 try 138 { 139 if (buffer.remaining() < b.length()) 140 { 141 throw new BufferOverflowException(); 142 } 143 144 final int pos = buffer.position(); 145 buffer.put(b.getBackingArray(), 0, b.length()); 146 buffer.limit(buffer.position()); 147 buffer.position(pos); 148 } 149 finally 150 { 151 if (b.capacity() > MAX_BUFFER_LENGTH) 152 { 153 b.setCapacity(MAX_BUFFER_LENGTH); 154 } 155 b.clear(); 156 } 157 } 158}