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
027
028/**
029 * Optimized ByteArrayOutputStream
030 *
031 *
032 */
033public class DataByteArrayOutputStream extends OutputStream implements DataOutput {
034    private static final int DEFAULT_SIZE = PageFile.DEFAULT_PAGE_SIZE;
035    protected byte buf[];
036    protected int pos;
037
038    /**
039     * Creates a new byte array output stream, with a buffer capacity of the
040     * specified size, in bytes.
041     *
042     * @param size the initial size.
043     * @exception IllegalArgumentException if size is negative.
044     */
045    public DataByteArrayOutputStream(int size) {
046        if (size < 0) {
047            throw new IllegalArgumentException("Invalid size: " + size);
048        }
049        buf = new byte[size];
050    }
051
052    /**
053     * Creates a new byte array output stream.
054     */
055    public DataByteArrayOutputStream() {
056        this(DEFAULT_SIZE);
057    }
058
059    /**
060     * start using a fresh byte array
061     *
062     * @param size
063     */
064    public void restart(int size) {
065        buf = new byte[size];
066        pos = 0;
067    }
068
069    /**
070     * start using a fresh byte array
071     */
072    public void restart() {
073        restart(DEFAULT_SIZE);
074    }
075
076    /**
077     * Get a ByteSequence from the stream
078     *
079     * @return the byte sequence
080     */
081    public ByteSequence toByteSequence() {
082        return new ByteSequence(buf, 0, pos);
083    }
084
085    /**
086     * Writes the specified byte to this byte array output stream.
087     *
088     * @param b the byte to be written.
089     * @throws IOException
090     */
091    public void write(int b) throws IOException {
092        int newcount = pos + 1;
093        ensureEnoughBuffer(newcount);
094        buf[pos] = (byte)b;
095        pos = newcount;
096        onWrite();
097    }
098
099    /**
100     * Writes <code>len</code> bytes from the specified byte array starting at
101     * offset <code>off</code> to this byte array output stream.
102     *
103     * @param b the data.
104     * @param off the start offset in the data.
105     * @param len the number of bytes to write.
106     * @throws IOException
107     */
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    public void writeBoolean(boolean v) throws IOException {
150        ensureEnoughBuffer(pos + 1);
151        buf[pos++] = (byte)(v ? 1 : 0);
152        onWrite();
153    }
154
155    public void writeByte(int v) throws IOException {
156        ensureEnoughBuffer(pos + 1);
157        buf[pos++] = (byte)(v >>> 0);
158        onWrite();
159    }
160
161    public void writeShort(int v) throws IOException {
162        ensureEnoughBuffer(pos + 2);
163        buf[pos++] = (byte)(v >>> 8);
164        buf[pos++] = (byte)(v >>> 0);
165        onWrite();
166    }
167
168    public void writeChar(int v) throws IOException {
169        ensureEnoughBuffer(pos + 2);
170        buf[pos++] = (byte)(v >>> 8);
171        buf[pos++] = (byte)(v >>> 0);
172        onWrite();
173    }
174
175    public void writeInt(int v) throws IOException {
176        ensureEnoughBuffer(pos + 4);
177        buf[pos++] = (byte)(v >>> 24);
178        buf[pos++] = (byte)(v >>> 16);
179        buf[pos++] = (byte)(v >>> 8);
180        buf[pos++] = (byte)(v >>> 0);
181        onWrite();
182    }
183
184    public void writeLong(long v) throws IOException {
185        ensureEnoughBuffer(pos + 8);
186        buf[pos++] = (byte)(v >>> 56);
187        buf[pos++] = (byte)(v >>> 48);
188        buf[pos++] = (byte)(v >>> 40);
189        buf[pos++] = (byte)(v >>> 32);
190        buf[pos++] = (byte)(v >>> 24);
191        buf[pos++] = (byte)(v >>> 16);
192        buf[pos++] = (byte)(v >>> 8);
193        buf[pos++] = (byte)(v >>> 0);
194        onWrite();
195    }
196
197    public void writeFloat(float v) throws IOException {
198        writeInt(Float.floatToIntBits(v));
199    }
200
201    public void writeDouble(double v) throws IOException {
202        writeLong(Double.doubleToLongBits(v));
203    }
204
205    public void writeBytes(String s) throws IOException {
206        int length = s.length();
207        for (int i = 0; i < length; i++) {
208            write((byte)s.charAt(i));
209        }
210    }
211
212    public void writeChars(String s) throws IOException {
213        int length = s.length();
214        for (int i = 0; i < length; i++) {
215            int c = s.charAt(i);
216            write((c >>> 8) & 0xFF);
217            write((c >>> 0) & 0xFF);
218        }
219    }
220
221    public void writeUTF(String str) throws IOException {
222        int strlen = str.length();
223        int encodedsize = 0;
224        int c;
225        for (int i = 0; i < strlen; i++) {
226            c = str.charAt(i);
227            if ((c >= 0x0001) && (c <= 0x007F)) {
228                encodedsize++;
229            } else if (c > 0x07FF) {
230                encodedsize += 3;
231            } else {
232                encodedsize += 2;
233            }
234        }
235        if (encodedsize > 65535) {
236            throw new UTFDataFormatException("encoded string too long: " + encodedsize + " bytes");
237        }
238        ensureEnoughBuffer(pos + encodedsize + 2);
239        writeShort(encodedsize);
240        for (int i = 0; i < strlen; i++) {
241            int charValue = str.charAt(i);
242            if (charValue > 0 && charValue <= 127) {
243                buf[pos++] = (byte) charValue;
244            } else if (charValue <= 2047) {
245                buf[pos++] = (byte) (0xc0 | (0x1f & (charValue >> 6)));
246                buf[pos++] = (byte) (0x80 | (0x3f & charValue));
247            } else {
248                buf[pos++] = (byte) (0xe0 | (0x0f & (charValue >> 12)));
249                buf[pos++] = (byte) (0x80 | (0x3f & (charValue >> 6)));
250                buf[pos++] = (byte) (0x80 | (0x3f & charValue));
251             }
252        }
253        onWrite();
254    }
255
256    private void ensureEnoughBuffer(int newcount) {
257        if (newcount > buf.length) {
258            byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
259            System.arraycopy(buf, 0, newbuf, 0, pos);
260            buf = newbuf;
261        }
262    }
263
264    /**
265     * This method is called after each write to the buffer.  This should allow subclasses
266     * to take some action based on the writes, for example flushing data to an external system based on size.
267     */
268    protected void onWrite() throws IOException {
269    }
270
271    public void skip(int size) throws IOException {
272        ensureEnoughBuffer(pos + size);
273        pos+=size;
274        onWrite();
275    }
276
277    public ByteSequence getByteSequence() {
278        return new ByteSequence(buf, 0, pos);
279    }
280}