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