001: /*
002:
003: Derby - Class org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.services.io;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025:
026: import java.io.InputStream;
027: import java.io.IOException;
028: import java.io.OutputStream;
029:
030: /**
031: A DynamicByteArrayOutputStream allows writing to a dynamically resizable
032: array of bytes. In addition to dynamic resizing, this extension allows
033: the user of this class to have more control over the position of the stream
034: and can get a direct reference of the array.
035: */
036: public class DynamicByteArrayOutputStream extends OutputStream {
037:
038: private static int INITIAL_SIZE = 4096;
039:
040: private byte[] buf;
041: private int position;
042: private int used; // how many bytes are used
043: private int beginPosition;
044:
045: public DynamicByteArrayOutputStream() {
046: this (INITIAL_SIZE);
047: }
048:
049: public DynamicByteArrayOutputStream(int size) {
050: super ();
051:
052: buf = new byte[size];
053: }
054:
055: public DynamicByteArrayOutputStream(byte[] data) {
056: super ();
057:
058: buf = data;
059: }
060:
061: public DynamicByteArrayOutputStream(
062: DynamicByteArrayOutputStream toBeCloned) {
063:
064: byte[] cbuf = toBeCloned.getByteArray();
065: buf = new byte[cbuf.length];
066:
067: write(cbuf, 0, cbuf.length);
068: position = toBeCloned.getPosition();
069: used = toBeCloned.getUsed();
070: beginPosition = toBeCloned.getBeginPosition();
071: }
072:
073: /*
074: * OutputStream methods
075: */
076: public void write(int b) {
077: if (position >= buf.length)
078: expandBuffer(INITIAL_SIZE);
079:
080: buf[position++] = (byte) b;
081:
082: if (position > used)
083: used = position;
084: }
085:
086: public void write(byte[] b, int off, int len) {
087: if ((position + len) > buf.length)
088: expandBuffer(len);
089:
090: System.arraycopy(b, off, buf, position, len);
091: position += len;
092:
093: if (position > used)
094: used = position;
095: }
096:
097: void writeCompleteStream(InputStream dataIn, int len)
098: throws IOException {
099: if ((position + len) > buf.length)
100: expandBuffer(len);
101:
102: org.apache.derby.iapi.services.io.InputStreamUtil.readFully(
103: dataIn, buf, position, len);
104: position += len;
105:
106: if (position > used)
107: used = position;
108: }
109:
110: public void close() {
111: buf = null;
112: reset();
113: }
114:
115: /*
116: * Specific methods
117: */
118:
119: /**
120: Reset the stream for reuse
121: */
122: public void reset() {
123: position = 0;
124: beginPosition = 0;
125: used = 0;
126: }
127:
128: /**
129: Get a reference to the byte array stored in the byte array output
130: stream. Note that the byte array may be longer that getPosition().
131: Bytes beyond and including the current poistion are invalid.
132: */
133: public byte[] getByteArray() {
134: return buf;
135: }
136:
137: /**
138: Get the number of bytes that was used.
139: */
140: public int getUsed() {
141: return used;
142: }
143:
144: /**
145: Get the current position in the stream
146: */
147: public int getPosition() {
148: return position;
149: }
150:
151: /**
152: Get the current position in the stream
153: */
154: public int getBeginPosition() {
155: return beginPosition;
156: }
157:
158: /**
159: Set the position of the stream pointer.
160: It is up to the caller to make sure the stream has no gap of garbage in
161: it or useful information is not left out at the end because the stream
162: does not remember anything about the previous position.
163: */
164: public void setPosition(int newPosition) {
165: if (newPosition > position) {
166: if (newPosition > buf.length)
167: expandBuffer(newPosition - buf.length);
168: }
169:
170: position = newPosition;
171:
172: if (position > used)
173: used = position;
174:
175: return;
176: }
177:
178: /**
179: Set the begin position of the stream pointer.
180: If the newBeginPosition is larger than the stream itself,
181: then, the begin position is not set.
182: */
183: public void setBeginPosition(int newBeginPosition) {
184:
185: if (newBeginPosition > buf.length)
186: return;
187:
188: beginPosition = newBeginPosition;
189: }
190:
191: /**
192: Shrink the buffer left by the amount given. Ie.
193: bytes from 0 to amountToShrinkBy are thrown away
194: */
195: public void discardLeft(int amountToShrinkBy) {
196:
197: System.arraycopy(buf, amountToShrinkBy, buf, 0, used
198: - amountToShrinkBy);
199:
200: position -= amountToShrinkBy;
201: used -= amountToShrinkBy;
202: }
203:
204: /**
205: Expand the buffer by at least the number of bytes requested in minExtension.
206:
207: To optimize performance and reduce memory copies and allocation, we have a staged buffer
208: expansion.
209:
210: <UL>
211: <LI> buf.length < 128k - increase by 4k
212: <LI> buf.length < 1Mb - increase by 128k
213: <LI> otherwise increase by 1Mb.
214: </UL>
215:
216: In all cases, if minExpansion is greater than the value about then the buffer will
217: be increased by minExtension.
218: */
219: private void expandBuffer(int minExtension) {
220: if (buf.length < (128 * 1024)) {
221: if (minExtension < INITIAL_SIZE)
222: minExtension = INITIAL_SIZE;
223: } else if (buf.length < (1024 * 1024)) {
224:
225: if (minExtension < (128 * 1024))
226: minExtension = (128 * 1024);
227: } else {
228: if (minExtension < (1024 * 1024))
229: minExtension = 1024 * 1024;
230: }
231:
232: int newsize = buf.length + minExtension;
233:
234: byte[] newbuf = new byte[newsize];
235: System.arraycopy(buf, 0, newbuf, 0, buf.length);
236: buf = newbuf;
237: }
238:
239: }
|