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