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 }