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