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 }