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}