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}