1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2 *
3 * This program and the accompanying materials are made available under
4 * the terms of the Common Public License v1.0 which accompanies this distribution,
5 * and is available at http://www.eclipse.org/legal/cpl-v10.html
6 *
7 * $Id: ByteArrayOStream.java,v 1.1.1.1 2004/05/09 16:57:52 vlad_r Exp $
8 */
9package com.vladium.util;
10
11import java.io.IOException;
12import java.io.OutputStream;
13
14import com.vladium.util.asserts.$assert;
15
16// ----------------------------------------------------------------------------
17/**
18 * An unsynchronized version of java.io.ByteArrayOutputStream that can expose
19 * the underlying byte array without a defensive clone and can also be converted
20 * to a {@link ByteArrayIStream} without intermediate array copies.<p>
21 *
22 * All argument validation is disabled in release mode.<p>
23 *
24 * NOTE: copy-on-write not supported
25 *
26 * @author (C) 2001, Vlad Roubtsov
27 */
28public
29final class ByteArrayOStream extends OutputStream
30{
31    // public: ................................................................
32
33    /**
34     * Callee takes ownership of 'buf'.
35     */
36    public ByteArrayOStream (final int initialCapacity)
37    {
38        if ($assert.ENABLED)
39            $assert.ASSERT (initialCapacity >= 0, "negative initial capacity: " + initialCapacity);
40
41        m_buf = new byte [initialCapacity];
42    }
43
44    public final ByteArrayIStream toByteIStream ()
45    {
46        return new ByteArrayIStream (m_buf, m_pos);
47    }
48
49    public final void write2 (final int b1, final int b2)
50    {
51        final int pos = m_pos;
52        final int capacity = pos + 2;
53        byte [] mbuf = m_buf;
54        final int mbuflen = mbuf.length;
55
56        if (mbuflen < capacity)
57        {
58            final byte [] newbuf = new byte [Math.max (mbuflen << 1, capacity)];
59
60            if (pos < NATIVE_COPY_THRESHOLD)
61                for (int i = 0; i < pos; ++ i) newbuf [i] = mbuf [i];
62            else
63                System.arraycopy (mbuf, 0, newbuf, 0, pos);
64
65            m_buf = mbuf = newbuf;
66        }
67
68        mbuf [pos] = (byte) b1;
69        mbuf [pos + 1] = (byte) b2;
70        m_pos = capacity;
71    }
72
73    public final void write3 (final int b1, final int b2, final int b3)
74    {
75        final int pos = m_pos;
76        final int capacity = pos + 3;
77        byte [] mbuf = m_buf;
78        final int mbuflen = mbuf.length;
79
80        if (mbuflen < capacity)
81        {
82            final byte [] newbuf = new byte [Math.max (mbuflen << 1, capacity)];
83
84            if (pos < NATIVE_COPY_THRESHOLD)
85                for (int i = 0; i < pos; ++ i) newbuf [i] = mbuf [i];
86            else
87                System.arraycopy (mbuf, 0, newbuf, 0, pos);
88
89            m_buf = mbuf = newbuf;
90        }
91
92        mbuf [pos] = (byte) b1;
93        mbuf [pos + 1] = (byte) b2;
94        mbuf [pos + 2] = (byte) b3;
95        m_pos = capacity;
96    }
97
98    public final void write4 (final int b1, final int b2, final int b3, final int b4)
99    {
100        final int pos = m_pos;
101        final int capacity = pos + 4;
102        byte [] mbuf = m_buf;
103        final int mbuflen = mbuf.length;
104
105        if (mbuflen < capacity)
106        {
107            final byte [] newbuf = new byte [Math.max (mbuflen << 1, capacity)];
108
109            if (pos < NATIVE_COPY_THRESHOLD)
110                for (int i = 0; i < pos; ++ i) newbuf [i] = mbuf [i];
111            else
112                System.arraycopy (mbuf, 0, newbuf, 0, pos);
113
114            m_buf = mbuf = newbuf;
115        }
116
117        mbuf [pos] = (byte) b1;
118        mbuf [pos + 1] = (byte) b2;
119        mbuf [pos + 2] = (byte) b3;
120        mbuf [pos + 3] = (byte) b4;
121        m_pos = capacity;
122    }
123
124    public final void writeTo (final OutputStream out)
125        throws IOException
126    {
127        out.write (m_buf, 0, m_pos);
128    }
129
130//    public final void readFully (final InputStream in)
131//        throws IOException
132//    {
133//        while (true)
134//        {
135//            int chunk = in.available ();
136//
137//            System.out.println ("available = " + chunk);
138//
139//            // TODO: this case is handled poorly (on EOF)
140//            if (chunk == 0) chunk = READ_CHUNK_SIZE;
141//
142//            // read at least 'available' bytes: extend the capacity as needed
143//
144//            int free = m_buf.length - m_pos;
145//
146//            final int read;
147//            if (free > chunk)
148//            {
149//                // try reading more than 'chunk' anyway:
150//                read = in.read (m_buf, m_pos, free);
151//            }
152//            else
153//            {
154//                // extend the capacity to match 'chunk':
155//                {
156//                    System.out.println ("reallocation");
157//                    final byte [] newbuf = new byte [m_pos + chunk];
158//
159//                    if (m_pos < NATIVE_COPY_THRESHOLD)
160//                        for (int i = 0; i < m_pos; ++ i) newbuf [i] = m_buf [i];
161//                    else
162//                        System.arraycopy (m_buf, 0, newbuf, 0, m_pos);
163//
164//                    m_buf = newbuf;
165//                }
166//
167//                read = in.read (m_buf, m_pos, chunk);
168//            }
169//
170//            if (read < 0)
171//                break;
172//            else
173//                m_pos += read;
174//        }
175//    }
176
177//    public final void addCapacity (final int extraCapacity)
178//    {
179//        final int pos = m_pos;
180//        final int capacity = pos + extraCapacity;
181//        byte [] mbuf = m_buf;
182//        final int mbuflen = mbuf.length;
183//
184//        if (mbuflen < capacity)
185//        {
186//            final byte [] newbuf = new byte [Math.max (mbuflen << 1, capacity)];
187//
188//            if (pos < NATIVE_COPY_THRESHOLD)
189//                for (int i = 0; i < pos; ++ i) newbuf [i] = mbuf [i];
190//            else
191//                System.arraycopy (mbuf, 0, newbuf, 0, pos);
192//
193//            m_buf = newbuf;
194//        }
195//    }
196
197    public final byte [] getByteArray ()
198    {
199        return m_buf;
200    }
201
202    /**
203     *
204     * @return [result.length = size()]
205     */
206    public final byte [] copyByteArray ()
207    {
208        final int pos = m_pos;
209
210        final byte [] result = new byte [pos];
211        final byte [] mbuf = m_buf;
212
213        if (pos < NATIVE_COPY_THRESHOLD)
214            for (int i = 0; i < pos; ++ i) result [i] = mbuf [i];
215        else
216            System.arraycopy (mbuf, 0, result, 0, pos);
217
218        return result;
219    }
220
221    public final int size ()
222    {
223        return m_pos;
224    }
225
226    public final int capacity ()
227    {
228        return m_buf.length;
229    }
230
231    /**
232     * Does not reduce the current capacity.
233     */
234    public final void reset ()
235    {
236        m_pos = 0;
237    }
238
239    // OutputStream:
240
241    public final void write (final int b)
242    {
243        final int pos = m_pos;
244        final int capacity = pos + 1;
245        byte [] mbuf = m_buf;
246        final int mbuflen = mbuf.length;
247
248        if (mbuflen < capacity)
249        {
250            final byte [] newbuf = new byte [Math.max (mbuflen << 1, capacity)];
251
252            if (pos < NATIVE_COPY_THRESHOLD)
253                for (int i = 0; i < pos; ++ i) newbuf [i] = mbuf [i];
254            else
255                System.arraycopy (mbuf, 0, newbuf, 0, pos);
256
257            m_buf = mbuf = newbuf;
258        }
259
260        mbuf [pos] = (byte) b;
261        m_pos = capacity;
262    }
263
264
265    public final void write (final byte [] buf, final int offset, final int length)
266    {
267        if ($assert.ENABLED)
268            $assert.ASSERT ((offset >= 0) && (offset <= buf.length) &&
269                (length >= 0) && ((offset + length) <= buf.length),
270                "invalid input (" + buf.length + ", " + offset + ", " + length + ")");
271
272        final int pos = m_pos;
273        final int capacity = pos + length;
274        byte [] mbuf = m_buf;
275        final int mbuflen = mbuf.length;
276
277        if (mbuflen < capacity)
278        {
279            final byte [] newbuf = new byte [Math.max (mbuflen << 1, capacity)];
280
281            if (pos < NATIVE_COPY_THRESHOLD)
282                for (int i = 0; i < pos; ++ i) newbuf [i] = mbuf [i];
283            else
284                System.arraycopy (mbuf, 0, newbuf, 0, pos);
285
286            m_buf = mbuf = newbuf;
287        }
288
289        if (length < NATIVE_COPY_THRESHOLD)
290            for (int i = 0; i < length; ++ i) mbuf [pos + i] = buf [offset + i];
291        else
292            System.arraycopy (buf, offset, mbuf, pos, length);
293
294        m_pos = capacity;
295    }
296
297
298    /**
299     * Equivalent to {@link #reset()}.
300     */
301    public final void close ()
302    {
303        reset ();
304    }
305
306    // protected: .............................................................
307
308    // package: ...............................................................
309
310    // private: ...............................................................
311
312
313    private byte [] m_buf;
314    private int m_pos;
315
316//    private static final int READ_CHUNK_SIZE        = 16 * 1024;
317    private static final int NATIVE_COPY_THRESHOLD  = 9;
318
319} // end of class
320// ----------------------------------------------------------------------------