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 }