1 /*
2 * The contents of this file are subject to the terms of the Common Development and
3 * Distribution License (the License). You may not use this file except in compliance with the
4 * License.
5 *
6 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7 * specific language governing permission and limitations under the License.
8 *
9 * When distributing Covered Software, include this CDDL Header Notice in each file and include
10 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11 * Header, with the fields enclosed by brackets [] replaced by your own identifying
12 * information: "Portions copyright [year] [name of copyright owner]".
13 *
14 * Portions copyright 2016 ForgeRock AS.
15 */
16
17 package org.forgerock.audit.handlers.json;
18
19 import java.io.OutputStream;
20 import java.nio.ByteBuffer;
21
22 /**
23 * Wraps a {@link ByteBuffer} to expose the {@link OutputStream} interface, and grows the underlying buffer as
24 * data is added to it.
25 */
26 class ByteBufferOutputStream extends OutputStream {
27
28 /**
29 * Buffer grows 30% when re-sized.
30 */
31 private static final double GROWTH_RATE = 1.33;
32
33 private ByteBuffer buffer;
34
35 /**
36 * Creates a {@code ByteBufferOutputStream} with the given {@link ByteBuffer}.
37 *
38 * @param buffer Initial buffer instance
39 */
40 public ByteBufferOutputStream(final ByteBuffer buffer) {
41 this.buffer = buffer;
42 }
43
44 /**
45 * Gets the underlying buffer.
46 *
47 * @return Underlying buffer
48 */
49 public ByteBuffer byteBuffer() {
50 return buffer;
51 }
52
53 /**
54 * Clears the buffer, but does not erase the underlying data, making it ready to be reused to accept new data.
55 */
56 public void clear() {
57 buffer.clear();
58 }
59
60 /**
61 * Replaces the existing buffer with a new buffer having increased size.
62 *
63 * @param newCapacity New capacity
64 */
65 private void growTo(final int newCapacity) {
66 final ByteBuffer oldBuffer = buffer;
67 if (buffer.isDirect()) {
68 buffer = ByteBuffer.allocateDirect(newCapacity);
69 } else {
70 buffer = ByteBuffer.allocate(newCapacity);
71 }
72 // copy data
73 oldBuffer.flip();
74 buffer.put(oldBuffer);
75 }
76
77 @Override
78 public void write(final int b) {
79 if (buffer.remaining() == 0) {
80 growTo((int) (buffer.capacity() * GROWTH_RATE));
81 }
82 buffer.put((byte) b);
83 }
84
85 @Override
86 public void write(final byte[] bytes) {
87 if (buffer.remaining() < bytes.length) {
88 final int newSize = Math.max((int) (buffer.capacity() * GROWTH_RATE),
89 buffer.position() + bytes.length);
90 growTo(newSize);
91 }
92 buffer.put(bytes);
93 }
94
95 @Override
96 public void write(final byte[] bytes, final int off, final int len) {
97 if (len < 0) {
98 throw new IllegalArgumentException("len must be positive");
99 }
100 if (buffer.remaining() < len) {
101 final int newSize = Math.max((int) (buffer.capacity() * GROWTH_RATE), buffer.position() + len);
102 growTo(newSize);
103 }
104 buffer.put(bytes, off, len);
105 }
106 }