001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.store.kahadb.disk.util;
018
019import org.apache.activemq.store.kahadb.disk.page.PageFile;
020import org.apache.activemq.util.ByteSequence;
021
022import java.io.DataOutput;
023import java.io.IOException;
024import java.io.OutputStream;
025import java.io.UTFDataFormatException;
026
027import org.apache.activemq.util.MarshallingSupport;
028
029/**
030 * Optimized ByteArrayOutputStream
031 *
032 *
033 */
034public class DataByteArrayOutputStream extends OutputStream implements DataOutput {
035    private static final int DEFAULT_SIZE = PageFile.DEFAULT_PAGE_SIZE;
036    protected byte buf[];
037    protected int pos;
038
039    /**
040     * Creates a new byte array output stream, with a buffer capacity of the
041     * specified size, in bytes.
042     *
043     * @param size the initial size.
044     * @exception IllegalArgumentException if size is negative.
045     */
046    public DataByteArrayOutputStream(int size) {
047        if (size < 0) {
048            throw new IllegalArgumentException("Invalid size: " + size);
049        }
050        buf = new byte[size];
051    }
052
053    /**
054     * Creates a new byte array output stream.
055     */
056    public DataByteArrayOutputStream() {
057        this(DEFAULT_SIZE);
058    }
059
060    /**
061     * start using a fresh byte array
062     *
063     * @param size
064     */
065    public void restart(int size) {
066        buf = new byte[size];
067        pos = 0;
068    }
069
070    /**
071     * start using a fresh byte array
072     */
073    public void restart() {
074        restart(DEFAULT_SIZE);
075    }
076
077    /**
078     * Get a ByteSequence from the stream
079     *
080     * @return the byte sequence
081     */
082    public ByteSequence toByteSequence() {
083        return new ByteSequence(buf, 0, pos);
084    }
085
086    /**
087     * Writes the specified byte to this byte array output stream.
088     *
089     * @param b the byte to be written.
090     * @throws IOException
091     */
092    public void write(int b) throws IOException {
093        int newcount = pos + 1;
094        ensureEnoughBuffer(newcount);
095        buf[pos] = (byte)b;
096        pos = newcount;
097        onWrite();
098    }
099
100    /**
101     * Writes <code>len</code> bytes from the specified byte array starting at
102     * offset <code>off</code> to this byte array output stream.
103     *
104     * @param b the data.
105     * @param off the start offset in the data.
106     * @param len the number of bytes to write.
107     * @throws IOException
108     */
109    public void write(byte b[], int off, int len) throws IOException {
110        if (len == 0) {
111            return;
112        }
113        int newcount = pos + len;
114        ensureEnoughBuffer(newcount);
115        System.arraycopy(b, off, buf, pos, len);
116        pos = newcount;
117        onWrite();
118    }
119
120    /**
121     * @return the underlying byte[] buffer
122     */
123    public byte[] getData() {
124        return buf;
125    }
126
127    /**
128     * reset the output stream
129     */
130    public void reset() {
131        pos = 0;
132    }
133
134    /**
135     * Set the current position for writing
136     *
137     * @param offset
138     * @throws IOException
139     */
140    public void position(int offset) throws IOException {
141        ensureEnoughBuffer(offset);
142        pos = offset;
143        onWrite();
144    }
145
146    public int size() {
147        return pos;
148    }
149
150    public void writeBoolean(boolean v) throws IOException {
151        ensureEnoughBuffer(pos + 1);
152        buf[pos++] = (byte)(v ? 1 : 0);
153        onWrite();
154    }
155
156    public void writeByte(int v) throws IOException {
157        ensureEnoughBuffer(pos + 1);
158        buf[pos++] = (byte)(v >>> 0);
159        onWrite();
160    }
161
162    public void writeShort(int v) throws IOException {
163        ensureEnoughBuffer(pos + 2);
164        buf[pos++] = (byte)(v >>> 8);
165        buf[pos++] = (byte)(v >>> 0);
166        onWrite();
167    }
168
169    public void writeChar(int v) throws IOException {
170        ensureEnoughBuffer(pos + 2);
171        buf[pos++] = (byte)(v >>> 8);
172        buf[pos++] = (byte)(v >>> 0);
173        onWrite();
174    }
175
176    public void writeInt(int v) throws IOException {
177        ensureEnoughBuffer(pos + 4);
178        buf[pos++] = (byte)(v >>> 24);
179        buf[pos++] = (byte)(v >>> 16);
180        buf[pos++] = (byte)(v >>> 8);
181        buf[pos++] = (byte)(v >>> 0);
182        onWrite();
183    }
184
185    public void writeLong(long v) throws IOException {
186        ensureEnoughBuffer(pos + 8);
187        buf[pos++] = (byte)(v >>> 56);
188        buf[pos++] = (byte)(v >>> 48);
189        buf[pos++] = (byte)(v >>> 40);
190        buf[pos++] = (byte)(v >>> 32);
191        buf[pos++] = (byte)(v >>> 24);
192        buf[pos++] = (byte)(v >>> 16);
193        buf[pos++] = (byte)(v >>> 8);
194        buf[pos++] = (byte)(v >>> 0);
195        onWrite();
196    }
197
198    public void writeFloat(float v) throws IOException {
199        writeInt(Float.floatToIntBits(v));
200    }
201
202    public void writeDouble(double v) throws IOException {
203        writeLong(Double.doubleToLongBits(v));
204    }
205
206    public void writeBytes(String s) throws IOException {
207        int length = s.length();
208        for (int i = 0; i < length; i++) {
209            write((byte)s.charAt(i));
210        }
211    }
212
213    public void writeChars(String s) throws IOException {
214        int length = s.length();
215        for (int i = 0; i < length; i++) {
216            int c = s.charAt(i);
217            write((c >>> 8) & 0xFF);
218            write((c >>> 0) & 0xFF);
219        }
220    }
221    @Override
222    public void writeUTF(String text) throws IOException {
223        long encodedsize = MarshallingSupport.countUTFBytes(text);
224        if (encodedsize > 65535) {
225            throw new UTFDataFormatException("encoded string too long: " + encodedsize + " bytes");
226        }
227        ensureEnoughBuffer((int)(pos + encodedsize + 2));
228        writeShort((int)encodedsize);
229
230        byte[] buffer = new byte[(int)encodedsize];
231        MarshallingSupport.writeUTFBytesToBuffer(text, (int) encodedsize, buf, pos);
232        pos += encodedsize;
233        onWrite();
234    }
235
236    private void ensureEnoughBuffer(int newcount) {
237        if (newcount > buf.length) {
238            byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
239            System.arraycopy(buf, 0, newbuf, 0, pos);
240            buf = newbuf;
241        }
242    }
243
244    /**
245     * This method is called after each write to the buffer.  This should allow subclasses
246     * to take some action based on the writes, for example flushing data to an external system based on size.
247     */
248    protected void onWrite() throws IOException {
249    }
250
251    public void skip(int size) throws IOException {
252        ensureEnoughBuffer(pos + size);
253        pos+=size;
254        onWrite();
255    }
256
257    public ByteSequence getByteSequence() {
258        return new ByteSequence(buf, 0, pos);
259    }
260}