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  */
  23 package jdk.incubator.http.internal.hpack;
  24 
  25 import java.nio.ByteBuffer;
  26 import java.util.*;
  27 import java.util.function.Consumer;
  28 import java.util.function.Function;
  29 import java.util.function.Supplier;
  30 
  31 import static java.nio.ByteBuffer.allocate;
  32 
  33 public 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 }