1 /*
   2  * Copyright (c) 2018, 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 jdk.nio.zipfs;
  27 
  28 import java.io.IOException;
  29 import java.nio.ByteBuffer;
  30 import java.nio.channels.ClosedChannelException;
  31 import java.nio.channels.NonWritableChannelException;
  32 import java.nio.channels.SeekableByteChannel;
  33 import java.util.Arrays;
  34 import java.util.concurrent.locks.ReadWriteLock;
  35 import java.util.concurrent.locks.ReentrantReadWriteLock;
  36 
  37 public class ByteArrayChannel implements SeekableByteChannel {
  38 
  39     private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
  40     private byte buf[];
  41 
  42     /*
  43      * The current position of this channel.
  44      */
  45     private int pos;
  46 
  47     /*
  48      * The index that is one greater than the last valid byte in the channel.
  49      */
  50     private int last;
  51 
  52     private boolean closed;
  53     private boolean readonly;
  54 
  55     /*
  56      * Creates a {@code ByteArrayChannel} with size {@code sz}.
  57      */
  58     ByteArrayChannel(int sz, boolean readonly) {
  59         this.buf = new byte[sz];
  60         this.pos = this.last = 0;
  61         this.readonly = readonly;
  62     }
  63 
  64     /*
  65      * Creates a ByteArrayChannel with its 'pos' at 0 and its 'last' at buf's end.
  66      * Note: no defensive copy of the 'buf', used directly.
  67      */
  68     ByteArrayChannel(byte[] buf, boolean readonly) {
  69         this.buf = buf;
  70         this.pos = 0;
  71         this.last = buf.length;
  72         this.readonly = readonly;
  73     }
  74 
  75     @Override
  76     public boolean isOpen() {
  77         return !closed;
  78     }
  79 
  80     @Override
  81     public long position() throws IOException {
  82         beginRead();
  83         try {
  84             ensureOpen();
  85             return pos;
  86         } finally {
  87             endRead();
  88         }
  89     }
  90 
  91     @Override
  92     public SeekableByteChannel position(long pos) throws IOException {
  93         beginWrite();
  94         try {
  95             ensureOpen();
  96             if (pos < 0 || pos >= Integer.MAX_VALUE)
  97                 throw new IllegalArgumentException("Illegal position " + pos);
  98             this.pos = Math.min((int)pos, last);
  99             return this;
 100         } finally {
 101             endWrite();
 102         }
 103     }
 104 
 105     @Override
 106     public int read(ByteBuffer dst) throws IOException {
 107         beginWrite();
 108         try {
 109             ensureOpen();
 110             if (pos == last)
 111                 return -1;
 112             int n = Math.min(dst.remaining(), last - pos);
 113             dst.put(buf, pos, n);
 114             pos += n;
 115             return n;
 116         } finally {
 117             endWrite();
 118         }
 119     }
 120 
 121     @Override
 122     public SeekableByteChannel truncate(long size) throws IOException {
 123         if (readonly)
 124             throw new NonWritableChannelException();
 125         ensureOpen();
 126         throw new UnsupportedOperationException();
 127     }
 128 
 129     @Override
 130     public int write(ByteBuffer src) throws IOException {
 131         if (readonly)
 132             throw new NonWritableChannelException();
 133         beginWrite();
 134         try {
 135             ensureOpen();
 136             int n = src.remaining();
 137             ensureCapacity(pos + n);
 138             src.get(buf, pos, n);
 139             pos += n;
 140             if (pos > last) {
 141                 last = pos;
 142             }
 143             return n;
 144         } finally {
 145             endWrite();
 146         }
 147     }
 148 
 149     @Override
 150     public long size() throws IOException {
 151         beginRead();
 152         try {
 153             ensureOpen();
 154             return last;
 155         } finally {
 156             endRead();
 157         }
 158     }
 159 
 160     @Override
 161     public void close() throws IOException {
 162         if (closed)
 163             return;
 164         beginWrite();
 165         try {
 166             closed = true;
 167             buf = null;
 168             pos = 0;
 169             last = 0;
 170         } finally {
 171             endWrite();
 172         }
 173     }
 174 
 175     /**
 176      * Creates a newly allocated byte array. Its size is the current
 177      * size of this channel and the valid contents of the buffer
 178      * have been copied into it.
 179      *
 180      * @return the current contents of this channel, as a byte array.
 181      */
 182     public byte[] toByteArray() {
 183         beginRead();
 184         try {
 185             // avoid copy if last == bytes.length?
 186             return Arrays.copyOf(buf, last);
 187         } finally {
 188             endRead();
 189         }
 190     }
 191 
 192     private void ensureOpen() throws IOException {
 193         if (closed)
 194             throw new ClosedChannelException();
 195     }
 196 
 197     private final void beginWrite() {
 198         rwlock.writeLock().lock();
 199     }
 200 
 201     private final void endWrite() {
 202         rwlock.writeLock().unlock();
 203     }
 204 
 205     private final void beginRead() {
 206         rwlock.readLock().lock();
 207     }
 208 
 209     private final void endRead() {
 210         rwlock.readLock().unlock();
 211     }
 212 
 213     private void ensureCapacity(int minCapacity) {
 214         // overflow-conscious code
 215         if (minCapacity - buf.length > 0) {
 216             grow(minCapacity);
 217         }
 218     }
 219 
 220     /**
 221      * The maximum size of array to allocate.
 222      * Some VMs reserve some header words in an array.
 223      * Attempts to allocate larger arrays may result in
 224      * OutOfMemoryError: Requested array size exceeds VM limit
 225      */
 226     private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 227 
 228     /**
 229      * Increases the capacity to ensure that it can hold at least the
 230      * number of elements specified by the minimum capacity argument.
 231      *
 232      * @param minCapacity the desired minimum capacity
 233      */
 234     private void grow(int minCapacity) {
 235         // overflow-conscious code
 236         int oldCapacity = buf.length;
 237         int newCapacity = oldCapacity << 1;
 238         if (newCapacity - minCapacity < 0)
 239             newCapacity = minCapacity;
 240         if (newCapacity - MAX_ARRAY_SIZE > 0)
 241             newCapacity = hugeCapacity(minCapacity);
 242         buf = Arrays.copyOf(buf, newCapacity);
 243     }
 244 
 245     private static int hugeCapacity(int minCapacity) {
 246         if (minCapacity < 0) // overflow
 247             throw new OutOfMemoryError();
 248         return (minCapacity > MAX_ARRAY_SIZE) ?
 249             Integer.MAX_VALUE :
 250             MAX_ARRAY_SIZE;
 251     }
 252 }