001    /*
002     * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003     *
004     * http://uio.imagero.com
005     *
006     * Redistribution and use in source and binary forms, with or without
007     * modification, are permitted provided that the following conditions are met:
008     *
009     *  o Redistributions of source code must retain the above copyright notice,
010     *    this list of conditions and the following disclaimer.
011     *
012     *  o Redistributions in binary form must reproduce the above copyright notice,
013     *    this list of conditions and the following disclaimer in the documentation
014     *    and/or other materials provided with the distribution.
015     *
016     *  o Neither the name of Andrey Kuznetsov nor the names of
017     *    its contributors may be used to endorse or promote products derived
018     *    from this software without specific prior written permission.
019     *
020     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
029     * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
030     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031     */
032    
033    package com.imagero.uio.blob;
034    
035    import com.imagero.uio.bio.content.IntArrayContent;
036    import com.imagero.uio.impl.array.RandomAccessIntArray;
037    import com.imagero.uio.io.IOutils;
038    import com.imagero.uio.io.SkipBytesInputStream;
039    import com.imagero.uio.io.UnexpectedEOFException;
040    import com.imagero.uio.xform.XtoByteBE;
041    import com.imagero.uio.RandomAccessInput;
042    import com.imagero.uio.RandomAccessIO;
043    import com.imagero.uio.ISeekable;
044    
045    import java.io.IOException;
046    import java.io.InputStream;
047    import java.util.Hashtable;
048    
049    /**
050     * Blob - Object which encapsulates (possible deferred)
051     * data which may come from different sources.
052     *
053     * @author Andrey Kuznetsov
054     */
055    public abstract class Blob {
056    
057        long length;
058    
059        private Hashtable properties = new Hashtable();
060    
061        /**
062         * retrieve data from this Blob
063         * @param start start offset
064         * @param length how much bytes to get
065         * @return byte array with data
066         * @throws java.io.IOException
067         */
068        public abstract byte[] get(long start, int length) throws IOException;
069    
070        /**
071         * retrieve data from this Blob
072         * @param start start offset
073         * @param dest where to copy data
074         * @return how much byte were copied
075         * @throws java.io.IOException
076         */
077        public abstract int get(long start, byte[] dest) throws IOException;
078    
079        /**
080         * determine if this Blob is writable and method set(long, byte[]) can be used to change content of this Blob
081         * @return true if Blob is writable
082         */
083        public abstract boolean writable();
084    
085        /**
086         * set data (work only if writable returns true)
087         * @param start start in destination
088         * @param data new data
089         * @throws java.io.IOException
090         */
091        public abstract void set(long start, byte[] data) throws IOException;
092    
093        /**
094         * release reloadable resources
095         */
096        public abstract void clear();
097    
098        protected boolean lengthKnown() {
099            return length > 0;
100        }
101    
102        protected abstract long computeLength() throws IOException;
103    
104        public long getLength() {
105            if (!lengthKnown()) {
106                try {
107                    length = computeLength();
108                } catch (IOException ex) {
109                    System.err.println("Can't compute Blob length. " + ex.getMessage());
110                }
111            }
112            return length;
113        }
114    
115        public InputStream getInputStream() throws IOException {
116            return new BlobInputStream(this);
117        }
118    
119        public Object getProperty(Object key) {
120            return properties.get(key);
121        }
122    
123        public void setProperty(Object key, Object property) {
124            properties.put(key, property);
125        }
126    
127        public static class BaBlob extends Blob {
128            int offset;
129    
130            byte[] blob;
131    
132            public BaBlob(byte[] blob) {
133                this(blob, 0, blob.length);
134            }
135    
136            public BaBlob(byte[] blob, int offset, int length) {
137                this.offset = offset;
138                this.length = length;
139                this.blob = blob;
140            }
141    
142            protected long computeLength() {
143                return length;
144            }
145    
146            static final byte[] empty = new byte[0];
147    
148            public byte[] get(long start, int length) {
149                int len = (int) Math.max(Math.min(length, this.length - start), 0);
150                if (len == 0) {
151                    return empty;
152                }
153                byte[] dest = new byte[len];
154                System.arraycopy(blob, (int) start + offset, dest, 0, len);
155                return dest;
156            }
157    
158            public int get(long start, byte[] dest) {
159                long max = Math.max(Math.min(dest.length, this.length - start), 0);
160                if (max > 0) {
161                    System.arraycopy(blob, (int) start + offset, dest, 0, (int) max);
162                }
163                return (int) max;
164            }
165    
166            public boolean writable() {
167                return true;
168            }
169    
170            public void set(long start, byte[] data) {
171                System.arraycopy(data, 0, blob, (int) start, data.length);
172            }
173    
174            public void clear() {
175            }
176        }
177    
178        public static class RoBlob extends Blob {
179            RandomAccessInput ro;
180            long start;
181    
182            public RoBlob(RandomAccessInput ro, long start, long length) {
183                this.ro = ro;
184                this.start = start;
185                this.length = length;
186            }
187    
188            protected long computeLength() {
189                return length;
190            }
191    
192            public byte[] get(long start, int length) throws IOException {
193                long pos = ro.getFilePointer();
194                try {
195                    byte[] dest = new byte[length];
196                    ro.seek(this.start + start);
197                    ro.readFully(dest);
198                    return dest;
199                } finally {
200                    ro.seek(pos);
201                }
202            }
203    
204            public int get(long start, byte[] dest) throws IOException {
205                long max = Math.min(dest.length, length - start);
206                long pos = ro.getFilePointer();
207                try {
208                    ro.seek(this.start + start);
209                    ro.readFully(dest, 0, (int) max);
210                } catch (UnexpectedEOFException ex) {
211                    return ex.getCount();
212                } finally {
213                    ro.seek(pos);
214                }
215                return (int) max;
216            }
217    
218            public boolean writable() {
219                return ro instanceof RandomAccessIO;
220            }
221    
222            public void set(long start, byte[] data) throws IOException {
223                if (writable()) {
224                    RandomAccessIO ra = (RandomAccessIO) ro;
225                    ra.seek(start);
226                    ra.write(data);
227                }
228            }
229    
230            public void clear() {
231            }
232        }
233    
234        public static class IaBlob extends Blob {
235    
236            int offset;
237            int length4;
238            int[] blob;
239    
240            int mask;
241            private int bytesPerInt;
242    
243            RandomAccessIntArray in;
244            SkipBytesInputStream skip;
245    
246            IntArrayContent content;
247    
248            public IaBlob(int[] blob) {
249                this(blob, 0, blob.length);
250            }
251    
252            public IaBlob(int[] blob, int offset, int length) {
253                this(blob, offset, length, 0, new int[]{24, 16, 8, 0});
254            }
255    
256            public IaBlob(int[] blob, int offset, int length, int[] shifts) {
257                this(blob, offset, length, 0, shifts);
258            }
259    
260            public IaBlob(int[] blob, int offset, int length, int mask) {
261                this(blob, offset, length, mask, new int[]{24, 16, 8, 0});
262            }
263    
264            public IaBlob(int[] blob, int offset, int length, int mask, int[] shifts) {
265                this.offset = offset;
266                this.length4 = length;
267                this.blob = blob;
268                this.mask = mask;
269                final int[] shiftsBE = new int[4];
270                final int[] shiftsLE = new int[4];
271                for (int i = 0; i < shifts.length; i++) {
272                    shiftsBE[i] = shifts[i];
273                    shiftsLE[i] = shifts[3 - i];
274                }
275                in = new XIntArrayInputStream(blob, offset, length, shiftsBE, shiftsLE, ISeekable.BIG_ENDIAN);
276                skip = new SkipBytesInputStream(in, mask, 4);
277                if (mask == 0) {
278                    bytesPerInt = 4;
279                } else {
280                    bytesPerInt = XtoByteBE.getBytesPerNumber(mask);
281                }
282            }
283    
284            public boolean writable() {
285                return false;
286            }
287    
288            protected long computeLength() throws IOException {
289                InputStream in = getInputStream();
290                long count = 0;
291                while (in.read() >= 0) {
292                    count++;
293                }
294                return count;
295            }
296    
297            public byte[] get(long start, int length) throws IOException {
298                int len = (int) Math.max(Math.min(this.length4 * bytesPerInt - start, length), 0);
299                byte[] b = new byte[len];
300                if (len > 0) {
301                    in.seek(start);
302                    IOutils.readFully(skip, b);
303                }
304                return b;
305            }
306    
307            public int get(long start, byte[] dest) throws IOException {
308                int len = (int) Math.max(Math.min(this.length4 * bytesPerInt - start, dest.length), 0);
309                if (len > 0) {
310                    in.seek(start);
311                    IOutils.readFully(skip, dest, 0, len);
312                }
313                return len;
314            }
315    
316            public InputStream getInputStream() throws IOException {
317                RandomAccessIntArray in = (RandomAccessIntArray) this.in.createInputChild(0, this.in.getByteOrder(), false);
318                InputStream skip = new SkipBytesInputStream(in, mask, 4);
319                return skip;
320            }
321    
322            public void set(long start, byte[] data) throws IOException {
323                throw new RuntimeException("Unsupported operation");
324            }
325    
326            private static class XIntArrayInputStream extends RandomAccessIntArray {
327                public XIntArrayInputStream(int[] blob, int offset, int length, int[] shiftsBE, int[] shiftsLE, int byteOrder) {
328                    super(blob, offset, length, byteOrder);
329                    BE_SHIFTS = shiftsBE;
330                    LE_SHIFTS = shiftsLE;
331                    setByteOrder(byteOrder);
332                }
333            }
334    
335            public void clear() {
336            }
337        }
338    }