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 package com.imagero.uio.bio;
033
034 import com.imagero.uio.ISeekable;
035 import com.imagero.uio.RandomAccessIO;
036 import com.imagero.uio.RandomAccessInput;
037 import com.imagero.uio.RandomAccessOutput;
038 import com.imagero.uio.io.UnexpectedEOFException;
039 import com.imagero.uio.impl.AbstractRandomAccessIO;
040
041 import java.io.DataOutput;
042 import java.io.EOFException;
043 import java.io.IOException;
044 import java.io.OutputStream;
045 import java.io.InputStream;
046
047 /**
048 * BufferedRandomAccessIO - buffered readable and writable stream with random access.
049 * @author Andrey Kuznetsov
050 */
051 public class BufferedRandomAccessIO extends AbstractRandomAccessIO {
052
053 FixedSizeByteBuffer buffer;
054 BufferIndex bufferIndex;
055 BufferPosition bufferPosition;
056 IOController controller;
057
058 StreamPosition streamPosition = new StreamPosition();
059 long offset;
060
061
062 public BufferedRandomAccessIO(IOController controller) {
063 this(controller, 0L);
064 }
065
066 public BufferedRandomAccessIO(IOController controller, long offset) {
067 this.controller = controller;
068 this.offset = offset;
069 bufferPosition = new BufferPosition(controller.bufferSize);
070 seek(0);
071 }
072
073 public final void setLength(long newLength) throws IOException {
074 controller.setLength(newLength);
075 }
076
077 public long flushBefore(long pos) {
078 return controller.flushBefore(pos);
079 }
080
081 protected void prepareBufferForReading(BufferIndex index) throws IOException {
082 if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
083 bufferIndex = index;
084 buffer = controller.getBuffer(streamPosition.pos, true);
085 }
086 bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
087 }
088
089 protected void prepareBufferForWriting(BufferIndex index) throws IOException {
090 if (!index.equals(bufferIndex) || buffer == null || buffer.buf == null) {
091 bufferIndex = index;
092 buffer = controller.getBuffer(streamPosition.pos, false);
093 }
094 bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
095 buffer.changed = true;
096 }
097
098 public void write(short[] data, int offset, int length, int byteOrder) throws IOException {
099 final boolean bigEndian = byteOrder == ISeekable.BIG_ENDIAN;
100
101 while (length > 0) {
102 ensureBuffer(false);
103 if (buffer.availableForWriting(bufferPosition) < 2) {
104 writeShort(data[offset++]);
105 length--;
106 }
107 if (length > 0) {
108 int copied = buffer.write(data, offset, length, bigEndian, bufferPosition);
109 length -= copied;
110 offset += copied;
111 streamPosition.pos += copied << 1;
112 }
113 }
114 }
115
116 public void write(char[] data, int offset, int length, int byteOrder) throws IOException {
117 final boolean bigEndian = byteOrder == ISeekable.BIG_ENDIAN;
118
119 while (length > 0) {
120 ensureBuffer(false);
121 if (buffer.availableForWriting(bufferPosition) < 2) {
122 writeShort(data[offset++]);
123 length--;
124 }
125 if (length > 0) {
126 int copied = buffer.write(data, offset, length, bigEndian, bufferPosition);
127 length -= copied;
128 offset += copied;
129 streamPosition.pos += copied << 1;
130 }
131 }
132 }
133
134 public void write(int[] data, int offset, int length, int byteOrder) throws IOException {
135 final boolean bigEndian = byteOrder == ISeekable.BIG_ENDIAN;
136
137 while (length > 0) {
138 ensureBuffer(false);
139 if (buffer.availableForWriting(bufferPosition) < 4) {
140 writeInt(data[offset++]);
141 length--;
142 }
143 if (length > 0) {
144 int copied = buffer.write(data, offset, length, bigEndian, bufferPosition);
145 length -= copied;
146 offset += copied;
147 streamPosition.pos += copied << 2;
148 }
149 }
150 }
151
152 public void write(float[] data, int offset, int length, int byteOrder) throws IOException {
153 final boolean bigEndian = byteOrder == ISeekable.BIG_ENDIAN;
154
155 while (length > 0) {
156 ensureBuffer(false);
157 if (buffer.availableForWriting(bufferPosition) < 4) {
158 writeFloat(data[offset++]);
159 length--;
160 }
161 if (length > 0) {
162 int copied = buffer.write(data, offset, length, bigEndian, bufferPosition);
163 length -= copied;
164 offset += copied;
165 streamPosition.pos += copied << 2;
166 }
167 }
168 }
169
170 public void write(long[] data, int offset, int length, int byteOrder) throws IOException {
171 final boolean bigEndian = byteOrder == ISeekable.BIG_ENDIAN;
172
173 while (length > 0) {
174 ensureBuffer(false);
175 if (buffer.availableForWriting(bufferPosition) < 8) {
176 writeLong(data[offset++]);
177 length--;
178 }
179 if (length > 0) {
180 int copied = buffer.write(data, offset, length, bigEndian, bufferPosition);
181 length -= copied;
182 offset += copied;
183 streamPosition.pos += copied << 3;
184 }
185 }
186 }
187
188 public void write(double[] data, int offset, int length, int byteOrder) throws IOException {
189 final boolean bigEndian = byteOrder == ISeekable.BIG_ENDIAN;
190
191 while (length > 0) {
192 ensureBuffer(false);
193 if (buffer.availableForWriting(bufferPosition) < 8) {
194 writeDouble(data[offset++]);
195 length--;
196 }
197 if (length > 0) {
198 int copied = buffer.write(data, offset, length, bigEndian, bufferPosition);
199 length -= copied;
200 offset += copied;
201 streamPosition.pos += copied << 3;
202 }
203 }
204 }
205
206 public long getFilePointer() {
207 return streamPosition.pos - offset;
208 }
209
210 public long length() throws IOException {
211 return controller.length();
212 }
213
214 public void seek(long pos) {
215 streamPosition.pos = pos + offset;
216 bufferPosition.pos = (int) ((streamPosition.pos) % controller.bufferSize);
217 }
218
219 public int available() throws IOException {
220 if (buffer != null) {
221 return buffer.availableForReading(bufferPosition);
222 }
223 return 0;
224 }
225
226 public int getByteOrder() {
227 return byteOrder;
228 }
229
230 public void setByteOrder(int byteOrder) {
231 switch (byteOrder) {
232 case ISeekable.BIG_ENDIAN:
233 case ISeekable.LITTLE_ENDIAN:
234 this.byteOrder = byteOrder;
235 break;
236 default:
237 throw new IllegalArgumentException("" + Integer.toHexString(byteOrder));
238 }
239 }
240
241 public void write(int b) throws IOException {
242 ensureBuffer(false);
243 buffer.write(b, bufferPosition);
244 streamPosition.pos++;
245 }
246
247 public void write(byte b[], int offset, int length) throws IOException {
248 while (length > 0) {
249 ensureBuffer(false);
250 int written = buffer.write(b, offset, length, bufferPosition);
251 length -= written;
252 offset += written;
253 streamPosition.pos += written;
254 }
255 }
256
257 public void close() throws IOException {
258 if (controller != null) {
259 controller.sync();
260 controller = null;
261 }
262 }
263
264 /**
265 * write buffer contents to given OutputStream
266 * @param out OutputStream
267 */
268 public void writeBuffer(OutputStream out) throws IOException {
269 controller.writeTo(out);
270 }
271
272 /**
273 * write buffer contents to DataOutput
274 * @param out OutputStream
275 */
276 public void writeBuffer(DataOutput out) throws IOException {
277 controller.writeTo(out);
278 }
279
280 public int read(short[] dest, int offset, int length, int byteOrder) throws IOException {
281 ensureBuffer(true);
282 if (buffer == null) {
283 return 0;
284 }
285 int read = 0;
286
287 int available = buffer.availableForReading(bufferPosition);
288 if (available < 2) {
289 dest[offset++] = readShort(byteOrder);
290 length--;
291 read++;
292 }
293 if (length > 0) {
294 int copied = buffer.read(dest, offset, length, byteOrder == BIG_ENDIAN, bufferPosition);
295 length -= copied;
296 offset += copied;
297 streamPosition.pos += copied << 1;
298 read += copied;
299 }
300 return read;
301 }
302
303 private void ensureBuffer(boolean read) throws IOException {
304 BufferIndex index = controller.getBufferIndex(streamPosition.pos);
305 if (read) {
306 if (buffer == null || buffer.availableForReading(bufferPosition) <= 0 || bufferIndex != index) {
307 prepareBufferForReading(index);
308 }
309 } else {
310 if (buffer == null || buffer.availableForWriting(bufferPosition) <= 0 || bufferIndex != index) {
311 prepareBufferForWriting(index);
312 }
313 }
314 }
315
316 public int read(char[] dest, int offset, int length, int byteOrder) throws IOException {
317 ensureBuffer(true);
318 if (buffer == null) {
319 return 0;
320 }
321
322 int read = 0;
323
324 int available = buffer.availableForReading(bufferPosition);
325 if (available < 2) {
326 dest[offset++] = readChar(byteOrder);
327 length--;
328 read++;
329 }
330 if (length > 0) {
331 int copied = buffer.read(dest, offset, length, byteOrder == BIG_ENDIAN, bufferPosition);
332 length -= copied;
333 offset += copied;
334 streamPosition.pos += copied << 1;
335 read += copied;
336 }
337 return read;
338 }
339
340 public int read(int[] dest, int offset, int length, int byteOrder) throws IOException {
341 ensureBuffer(true);
342 if (buffer == null) {
343 return 0;
344 }
345
346 int read = 0;
347
348 int available = buffer.availableForReading(bufferPosition);
349 if (available < 4) {
350 dest[offset++] = readInt(byteOrder);
351 length--;
352 read++;
353 }
354 if (length > 0) {
355 int copied = buffer.read(dest, offset, length, byteOrder == BIG_ENDIAN, bufferPosition);
356 length -= copied;
357 offset += copied;
358 streamPosition.pos += copied << 2;
359 read += copied;
360 }
361 return read;
362 }
363
364 public int read(long[] dest, int offset, int length, int byteOrder) throws IOException {
365 ensureBuffer(true);
366 if (buffer == null) {
367 return 0;
368 }
369
370 int read = 0;
371
372 int available = buffer.availableForReading(bufferPosition);
373 if (available < 8) {
374 dest[offset++] = readLong(byteOrder);
375 length--;
376 read++;
377 }
378 if (length > 0) {
379 int copied = buffer.read(dest, offset, length, byteOrder == BIG_ENDIAN, bufferPosition);
380 length -= copied;
381 offset += copied;
382 streamPosition.pos += copied << 3;
383 read += copied;
384 }
385 return read;
386 }
387
388 public int read(float[] dest, int offset, int length, int byteOrder) throws IOException {
389 ensureBuffer(true);
390 if (buffer == null) {
391 return 0;
392 }
393
394 int read = 0;
395
396 int available = buffer.availableForReading(bufferPosition);
397 if (available < 4) {
398 dest[offset++] = readFloat(byteOrder);
399 length--;
400 read++;
401 }
402 if (length > 0) {
403 int copied = buffer.read(dest, offset, length, byteOrder == BIG_ENDIAN, bufferPosition);
404 length -= copied;
405 offset += copied;
406 streamPosition.pos += copied << 2;
407 read += copied;
408 }
409 return read;
410 }
411
412 public int read(double[] dest, int offset, int length, int byteOrder) throws IOException {
413 ensureBuffer(true);
414 if (buffer == null) {
415 return 0;
416 }
417
418 int read = 0;
419
420 int available = buffer.availableForReading(bufferPosition);
421 if (available < 8) {
422 dest[offset++] = readDouble(byteOrder);
423 length--;
424 read++;
425 }
426 if (length > 0) {
427 int copied = buffer.read(dest, offset, length, byteOrder == BIG_ENDIAN, bufferPosition);
428 length -= copied;
429 offset += copied;
430 streamPosition.pos += copied << 3;
431 read += copied;
432 }
433 return read;
434 }
435
436 private void assertBufferIsNotNull(int count) throws EOFException {
437 if (buffer == null) {
438 throw new UnexpectedEOFException(count);
439 }
440 }
441
442 public int read() throws IOException {
443 try {
444 ensureBuffer(true);
445 } catch (IOException ex) {
446 return -1;
447 }
448 if (buffer != null) {
449 streamPosition.pos++;
450 return buffer.read(bufferPosition);
451 }
452 return -1;
453 }
454
455 public long skip(long n) throws IOException {
456 ensureBuffer(true);
457 if (buffer == null) {
458 return 0;
459 }
460 long skipped = buffer.skip(n, bufferPosition);
461 streamPosition.pos += skipped;
462 return skipped;
463 }
464
465 public int read(byte[] b, int offset, int length) throws IOException {
466 ensureBuffer(true);
467 if (buffer == null) {
468 return 0;
469 }
470 int rc = buffer.read(b, offset, length, bufferPosition);
471 if (rc > 0) {
472 streamPosition.pos += rc;
473 }
474 return rc;
475 }
476
477 public void readFully(byte b[], int offset, int length) throws IOException {
478 // super.readFully(b, offset, length);
479 int sum = 0;
480 while (length > 0) {
481 ensureBuffer(true);
482 assertBufferIsNotNull(sum);
483 int copied = buffer.read(b, offset, length, bufferPosition);
484 if(copied <= 0) {
485 throw new UnexpectedEOFException(sum);
486 }
487 sum += copied;
488 length -= copied;
489 offset += copied;
490 streamPosition.pos += copied;
491 }
492 }
493
494 public RandomAccessIO createIOChild(long offset, int byteOrder, boolean syncPointer) {
495 BufferedRandomAccessIO io = new BufferedRandomAccessIO(controller, offset);
496 io.setByteOrder(byteOrder);
497 if (syncPointer) {
498 io.streamPosition = streamPosition;
499 }
500 return io;
501 }
502
503 public RandomAccessInput createInputChild(long offset, int byteOrder, boolean syncPointer) {
504 return createIOChild(offset, byteOrder, syncPointer);
505 }
506
507 public InputStream createInputStream(long offset) {
508 return new IOCInputStream(controller, offset);
509 }
510
511 public RandomAccessOutput createOutputChild(long offset, int byteOrder, boolean syncPointer) {
512 return createIOChild(offset, byteOrder, syncPointer);
513 }
514
515 public OutputStream createOutputStream(long offset) {
516 return new IOCOutputStream(controller, offset);
517 }
518
519 public void flush() throws IOException {
520 controller.sync();
521 }
522
523 public boolean isBuffered() {
524 return true;
525 }
526 }