View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2.provider.ram;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.DataInputStream;
21  import java.io.DataOutputStream;
22  import java.io.EOFException;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.nio.charset.Charset;
26  import java.util.Objects;
27  
28  import org.apache.commons.vfs2.RandomAccessContent;
29  import org.apache.commons.vfs2.util.RandomAccessMode;
30  
31  /**
32   * RAM File Random Access Content.
33   */
34  public class RamFileRandomAccessContent implements RandomAccessContent {
35  
36      private static final int BYTE_VALUE_MASK = 0xFF;
37  
38      /**
39       * Build an 8-byte array from a long. No check is performed on the array length.
40       *
41       * @param n The number to convert.
42       * @param b The array to fill.
43       * @return A byte[].
44       */
45      public static byte[] toBytes(long n, final byte[] b) {
46          b[7] = (byte) n;
47          n >>>= 8;
48          b[6] = (byte) n;
49          n >>>= 8;
50          b[5] = (byte) n;
51          n >>>= 8;
52          b[4] = (byte) n;
53          n >>>= 8;
54          b[3] = (byte) n;
55          n >>>= 8;
56          b[2] = (byte) n;
57          n >>>= 8;
58          b[1] = (byte) n;
59          n >>>= 8;
60          b[0] = (byte) n;
61          return b;
62      }
63  
64      /**
65       * Build a long from first 8 bytes of the array.
66       *
67       * @param b The byte[] to convert.
68       * @return A long.
69       */
70      public static long toLong(final byte[] b) {
71          return ((long) b[7] & BYTE_VALUE_MASK) + (((long) b[6] & BYTE_VALUE_MASK) << 8) + (((long) b[5] & BYTE_VALUE_MASK) << 16)
72                  + (((long) b[4] & BYTE_VALUE_MASK) << 24) + (((long) b[3] & BYTE_VALUE_MASK) << 32) + (((long) b[2] & BYTE_VALUE_MASK) << 40)
73                  + (((long) b[1] & BYTE_VALUE_MASK) << 48) + (((long) b[0] & BYTE_VALUE_MASK) << 56);
74      }
75  
76      /**
77       * Build a short from first 2 bytes of the array.
78       *
79       * @param b The byte[] to convert.
80       * @return A short.
81       */
82      public static short toShort(final byte[] b) {
83          return (short) toUnsignedShort(b);
84      }
85  
86      /**
87       * Build a short from first 2 bytes of the array.
88       *
89       * @param b The byte[] to convert.
90       * @return A short.
91       */
92      public static int toUnsignedShort(final byte[] b) {
93          return (b[1] & BYTE_VALUE_MASK) + ((b[0] & BYTE_VALUE_MASK) << 8);
94      }
95  
96      /**
97       * File Pointer
98       */
99      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 }