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