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