001    /*
002     * Copyright 2008-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 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    }