1/* 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23package jdk.incubator.http.internal.hpack; 24 25import java.nio.ByteBuffer; 26import java.util.*; 27import java.util.function.Consumer; 28import java.util.function.Function; 29import java.util.function.Supplier; 30 31import static java.nio.ByteBuffer.allocate; 32 33public final class BuffersTestingKit { 34 35 /** 36 * Relocates a {@code [position, limit)} region of the given buffer to 37 * corresponding region in a new buffer starting with provided {@code 38 * newPosition}. 39 * 40 * <p> Might be useful to make sure ByteBuffer's users do not rely on any 41 * absolute positions, but solely on what's reported by position(), limit(). 42 * 43 * <p> The contents between the given buffer and the returned one are not 44 * shared. 45 */ 46 public static ByteBuffer relocate(ByteBuffer buffer, int newPosition, 47 int newCapacity) { 48 int oldPosition = buffer.position(); 49 int oldLimit = buffer.limit(); 50 51 if (newPosition + oldLimit - oldPosition > newCapacity) { 52 throw new IllegalArgumentException(); 53 } 54 55 ByteBuffer result; 56 if (buffer.isDirect()) { 57 result = ByteBuffer.allocateDirect(newCapacity); 58 } else { 59 result = allocate(newCapacity); 60 } 61 62 result.position(newPosition); 63 result.put(buffer).limit(result.position()).position(newPosition); 64 buffer.position(oldPosition); 65 66 if (buffer.isReadOnly()) { 67 return result.asReadOnlyBuffer(); 68 } 69 return result; 70 } 71 72 public static Iterable<? extends ByteBuffer> relocateBuffers( 73 Iterable<? extends ByteBuffer> source) { 74 return () -> 75 new Iterator<ByteBuffer>() { 76 77 private final Iterator<? extends ByteBuffer> it = source.iterator(); 78 79 @Override 80 public boolean hasNext() { 81 return it.hasNext(); 82 } 83 84 @Override 85 public ByteBuffer next() { 86 ByteBuffer buf = it.next(); 87 int remaining = buf.remaining(); 88 int newCapacity = remaining + random.nextInt(17); 89 int newPosition = random.nextInt(newCapacity - remaining + 1); 90 return relocate(buf, newPosition, newCapacity); 91 } 92 }; 93 } 94 95 // TODO: not always of size 0 (it's fine for buffer to report !b.hasRemaining()) 96 public static Iterable<? extends ByteBuffer> injectEmptyBuffers( 97 Iterable<? extends ByteBuffer> source) { 98 return injectEmptyBuffers(source, () -> allocate(0)); 99 } 100 101 public static Iterable<? extends ByteBuffer> injectEmptyBuffers( 102 Iterable<? extends ByteBuffer> source, 103 Supplier<? extends ByteBuffer> emptyBufferFactory) { 104 105 return () -> 106 new Iterator<ByteBuffer>() { 107 108 private final Iterator<? extends ByteBuffer> it = source.iterator(); 109 private ByteBuffer next = calculateNext(); 110 111 private ByteBuffer calculateNext() { 112 if (random.nextBoolean()) { 113 return emptyBufferFactory.get(); 114 } else if (it.hasNext()) { 115 return it.next(); 116 } else { 117 return null; 118 } 119 } 120 121 @Override 122 public boolean hasNext() { 123 return next != null; 124 } 125 126 @Override 127 public ByteBuffer next() { 128 if (!hasNext()) { 129 throw new NoSuchElementException(); 130 } 131 ByteBuffer next = this.next; 132 this.next = calculateNext(); 133 return next; 134 } 135 }; 136 } 137 138 public static ByteBuffer concat(Iterable<? extends ByteBuffer> split) { 139 return concat(split, ByteBuffer::allocate); 140 } 141 142 public static ByteBuffer concat(Iterable<? extends ByteBuffer> split, 143 Function<? super Integer, ? extends ByteBuffer> concatBufferFactory) { 144 int size = 0; 145 for (ByteBuffer bb : split) { 146 size += bb.remaining(); 147 } 148 149 ByteBuffer result = concatBufferFactory.apply(size); 150 for (ByteBuffer bb : split) { 151 result.put(bb); 152 } 153 154 result.flip(); 155 return result; 156 } 157 158 public static void forEachSplit(ByteBuffer bb, 159 Consumer<? super Iterable<? extends ByteBuffer>> action) { 160 forEachSplit(bb.remaining(), 161 (lengths) -> { 162 int end = bb.position(); 163 List<ByteBuffer> buffers = new LinkedList<>(); 164 for (int len : lengths) { 165 ByteBuffer d = bb.duplicate(); 166 d.position(end); 167 d.limit(end + len); 168 end += len; 169 buffers.add(d); 170 } 171 action.accept(buffers); 172 }); 173 } 174 175 private static void forEachSplit(int n, Consumer<? super Iterable<? extends Integer>> action) { 176 forEachSplit(n, new Stack<>(), action); 177 } 178 179 private static void forEachSplit(int n, Stack<Integer> path, 180 Consumer<? super Iterable<? extends Integer>> action) { 181 if (n == 0) { 182 action.accept(path); 183 } else { 184 for (int i = 1; i <= n; i++) { 185 path.push(i); 186 forEachSplit(n - i, path, action); 187 path.pop(); 188 } 189 } 190 } 191 192 private static final Random random = new Random(); 193 194 private BuffersTestingKit() { 195 throw new InternalError(); 196 } 197 198// public static void main(String[] args) { 199// 200// List<ByteBuffer> buffers = Arrays.asList( 201// (ByteBuffer) allocate(3).position(1).limit(2), 202// allocate(0), 203// allocate(7)); 204// 205// Iterable<? extends ByteBuffer> buf = relocateBuffers(injectEmptyBuffers(buffers)); 206// List<ByteBuffer> result = new ArrayList<>(); 207// buf.forEach(result::add); 208// System.out.println(result); 209// } 210} 211