Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2009,2010 Matthias Treydte <mt (at) waldheinz.de>
      3  *
      4  * This library is free software; you can redistribute it and/or modify it
      5  * under the terms of the GNU Lesser General Public License as published
      6  * by the Free Software Foundation; either version 2.1 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
     12  * License for more details.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this library; If not, write to the Free Software Foundation, Inc.,
     16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     17  */
     18 
     19 package de.waldheinz.fs.util;
     20 
     21 import de.waldheinz.fs.*;
     22 import java.io.ByteArrayOutputStream;
     23 import java.io.IOException;
     24 import java.io.InputStream;
     25 import java.nio.ByteBuffer;
     26 import java.util.zip.GZIPInputStream;
     27 
     28 /**
     29  * A {@link BlockDevice} that lives entirely in heap memory. This is basically
     30  * a RAM disk. A {@code RamDisk} is always writable.
     31  *
     32  * @author Matthias Treydte &lt;waldheinz at gmail.com&gt;
     33  */
     34 public final class RamDisk implements BlockDevice {
     35 
     36     /**
     37      * The default sector size for {@code RamDisk}s.
     38      */
     39     public final static int DEFAULT_SECTOR_SIZE = 512;
     40 
     41     private final int sectorSize;
     42     private final ByteBuffer data;
     43     private final int size;
     44     private boolean closed;
     45 
     46     /**
     47      * Reads a GZIP compressed disk image from the specified input stream and
     48      * returns a {@code RamDisk} holding the decompressed image.
     49      *
     50      * @param in the stream to read the disk image from
     51      * @return the decompressed {@code RamDisk}
     52      * @throws IOException on read or decompression error
     53      */
     54     public static RamDisk readGzipped(InputStream in) throws IOException {
     55         final GZIPInputStream zis = new GZIPInputStream(in);
     56         ByteArrayOutputStream bos = new ByteArrayOutputStream();
     57 
     58         final byte[] buffer = new byte[4096];
     59 
     60         int read = zis.read(buffer);
     61         int total = 0;
     62 
     63         while (read >= 0) {
     64             total += read;
     65             bos.write(buffer, 0, read);
     66             read = zis.read(buffer);
     67         }
     68 
     69         if (total < DEFAULT_SECTOR_SIZE) throw new IOException(
     70                 "read only " + total + " bytes"); //NOI18N
     71 
     72         final ByteBuffer bb = ByteBuffer.wrap(bos.toByteArray(), 0, total);
     73         return new RamDisk(bb, DEFAULT_SECTOR_SIZE);
     74     }
     75 
     76     private RamDisk(ByteBuffer buffer, int sectorSize) {
     77         this.size = buffer.limit();
     78         this.sectorSize = sectorSize;
     79         this.data = buffer;
     80         this.closed = false;
     81     }
     82 
     83     /**
     84      * Creates a new instance of {@code RamDisk} of this specified
     85      * size and using the {@link #DEFAULT_SECTOR_SIZE}.
     86      *
     87      * @param size the size of the new block device
     88      */
     89     public RamDisk(int size) {
     90         this(size, DEFAULT_SECTOR_SIZE);
     91     }
     92 
     93     /**
     94      * Creates a new instance of {@code RamDisk} of this specified
     95      * size and sector size
     96      *
     97      * @param size the size of the new block device
     98      * @param sectorSize the sector size of the new block device
     99      */
    100     public RamDisk(int size, int sectorSize) {
    101         if (sectorSize < 1) throw new IllegalArgumentException(
    102                 "invalid sector size"); //NOI18N
    103 
    104         this.sectorSize = sectorSize;
    105         this.size = size;
    106         this.data = ByteBuffer.allocate(size);
    107     }
    108 
    109     @Override
    110     public long getSize() {
    111         checkClosed();
    112         return this.size;
    113     }
    114 
    115     @Override
    116     public void read(long devOffset, ByteBuffer dest) throws IOException {
    117         checkClosed();
    118 
    119         if (devOffset > getSize()){
    120             final StringBuilder sb = new StringBuilder();
    121             sb.append("read at ").append(devOffset);
    122             sb.append(" is off size (").append(getSize()).append(")");
    123 
    124             throw new IllegalArgumentException(sb.toString());
    125         }
    126 
    127         data.limit((int) (devOffset + dest.remaining()));
    128         data.position((int) devOffset);
    129 
    130         dest.put(data);
    131     }
    132 
    133     @Override
    134     public void write(long devOffset, ByteBuffer src) throws IOException {
    135         checkClosed();
    136 
    137         if (devOffset + src.remaining() > getSize()) throw new
    138                 IllegalArgumentException(
    139                 "offset=" + devOffset +
    140                 ", length=" + src.remaining() +
    141                 ", size=" + getSize());
    142 
    143         data.limit((int) (devOffset + src.remaining()));
    144         data.position((int) devOffset);
    145 
    146 
    147         data.put(src);
    148     }
    149 
    150     /**
    151      * Returns a slice of the {@code ByteBuffer} that is used by this
    152      * {@code RamDisk} as it's backing store. The returned buffer will be
    153      * live (reflecting any changes made through the
    154      * {@link #write(long, java.nio.ByteBuffer) method}, but read-only.
    155      *
    156      * @return a buffer holding the contents of this {@code RamDisk}
    157      */
    158     public ByteBuffer getBuffer() {
    159         return this.data.asReadOnlyBuffer();
    160     }
    161 
    162     @Override
    163     public void flush() throws IOException {
    164         checkClosed();
    165     }
    166 
    167     @Override
    168     public int getSectorSize() {
    169         checkClosed();
    170         return this.sectorSize;
    171     }
    172 
    173     @Override
    174     public void close() throws IOException {
    175         this.closed = true;
    176     }
    177 
    178     @Override
    179     public boolean isClosed() {
    180         return this.closed;
    181     }
    182 
    183     private void checkClosed() {
    184         if (closed) throw new IllegalStateException("device already closed");
    185     }
    186 
    187     /**
    188      * Returns always {@code false}, as a {@code RamDisk} is always writable.
    189      *
    190      * @return always {@code false}
    191      */
    192     @Override
    193     public boolean isReadOnly() {
    194         checkClosed();
    195 
    196         return false;
    197     }
    198 
    199 }
    200