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