1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt; 18 19 import static java.lang.Math.min; 20 import static org.conscrypt.Preconditions.checkArgument; 21 22 import java.nio.ByteBuffer; 23 24 /** 25 * Utility methods for dealing with arrays of ByteBuffers. 26 * 27 * @hide This class is not part of the Android public SDK API 28 */ 29 public final class BufferUtils { BufferUtils()30 private BufferUtils() {} 31 32 /** 33 * Throws {@link IllegalArgumentException} if any of the buffers in the array are null. 34 */ checkNotNull(ByteBuffer[] buffers)35 public static void checkNotNull(ByteBuffer[] buffers) { 36 for (ByteBuffer buffer : buffers) { 37 if (buffer == null) { 38 throw new IllegalArgumentException("Null buffer in array"); 39 } 40 } 41 } 42 43 /** 44 * Returns the total number of bytes remaining in the buffer array. 45 */ remaining(ByteBuffer[] buffers)46 public static long remaining(ByteBuffer[] buffers) { 47 long size = 0; 48 for (ByteBuffer buffer : buffers) { 49 size += buffer.remaining(); 50 } 51 return size; 52 } 53 54 /** 55 * Marks {@code toConsume} bytes of data as consumed from the buffer array. 56 * 57 * @throws IllegalArgumentException if there are fewer than {@code toConsume} bytes remaining 58 */ consume(ByteBuffer[] sourceBuffers, int toConsume)59 public static void consume(ByteBuffer[] sourceBuffers, int toConsume) { 60 for (ByteBuffer sourceBuffer : sourceBuffers) { 61 int amount = min(sourceBuffer.remaining(), toConsume); 62 if (amount > 0) { 63 sourceBuffer.position(sourceBuffer.position() + amount); 64 toConsume -= amount; 65 if (toConsume == 0) { 66 break; 67 } 68 } 69 } 70 if (toConsume > 0) { 71 throw new IllegalArgumentException("toConsume > data size"); 72 } 73 } 74 75 /** 76 * Looks for a buffer in the buffer array which EITHER is larger than {@code minSize} AND 77 * has no preceding non-empty buffers OR is the only non-empty buffer in the array. 78 */ getBufferLargerThan(ByteBuffer[] buffers, int minSize)79 public static ByteBuffer getBufferLargerThan(ByteBuffer[] buffers, int minSize) { 80 int length = buffers.length; 81 for (int i = 0; i < length; i++) { 82 ByteBuffer buffer = buffers[i]; 83 int remaining = buffer.remaining(); 84 if (remaining > 0) { 85 if (remaining >= minSize) { 86 return buffer; 87 } 88 for (int j = i + 1; j < length; j++) { 89 if (buffers[j].remaining() > 0) { 90 return null; 91 } 92 } 93 return buffer; 94 } 95 } 96 return null; 97 } 98 99 /** 100 * Copies up to {@code maxAmount} bytes from a buffer array to {@code destination}. 101 * The copied data is <b>not</b> marked as consumed from the source buffers, on the 102 * assumption the copy will be passed to some method which will consume between 0 and 103 * {@code maxAmount} bytes which can then be reflected in the source array using the 104 * {@code consume()} method. 105 * 106 */ copyNoConsume(ByteBuffer[] buffers, ByteBuffer destination, int maxAmount)107 public static ByteBuffer copyNoConsume(ByteBuffer[] buffers, ByteBuffer destination, int maxAmount) { 108 checkArgument(destination.remaining() >= maxAmount, "Destination buffer too small"); 109 int needed = maxAmount; 110 for (ByteBuffer buffer : buffers) { 111 int remaining = buffer.remaining(); 112 if (remaining > 0) { 113 // If this buffer can fit completely then copy it all, otherwise temporarily 114 // adjust its limit to fill so as to the output buffer completely 115 int oldPosition = buffer.position(); 116 if (remaining <= needed) { 117 destination.put(buffer); 118 needed -= remaining; 119 } else { 120 int oldLimit = buffer.limit(); 121 buffer.limit(buffer.position() + needed); 122 destination.put(buffer); 123 buffer.limit(oldLimit); 124 needed = 0; 125 } 126 // Restore the buffer's position, the data won't get marked as consumed until 127 // outputBuffer has been successfully consumed. 128 buffer.position(oldPosition); 129 if (needed == 0) { 130 break; 131 } 132 } 133 } 134 destination.flip(); 135 return destination; 136 } 137 } 138