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