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