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