Mercurial > jhg
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 } |
