1 | /* |
2 | |
3 | Derby - Class org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream |
4 | |
5 | Copyright 1998, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.iapi.services.io; |
22 | |
23 | import org.apache.derby.iapi.services.sanity.SanityManager; |
24 | |
25 | import java.io.InputStream; |
26 | import java.io.IOException; |
27 | import java.io.OutputStream; |
28 | |
29 | /** |
30 | A DynamicByteArrayOutputStream allows writing to a dynamically resizable |
31 | array of bytes. In addition to dynamic resizing, this extension allows |
32 | the user of this class to have more control over the position of the stream |
33 | and can get a direct reference of the array. |
34 | */ |
35 | public class DynamicByteArrayOutputStream extends OutputStream { |
36 | |
37 | private static int INITIAL_SIZE = 4096; |
38 | |
39 | private byte[] buf; |
40 | private int position; |
41 | private int used; // how many bytes are used |
42 | private int beginPosition; |
43 | |
44 | public DynamicByteArrayOutputStream() { |
45 | this(INITIAL_SIZE); |
46 | } |
47 | |
48 | public DynamicByteArrayOutputStream(int size) { |
49 | super(); |
50 | |
51 | buf = new byte[size]; |
52 | } |
53 | |
54 | public DynamicByteArrayOutputStream(byte[] data) { |
55 | super(); |
56 | |
57 | buf = data; |
58 | } |
59 | |
60 | public DynamicByteArrayOutputStream(DynamicByteArrayOutputStream toBeCloned) { |
61 | |
62 | byte[] cbuf = toBeCloned.getByteArray(); |
63 | buf = new byte[cbuf.length]; |
64 | |
65 | write(cbuf, 0, cbuf.length); |
66 | position = toBeCloned.getPosition(); |
67 | used = toBeCloned.getUsed(); |
68 | beginPosition = toBeCloned.getBeginPosition(); |
69 | } |
70 | |
71 | /* |
72 | * OutputStream methods |
73 | */ |
74 | public void write(int b) |
75 | { |
76 | if (position >= buf.length) |
77 | expandBuffer(INITIAL_SIZE); |
78 | |
79 | buf[position++] = (byte) b; |
80 | |
81 | if (position > used) |
82 | used = position; |
83 | } |
84 | |
85 | public void write(byte[] b, int off, int len) |
86 | { |
87 | if ((position+len) > buf.length) |
88 | expandBuffer(len); |
89 | |
90 | System.arraycopy(b, off, buf, position, len); |
91 | position += len; |
92 | |
93 | if (position > used) |
94 | used = position; |
95 | } |
96 | |
97 | void writeCompleteStream(InputStream dataIn, int len) throws IOException |
98 | { |
99 | if ((position+len) > buf.length) |
100 | expandBuffer(len); |
101 | |
102 | org.apache.derby.iapi.services.io.InputStreamUtil.readFully(dataIn, buf, position, len); |
103 | position += len; |
104 | |
105 | if (position > used) |
106 | used = position; |
107 | } |
108 | |
109 | public void close() |
110 | { |
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 | { |
124 | position = 0; |
125 | beginPosition = 0; |
126 | used = 0; |
127 | } |
128 | |
129 | /** |
130 | Get a reference to the byte array stored in the byte array output |
131 | stream. Note that the byte array may be longer that getPosition(). |
132 | Bytes beyond and including the current poistion are invalid. |
133 | */ |
134 | public byte[] getByteArray() |
135 | { |
136 | return buf; |
137 | } |
138 | |
139 | /** |
140 | Get the number of bytes that was used. |
141 | */ |
142 | public int getUsed() |
143 | { |
144 | return used; |
145 | } |
146 | |
147 | /** |
148 | Get the current position in the stream |
149 | */ |
150 | public int getPosition() |
151 | { |
152 | return position; |
153 | } |
154 | |
155 | /** |
156 | Get the current position in the stream |
157 | */ |
158 | public int getBeginPosition() |
159 | { |
160 | return beginPosition; |
161 | } |
162 | |
163 | /** |
164 | Set the position of the stream pointer. |
165 | It is up to the caller to make sure the stream has no gap of garbage in |
166 | it or useful information is not left out at the end because the stream |
167 | does not remember anything about the previous position. |
168 | */ |
169 | public void setPosition(int newPosition) |
170 | { |
171 | if (newPosition > position) |
172 | { |
173 | if (newPosition > buf.length) |
174 | expandBuffer(newPosition - buf.length); |
175 | } |
176 | |
177 | position = newPosition; |
178 | |
179 | if (position > used) |
180 | used = position; |
181 | |
182 | return ; |
183 | } |
184 | |
185 | /** |
186 | Set the begin position of the stream pointer. |
187 | If the newBeginPosition is larger than the stream itself, |
188 | then, the begin position is not set. |
189 | */ |
190 | public void setBeginPosition(int newBeginPosition) |
191 | { |
192 | |
193 | if (newBeginPosition > buf.length) |
194 | return; |
195 | |
196 | beginPosition = newBeginPosition; |
197 | } |
198 | |
199 | /** |
200 | Shrink the buffer left by the amount given. Ie. |
201 | bytes from 0 to amountToShrinkBy are thrown away |
202 | */ |
203 | public void discardLeft(int amountToShrinkBy) { |
204 | |
205 | System.arraycopy(buf, amountToShrinkBy, buf, 0, |
206 | used - amountToShrinkBy); |
207 | |
208 | position -= amountToShrinkBy; |
209 | used -= amountToShrinkBy; |
210 | } |
211 | |
212 | /** |
213 | Expand the buffer by at least the number of bytes requested in minExtension. |
214 | |
215 | To optimize performance and reduce memory copies and allocation, we have a staged buffer |
216 | expansion. |
217 | |
218 | <UL> |
219 | <LI> buf.length < 128k - increase by 4k |
220 | <LI> buf.length < 1Mb - increase by 128k |
221 | <LI> otherwise increase by 1Mb. |
222 | </UL> |
223 | |
224 | In all cases, if minExpansion is greater than the value about then the buffer will |
225 | be increased by minExtension. |
226 | */ |
227 | private void expandBuffer(int minExtension) |
228 | { |
229 | if (buf.length < (128 * 1024)) { |
230 | if (minExtension < INITIAL_SIZE) |
231 | minExtension = INITIAL_SIZE; |
232 | } else if (buf.length < (1024 * 1024)) { |
233 | |
234 | if (minExtension < (128 * 1024)) |
235 | minExtension = (128 * 1024); |
236 | } else { |
237 | if (minExtension < (1024 * 1024)) |
238 | minExtension = 1024 * 1024; |
239 | } |
240 | |
241 | int newsize = buf.length + minExtension; |
242 | |
243 | byte[] newbuf = new byte[newsize]; |
244 | System.arraycopy(buf, 0, newbuf, 0, buf.length); |
245 | buf = newbuf; |
246 | } |
247 | |
248 | } |