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