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             final int c = b[i1];
142             if ((c >= 0x20 && c <= 0x7E) || // Visible ASCII
143                 (c >= 0xA0 && c <= 0xFF) || // Visible ISO-8859-1
144                 c == 0x09) {                // TAB
145                 this.array[i2] = (byte) c;
146             } else {
147                 this.array[i2] = '?';
148             }
149         }
150         this.len = newlen;
151     }
152 
153     /**
154      * Appends {@code len} chars to this buffer from the given source
155      * char array buffer starting at index {@code off}. The capacity
156      * of the buffer is increased if necessary to accommodate all
157      * {@code len} chars.
158      * <p>
159      * The chars are converted to bytes using simple cast.
160      *
161      * @param   b        the chars to be appended.
162      * @param   off      the index of the first char to append.
163      * @param   len      the number of bytes to append.
164      * @throws IndexOutOfBoundsException if {@code off} if out of
165      * range, {@code len} is negative, or
166      * {@code off} + {@code len} is out of range.
167      */
168     public void append(final CharArrayBuffer b, final int off, final int len) {
169         if (b == null) {
170             return;
171         }
172         append(b.array(), off, len);
173     }
174 
175     public void append(final ByteBuffer buffer) {
176         if (buffer == null) {
177             return;
178         }
179         final int bufferLength = buffer.remaining();
180         if (bufferLength > 0) {
181             final int newLength = this.len + bufferLength;
182             if (newLength > this.array.length) {
183                 expand(newLength);
184             }
185             buffer.get(this.array, this.len, bufferLength);
186             this.len = newLength;
187         }
188     }
189 
190     /**
191      * Clears content of the buffer. The underlying byte array is not resized.
192      */
193     public void clear() {
194         this.len = 0;
195     }
196 
197     /**
198      * Converts the content of this buffer to an array of bytes.
199      *
200      * @return byte array
201      */
202     public byte[] toByteArray() {
203         final byte[] b = new byte[this.len];
204         if (this.len > 0) {
205             System.arraycopy(this.array, 0, b, 0, this.len);
206         }
207         return b;
208     }
209 
210     /**
211      * Returns the {@code byte} value in this buffer at the specified
212      * index. The index argument must be greater than or equal to
213      * {@code 0}, and less than the length of this buffer.
214      *
215      * @param      i   the index of the desired byte value.
216      * @return     the byte value at the specified index.
217      * @throws     IndexOutOfBoundsException  if {@code index} is
218      *             negative or greater than or equal to {@link #length()}.
219      */
220     public int byteAt(final int i) {
221         return this.array[i];
222     }
223 
224     /**
225      * Returns the current capacity. The capacity is the amount of storage
226      * available for newly appended bytes, beyond which an allocation
227      * will occur.
228      *
229      * @return  the current capacity
230      */
231     public int capacity() {
232         return this.array.length;
233     }
234 
235     /**
236      * Returns the length of the buffer (byte count).
237      *
238      * @return  the length of the buffer
239      */
240     public int length() {
241         return this.len;
242     }
243 
244     /**
245      * Ensures that the capacity is at least equal to the specified minimum.
246      * If the current capacity is less than the argument, then a new internal
247      * array is allocated with greater capacity. If the {@code required}
248      * argument is non-positive, this method takes no action.
249      *
250      * @param   required   the minimum required capacity.
251      *
252      * @since 4.1
253      */
254     public void ensureCapacity(final int required) {
255         if (required <= 0) {
256             return;
257         }
258         final int available = this.array.length - this.len;
259         if (required > available) {
260             expand(this.len + required);
261         }
262     }
263 
264     /**
265      * Returns reference to the underlying byte array.
266      *
267      * @return the byte array.
268      */
269     public byte[] array() {
270         return this.array;
271     }
272 
273     /**
274      * Sets the length of the buffer. The new length value is expected to be
275      * less than the current capacity and greater than or equal to
276      * {@code 0}.
277      *
278      * @param      len   the new length
279      * @throws     IndexOutOfBoundsException  if the
280      *               {@code len} argument is greater than the current
281      *               capacity of the buffer or less than {@code 0}.
282      */
283     public void setLength(final int len) {
284         if (len < 0 || len > this.array.length) {
285             throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.array.length);
286         }
287         this.len = len;
288     }
289 
290     /**
291      * Returns {@code true} if this buffer is empty, that is, its
292      * {@link #length()} is equal to {@code 0}.
293      * @return {@code true} if this buffer is empty, {@code false}
294      *   otherwise.
295      */
296     public boolean isEmpty() {
297         return this.len == 0;
298     }
299 
300     /**
301      * Returns {@code true} if this buffer is full, that is, its
302      * {@link #length()} is equal to its {@link #capacity()}.
303      * @return {@code true} if this buffer is full, {@code false}
304      *   otherwise.
305      */
306     public boolean isFull() {
307         return this.len == this.array.length;
308     }
309 
310     /**
311      * Returns the index within this buffer of the first occurrence of the
312      * specified byte, starting the search at the specified
313      * {@code beginIndex} and finishing at {@code endIndex}.
314      * If no such byte occurs in this buffer within the specified bounds,
315      * {@code -1} is returned.
316      * <p>
317      * There is no restriction on the value of {@code beginIndex} and
318      * {@code endIndex}. If {@code beginIndex} is negative,
319      * it has the same effect as if it were zero. If {@code endIndex} is
320      * greater than {@link #length()}, it has the same effect as if it were
321      * {@link #length()}. If the {@code beginIndex} is greater than
322      * the {@code endIndex}, {@code -1} is returned.
323      *
324      * @param   b            the byte to search for.
325      * @param   from         the index to start the search from.
326      * @param   to           the index to finish the search at.
327      * @return  the index of the first occurrence of the byte in the buffer
328      *   within the given bounds, or {@code -1} if the byte does
329      *   not occur.
330      *
331      * @since 4.1
332      */
333     public int indexOf(final byte b, final int from, final int to) {
334         int beginIndex = from;
335         if (beginIndex < 0) {
336             beginIndex = 0;
337         }
338         int endIndex = to;
339         if (endIndex > this.len) {
340             endIndex = this.len;
341         }
342         if (beginIndex > endIndex) {
343             return -1;
344         }
345         for (int i = beginIndex; i < endIndex; i++) {
346             if (this.array[i] == b) {
347                 return i;
348             }
349         }
350         return -1;
351     }
352 
353     /**
354      * Returns the index within this buffer of the first occurrence of the
355      * specified byte, starting the search at {@code 0} and finishing
356      * at {@link #length()}. If no such byte occurs in this buffer within
357      * those bounds, {@code -1} is returned.
358      *
359      * @param   b   the byte to search for.
360      * @return  the index of the first occurrence of the byte in the
361      *   buffer, or {@code -1} if the byte does not occur.
362      *
363      * @since 4.1
364      */
365     public int indexOf(final byte b) {
366         return indexOf(b, 0, this.len);
367     }
368 }