/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * 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 org.jnode.fs.ramfs; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; import org.jnode.fs.FSAccessRights; import org.jnode.fs.FSDirectory; import org.jnode.fs.FSEntry; import org.jnode.fs.FSFile; import org.jnode.fs.FileSystem; import org.jnode.fs.FileSystemFullException; /** * A File implementation in the system RAM * * @author peda * @author Levente S\u00e1ntha */ public class RAMFile implements FSEntry, FSFile { private RAMFileSystem fileSystem; private RAMDirectory parent; private String filename; private BufferList bufferList; private long created; private long lastModified; private long lastAccessed; private FSAccessRights accessRights; private boolean isValid = true; /** * Constructor for a new RAMFile * * @param parent * @param filename */ public RAMFile(RAMDirectory parent, String filename) { this.parent = parent; this.filename = filename; this.created = this.lastModified = this.lastAccessed = System.currentTimeMillis(); // TODO accessRights bufferList = new BufferList(); fileSystem = (RAMFileSystem) parent.getFileSystem(); fileSystem.addSummmedBufferSize(bufferList.capacity()); } private void enlargeBuffer() throws FileSystemFullException { int oldCapacity = bufferList.capacity(); if (oldCapacity > fileSystem.getFreeSpace()) throw new FileSystemFullException("RAMFileSystem reached maxSize"); bufferList.enlarge(); int newCapacity = bufferList.capacity(); // update fileSystem values fileSystem.addSummmedBufferSize(newCapacity - oldCapacity); } private void shrinkBuffer() { int oldCapacity = bufferList.capacity(); bufferList.shrink(); int newCapacity = bufferList.capacity(); // update fileSystem counter fileSystem.addSummmedBufferSize(newCapacity - oldCapacity); } @Override public String getId() { return getName(); } /** * (non-Javadoc) * * @see org.jnode.fs.FSEntry#getName() */ public String getName() { return filename; } /** * (non-Javadoc) * * @see org.jnode.fs.FSEntry#getParent() */ public FSDirectory getParent() { return parent; } public long getCreated() throws IOException { return created; } public long getLastModified() throws IOException { return lastModified; } public long getLastAccessed() throws IOException { return lastAccessed; } /** * (non-Javadoc) * * @see org.jnode.fs.FSEntry#isFile() */ public boolean isFile() { return true; } /** * (non-Javadoc) * * @see org.jnode.fs.FSEntry#isDirectory() */ public boolean isDirectory() { return false; } /** * (non-Javadoc) * * @see org.jnode.fs.FSEntry#setName(java.lang.String) */ public void setName(String newName) throws IOException { // TODO check for special chars / normalize name filename = newName; setLastModified(System.currentTimeMillis()); } public void setCreated(long created) throws IOException { this.created = created; } public void setLastModified(long lastModified) throws IOException { this.lastModified = lastModified; } public void setLastAccessed(long lastAccessed) throws IOException { this.lastAccessed = lastAccessed; } /** * (non-Javadoc) * * @see org.jnode.fs.FSEntry#getFile() */ public FSFile getFile() throws IOException { return this; } /** * (non-Javadoc) * * @see org.jnode.fs.FSEntry#getDirectory() */ public FSDirectory getDirectory() throws IOException { throw new IOException("Not a directory"); } /** * (non-Javadoc) * * @see org.jnode.fs.FSEntry#getAccessRights() */ public FSAccessRights getAccessRights() throws IOException { return accessRights; } /** * (non-Javadoc) * * @see org.jnode.fs.FSEntry#isDirty() */ public boolean isDirty() throws IOException { return false; } /** * (non-Javadoc) * * @see org.jnode.fs.FSObject#isValid() */ public boolean isValid() { return isValid; } /** * (non-Javadoc) * * @see org.jnode.fs.FSObject#getFileSystem() */ public FileSystem<?> getFileSystem() { return fileSystem; } /** * (non-Javadoc) * * @see org.jnode.fs.FSFile#getLength() */ public long getLength() { return bufferList.limit(); } /** * (non-Javadoc) * * @see org.jnode.fs.FSFile#setLength(long) */ public void setLength(long length) throws IOException { if (length > Integer.MAX_VALUE) throw new IOException("Filesize too large"); while (bufferList.capacity() < length) enlargeBuffer(); long toEnlarge = length - bufferList.limit(); while (length < bufferList.capacity() / 2) shrinkBuffer(); bufferList.limit((int) length); // update fileSystem counters fileSystem.addSummedFileSize(toEnlarge); setLastModified(System.currentTimeMillis()); } /** * (non-Javadoc) * * @see org.jnode.fs.FSFile#read(long, java.nio.ByteBuffer) */ public void read(long fileOffset, ByteBuffer dest) throws IOException { long currentSize = bufferList.limit(); long toRead = dest.limit(); if (fileOffset + toRead > currentSize) throw new IOException("FileOffest outside file"); bufferList.position((int) fileOffset); bufferList.get(dest.array(), 0, dest.limit()); } /** * (non-Javadoc) * * @see org.jnode.fs.FSFile#write(long, java.nio.ByteBuffer) */ public void write(long fileOffset, ByteBuffer src) throws IOException { long currentSize = bufferList.limit(); long toWrite = src.limit(); if (fileOffset + toWrite >= currentSize) setLength(fileOffset + toWrite); bufferList.position((int) fileOffset); bufferList.put(src); setLastModified(System.currentTimeMillis()); } /** * (non-Javadoc) * * @see org.jnode.fs.FSFile#flush() */ public void flush() throws IOException { // nothing todo here } void remove() throws IOException { long capacity = bufferList.capacity(); long filesize = getLength(); this.parent = null; this.bufferList = null; fileSystem.addSummedFileSize(-filesize); fileSystem.addSummmedBufferSize(-capacity); } /** * A resizing Buffer-like structure combining a set of NIO Buffers into one entity. * * @author Levente S\u00e1ntha */ private static class BufferList { private static final int MAX_BUFFER_SIZE = 12 * 1024 * 1024; private final ArrayList<ByteBuffer> bufferList; BufferList() { bufferList = new ArrayList<ByteBuffer>(); ByteBuffer buffer = ByteBuffer.allocate(128); buffer.limit(0); bufferList.add(buffer); } private void enlarge() { int oldCapacity = capacity(); if (bufferList.size() == 1) { final ByteBuffer oldBuffer = bufferList.get(0); final int newCapacity = oldCapacity * 2; bufferList.clear(); if (newCapacity > MAX_BUFFER_SIZE) { final ByteBuffer newBuffer = ByteBuffer.allocate(MAX_BUFFER_SIZE); oldBuffer.position(0); newBuffer.put(oldBuffer); newBuffer.position(0); bufferList.add(newBuffer); bufferList.add(ByteBuffer.allocate(newCapacity - MAX_BUFFER_SIZE)); } else { ByteBuffer buffer2 = ByteBuffer.allocate(newCapacity); oldBuffer.position(0); buffer2.put(oldBuffer); buffer2.position(0); bufferList.add(buffer2); } } else { bufferList.add(ByteBuffer.allocate(MAX_BUFFER_SIZE)); } } private void shrink() { final int oldCapacity = capacity(); if (bufferList.size() == 1) { final ByteBuffer oldBuffer = bufferList.get(0); final int newCapacity = oldCapacity / 2; bufferList.clear(); final ByteBuffer newBuffer = ByteBuffer.allocate(newCapacity); oldBuffer.position(0); oldBuffer.limit(newCapacity); newBuffer.put(oldBuffer); newBuffer.position(0); bufferList.add(newBuffer); } else { bufferList.remove(bufferList.size() - 1); } } private int capacity() { int capacity = 0; for (ByteBuffer buffer : bufferList) { capacity += buffer.capacity(); } return capacity; } private int limit() { int limit = 0; for (ByteBuffer buffer : bufferList) { limit += buffer.limit(); } return limit; } private void limit(int limit) { for (ByteBuffer buffer : bufferList) { if (limit < 0) { buffer.limit(0); } else { int capacity = buffer.capacity(); if (limit <= capacity) { buffer.limit(limit); } else { buffer.limit(capacity); } limit -= capacity; } } } private int position() { int position = 0; for (ByteBuffer buffer : bufferList) { position += buffer.position(); } return position; } public void position(int position) { for (ByteBuffer buffer : bufferList) { if (position < 0) { buffer.position(0); } else { int limit = buffer.limit(); if (position <= limit) { buffer.position(position); } else { buffer.position(limit); } position -= limit; } } } public int remaining() { int remaining = 0; for (ByteBuffer buffer : bufferList) { remaining += buffer.remaining(); } return remaining; } public void get(byte[] array, int offset, int length) { if ((offset | length | (offset + length) | (array.length - (offset + length))) < 0) throw new IndexOutOfBoundsException(); if (length > remaining()) throw new BufferUnderflowException(); for (ByteBuffer buffer : bufferList) { int remaining = buffer.remaining(); if (remaining > 0) { if (length > remaining) { buffer.get(array, offset, remaining); offset += remaining; length -= remaining; } else { buffer.get(array, offset, length); return; } } } } public void put(ByteBuffer src) { int length = src.remaining(); if (length > remaining()) throw new BufferOverflowException(); for (ByteBuffer buffer : bufferList) { int remaining = buffer.remaining(); if (remaining > 0) { if (length > remaining) { src.limit(src.position() + remaining); buffer.put(src); length -= remaining; } else { src.limit(src.position() + length); buffer.put(src); return; } } } } } }