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