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 }