001/*
002 * Copyright 2008-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.io.IOException;
026import java.io.OutputStream;
027import java.nio.BufferOverflowException;
028import java.nio.ByteBuffer;
029
030import com.unboundid.util.ByteStringBuffer;
031import com.unboundid.util.ThreadSafety;
032import com.unboundid.util.ThreadSafetyLevel;
033
034import 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)
043public 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}