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 }