1 /* 2 * Copyright (c) 2007, 2013, 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 package com.sun.media.sound; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.DataInputStream; 29 import java.io.File; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStream; 33 import java.io.RandomAccessFile; 34 import java.util.Collection; 35 36 /** 37 * This class is a pointer to a binary array either in memory or on disk. 38 * 39 * @author Karl Helgason 40 */ 41 public final class ModelByteBuffer { 42 43 private ModelByteBuffer root = this; 44 private File file; 45 private long fileoffset; 46 private byte[] buffer; 47 private long offset; 48 private final long len; 49 50 private class RandomFileInputStream extends InputStream { 51 52 private final RandomAccessFile raf; 53 private long left; 54 private long mark = 0; 55 private long markleft = 0; 56 57 RandomFileInputStream() throws IOException { 58 raf = new RandomAccessFile(root.file, "r"); 59 raf.seek(root.fileoffset + arrayOffset()); 60 left = capacity(); 61 } 62 63 public int available() throws IOException { 64 if (left > Integer.MAX_VALUE) 65 return Integer.MAX_VALUE; 66 return (int)left; 67 } 68 69 public synchronized void mark(int readlimit) { 70 try { 71 mark = raf.getFilePointer(); 72 markleft = left; 73 } catch (IOException e) { 74 //e.printStackTrace(); 75 } 76 } 77 78 public boolean markSupported() { 79 return true; 80 } 81 82 public synchronized void reset() throws IOException { 83 raf.seek(mark); 84 left = markleft; 85 } 86 87 public long skip(long n) throws IOException { 88 if( n < 0) 89 return 0; 90 if (n > left) 91 n = left; 92 long p = raf.getFilePointer(); 93 raf.seek(p + n); 94 left -= n; 95 return n; 96 } 97 98 public int read(byte b[], int off, int len) throws IOException { 99 if (len > left) 100 len = (int)left; 101 if (left == 0) 102 return -1; 103 len = raf.read(b, off, len); 104 if (len == -1) 105 return -1; 106 left -= len; 107 return len; 108 } 109 110 public int read(byte[] b) throws IOException { 111 int len = b.length; 112 if (len > left) 113 len = (int)left; 114 if (left == 0) 115 return -1; 116 len = raf.read(b, 0, len); 117 if (len == -1) 118 return -1; 119 left -= len; 120 return len; 121 } 122 123 public int read() throws IOException { 124 if (left == 0) 125 return -1; 126 int b = raf.read(); 127 if (b == -1) 128 return -1; 129 left--; 130 return b; 131 } 132 133 public void close() throws IOException { 134 raf.close(); 135 } 136 } 137 138 private ModelByteBuffer(ModelByteBuffer parent, 139 long beginIndex, long endIndex, boolean independent) { 140 this.root = parent.root; 141 this.offset = 0; 142 long parent_len = parent.len; 143 if (beginIndex < 0) 144 beginIndex = 0; 145 if (beginIndex > parent_len) 146 beginIndex = parent_len; 147 if (endIndex < 0) 148 endIndex = 0; 149 if (endIndex > parent_len) 150 endIndex = parent_len; 151 if (beginIndex > endIndex) 152 beginIndex = endIndex; 153 offset = beginIndex; 154 len = endIndex - beginIndex; 155 if (independent) { 156 buffer = root.buffer; 157 if (root.file != null) { 158 file = root.file; 159 fileoffset = root.fileoffset + arrayOffset(); 160 offset = 0; 161 } else 162 offset = arrayOffset(); 163 root = this; 164 } 165 } 166 167 public ModelByteBuffer(byte[] buffer) { 168 this.buffer = buffer; 169 this.offset = 0; 170 this.len = buffer.length; 171 } 172 173 public ModelByteBuffer(byte[] buffer, int offset, int len) { 174 this.buffer = buffer; 175 this.offset = offset; 176 this.len = len; 177 } 178 179 public ModelByteBuffer(File file) { 180 this.file = file; 181 this.fileoffset = 0; 182 this.len = file.length(); 183 } 184 185 public ModelByteBuffer(File file, long offset, long len) { 186 this.file = file; 187 this.fileoffset = offset; 188 this.len = len; 189 } 190 191 public void writeTo(OutputStream out) throws IOException { 192 if (root.file != null && root.buffer == null) { 193 InputStream is = getInputStream(); 194 byte[] buff = new byte[1024]; 195 int ret; 196 while ((ret = is.read(buff)) != -1) 197 out.write(buff, 0, ret); 198 } else 199 out.write(array(), (int) arrayOffset(), (int) capacity()); 200 } 201 202 public InputStream getInputStream() { 203 if (root.file != null && root.buffer == null) { 204 try { 205 return new RandomFileInputStream(); 206 } catch (IOException e) { 207 //e.printStackTrace(); 208 return null; 209 } 210 } 211 return new ByteArrayInputStream(array(), 212 (int)arrayOffset(), (int)capacity()); 213 } 214 215 public ModelByteBuffer subbuffer(long beginIndex) { 216 return subbuffer(beginIndex, capacity()); 217 } 218 219 public ModelByteBuffer subbuffer(long beginIndex, long endIndex) { 220 return subbuffer(beginIndex, endIndex, false); 221 } 222 223 public ModelByteBuffer subbuffer(long beginIndex, long endIndex, 224 boolean independent) { 225 return new ModelByteBuffer(this, beginIndex, endIndex, independent); 226 } 227 228 public byte[] array() { 229 return root.buffer; 230 } 231 232 public long arrayOffset() { 233 if (root != this) 234 return root.arrayOffset() + offset; 235 return offset; 236 } 237 238 public long capacity() { 239 return len; 240 } 241 242 public ModelByteBuffer getRoot() { 243 return root; 244 } 245 246 public File getFile() { 247 return file; 248 } 249 250 public long getFilePointer() { 251 return fileoffset; 252 } 253 254 public static void loadAll(Collection<ModelByteBuffer> col) 255 throws IOException { 256 File selfile = null; 257 RandomAccessFile raf = null; 258 try { 259 for (ModelByteBuffer mbuff : col) { 260 mbuff = mbuff.root; 261 if (mbuff.file == null) 262 continue; 263 if (mbuff.buffer != null) 264 continue; 265 if (selfile == null || !selfile.equals(mbuff.file)) { 266 if (raf != null) { 267 raf.close(); 268 raf = null; 269 } 270 selfile = mbuff.file; 271 raf = new RandomAccessFile(mbuff.file, "r"); 272 } 273 raf.seek(mbuff.fileoffset); 274 byte[] buffer = new byte[(int) mbuff.capacity()]; 275 276 int read = 0; 277 int avail = buffer.length; 278 while (read != avail) { 279 if (avail - read > 65536) { 280 raf.readFully(buffer, read, 65536); 281 read += 65536; 282 } else { 283 raf.readFully(buffer, read, avail - read); 284 read = avail; 285 } 286 287 } 288 289 mbuff.buffer = buffer; 290 mbuff.offset = 0; 291 } 292 } finally { 293 if (raf != null) 294 raf.close(); 295 } 296 } 297 298 public void load() throws IOException { 299 if (root != this) { 300 root.load(); 301 return; 302 } 303 if (buffer != null) 304 return; 305 if (file == null) { 306 throw new IllegalStateException( 307 "No file associated with this ByteBuffer!"); 308 } 309 310 DataInputStream is = new DataInputStream(getInputStream()); 311 buffer = new byte[(int) capacity()]; 312 offset = 0; 313 is.readFully(buffer); 314 is.close(); 315 316 } 317 318 public void unload() { 319 if (root != this) { 320 root.unload(); 321 return; 322 } 323 if (file == null) { 324 throw new IllegalStateException( 325 "No file associated with this ByteBuffer!"); 326 } 327 root.buffer = null; 328 } 329 }