src/jdk.zipfs/share/classes/jdk/nio/zipfs/ByteArrayChannel.java
changeset 51787 ba51515b64e5
equal deleted inserted replaced
51786:c93f14a4ae29 51787:ba51515b64e5
       
     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 }