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