/* * Copyright (C) 2009,2010 Matthias Treydte * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package de.waldheinz.fs.util; import de.waldheinz.fs.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.zip.GZIPInputStream; /** * A {@link BlockDevice} that lives entirely in heap memory. This is basically * a RAM disk. A {@code RamDisk} is always writable. * * @author Matthias Treydte <waldheinz at gmail.com> */ public final class RamDisk implements BlockDevice { /** * The default sector size for {@code RamDisk}s. */ public final static int DEFAULT_SECTOR_SIZE = 512; private final int sectorSize; private final ByteBuffer data; private final int size; private boolean closed; /** * Reads a GZIP compressed disk image from the specified input stream and * returns a {@code RamDisk} holding the decompressed image. * * @param in the stream to read the disk image from * @return the decompressed {@code RamDisk} * @throws IOException on read or decompression error */ public static RamDisk readGzipped(InputStream in) throws IOException { final GZIPInputStream zis = new GZIPInputStream(in); ByteArrayOutputStream bos = new ByteArrayOutputStream(); final byte[] buffer = new byte[4096]; int read = zis.read(buffer); int total = 0; while (read >= 0) { total += read; bos.write(buffer, 0, read); read = zis.read(buffer); } if (total < DEFAULT_SECTOR_SIZE) throw new IOException( "read only " + total + " bytes"); //NOI18N final ByteBuffer bb = ByteBuffer.wrap(bos.toByteArray(), 0, total); return new RamDisk(bb, DEFAULT_SECTOR_SIZE); } private RamDisk(ByteBuffer buffer, int sectorSize) { this.size = buffer.limit(); this.sectorSize = sectorSize; this.data = buffer; this.closed = false; } /** * Creates a new instance of {@code RamDisk} of this specified * size and using the {@link #DEFAULT_SECTOR_SIZE}. * * @param size the size of the new block device */ public RamDisk(int size) { this(size, DEFAULT_SECTOR_SIZE); } /** * Creates a new instance of {@code RamDisk} of this specified * size and sector size * * @param size the size of the new block device * @param sectorSize the sector size of the new block device */ public RamDisk(int size, int sectorSize) { if (sectorSize < 1) throw new IllegalArgumentException( "invalid sector size"); //NOI18N this.sectorSize = sectorSize; this.size = size; this.data = ByteBuffer.allocate(size); } @Override public long getSize() { checkClosed(); return this.size; } @Override public void read(long devOffset, ByteBuffer dest) throws IOException { checkClosed(); if (devOffset > getSize()){ final StringBuilder sb = new StringBuilder(); sb.append("read at ").append(devOffset); sb.append(" is off size (").append(getSize()).append(")"); throw new IllegalArgumentException(sb.toString()); } data.limit((int) (devOffset + dest.remaining())); data.position((int) devOffset); dest.put(data); } @Override public void write(long devOffset, ByteBuffer src) throws IOException { checkClosed(); if (devOffset + src.remaining() > getSize()) throw new IllegalArgumentException( "offset=" + devOffset + ", length=" + src.remaining() + ", size=" + getSize()); data.limit((int) (devOffset + src.remaining())); data.position((int) devOffset); data.put(src); } /** * Returns a slice of the {@code ByteBuffer} that is used by this * {@code RamDisk} as it's backing store. The returned buffer will be * live (reflecting any changes made through the * {@link #write(long, java.nio.ByteBuffer) method}, but read-only. * * @return a buffer holding the contents of this {@code RamDisk} */ public ByteBuffer getBuffer() { return this.data.asReadOnlyBuffer(); } @Override public void flush() throws IOException { checkClosed(); } @Override public int getSectorSize() { checkClosed(); return this.sectorSize; } @Override public void close() throws IOException { this.closed = true; } @Override public boolean isClosed() { return this.closed; } private void checkClosed() { if (closed) throw new IllegalStateException("device already closed"); } /** * Returns always {@code false}, as a {@code RamDisk} is always writable. * * @return always {@code false} */ @Override public boolean isReadOnly() { checkClosed(); return false; } }