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