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 }