Mercurial > hg4j
comparison src/com/tmate/hgkit/fs/DataAccessProvider.java @ 10:382cfe9463db
Dirstate parsing. DataAccess refactored to allow reuse and control over constants
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Sat, 25 Dec 2010 21:50:12 +0100 |
parents | |
children | 6f9aca1a97be |
comparison
equal
deleted
inserted
replaced
9:d6d2a630f4a6 | 10:382cfe9463db |
---|---|
1 /* | |
2 * Copyright (c) 2010 Artem Tikhomirov | |
3 */ | |
4 package com.tmate.hgkit.fs; | |
5 | |
6 import java.io.File; | |
7 import java.io.FileInputStream; | |
8 import java.io.IOException; | |
9 import java.nio.ByteBuffer; | |
10 import java.nio.MappedByteBuffer; | |
11 import java.nio.channels.FileChannel; | |
12 | |
13 /** | |
14 * | |
15 * @author artem | |
16 */ | |
17 public class DataAccessProvider { | |
18 | |
19 private final int mapioMagicBoundary; | |
20 private final int bufferSize; | |
21 | |
22 public DataAccessProvider() { | |
23 mapioMagicBoundary = 100 * 1024; | |
24 bufferSize = 8 * 1024; | |
25 } | |
26 | |
27 public DataAccess create(File f) { | |
28 if (!f.exists()) { | |
29 return new DataAccess(); | |
30 } | |
31 try { | |
32 FileChannel fc = new FileInputStream(f).getChannel(); | |
33 if (fc.size() > mapioMagicBoundary) { | |
34 return new MemoryMapFileAccess(fc, fc.size()); | |
35 } else { | |
36 // XXX once implementation is more or less stable, | |
37 // may want to try ByteBuffer.allocateDirect() to see | |
38 // if there's any performance gain. | |
39 boolean useDirectBuffer = false; | |
40 return new FileAccess(fc, fc.size(), bufferSize, useDirectBuffer); | |
41 } | |
42 } catch (IOException ex) { | |
43 // unlikely to happen, we've made sure file exists. | |
44 ex.printStackTrace(); // FIXME log error | |
45 } | |
46 return new DataAccess(); // non-null, empty. | |
47 } | |
48 | |
49 // DOESN'T WORK YET | |
50 private static class MemoryMapFileAccess extends DataAccess { | |
51 private FileChannel fileChannel; | |
52 private final long size; | |
53 private long position = 0; | |
54 | |
55 public MemoryMapFileAccess(FileChannel fc, long channelSize) { | |
56 fileChannel = fc; | |
57 size = channelSize; | |
58 } | |
59 | |
60 @Override | |
61 public void seek(long offset) { | |
62 position = offset; | |
63 } | |
64 | |
65 @Override | |
66 public void skip(int bytes) throws IOException { | |
67 position += bytes; | |
68 } | |
69 | |
70 private boolean fill() throws IOException { | |
71 final int BUFFER_SIZE = 8 * 1024; | |
72 long left = size - position; | |
73 MappedByteBuffer rv = fileChannel.map(FileChannel.MapMode.READ_ONLY, position, left < BUFFER_SIZE ? left : BUFFER_SIZE); | |
74 position += rv.capacity(); | |
75 return rv.hasRemaining(); | |
76 } | |
77 | |
78 @Override | |
79 public void done() { | |
80 if (fileChannel != null) { | |
81 try { | |
82 fileChannel.close(); | |
83 } catch (IOException ex) { | |
84 ex.printStackTrace(); // log debug | |
85 } | |
86 fileChannel = null; | |
87 } | |
88 } | |
89 } | |
90 | |
91 // (almost) regular file access - FileChannel and buffers. | |
92 private static class FileAccess extends DataAccess { | |
93 private FileChannel fileChannel; | |
94 private final long size; | |
95 private ByteBuffer buffer; | |
96 private long bufferStartInFile = 0; // offset of this.buffer in the file. | |
97 | |
98 public FileAccess(FileChannel fc, long channelSize, int bufferSizeHint, boolean useDirect) { | |
99 fileChannel = fc; | |
100 size = channelSize; | |
101 final int capacity = size < bufferSizeHint ? (int) size : bufferSizeHint; | |
102 buffer = useDirect ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity); | |
103 buffer.flip(); // or .limit(0) to indicate it's empty | |
104 } | |
105 | |
106 @Override | |
107 public boolean isEmpty() { | |
108 return bufferStartInFile + buffer.position() >= size; | |
109 } | |
110 | |
111 @Override | |
112 public void seek(long offset) throws IOException { | |
113 if (offset < bufferStartInFile + buffer.limit() && offset >= bufferStartInFile) { | |
114 buffer.position((int) (offset - bufferStartInFile)); | |
115 } else { | |
116 // out of current buffer, invalidate it (force re-read) | |
117 // XXX or ever re-read it right away? | |
118 bufferStartInFile = offset; | |
119 buffer.clear(); | |
120 buffer.limit(0); // or .flip() to indicate we switch to reading | |
121 fileChannel.position(offset); | |
122 } | |
123 } | |
124 | |
125 @Override | |
126 public void skip(int bytes) throws IOException { | |
127 final int newPos = buffer.position() + bytes; | |
128 if (newPos >= 0 && newPos < buffer.limit()) { | |
129 // no need to move file pointer, just rewind/seek buffer | |
130 buffer.position(newPos); | |
131 } else { | |
132 // | |
133 seek(fileChannel.position()+ bytes); | |
134 } | |
135 } | |
136 | |
137 private boolean fill() throws IOException { | |
138 if (!buffer.hasRemaining()) { | |
139 bufferStartInFile += buffer.limit(); | |
140 buffer.clear(); | |
141 if (bufferStartInFile < size) { // just in case there'd be any exception on EOF, not -1 | |
142 fileChannel.read(buffer); | |
143 // may return -1 when EOF, but empty will reflect this, hence no explicit support here | |
144 } | |
145 buffer.flip(); | |
146 } | |
147 return buffer.hasRemaining(); | |
148 } | |
149 | |
150 @Override | |
151 public void readBytes(byte[] buf, int offset, int length) throws IOException { | |
152 final int tail = buffer.remaining(); | |
153 if (tail >= length) { | |
154 buffer.get(buf, offset, length); | |
155 } else { | |
156 buffer.get(buf, offset, tail); | |
157 if (fill()) { | |
158 buffer.get(buf, offset + tail, length - tail); | |
159 } else { | |
160 throw new IOException(); // shall not happen provided stream contains expected data and no attempts to read past nonEmpty() == false are made. | |
161 } | |
162 } | |
163 } | |
164 | |
165 @Override | |
166 public byte readByte() throws IOException { | |
167 if (buffer.hasRemaining()) { | |
168 return buffer.get(); | |
169 } | |
170 if (fill()) { | |
171 return buffer.get(); | |
172 } | |
173 throw new IOException(); | |
174 } | |
175 | |
176 @Override | |
177 public void done() { | |
178 if (buffer != null) { | |
179 buffer = null; | |
180 } | |
181 if (fileChannel != null) { | |
182 try { | |
183 fileChannel.close(); | |
184 } catch (IOException ex) { | |
185 ex.printStackTrace(); // log debug | |
186 } | |
187 fileChannel = null; | |
188 } | |
189 } | |
190 } | |
191 } |