package org.simantics.acorn; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileAttribute; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.simantics.databoard.file.RuntimeIOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FileIO { private static final Logger LOGGER = LoggerFactory.getLogger(FileIO.class); private static final FileAttribute[] NO_ATTRIBUTES = {}; private static final Set CREATE_OPTIONS = new HashSet<>(2); private static final Set APPEND_OPTIONS = new HashSet<>(1); static { CREATE_OPTIONS.add(StandardOpenOption.WRITE); CREATE_OPTIONS.add(StandardOpenOption.CREATE); APPEND_OPTIONS.add(StandardOpenOption.APPEND); } private Path path; private int writePosition = 0; public FileIO(Path path) { this.path = path; } //private static final boolean TRACE_SWAP = false; private static final boolean TRACE_PERF = false; public synchronized int saveBytes(byte[] bytes, int length, boolean overwrite) throws IOException { if(overwrite) writePosition = 0; int result = writePosition; long start = System.nanoTime(); Set options = writePosition == 0 ? CREATE_OPTIONS : APPEND_OPTIONS; ByteBuffer bb = ByteBuffer.wrap(bytes, 0, length); try (FileChannel fc = FileChannel.open(path, options, NO_ATTRIBUTES)) { fc.write(bb); writePosition += length; if(TRACE_PERF) { long duration = System.nanoTime()-start; double ds = 1e-9*duration; LOGGER.info("Wrote " + bytes.length + " bytes @ " + 1e-6*bytes.length / ds + "MB/s"); } return result; } catch (Throwable t) { throw new IOException("An error occured file saving bytes for file " + path.toAbsolutePath().toString(), t); } } public synchronized byte[] readBytes(int offset, int length) throws IOException { long start = System.nanoTime(); try (SeekableByteChannel channel = Files.newByteChannel(path)) { channel.position(offset); ByteBuffer buf = ByteBuffer.allocate(length); int read = 0; while (read < length) { read += channel.read(buf); } byte[] result = buf.array(); if (result.length != length) LOGGER.info("result length does not match expected {} {} {}", this, result.length, length); if (TRACE_PERF) { long duration = System.nanoTime() - start; double ds = 1e-9 * duration; LOGGER.info("Read " + result.length + " bytes @ " + 1e-6 * result.length / ds + "MB/s"); } return result; } } public static void syncPath(Path f) throws IOException { // Does not seem to need 's' according to unit test in Windows try (RandomAccessFile raf = new RandomAccessFile(f.toFile(), "rw")) { raf.getFD().sync(); } } static void uncheckedSyncPath(Path f) { try { syncPath(f); } catch (IOException e) { throw new RuntimeIOException(e); } } public static void main(String[] args) throws Exception { byte[] buf = new byte[1024*1024]; long s = System.nanoTime(); Path test = Paths.get("e:/work/test.dat"); OutputStream fs = Files.newOutputStream(test); OutputStream os = new BufferedOutputStream(fs, 128*1024); for(int i=0;i<40;i++) { os.write(buf); } os.flush(); //fs.getFD().sync(); os.close(); syncPath(test); if (LOGGER.isDebugEnabled()) { long duration = System.nanoTime()-s; LOGGER.info("Took " + 1e-6*duration + "ms."); } } }