package org.opendedup.collections; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel.MapMode; //import java.nio.file.Path; //import java.nio.file.Paths; import java.util.Arrays; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import org.apache.commons.io.FileUtils; import org.opendedup.collections.threads.SyncThread; import org.opendedup.sdfs.Main; public class LongByteArrayMap implements AbstractMap { private static Logger log = Logger.getLogger("sdfs"); //RandomAccessFile bdbf = null; MappedByteBuffer bdb = null; int arrayLength = 0; String filePath = null; private ReentrantLock hashlock = new ReentrantLock(); private boolean closed = true; public byte[] FREE = new byte[16]; public int iterPos = 0; public String fileParams = "rw"; public LongByteArrayMap(int arrayLength, String filePath) throws IOException { this.arrayLength = arrayLength; this.filePath = filePath; FREE = new byte[arrayLength]; Arrays.fill(FREE, (byte) 0); this.openFile(); new SyncThread(this); } public LongByteArrayMap(int arrayLength, String filePath, String fileParams) throws IOException { this.fileParams = fileParams; this.arrayLength = arrayLength; this.filePath = filePath; FREE = new byte[arrayLength]; Arrays.fill(FREE, (byte) 0); this.openFile(); new SyncThread(this); } public void iterInit() { this.iterPos = 0; } public long nextKey() throws IOException { File f = new File(this.filePath); long pos = (long) iterPos * (long) Main.CHUNK_LENGTH; long fLen = ((f.length() * (long) Main.CHUNK_LENGTH) / arrayLength); if (iterPos == 0) log.info("fLen = " + fLen); while (pos <= fLen) { try { try { this.hashlock.lock(); pos = (long) iterPos * (long) Main.CHUNK_LENGTH; iterPos++; } catch (Exception e1) { } finally { this.hashlock.unlock(); } byte[] b = this.get(pos); if (b != null) return pos; } catch (Exception e) { } finally { } } if (pos == fLen) log.info("length end " + pos); return -1; } public byte[] nextValue() throws IOException { int pos = iterPos * this.arrayLength; byte[] val = null; File f = new File(this.filePath); while (pos < f.length()) { val = new byte[this.arrayLength]; try { this.hashlock.lock(); pos = iterPos * this.arrayLength; iterPos++; this.bdb.position(pos); this.bdb.get(val); if (!Arrays.equals(FREE, val)) return val; } catch (Exception e) { } finally { this.hashlock.unlock(); } } return null; } /* * (non-Javadoc) * * @see com.annesam.collections.AbstractMap#isClosed() */ public boolean isClosed() { return this.closed; } private void openFile() throws IOException { if (this.closed) { this.hashlock.lock(); RandomAccessFile bdbf = null; try { File f = new File(filePath); boolean fileExists = f.exists(); if (!f.getParentFile().exists()) { f.getParentFile().mkdirs(); } bdbf = new RandomAccessFile(filePath, this.fileParams); log.finer("opening [" + this.filePath + "]"); if (!fileExists) bdbf.setLength(32768); // initiall allocate 32k if (this.fileParams.equalsIgnoreCase("r")) { this.bdb = bdbf.getChannel().map(MapMode.READ_ONLY, 0, bdbf.length()); } else { this.bdb = bdbf.getChannel().map(MapMode.READ_WRITE, 0, bdbf.length()); } this.bdb.load(); this.closed = false; } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException(e); } finally { try { bdbf.close(); }catch(Exception e) {} this.hashlock.unlock(); } } } private int calcMapFilePos(long fpos) throws IOException { long pos = (fpos / Main.CHUNK_LENGTH) * FREE.length; if (pos > Integer.MAX_VALUE) throw new IOException( "Requested file position " + fpos + " is larger than the maximum length of a file for this file system " + (Integer.MAX_VALUE * Main.CHUNK_LENGTH) / FREE.length); return (int) pos; } private void setMapFileLength(int len) throws IOException { RandomAccessFile bdbf = null; try { bdbf = new RandomAccessFile(filePath, this.fileParams); if (len > bdbf.length()) { bdbf.setLength(len); // initiall allocate 1 megabyte this.bdb = bdbf.getChannel().map(MapMode.READ_WRITE, 0, len); this.bdb.load(); } }catch(IOException e) { throw e; } finally { bdbf.close(); } } private int getMapFilePosition(long pos) throws IOException { int propLen = this.calcMapFilePos(pos); File f = new File(filePath); if ((propLen + 262144) > f.length()) this.setMapFileLength(propLen + 1048576); return propLen; } /* * (non-Javadoc) * * @see com.annesam.collections.AbstractMap#put(long, byte[]) */ public void put(long pos, byte[] data) throws IOException { if (this.isClosed()) { throw new IOException("hashtable [" + this.filePath + "] is close"); } if (data.length != this.arrayLength) throw new IOException("data length " + data.length + " does not equal " + this.arrayLength); this.hashlock.lock(); int fpos = 0; try { fpos = this.getMapFilePosition(pos); this.bdb.position(fpos); this.bdb.put(data); } catch (BufferOverflowException e) { log.severe("trying to write at " + fpos + " but file length is " + this.bdb.capacity()); throw e; } catch (Exception e) { throw new IOException(e); } finally { this.hashlock.unlock(); } } /* * (non-Javadoc) * * @see com.annesam.collections.AbstractMap#remove(long) */ public void remove(long pos) throws IOException { if (this.isClosed()) { throw new IOException("hashtable [" + this.filePath + "] is close"); } this.hashlock.lock(); try { int fpos = this.getMapFilePosition(pos); this.bdb.position(fpos); this.bdb.put(FREE); } catch (Exception e) { throw new IOException(e); } finally { this.hashlock.unlock(); } } /* * (non-Javadoc) * * @see com.annesam.collections.AbstractMap#get(long) */ public byte[] get(long pos) throws IOException { if (this.isClosed()) { throw new IOException("hashtable [" + this.filePath + "] is close"); } this.hashlock.lock(); int fpos = 0; try { fpos = this.getMapFilePosition(pos); byte[] b = new byte[this.arrayLength]; this.bdb.position(fpos); this.bdb.get(b); if (Arrays.equals(b, this.FREE)) return null; return b; } catch (BufferUnderflowException e) { return null; } catch (Exception e) { log.severe("error getting data at " + fpos); throw new IOException(e); } finally { this.hashlock.unlock(); } } /* * (non-Javadoc) * * @see com.annesam.collections.AbstractMap#sync() */ public void sync() throws IOException { this.bdb.force(); } /* * (non-Javadoc) * * @see com.annesam.collections.AbstractMap#vanish() */ public void vanish() throws IOException { this.hashlock.lock(); try { if (!this.isClosed()) this.close(); File f = new File(this.filePath); f.delete(); } catch (Exception e) { throw new IOException(e); } finally { this.hashlock.unlock(); } } public void copy(String destFilePath) throws IOException { this.hashlock.lock(); // Path p = null; File p = null; // Path dest = null; File dest = null; try { this.sync(); File f = new File(destFilePath); if (f.exists()) f.delete(); else f.getParentFile().mkdirs(); // p = Paths.get(this.filePath); p = new File(this.filePath); // dest = Paths.get(destFilePath); dest = new File(destFilePath); // p.copyTo(dest); FileUtils.copyFile(p, dest); } catch (Exception e) { throw new IOException(e); } finally { p = null; dest = null; this.hashlock.unlock(); } } /* * (non-Javadoc) * * @see com.annesam.collections.AbstractMap#close() */ public void close() { this.hashlock.lock(); try { if (!this.isClosed()) { this.closed = true; try { bdb.force(); bdb = null; } catch (Exception e) { e.printStackTrace(); } System.gc(); } } catch (Exception e) { e.printStackTrace(); } finally { this.hashlock.unlock(); } } }