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 }