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.kahadb.util;
018    
019    import java.io.DataOutput;
020    import java.io.IOException;
021    import java.io.OutputStream;
022    import java.io.UTFDataFormatException;
023    
024    import org.apache.kahadb.page.PageFile;
025    
026    /**
027     * Optimized ByteArrayOutputStream
028     *
029     *
030     */
031    public class DataByteArrayOutputStream extends OutputStream implements DataOutput {
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        public void write(int b) throws IOException {
090            int newcount = pos + 1;
091            ensureEnoughBuffer(newcount);
092            buf[pos] = (byte)b;
093            pos = newcount;
094            onWrite();
095        }
096    
097        /**
098         * Writes <code>len</code> bytes from the specified byte array starting at
099         * offset <code>off</code> to this byte array output stream.
100         *
101         * @param b the data.
102         * @param off the start offset in the data.
103         * @param len the number of bytes to write.
104         * @throws IOException
105         */
106        public void write(byte b[], int off, int len) throws IOException {
107            if (len == 0) {
108                return;
109            }
110            int newcount = pos + len;
111            ensureEnoughBuffer(newcount);
112            System.arraycopy(b, off, buf, pos, len);
113            pos = newcount;
114            onWrite();
115        }
116    
117        /**
118         * @return the underlying byte[] buffer
119         */
120        public byte[] getData() {
121            return buf;
122        }
123    
124        /**
125         * reset the output stream
126         */
127        public void reset() {
128            pos = 0;
129        }
130    
131        /**
132         * Set the current position for writing
133         *
134         * @param offset
135         * @throws IOException
136         */
137        public void position(int offset) throws IOException {
138            ensureEnoughBuffer(offset);
139            pos = offset;
140            onWrite();
141        }
142    
143        public int size() {
144            return pos;
145        }
146    
147        public void writeBoolean(boolean v) throws IOException {
148            ensureEnoughBuffer(pos + 1);
149            buf[pos++] = (byte)(v ? 1 : 0);
150            onWrite();
151        }
152    
153        public void writeByte(int v) throws IOException {
154            ensureEnoughBuffer(pos + 1);
155            buf[pos++] = (byte)(v >>> 0);
156            onWrite();
157        }
158    
159        public void writeShort(int v) throws IOException {
160            ensureEnoughBuffer(pos + 2);
161            buf[pos++] = (byte)(v >>> 8);
162            buf[pos++] = (byte)(v >>> 0);
163            onWrite();
164        }
165    
166        public void writeChar(int v) throws IOException {
167            ensureEnoughBuffer(pos + 2);
168            buf[pos++] = (byte)(v >>> 8);
169            buf[pos++] = (byte)(v >>> 0);
170            onWrite();
171        }
172    
173        public void writeInt(int v) throws IOException {
174            ensureEnoughBuffer(pos + 4);
175            buf[pos++] = (byte)(v >>> 24);
176            buf[pos++] = (byte)(v >>> 16);
177            buf[pos++] = (byte)(v >>> 8);
178            buf[pos++] = (byte)(v >>> 0);
179            onWrite();
180        }
181    
182        public void writeLong(long v) throws IOException {
183            ensureEnoughBuffer(pos + 8);
184            buf[pos++] = (byte)(v >>> 56);
185            buf[pos++] = (byte)(v >>> 48);
186            buf[pos++] = (byte)(v >>> 40);
187            buf[pos++] = (byte)(v >>> 32);
188            buf[pos++] = (byte)(v >>> 24);
189            buf[pos++] = (byte)(v >>> 16);
190            buf[pos++] = (byte)(v >>> 8);
191            buf[pos++] = (byte)(v >>> 0);
192            onWrite();
193        }
194    
195        public void writeFloat(float v) throws IOException {
196            writeInt(Float.floatToIntBits(v));
197        }
198    
199        public void writeDouble(double v) throws IOException {
200            writeLong(Double.doubleToLongBits(v));
201        }
202    
203        public void writeBytes(String s) throws IOException {
204            int length = s.length();
205            for (int i = 0; i < length; i++) {
206                write((byte)s.charAt(i));
207            }
208        }
209    
210        public void writeChars(String s) throws IOException {
211            int length = s.length();
212            for (int i = 0; i < length; i++) {
213                int c = s.charAt(i);
214                write((c >>> 8) & 0xFF);
215                write((c >>> 0) & 0xFF);
216            }
217        }
218    
219        public void writeUTF(String str) throws IOException {
220            int strlen = str.length();
221            int encodedsize = 0;
222            int c;
223            for (int i = 0; i < strlen; i++) {
224                c = str.charAt(i);
225                if ((c >= 0x0001) && (c <= 0x007F)) {
226                    encodedsize++;
227                } else if (c > 0x07FF) {
228                    encodedsize += 3;
229                } else {
230                    encodedsize += 2;
231                }
232            }
233            if (encodedsize > 65535) {
234                throw new UTFDataFormatException("encoded string too long: " + encodedsize + " bytes");
235            }
236            ensureEnoughBuffer(pos + encodedsize + 2);
237            writeShort(encodedsize);
238            for (int i = 0; i < strlen; i++) {
239                int charValue = str.charAt(i);
240                if (charValue > 0 && charValue <= 127) {
241                    buf[pos++] = (byte) charValue;
242                } else if (charValue <= 2047) {
243                    buf[pos++] = (byte) (0xc0 | (0x1f & (charValue >> 6)));
244                    buf[pos++] = (byte) (0x80 | (0x3f & charValue));
245                } else {
246                    buf[pos++] = (byte) (0xe0 | (0x0f & (charValue >> 12)));
247                    buf[pos++] = (byte) (0x80 | (0x3f & (charValue >> 6)));
248                    buf[pos++] = (byte) (0x80 | (0x3f & charValue));
249                 }
250            }
251            onWrite();
252        }
253    
254        private void ensureEnoughBuffer(int newcount) {
255            if (newcount > buf.length) {
256                byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
257                System.arraycopy(buf, 0, newbuf, 0, pos);
258                buf = newbuf;
259            }
260        }
261    
262        /**
263         * This method is called after each write to the buffer.  This should allow subclasses
264         * to take some action based on the writes, for example flushing data to an external system based on size.
265         */
266        protected void onWrite() throws IOException {
267        }
268    
269        public void skip(int size) throws IOException {
270            ensureEnoughBuffer(pos + size);
271            pos+=size;
272            onWrite();
273        }
274    
275        public ByteSequence getByteSequence() {
276            return new ByteSequence(buf, 0, pos);
277        }
278    }