• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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