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      */
ByteArrayChannel(int sz, boolean readonly)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      */
ByteArrayChannel(byte[] buf, boolean readonly)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
isOpen()76     public boolean isOpen() {
77         return !closed;
78     }
79 
80     @Override
position()81     public long position() throws IOException {
82         beginRead();
83         try {
84             ensureOpen();
85             return pos;
86         } finally {
87             endRead();
88         }
89     }
90 
91     @Override
position(long pos)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
read(ByteBuffer dst)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
truncate(long size)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
write(ByteBuffer src)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
size()150     public long size() throws IOException {
151         beginRead();
152         try {
153             ensureOpen();
154             return last;
155         } finally {
156             endRead();
157         }
158     }
159 
160     @Override
close()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      */
toByteArray()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 
ensureOpen()192     private void ensureOpen() throws IOException {
193         if (closed)
194             throw new ClosedChannelException();
195     }
196 
beginWrite()197     private final void beginWrite() {
198         rwlock.writeLock().lock();
199     }
200 
endWrite()201     private final void endWrite() {
202         rwlock.writeLock().unlock();
203     }
204 
beginRead()205     private final void beginRead() {
206         rwlock.readLock().lock();
207     }
208 
endRead()209     private final void endRead() {
210         rwlock.readLock().unlock();
211     }
212 
ensureCapacity(int minCapacity)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      */
grow(int minCapacity)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 
hugeCapacity(int minCapacity)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 }
253