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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.net.http;
  27 
  28 import java.nio.ByteBuffer;
  29 import java.util.ArrayList;
  30 
  31 /**
  32  * Manages a ByteBuffer[] for writing frames into for output. The last
  33  * ByteBuffer in the list is always unflipped (able to receive more bytes for
  34  * sending) until getBufferArray() is called, which calls finish().
  35  *
  36  * This allows multiple frames to be written to the same BBG.
  37  *
  38  * Buffers added with addByteBuffer() must be already flipped.
  39  */
  40 class ByteBufferGenerator {
  41 
  42     ByteBuffer currentBuffer;
  43     // source is assumed to always return the same sized buffer
  44     final BufferHandler pool;
  45     final ArrayList<ByteBuffer> buflist;
  46     final int bufsize;
  47     boolean finished;
  48 
  49     ByteBufferGenerator(BufferHandler pool) {
  50         this.buflist = new ArrayList<>();
  51         this.pool = pool;
  52         this.currentBuffer = pool.getBuffer();
  53         this.bufsize = currentBuffer.capacity();
  54     }
  55 
  56     private static final ByteBuffer[] EMPTY = new ByteBuffer[0];
  57 
  58     public ByteBuffer[] getBufferArray() {
  59         finish();
  60         return buflist.toArray(EMPTY);
  61     }
  62 
  63     public ArrayList<ByteBuffer> getBufferList() {
  64         finish();
  65         return buflist;
  66     }
  67 
  68     private synchronized void finish() {
  69         if (finished) {
  70             return;
  71         }
  72         finished = true;
  73         currentBuffer.flip();
  74         if (currentBuffer.hasRemaining()) {
  75             buflist.add(currentBuffer);
  76         } else {
  77             pool.returnBuffer(currentBuffer);
  78         }
  79     }
  80 
  81     // only used for SettingsFrame: offset is number of bytes to
  82     // ignore at start (we only want the payload of the settings frame)
  83     public byte[] asByteArray(int offset) {
  84         ByteBuffer[] bufs = getBufferArray();
  85         int size = 0;
  86         for (ByteBuffer buf : bufs) {
  87             size += buf.remaining();
  88         }
  89         byte[] bytes = new byte[size-offset];
  90         int pos = 0;
  91         for (ByteBuffer buf : bufs) {
  92             int rem = buf.remaining();
  93             int ignore = Math.min(rem, offset);
  94             buf.position(buf.position()+ignore);
  95             rem -= ignore;
  96             offset -= ignore;
  97             buf.get(bytes, pos, rem);
  98             pos += rem;
  99         }
 100         return bytes;
 101     }
 102 
 103     ByteBuffer getBuffer(long n) {
 104         if (currentBuffer.remaining() < n) {
 105             getNewBuffer();
 106             if (n > currentBuffer.capacity()) {
 107                 throw new IllegalArgumentException("requested buffer too large");
 108             }
 109         }
 110         return currentBuffer;
 111     }
 112 
 113     void getNewBuffer() {
 114         currentBuffer.flip();
 115         if (currentBuffer.hasRemaining()) {
 116             buflist.add(currentBuffer);
 117         } else {
 118             pool.returnBuffer(currentBuffer);
 119         }
 120         currentBuffer = pool.getBuffer();
 121     }
 122 
 123     void addByteBuffer(ByteBuffer buf) {
 124         getNewBuffer();
 125         buflist.add(buf);
 126     }
 127 
 128     void addPadding(int length) {
 129         while (length > 0) {
 130             int n = Math.min(length, bufsize);
 131             ByteBuffer b = getBuffer(n);
 132             // TODO: currently zeroed?
 133             b.position(b.position() + n);
 134             length -= n;
 135         }
 136     }
 137 }