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