View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  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,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.util;
29  
30  import java.io.Serializable;
31  import java.nio.ByteBuffer;
32  
33  /**
34   * A resizable byte array.
35   *
36   * @since 4.0
37   */
38  public final class ByteArrayBuffer implements Serializable {
39  
40      private static final long serialVersionUID = 4359112959524048036L;
41  
42      private byte[] array;
43      private int len;
44  
45      /**
46       * Creates an instance of {@link ByteArrayBuffer} with the given initial
47       * capacity.
48       *
49       * @param capacity the capacity
50       */
51      public ByteArrayBuffer(final int capacity) {
52          super();
53          Args.notNegative(capacity, "Buffer capacity");
54          this.array = new byte[capacity];
55      }
56  
57      private void expand(final int newlen) {
58          final byte[] newArray = new byte[Math.max(this.array.length << 1, newlen)];
59          System.arraycopy(this.array, 0, newArray, 0, this.len);
60          this.array = newArray;
61      }
62  
63      /**
64       * Appends {@code len} bytes to this buffer from the given source
65       * array starting at index {@code off}. The capacity of the buffer
66       * is increased, if necessary, to accommodate all {@code len} bytes.
67       *
68       * @param   b        the bytes to be appended.
69       * @param   off      the index of the first byte to append.
70       * @param   len      the number of bytes to append.
71       * @throws IndexOutOfBoundsException if {@code off} if out of
72       * range, {@code len} is negative, or
73       * {@code off} + {@code len} is out of range.
74       */
75      public void append(final byte[] b, final int off, final int len) {
76          if (b == null) {
77              return;
78          }
79          if ((off < 0) || (off > b.length) || (len < 0) ||
80                  ((off + len) < 0) || ((off + len) > b.length)) {
81              throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length);
82          }
83          if (len == 0) {
84              return;
85          }
86          final int newlen = this.len + len;
87          if (newlen > this.array.length) {
88              expand(newlen);
89          }
90          System.arraycopy(b, off, this.array, this.len, len);
91          this.len = newlen;
92      }
93  
94      /**
95       * Appends {@code b} byte to this buffer. The capacity of the buffer
96       * is increased, if necessary, to accommodate the additional byte.
97       *
98       * @param   b        the byte to be appended.
99       */
100     public void append(final int b) {
101         final int newlen = this.len + 1;
102         if (newlen > this.array.length) {
103             expand(newlen);
104         }
105         this.array[this.len] = (byte)b;
106         this.len = newlen;
107     }
108 
109     /**
110      * Appends {@code len} chars to this buffer from the given source
111      * array starting at index {@code off}. The capacity of the buffer
112      * is increased if necessary to accommodate all {@code len} chars.
113      * <p>
114      * The chars are converted to bytes using simple cast.
115      *
116      * @param   b        the chars to be appended.
117      * @param   off      the index of the first char to append.
118      * @param   len      the number of bytes to append.
119      * @throws IndexOutOfBoundsException if {@code off} if out of
120      * range, {@code len} is negative, or
121      * {@code off} + {@code len} is out of range.
122      */
123     public void append(final char[] b, final int off, final int len) {
124         if (b == null) {
125             return;
126         }
127         if ((off < 0) || (off > b.length) || (len < 0) ||
128                 ((off + len) < 0) || ((off + len) > b.length)) {
129             throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length);
130         }
131         if (len == 0) {
132             return;
133         }
134         final int oldlen = this.len;
135         final int newlen = oldlen + len;
136         if (newlen > this.array.length) {
137             expand(newlen);
138         }
139 
140         for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
141             this.array[i2] = TextUtils.castAsByte(b[i1]);
142         }
143         this.len = newlen;
144     }
145 
146     /**
147      * Appends {@code len} chars to this buffer from the given source
148      * char array buffer starting at index {@code off}. The capacity
149      * of the buffer is increased if necessary to accommodate all
150      * {@code len} chars.
151      * <p>
152      * The chars are converted to bytes using simple cast.
153      *
154      * @param   b        the chars to be appended.
155      * @param   off      the index of the first char to append.
156      * @param   len      the number of bytes to append.
157      * @throws IndexOutOfBoundsException if {@code off} if out of
158      * range, {@code len} is negative, or
159      * {@code off} + {@code len} is out of range.
160      */
161     public void append(final CharArrayBuffer b, final int off, final int len) {
162         if (b == null) {
163             return;
164         }
165         append(b.array(), off, len);
166     }
167 
168     public void append(final ByteBuffer buffer) {
169         if (buffer == null) {
170             return;
171         }
172         final int bufferLength = buffer.remaining();
173         if (bufferLength > 0) {
174             final int newLength = this.len + bufferLength;
175             if (newLength > this.array.length) {
176                 expand(newLength);
177             }
178             buffer.get(this.array, this.len, bufferLength);
179             this.len = newLength;
180         }
181     }
182 
183     /**
184      * Clears content of the buffer. The underlying byte array is not resized.
185      */
186     public void clear() {
187         this.len = 0;
188     }
189 
190     /**
191      * Converts the content of this buffer to an array of bytes.
192      *
193      * @return byte array
194      */
195     public byte[] toByteArray() {
196         final byte[] b = new byte[this.len];
197         if (this.len > 0) {
198             System.arraycopy(this.array, 0, b, 0, this.len);
199         }
200         return b;
201     }
202 
203     /**
204      * Returns the {@code byte} value in this buffer at the specified
205      * index. The index argument must be greater than or equal to
206      * {@code 0}, and less than the length of this buffer.
207      *
208      * @param      i   the index of the desired byte value.
209      * @return     the byte value at the specified index.
210      * @throws     IndexOutOfBoundsException  if {@code index} is
211      *             negative or greater than or equal to {@link #length()}.
212      */
213     public int byteAt(final int i) {
214         return this.array[i];
215     }
216 
217     /**
218      * Returns the current capacity. The capacity is the amount of storage
219      * available for newly appended bytes, beyond which an allocation
220      * will occur.
221      *
222      * @return  the current capacity
223      */
224     public int capacity() {
225         return this.array.length;
226     }
227 
228     /**
229      * Returns the length of the buffer (byte count).
230      *
231      * @return  the length of the buffer
232      */
233     public int length() {
234         return this.len;
235     }
236 
237     /**
238      * Ensures that the capacity is at least equal to the specified minimum.
239      * If the current capacity is less than the argument, then a new internal
240      * array is allocated with greater capacity. If the {@code required}
241      * argument is non-positive, this method takes no action.
242      *
243      * @param   required   the minimum required capacity.
244      *
245      * @since 4.1
246      */
247     public void ensureCapacity(final int required) {
248         if (required <= 0) {
249             return;
250         }
251         final int available = this.array.length - this.len;
252         if (required > available) {
253             expand(this.len + required);
254         }
255     }
256 
257     /**
258      * Returns reference to the underlying byte array.
259      *
260      * @return the byte array.
261      */
262     public byte[] array() {
263         return this.array;
264     }
265 
266     /**
267      * Sets the length of the buffer. The new length value is expected to be
268      * less than the current capacity and greater than or equal to
269      * {@code 0}.
270      *
271      * @param      len   the new length
272      * @throws     IndexOutOfBoundsException  if the
273      *               {@code len} argument is greater than the current
274      *               capacity of the buffer or less than {@code 0}.
275      */
276     public void setLength(final int len) {
277         if (len < 0 || len > this.array.length) {
278             throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.array.length);
279         }
280         this.len = len;
281     }
282 
283     /**
284      * Returns {@code true} if this buffer is empty, that is, its
285      * {@link #length()} is equal to {@code 0}.
286      * @return {@code true} if this buffer is empty, {@code false}
287      *   otherwise.
288      */
289     public boolean isEmpty() {
290         return this.len == 0;
291     }
292 
293     /**
294      * Returns {@code true} if this buffer is full, that is, its
295      * {@link #length()} is equal to its {@link #capacity()}.
296      * @return {@code true} if this buffer is full, {@code false}
297      *   otherwise.
298      */
299     public boolean isFull() {
300         return this.len == this.array.length;
301     }
302 
303     /**
304      * Returns the index within this buffer of the first occurrence of the
305      * specified byte, starting the search at the specified
306      * {@code beginIndex} and finishing at {@code endIndex}.
307      * If no such byte occurs in this buffer within the specified bounds,
308      * {@code -1} is returned.
309      * <p>
310      * There is no restriction on the value of {@code beginIndex} and
311      * {@code endIndex}. If {@code beginIndex} is negative,
312      * it has the same effect as if it were zero. If {@code endIndex} is
313      * greater than {@link #length()}, it has the same effect as if it were
314      * {@link #length()}. If the {@code beginIndex} is greater than
315      * the {@code endIndex}, {@code -1} is returned.
316      *
317      * @param   b            the byte to search for.
318      * @param   from         the index to start the search from.
319      * @param   to           the index to finish the search at.
320      * @return  the index of the first occurrence of the byte in the buffer
321      *   within the given bounds, or {@code -1} if the byte does
322      *   not occur.
323      *
324      * @since 4.1
325      */
326     public int indexOf(final byte b, final int from, final int to) {
327         int beginIndex = from;
328         if (beginIndex < 0) {
329             beginIndex = 0;
330         }
331         int endIndex = to;
332         if (endIndex > this.len) {
333             endIndex = this.len;
334         }
335         if (beginIndex > endIndex) {
336             return -1;
337         }
338         for (int i = beginIndex; i < endIndex; i++) {
339             if (this.array[i] == b) {
340                 return i;
341             }
342         }
343         return -1;
344     }
345 
346     /**
347      * Returns the index within this buffer of the first occurrence of the
348      * specified byte, starting the search at {@code 0} and finishing
349      * at {@link #length()}. If no such byte occurs in this buffer within
350      * those bounds, {@code -1} is returned.
351      *
352      * @param   b   the byte to search for.
353      * @return  the index of the first occurrence of the byte in the
354      *   buffer, or {@code -1} if the byte does not occur.
355      *
356      * @since 4.1
357      */
358     public int indexOf(final byte b) {
359         return indexOf(b, 0, this.len);
360     }
361 }