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     */
017    package org.apache.activemq.store.kahadb.disk.util;
018    
019    import org.apache.activemq.store.kahadb.disk.page.PageFile;
020    import org.apache.activemq.util.ByteSequence;
021    
022    import java.io.DataOutput;
023    import java.io.IOException;
024    import java.io.OutputStream;
025    import java.io.UTFDataFormatException;
026    
027    
028    /**
029     * Optimized ByteArrayOutputStream
030     *
031     *
032     */
033    public 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    }