001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.vfs2.provider.ram;
018
019import java.io.ByteArrayOutputStream;
020import java.io.DataInputStream;
021import java.io.DataOutputStream;
022import java.io.EOFException;
023import java.io.IOException;
024import java.io.InputStream;
025
026import org.apache.commons.vfs2.RandomAccessContent;
027import org.apache.commons.vfs2.util.RandomAccessMode;
028
029/**
030 * RAM File Random Access Content.
031 */
032public class RamFileRandomAccessContent implements RandomAccessContent {
033    /**
034     * File Pointer
035     */
036    protected int filePointer = 0;
037
038    /**
039     * Buffer
040     */
041    private byte[] buf;
042
043    /**
044     * buffer
045     */
046    private final byte[] buffer8 = new byte[8];
047
048    /**
049     * buffer
050     */
051    private final byte[] buffer4 = new byte[4];
052
053    /**
054     * buffer
055     */
056    private final byte[] buffer2 = new byte[2];
057
058    /**
059     * buffer
060     */
061    private final byte[] buffer1 = new byte[1];
062
063    /**
064     * File
065     */
066    private final RamFileObject file;
067
068    private final InputStream rafis;
069
070    /**
071     * @param file The file to access.
072     * @param mode The access mode.
073     */
074    public RamFileRandomAccessContent(final RamFileObject file, final RandomAccessMode mode) {
075        super();
076        this.buf = file.getData().getContent();
077        this.file = file;
078
079        rafis = new InputStream() {
080            @Override
081            public int read() throws IOException {
082                try {
083                    return readByte();
084                } catch (final EOFException e) {
085                    return -1;
086                }
087            }
088
089            @Override
090            public long skip(final long n) throws IOException {
091                seek(getFilePointer() + n);
092                return n;
093            }
094
095            @Override
096            public void close() throws IOException {
097            }
098
099            @Override
100            public int read(final byte[] b) throws IOException {
101                return read(b, 0, b.length);
102            }
103
104            @Override
105            public int read(final byte[] b, final int off, final int len) throws IOException {
106                int retLen = -1;
107                final int left = getLeftBytes();
108                if (left > 0) {
109                    retLen = Math.min(len, left);
110                    RamFileRandomAccessContent.this.readFully(b, off, retLen);
111                }
112                return retLen;
113            }
114
115            @Override
116            public int available() throws IOException {
117                return getLeftBytes();
118            }
119        };
120    }
121
122    /*
123     * (non-Javadoc)
124     *
125     * @see org.apache.commons.vfs2.RandomAccessContent#getFilePointer()
126     */
127    @Override
128    public long getFilePointer() throws IOException {
129        return this.filePointer;
130    }
131
132    /*
133     * (non-Javadoc)
134     *
135     * @see org.apache.commons.vfs2.RandomAccessContent#seek(long)
136     */
137    @Override
138    public void seek(final long pos) throws IOException {
139        if (pos < 0) {
140            throw new IOException("Attempt to position before the start of the file");
141        }
142        this.filePointer = (int) pos;
143    }
144
145    /*
146     * (non-Javadoc)
147     *
148     * @see org.apache.commons.vfs2.RandomAccessContent#length()
149     */
150    @Override
151    public long length() throws IOException {
152        return buf.length;
153    }
154
155    /*
156     * (non-Javadoc)
157     *
158     * @see org.apache.commons.vfs2.RandomAccessContent#close()
159     */
160    @Override
161    public void close() throws IOException {
162
163    }
164
165    /*
166     * (non-Javadoc)
167     *
168     * @see java.io.DataInput#readByte()
169     */
170    @Override
171    public byte readByte() throws IOException {
172        return (byte) this.readUnsignedByte();
173    }
174
175    /*
176     * (non-Javadoc)
177     *
178     * @see java.io.DataInput#readChar()
179     */
180    @Override
181    public char readChar() throws IOException {
182        final int ch1 = this.readUnsignedByte();
183        final int ch2 = this.readUnsignedByte();
184        return (char) ((ch1 << 8) + (ch2 << 0));
185    }
186
187    /*
188     * (non-Javadoc)
189     *
190     * @see java.io.DataInput#readDouble()
191     */
192    @Override
193    public double readDouble() throws IOException {
194        return Double.longBitsToDouble(this.readLong());
195    }
196
197    /*
198     * (non-Javadoc)
199     *
200     * @see java.io.DataInput#readFloat()
201     */
202    @Override
203    public float readFloat() throws IOException {
204        return Float.intBitsToFloat(this.readInt());
205    }
206
207    /*
208     * (non-Javadoc)
209     *
210     * @see java.io.DataInput#readInt()
211     */
212    @Override
213    public int readInt() throws IOException {
214        return (readUnsignedByte() << 24) | (readUnsignedByte() << 16) | (readUnsignedByte() << 8) | readUnsignedByte();
215    }
216
217    /*
218     * (non-Javadoc)
219     *
220     * @see java.io.DataInput#readUnsignedByte()
221     */
222    @Override
223    public int readUnsignedByte() throws IOException {
224        if (filePointer < buf.length) {
225            return buf[filePointer++] & 0xFF;
226        }
227        throw new EOFException();
228    }
229
230    /*
231     * (non-Javadoc)
232     *
233     * @see java.io.DataInput#readUnsignedShort()
234     */
235    @Override
236    public int readUnsignedShort() throws IOException {
237        this.readFully(buffer2);
238        return toUnsignedShort(buffer2);
239    }
240
241    /*
242     * (non-Javadoc)
243     *
244     * @see java.io.DataInput#readLong()
245     */
246    @Override
247    public long readLong() throws IOException {
248        this.readFully(buffer8);
249        return toLong(buffer8);
250    }
251
252    /*
253     * (non-Javadoc)
254     *
255     * @see java.io.DataInput#readShort()
256     */
257    @Override
258    public short readShort() throws IOException {
259        this.readFully(buffer2);
260        return toShort(buffer2);
261    }
262
263    /*
264     * (non-Javadoc)
265     *
266     * @see java.io.DataInput#readBoolean()
267     */
268    @Override
269    public boolean readBoolean() throws IOException {
270        return this.readUnsignedByte() != 0;
271    }
272
273    /*
274     * (non-Javadoc)
275     *
276     * @see java.io.DataInput#skipBytes(int)
277     */
278    @Override
279    public int skipBytes(final int n) throws IOException {
280        if (n < 0) {
281            throw new IndexOutOfBoundsException("The skip number can't be negative");
282        }
283
284        final long newPos = filePointer + n;
285
286        if (newPos > buf.length) {
287            throw new IndexOutOfBoundsException("Tyring to skip too much bytes");
288        }
289
290        seek(newPos);
291
292        return n;
293    }
294
295    /*
296     * (non-Javadoc)
297     *
298     * @see java.io.DataInput#readFully(byte[])
299     */
300    @Override
301    public void readFully(final byte[] b) throws IOException {
302        this.readFully(b, 0, b.length);
303    }
304
305    /*
306     * (non-Javadoc)
307     *
308     * @see java.io.DataInput#readFully(byte[], int, int)
309     */
310    @Override
311    public void readFully(final byte[] b, final int off, final int len) throws IOException {
312        if (len < 0) {
313            throw new IndexOutOfBoundsException("Length is lower than 0");
314        }
315
316        if (len > this.getLeftBytes()) {
317            throw new IndexOutOfBoundsException(
318                    "Read length (" + len + ") is higher than buffer left bytes (" + this.getLeftBytes() + ") ");
319        }
320
321        System.arraycopy(buf, filePointer, b, off, len);
322
323        filePointer += len;
324    }
325
326    private int getLeftBytes() {
327        return buf.length - filePointer;
328    }
329
330    /*
331     * (non-Javadoc)
332     *
333     * @see java.io.DataInput#readUTF()
334     */
335    @Override
336    public String readUTF() throws IOException {
337        return DataInputStream.readUTF(this);
338    }
339
340    /*
341     * (non-Javadoc)
342     *
343     * @see java.io.DataOutput#write(byte[], int, int)
344     */
345    @Override
346    public void write(final byte[] b, final int off, final int len) throws IOException {
347        if (this.getLeftBytes() < len) {
348            final int newSize = this.buf.length + len - this.getLeftBytes();
349            this.file.resize(newSize);
350            this.buf = this.file.getData().getContent();
351        }
352        System.arraycopy(b, off, this.buf, filePointer, len);
353        this.filePointer += len;
354    }
355
356    /*
357     * (non-Javadoc)
358     *
359     * @see java.io.DataOutput#write(byte[])
360     */
361    @Override
362    public void write(final byte[] b) throws IOException {
363        this.write(b, 0, b.length);
364    }
365
366    /*
367     * (non-Javadoc)
368     *
369     * @see java.io.DataOutput#writeByte(int)
370     */
371    @Override
372    public void writeByte(final int i) throws IOException {
373        this.write(i);
374    }
375
376    /**
377     * Build a long from first 8 bytes of the array.
378     *
379     * @param b The byte[] to convert.
380     * @return A long.
381     */
382    public static long toLong(final byte[] b) {
383        return ((((long) b[7]) & 0xFF) + ((((long) b[6]) & 0xFF) << 8) + ((((long) b[5]) & 0xFF) << 16)
384                + ((((long) b[4]) & 0xFF) << 24) + ((((long) b[3]) & 0xFF) << 32) + ((((long) b[2]) & 0xFF) << 40)
385                + ((((long) b[1]) & 0xFF) << 48) + ((((long) b[0]) & 0xFF) << 56));
386    }
387
388    /**
389     * Build a 8-byte array from a long. No check is performed on the array length.
390     *
391     * @param n The number to convert.
392     * @param b The array to fill.
393     * @return A byte[].
394     */
395    public static byte[] toBytes(long n, final byte[] b) {
396        b[7] = (byte) (n);
397        n >>>= 8;
398        b[6] = (byte) (n);
399        n >>>= 8;
400        b[5] = (byte) (n);
401        n >>>= 8;
402        b[4] = (byte) (n);
403        n >>>= 8;
404        b[3] = (byte) (n);
405        n >>>= 8;
406        b[2] = (byte) (n);
407        n >>>= 8;
408        b[1] = (byte) (n);
409        n >>>= 8;
410        b[0] = (byte) (n);
411        return b;
412    }
413
414    /**
415     * Build a short from first 2 bytes of the array.
416     *
417     * @param b The byte[] to convert.
418     * @return A short.
419     */
420    public static short toShort(final byte[] b) {
421        return (short) toUnsignedShort(b);
422    }
423
424    /**
425     * Build a short from first 2 bytes of the array.
426     *
427     * @param b The byte[] to convert.
428     * @return A short.
429     */
430    public static int toUnsignedShort(final byte[] b) {
431        return ((b[1] & 0xFF) + ((b[0] & 0xFF) << 8));
432    }
433
434    /*
435     * (non-Javadoc)
436     *
437     * @see java.io.DataOutput#write(int)
438     */
439    @Override
440    public void write(final int b) throws IOException {
441        buffer1[0] = (byte) b;
442        this.write(buffer1);
443    }
444
445    /*
446     * (non-Javadoc)
447     *
448     * @see java.io.DataOutput#writeBoolean(boolean)
449     */
450    @Override
451    public void writeBoolean(final boolean v) throws IOException {
452        this.write(v ? 1 : 0);
453    }
454
455    /*
456     * (non-Javadoc)
457     *
458     * @see java.io.DataOutput#writeBytes(java.lang.String)
459     */
460    @Override
461    public void writeBytes(final String s) throws IOException {
462        write(s.getBytes());
463    }
464
465    /*
466     * (non-Javadoc)
467     *
468     * @see java.io.DataOutput#writeChar(int)
469     */
470    @Override
471    public void writeChar(final int v) throws IOException {
472        buffer2[0] = (byte) ((v >>> 8) & 0xFF);
473        buffer2[1] = (byte) ((v >>> 0) & 0xFF);
474        write(buffer2);
475    }
476
477    /*
478     * (non-Javadoc)
479     *
480     * @see java.io.DataOutput#writeChars(java.lang.String)
481     */
482    @Override
483    public void writeChars(final String s) throws IOException {
484        final int len = s.length();
485        for (int i = 0; i < len; i++) {
486            writeChar(s.charAt(i));
487        }
488    }
489
490    /*
491     * (non-Javadoc)
492     *
493     * @see java.io.DataOutput#writeDouble(double)
494     */
495    @Override
496    public void writeDouble(final double v) throws IOException {
497        writeLong(Double.doubleToLongBits(v));
498    }
499
500    /*
501     * (non-Javadoc)
502     *
503     * @see java.io.DataOutput#writeFloat(float)
504     */
505    @Override
506    public void writeFloat(final float v) throws IOException {
507        writeInt(Float.floatToIntBits(v));
508    }
509
510    /*
511     * (non-Javadoc)
512     *
513     * @see java.io.DataOutput#writeInt(int)
514     */
515    @Override
516    public void writeInt(final int v) throws IOException {
517        buffer4[0] = (byte) ((v >>> 24) & 0xFF);
518        buffer4[1] = (byte) ((v >>> 16) & 0xFF);
519        buffer4[2] = (byte) ((v >>> 8) & 0xFF);
520        buffer4[3] = (byte) (v & 0xFF);
521        write(buffer4);
522    }
523
524    /*
525     * (non-Javadoc)
526     *
527     * @see java.io.DataOutput#writeLong(long)
528     */
529    @Override
530    public void writeLong(final long v) throws IOException {
531        write(toBytes(v, buffer8));
532    }
533
534    /*
535     * (non-Javadoc)
536     *
537     * @see java.io.DataOutput#writeShort(int)
538     */
539    @Override
540    public void writeShort(final int v) throws IOException {
541        buffer2[0] = (byte) ((v >>> 8) & 0xFF);
542        buffer2[1] = (byte) (v & 0xFF);
543        write(buffer2);
544    }
545
546    /*
547     * (non-Javadoc)
548     *
549     * @see java.io.DataOutput#writeUTF(java.lang.String)
550     */
551    @Override
552    public void writeUTF(final String str) throws IOException {
553        final ByteArrayOutputStream out = new ByteArrayOutputStream(str.length());
554        final DataOutputStream dataOut = new DataOutputStream(out);
555        dataOut.writeUTF(str);
556        dataOut.flush();
557        dataOut.close();
558        final byte[] b = out.toByteArray();
559        write(b);
560    }
561
562    /*
563     * (non-Javadoc)
564     *
565     * @see java.io.DataInput#readLine()
566     */
567    @Override
568    public String readLine() throws IOException {
569        throw new UnsupportedOperationException("deprecated");
570    }
571
572    @Override
573    public InputStream getInputStream() throws IOException {
574        return rafis;
575    }
576
577    @Override
578    public void setLength(final long newLength) throws IOException {
579        this.file.resize(newLength);
580        this.buf = this.file.getData().getContent();
581    }
582}