1 /*
2 * Copyright 2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17 package org.apache.asn1.ber.digester.rules ;
18
19
20 import java.nio.ByteBuffer;
21
22 import org.apache.commons.lang.ArrayUtils;
23
24
25 /**
26 * Gathers bytes from buffers while dynamically growing to accomodate a new size.
27 *
28 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
29 * @version $Rev: 157644 $
30 */
31 public class ByteAccumulator
32 {
33 /** the default initial size */
34 private static final int DEFAULT_INIT_SIZE = 0 ;
35 /** the default initial size */
36 private static final int DEFAULT_INCREMENT = 100 ;
37
38 /** the accumulator's backing store */
39 private byte[] bs ;
40 /** the current position of the accumulator */
41 private int pos ;
42 /** the growth increment used to augment the backing store */
43 private int increment ;
44 /** the initial size of the backing store */
45 private int initial = DEFAULT_INIT_SIZE ;
46
47
48 // -----------------------------------------------------------------------
49 // C O N S T R U C T O R S
50 // -----------------------------------------------------------------------
51
52 /**
53 * Creates a ByteAccumulator used to gather bytes from various sources
54 * with a default initial size and a default growth increment.
55 */
56 public ByteAccumulator()
57 {
58 bs = ArrayUtils.EMPTY_BYTE_ARRAY ;
59 pos = 0 ;
60 initial = DEFAULT_INIT_SIZE ;
61 increment = DEFAULT_INCREMENT ;
62 }
63
64
65 /**
66 * Creates a ByteAccumulator used to gather bytes from various sources
67 * with a default initial size and a default growth increment.
68 *
69 * @param initial the initial size for the backing store
70 */
71 ByteAccumulator( int initial )
72 {
73 if ( initial <= 0 )
74 {
75 bs = ArrayUtils.EMPTY_BYTE_ARRAY ;
76 this.initial = 0 ;
77 }
78 else
79 {
80 bs = new byte[initial] ;
81 this.initial = initial ;
82 }
83
84 pos = 0 ;
85 increment = DEFAULT_INCREMENT ;
86 }
87
88
89 /**
90 * Fills this accumulator with the content of the argument buffer into
91 * this accumulator. The buffer argument is fully drained when this
92 * operation completes.
93 *
94 * @param buf the buffer to fill into this accumulator
95 */
96 public void fill( ByteBuffer buf )
97 {
98 while ( buf.hasRemaining() )
99 {
100 if ( pos >= bs.length )
101 {
102 int size = 0 ;
103
104 if ( buf.remaining() > increment )
105 {
106 size = bs.length + buf.remaining() ;
107 }
108 else
109 {
110 size = bs.length + increment ;
111 }
112
113 byte[] dest = new byte[ size ] ;
114 System.arraycopy( bs, 0, dest, 0, bs.length ) ;
115 bs = dest ;
116 }
117
118 /*
119 * Find out how much space we have left and if it can hold the
120 * remaining contents of the buffer.
121 */
122 int spaceLeft = bs.length - pos ;
123 if ( buf.remaining() <= spaceLeft )
124 {
125 int remaining = buf.remaining() ;
126 buf.get( bs, pos, remaining ) ;
127 pos += remaining ;
128 return ;
129 }
130
131 /*
132 * there are more bytes in the buffer than we have space so we read
133 * as much as we can into the empty space filling it up all the way
134 * until another cycle of this loop allocates more space.
135 */
136 buf.get( bs, pos, spaceLeft ) ;
137 pos += spaceLeft ;
138 }
139 }
140
141
142 /**
143 * Wraps a ByteBuffer around the populated bytes of this ByteAccumulator
144 * and resets the backing store to a newly allocated byte array of initial
145 * size.
146 *
147 * @return the compacted byte[] wrapped as a ByteBuffer
148 */
149 public ByteBuffer drain()
150 {
151 ByteBuffer compacted ;
152
153 if ( pos == bs.length )
154 {
155 compacted = ByteBuffer.wrap( bs ) ;
156 }
157 else
158 {
159 compacted = ByteBuffer.wrap( bs, 0, pos ) ;
160 }
161
162 if ( initial <= 0 )
163 {
164 bs = ArrayUtils.EMPTY_BYTE_ARRAY ;
165 }
166 else
167 {
168 bs = new byte[initial] ;
169 }
170
171 pos = 0 ;
172 return compacted ;
173 }
174
175
176 /**
177 * Wraps a ByteBuffer around the populated bytes of this ByteAccumulator
178 * and resets the backing store to a newly allocated byte array of initial
179 * size.
180 *
181 * @return the compacted byte[] wrapped as a ByteBuffer
182 */
183 public ByteBuffer drain( int initial )
184 {
185 ByteBuffer compacted ;
186
187 if ( pos == bs.length )
188 {
189 compacted = ByteBuffer.wrap( bs ) ;
190 }
191 else
192 {
193 compacted = ByteBuffer.wrap( bs, 0, pos ) ;
194 }
195
196 if ( initial <= 0 )
197 {
198 bs = ArrayUtils.EMPTY_BYTE_ARRAY ;
199 }
200 else
201 {
202 bs = new byte[initial] ;
203 }
204
205 pos = 0 ;
206 return compacted ;
207 }
208
209
210 /**
211 * Allocates memory to handle a capacity without the need to grow.
212 * This serves to control growth for more efficient use.
213 *
214 * @param capacity the capacity to hold without the need to grow
215 */
216 public void ensureCapacity( int capacity )
217 {
218 if ( bs.length < capacity )
219 {
220 byte[] newArray = new byte[capacity] ;
221
222 if ( bs != ArrayUtils.EMPTY_BYTE_ARRAY )
223 {
224 System.arraycopy( bs, 0, newArray, 0, pos + 1 ) ;
225 }
226
227 bs = newArray ;
228 }
229 }
230
231
232 /**
233 * The growth increment by which the backing store is augmented.
234 *
235 * @return the number of bytes to grow the backing store by
236 */
237 public int getGrowthIncrement()
238 {
239 return increment ;
240 }
241
242
243 /**
244 * The initial size of the backing store.
245 *
246 * @return the initial size in bytes of the backing store
247 */
248 public int getInitialSize()
249 {
250 return initial ;
251 }
252
253
254 /**
255 * The current capacity of the backing store which may change as this
256 * accumulator is filled with bytes.
257 *
258 * @return the current capacity in bytes
259 */
260 public int getCapacity()
261 {
262 return bs.length ;
263 }
264
265
266 /**
267 * The remaining free space that can be filled before having to grow the
268 * backing store of the accumulator.
269 *
270 * @return the remaining free space until the next growth spurt
271 */
272 public int getRemainingSpace()
273 {
274 return bs.length - pos ;
275 }
276
277
278 /**
279 * The current position within the backing store marking the point to
280 * which this accumulator is filled.
281 *
282 * @return the current fill position
283 */
284 public int getPosition()
285 {
286 return pos ;
287 }
288
289
290 /**
291 * Gets a compacted copy of this ByteAccumulator's backing store. The
292 * compacted byte array is equal to the amount of bytes put into this
293 * ByteAccumulator which may still have free space to populate since the
294 * last growth took place.
295 *
296 * @return the compacted copy of this ByteAccumulator
297 */
298 /*
299 public byte[] getCompactedCopy()
300 {
301 byte[] compacted ;
302
303 if ( pos == bs.length )
304 {
305 compacted = (byte[]) bs.clone() ;
306 }
307 else
308 {
309 compacted = new byte[pos+1] ;
310 }
311
312 System.arraycopy( bs, 0, compacted, 0, pos + 1 ) ;
313 return compacted ;
314 }
315 */
316 }