]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.acorn/src/org/simantics/acorn/FileIO.java
8f3009afab3f82b84c23156775efd901a925c1ae
[simantics/platform.git] / bundles / org.simantics.acorn / src / org / simantics / acorn / FileIO.java
1 package org.simantics.acorn;
2
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;
18 import java.util.Map;
19 import java.util.Set;
20
21 import org.simantics.databoard.file.RuntimeIOException;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 public class FileIO {
26
27     private static final Logger LOGGER = LoggerFactory.getLogger(FileIO.class);
28     private static final FileAttribute<?>[] NO_ATTRIBUTES = new FileAttribute[0];
29     
30     private static final Set<OpenOption> CREATE_OPTIONS = new HashSet<>(2);
31     private static final Set<OpenOption> APPEND_OPTIONS = new HashSet<>(1);
32     
33     static {
34         CREATE_OPTIONS.add(StandardOpenOption.WRITE);
35         CREATE_OPTIONS.add(StandardOpenOption.CREATE);
36         
37         APPEND_OPTIONS.add(StandardOpenOption.APPEND);
38     }
39     
40         private Path path;
41         private int writePosition = 0;
42
43         private FileIO(Path path) {
44                 this.path = path;
45         }
46         
47         private static Map<Path, FileIO> map = new HashMap<Path, FileIO>();
48         
49         public static FileIO get(Path path) {
50                 synchronized(map) {
51                         FileIO existing = map.get(path);
52                         if(existing == null) {
53                                 existing = new FileIO(path);
54                                 map.put(path, existing);
55                         }
56                         return existing;
57                 }
58         }
59         
60         //private static final boolean TRACE_SWAP = false;
61         private static final boolean TRACE_PERF = false;
62
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;
68                 
69                 ByteBuffer bb = ByteBuffer.wrap(bytes, 0, length);
70                 try (FileChannel fc = FileChannel.open(path, options, NO_ATTRIBUTES)) {
71             fc.write(bb);
72             
73             writePosition += length;
74             if(TRACE_PERF) {
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");
78             }
79             return result;
80                 } catch (Throwable t) {
81                     throw new IOException("An error occured file saving bytes for file " + path.toAbsolutePath().toString(), t);
82                 }
83         }
84
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);
90             int read = 0;
91             while (read < length) {
92                 read += channel.read(buf);
93             }
94             byte[] result = buf.array();
95             if (result.length != length)
96                 LOGGER.info("result length does not match expected {} {} {}", this, result.length, length);
97             if (TRACE_PERF) {
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");
101             }
102             return result;
103         }
104     }
105
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")) {
109                         raf.getFD().sync();
110                 }
111         }
112
113         static void uncheckedSyncPath(Path f) {
114                 try {
115                         syncPath(f);
116                 } catch (IOException e) {
117                         throw new RuntimeIOException(e);
118                 }
119         }
120
121         public static void main(String[] args) throws Exception {
122
123                 byte[] buf = new byte[1024*1024];
124                 
125                 long s = System.nanoTime();
126                 
127                 Path test = Paths.get("e:/work/test.dat");
128                 OutputStream fs = Files.newOutputStream(test);
129                 OutputStream os = new BufferedOutputStream(fs, 128*1024);
130                 
131                 for(int i=0;i<40;i++) {
132                         os.write(buf);
133                 }
134                 
135                 os.flush();
136                 //fs.getFD().sync();
137                 os.close();
138                 
139                 syncPath(test);
140                 
141                 if (LOGGER.isDebugEnabled()) {
142                 long duration = System.nanoTime()-s;
143                 LOGGER.info("Took " + 1e-6*duration + "ms.");
144                 }
145         }
146         
147 }