1 package org.simantics.acorn;
3 import java.io.BufferedOutputStream;
4 import java.io.IOException;
5 import java.io.OutputStream;
6 import java.io.RandomAccessFile;
7 import java.nio.ByteBuffer;
8 import java.nio.channels.FileChannel;
9 import java.nio.channels.SeekableByteChannel;
10 import java.nio.file.Files;
11 import java.nio.file.OpenOption;
12 import java.nio.file.Path;
13 import java.nio.file.Paths;
14 import java.nio.file.StandardOpenOption;
15 import java.nio.file.attribute.FileAttribute;
16 import java.util.HashMap;
17 import java.util.HashSet;
21 import org.simantics.databoard.file.RuntimeIOException;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
27 private static final Logger LOGGER = LoggerFactory.getLogger(FileIO.class);
28 private static final FileAttribute<?>[] NO_ATTRIBUTES = new FileAttribute[0];
30 private static final Set<OpenOption> CREATE_OPTIONS = new HashSet<>(2);
31 private static final Set<OpenOption> APPEND_OPTIONS = new HashSet<>(1);
34 CREATE_OPTIONS.add(StandardOpenOption.WRITE);
35 CREATE_OPTIONS.add(StandardOpenOption.CREATE);
37 APPEND_OPTIONS.add(StandardOpenOption.APPEND);
41 private int writePosition = 0;
43 private FileIO(Path path) {
47 private static Map<Path, FileIO> map = new HashMap<Path, FileIO>();
49 public static FileIO get(Path path) {
51 FileIO existing = map.get(path);
52 if(existing == null) {
53 existing = new FileIO(path);
54 map.put(path, existing);
60 //private static final boolean TRACE_SWAP = false;
61 private static final boolean TRACE_PERF = false;
63 public synchronized int saveBytes(byte[] bytes, int length, boolean overwrite) throws IOException {
64 if(overwrite) writePosition = 0;
65 int result = writePosition;
66 long start = System.nanoTime();
67 Set<OpenOption> options = writePosition == 0 ? CREATE_OPTIONS : APPEND_OPTIONS;
69 ByteBuffer bb = ByteBuffer.wrap(bytes, 0, length);
70 try (FileChannel fc = FileChannel.open(path, options, NO_ATTRIBUTES)) {
73 writePosition += length;
75 long duration = System.nanoTime()-start;
76 double ds = 1e-9*duration;
77 LOGGER.info("Wrote " + bytes.length + " bytes @ " + 1e-6*bytes.length / ds + "MB/s");
80 } catch (Throwable t) {
81 throw new IOException("An error occured file saving bytes for file " + path.toAbsolutePath().toString(), t);
85 public synchronized byte[] readBytes(int offset, int length) throws IOException {
86 long start = System.nanoTime();
87 try (SeekableByteChannel channel = Files.newByteChannel(path)) {
88 channel.position(offset);
89 ByteBuffer buf = ByteBuffer.allocate(length);
91 while (read < length) {
92 read += channel.read(buf);
94 byte[] result = buf.array();
95 if (result.length != length)
96 LOGGER.info("result length does not match expected {} {} {}", this, result.length, length);
98 long duration = System.nanoTime() - start;
99 double ds = 1e-9 * duration;
100 LOGGER.info("Read " + result.length + " bytes @ " + 1e-6 * result.length / ds + "MB/s");
106 public static void syncPath(Path f) throws IOException {
107 // Does not seem to need 's' according to unit test in Windows
108 try (RandomAccessFile raf = new RandomAccessFile(f.toFile(), "rw")) {
113 static void uncheckedSyncPath(Path f) {
116 } catch (IOException e) {
117 throw new RuntimeIOException(e);
121 public static void main(String[] args) throws Exception {
123 byte[] buf = new byte[1024*1024];
125 long s = System.nanoTime();
127 Path test = Paths.get("e:/work/test.dat");
128 OutputStream fs = Files.newOutputStream(test);
129 OutputStream os = new BufferedOutputStream(fs, 128*1024);
131 for(int i=0;i<40;i++) {
141 if (LOGGER.isDebugEnabled()) {
142 long duration = System.nanoTime()-s;
143 LOGGER.info("Took " + 1e-6*duration + "ms.");