/** -*- tab-width: 4 -*- * This file is part of Erjang - A JVM-based Erlang VM * * Copyright (c) 2010 by Trifork * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ package erjang.driver.ram_file; import erjang.EHandle; import erjang.EObject; import erjang.EPID; import erjang.EString; import erjang.EBinary; import erjang.ERef; import erjang.NotImplemented; import erjang.driver.EDriverInstance; import erjang.driver.EAsync; import erjang.driver.IO; import erjang.driver.efile.Posix; import java.io.IOException; import java.io.ByteArrayOutputStream; import java.util.logging.Level; import java.util.zip.Inflater; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import kilim.Pausable; import static java.util.zip.GZIPInputStream.GZIP_MAGIC; import static erjang.driver.efile.EFile.FILE_OPEN; import static erjang.driver.efile.EFile.FILE_READ; import static erjang.driver.efile.EFile.FILE_LSEEK; import static erjang.driver.efile.EFile.FILE_WRITE; import static erjang.driver.efile.EFile.FILE_FSYNC; import static erjang.driver.efile.EFile.FILE_TRUNCATE; import static erjang.driver.efile.EFile.FILE_RESP_OK; import static erjang.driver.efile.EFile.FILE_RESP_ERROR; import static erjang.driver.efile.EFile.FILE_RESP_DATA; import static erjang.driver.efile.EFile.FILE_RESP_NUMBER; import static erjang.driver.efile.EFile.FILE_RESP_INFO; import static erjang.driver.efile.EFile.EFILE_MODE_READ; import static erjang.driver.efile.EFile.EFILE_MODE_WRITE; import static erjang.driver.efile.EFile.EFILE_MODE_READ_WRITE; public class RamFile extends EDriverInstance { /* Commands like file interface's */ public static final int FILE_PREAD = 17; public static final int FILE_PWRITE = 18; public static final int RAM_FILE_FDATASYNC = 19; /* Special ram_file commands */ public static final int RAM_FILE_GET = 30; public static final int RAM_FILE_SET = 31; public static final int RAM_FILE_GET_CLOSE = 32; public static final int RAM_FILE_COMPRESS = 33; public static final int RAM_FILE_UNCOMPRESS = 34; public static final int RAM_FILE_UUENCODE = 35; public static final int RAM_FILE_UUDECODE = 36; public static final int RAM_FILE_SIZE = 37; public static final int RAM_FILE_ADVISE = 38; private static final byte[] FILE_RESP_NUMBER_HEADER = new byte[]{ FILE_RESP_NUMBER }; private ByteBuffer contents; private int flags; RamFile(EString command, Driver driver) { super(driver); } @Override protected void outputv(EHandle caller, ByteBuffer[] ev) throws IOException, Pausable { if (ev.length == 0 || ev[0].remaining() == 0) { reply_posix_error(Posix.EINVAL); return; } byte command = ev[0].get(); //TODO: Handle more commands here, without flattening. switch (command) { // case FILE_CLOSE: { // if (ev.length > 1 && ev[0].hasRemaining()) { // reply_posix_error(Posix.EINVAL); // return; // } // ByteBuffer last = ev[ev.length - 1]; // throw new NotImplemented(); // } default: // undo the get() we did to find command ev[0].position(ev[0].position() - 1); output(caller, flatten(ev)); } // switch } protected void output(EHandle caller, ByteBuffer data) throws IOException, Pausable { byte cmd = data.get(); switch (cmd) { case FILE_OPEN: { if (data.remaining() < 4) { reply_posix_error(Posix.EINVAL); return; } flags = data.getInt(); try { contents = ByteBuffer.allocate(data.remaining()).put(data); } catch (OutOfMemoryError e) { reply_posix_error(Posix.ENOMEM); return; } reply_number(0); } break; case RAM_FILE_UNCOMPRESS: { if (data.hasRemaining()) { reply_posix_error(Posix.EINVAL); return; } // Uncompress only when a known header is there: data.mark(); boolean is_gzip = (data.remaining() >= 2 && data.getShort() == GZIP_MAGIC); data.reset(); if (is_gzip) { final Inflater inflater = new Inflater(true); inflater.setInput(contents.array(), 0, contents.limit()); byte[] buf = new byte[4096]; ByteArrayOutputStream baos = new ByteArrayOutputStream(contents.limit()); try { while (! inflater.finished()) { int inflated = inflater.inflate(buf, 0, buf.length); baos.write(buf, 0, inflated); } } catch (Exception e) { log.severe("DB| inflation error: "+e.getMessage()); log.log(Level.FINE, "details: ", e); reply_posix_error(Posix.EINVAL); return; } contents = ByteBuffer.wrap(baos.toByteArray()); } reply_number(contents.limit()); } break; case RAM_FILE_GET: { if (data.hasRemaining()) { reply_posix_error(Posix.EINVAL); return; } reply_buf(contents); } break; default: throw new NotImplemented("ram_file output command:" + ((int) cmd) + " " + EBinary.make(data)); } // switch } public void reply_ok() throws Pausable { ByteBuffer header = ByteBuffer.allocate(1); header.put(FILE_RESP_OK); driver_output2(header, null); } public void reply_number(int val) throws Pausable { ByteBuffer reply = ByteBuffer.allocate(1 + 4); reply.put(FILE_RESP_NUMBER); reply.putInt(val); driver_output2(reply, null); } void reply_buf(ByteBuffer buf) throws Pausable { ByteBuffer header = ByteBuffer.allocate(1 + 4); header.put(FILE_RESP_DATA); header.putInt(buf.position()); driver_output2(header, buf); } /** * @param reply_Uint * @throws Pausable */ public void reply_Uint(int value) throws Pausable { ByteBuffer response = ByteBuffer.allocate(4); response.putInt(value); driver_output2(response, null); } /** * @param error * @throws Pausable */ public void reply_posix_error(int posix_errno) throws Pausable { ByteBuffer response = ByteBuffer.allocate(256); response.put(FILE_RESP_ERROR); String err = Posix.errno_id(posix_errno); IO.putstr(response, err, false); driver_output2(response, null); } @Override protected EObject call(EPID caller, int command, EObject data) throws Pausable { // TODO return null; } @Override protected void flush() throws Pausable { // TODO } @Override public void processExit(ERef monitor) throws Pausable { // TODO } @Override protected void readyInput(SelectableChannel ch) throws Pausable { // TODO Auto-generated method stub } @Override protected void readyOutput(SelectableChannel evt) throws Pausable { // TODO Auto-generated method stub } @Override protected void timeout() throws Pausable { // TODO } @Override protected void readyAsync(EAsync data) throws Pausable { // TODO } }