1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.mvstore.cache; |
7 | |
8 | import java.io.IOException; |
9 | import java.nio.ByteBuffer; |
10 | import java.nio.channels.FileChannel; |
11 | import java.nio.channels.FileLock; |
12 | import org.h2.store.fs.FileBase; |
13 | import org.h2.store.fs.FilePathWrapper; |
14 | |
15 | /** |
16 | * A file with a read cache. |
17 | */ |
18 | public class FilePathCache extends FilePathWrapper { |
19 | |
20 | public static FileChannel wrap(FileChannel f) { |
21 | return new FileCache(f); |
22 | } |
23 | |
24 | @Override |
25 | public FileChannel open(String mode) throws IOException { |
26 | return new FileCache(getBase().open(mode)); |
27 | } |
28 | |
29 | @Override |
30 | public String getScheme() { |
31 | return "cache"; |
32 | } |
33 | |
34 | /** |
35 | * A file with a read cache. |
36 | */ |
37 | public static class FileCache extends FileBase { |
38 | |
39 | private static final int CACHE_BLOCK_SIZE = 4 * 1024; |
40 | private final FileChannel base; |
41 | // 1 MB cache size |
42 | private final CacheLongKeyLIRS<ByteBuffer> cache = |
43 | new CacheLongKeyLIRS<ByteBuffer>(1024 * 1024); |
44 | |
45 | FileCache(FileChannel base) { |
46 | this.base = base; |
47 | } |
48 | |
49 | @Override |
50 | protected void implCloseChannel() throws IOException { |
51 | base.close(); |
52 | } |
53 | |
54 | @Override |
55 | public FileChannel position(long newPosition) throws IOException { |
56 | base.position(newPosition); |
57 | return this; |
58 | } |
59 | |
60 | @Override |
61 | public long position() throws IOException { |
62 | return base.position(); |
63 | } |
64 | |
65 | @Override |
66 | public int read(ByteBuffer dst) throws IOException { |
67 | return base.read(dst); |
68 | } |
69 | |
70 | @Override |
71 | public int read(ByteBuffer dst, long position) throws IOException { |
72 | long cachePos = getCachePos(position); |
73 | int off = (int) (position - cachePos); |
74 | int len = CACHE_BLOCK_SIZE - off; |
75 | len = Math.min(len, dst.remaining()); |
76 | ByteBuffer buff = cache.get(cachePos); |
77 | if (buff == null) { |
78 | buff = ByteBuffer.allocate(CACHE_BLOCK_SIZE); |
79 | long pos = cachePos; |
80 | while (true) { |
81 | int read = base.read(buff, pos); |
82 | if (read <= 0) { |
83 | break; |
84 | } |
85 | if (buff.remaining() == 0) { |
86 | break; |
87 | } |
88 | pos += read; |
89 | } |
90 | int read = buff.position(); |
91 | if (read == CACHE_BLOCK_SIZE) { |
92 | cache.put(cachePos, buff, CACHE_BLOCK_SIZE); |
93 | } else { |
94 | if (read <= 0) { |
95 | return -1; |
96 | } |
97 | len = Math.min(len, read - off); |
98 | } |
99 | } |
100 | dst.put(buff.array(), off, len); |
101 | return len == 0 ? -1 : len; |
102 | } |
103 | |
104 | private static long getCachePos(long pos) { |
105 | return (pos / CACHE_BLOCK_SIZE) * CACHE_BLOCK_SIZE; |
106 | } |
107 | |
108 | @Override |
109 | public long size() throws IOException { |
110 | return base.size(); |
111 | } |
112 | |
113 | @Override |
114 | public FileChannel truncate(long newSize) throws IOException { |
115 | cache.clear(); |
116 | base.truncate(newSize); |
117 | return this; |
118 | } |
119 | |
120 | @Override |
121 | public int write(ByteBuffer src, long position) throws IOException { |
122 | clearCache(src, position); |
123 | return base.write(src, position); |
124 | } |
125 | |
126 | @Override |
127 | public int write(ByteBuffer src) throws IOException { |
128 | clearCache(src, position()); |
129 | return base.write(src); |
130 | } |
131 | |
132 | private void clearCache(ByteBuffer src, long position) { |
133 | if (cache.size() > 0) { |
134 | int len = src.remaining(); |
135 | long p = getCachePos(position); |
136 | while (len > 0) { |
137 | cache.remove(p); |
138 | p += CACHE_BLOCK_SIZE; |
139 | len -= CACHE_BLOCK_SIZE; |
140 | } |
141 | } |
142 | } |
143 | |
144 | @Override |
145 | public void force(boolean metaData) throws IOException { |
146 | base.force(metaData); |
147 | } |
148 | |
149 | @Override |
150 | public FileLock tryLock(long position, long size, boolean shared) |
151 | throws IOException { |
152 | return base.tryLock(position, size, shared); |
153 | } |
154 | |
155 | @Override |
156 | public String toString() { |
157 | return "cache:" + base.toString(); |
158 | } |
159 | |
160 | } |
161 | |
162 | } |