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; |
7 | |
8 | import java.nio.ByteBuffer; |
9 | import java.util.Iterator; |
10 | import java.util.Map.Entry; |
11 | import java.util.TreeMap; |
12 | |
13 | /** |
14 | * A storage mechanism that "persists" data in the off-heap area of the main |
15 | * memory. |
16 | */ |
17 | public class OffHeapStore extends FileStore { |
18 | |
19 | private final TreeMap<Long, ByteBuffer> memory = |
20 | new TreeMap<Long, ByteBuffer>(); |
21 | |
22 | @Override |
23 | public void open(String fileName, boolean readOnly, char[] encryptionKey) { |
24 | memory.clear(); |
25 | } |
26 | |
27 | @Override |
28 | public String toString() { |
29 | return memory.toString(); |
30 | } |
31 | |
32 | @Override |
33 | public ByteBuffer readFully(long pos, int len) { |
34 | Entry<Long, ByteBuffer> memEntry = memory.floorEntry(pos); |
35 | if (memEntry == null) { |
36 | throw DataUtils.newIllegalStateException( |
37 | DataUtils.ERROR_READING_FAILED, |
38 | "Could not read from position {0}", pos); |
39 | } |
40 | readCount++; |
41 | readBytes += len; |
42 | ByteBuffer buff = memEntry.getValue(); |
43 | ByteBuffer read = buff.duplicate(); |
44 | int offset = (int) (pos - memEntry.getKey()); |
45 | read.position(offset); |
46 | read.limit(len + offset); |
47 | return read.slice(); |
48 | } |
49 | |
50 | @Override |
51 | public void free(long pos, int length) { |
52 | freeSpace.free(pos, length); |
53 | ByteBuffer buff = memory.remove(pos); |
54 | if (buff == null) { |
55 | // nothing was written (just allocated) |
56 | } else if (buff.remaining() != length) { |
57 | throw DataUtils.newIllegalStateException( |
58 | DataUtils.ERROR_READING_FAILED, |
59 | "Partial remove is not supported at position {0}", pos); |
60 | } |
61 | } |
62 | |
63 | @Override |
64 | public void writeFully(long pos, ByteBuffer src) { |
65 | fileSize = Math.max(fileSize, pos + src.remaining()); |
66 | Entry<Long, ByteBuffer> mem = memory.floorEntry(pos); |
67 | if (mem == null) { |
68 | // not found: create a new entry |
69 | writeNewEntry(pos, src); |
70 | return; |
71 | } |
72 | long prevPos = mem.getKey(); |
73 | ByteBuffer buff = mem.getValue(); |
74 | int prevLength = buff.capacity(); |
75 | int length = src.remaining(); |
76 | if (prevPos == pos) { |
77 | if (prevLength != length) { |
78 | throw DataUtils.newIllegalStateException( |
79 | DataUtils.ERROR_READING_FAILED, |
80 | "Could not write to position {0}; " + |
81 | "partial overwrite is not supported", pos); |
82 | } |
83 | writeCount++; |
84 | writeBytes += length; |
85 | buff.rewind(); |
86 | buff.put(src); |
87 | return; |
88 | } |
89 | if (prevPos + prevLength > pos) { |
90 | throw DataUtils.newIllegalStateException( |
91 | DataUtils.ERROR_READING_FAILED, |
92 | "Could not write to position {0}; " + |
93 | "partial overwrite is not supported", pos); |
94 | } |
95 | writeNewEntry(pos, src); |
96 | } |
97 | |
98 | private void writeNewEntry(long pos, ByteBuffer src) { |
99 | int length = src.remaining(); |
100 | writeCount++; |
101 | writeBytes += length; |
102 | ByteBuffer buff = ByteBuffer.allocateDirect(length); |
103 | buff.put(src); |
104 | buff.rewind(); |
105 | memory.put(pos, buff); |
106 | } |
107 | |
108 | @Override |
109 | public void truncate(long size) { |
110 | writeCount++; |
111 | if (size == 0) { |
112 | fileSize = 0; |
113 | memory.clear(); |
114 | return; |
115 | } |
116 | fileSize = size; |
117 | for (Iterator<Long> it = memory.keySet().iterator(); it.hasNext();) { |
118 | long pos = it.next(); |
119 | if (pos < size) { |
120 | break; |
121 | } |
122 | ByteBuffer buff = memory.get(pos); |
123 | if (buff.capacity() > size) { |
124 | throw DataUtils.newIllegalStateException( |
125 | DataUtils.ERROR_READING_FAILED, |
126 | "Could not truncate to {0}; " + |
127 | "partial truncate is not supported", pos); |
128 | } |
129 | it.remove(); |
130 | } |
131 | } |
132 | |
133 | @Override |
134 | public void close() { |
135 | memory.clear(); |
136 | } |
137 | |
138 | @Override |
139 | public void sync() { |
140 | // nothing to do |
141 | } |
142 | |
143 | @Override |
144 | public int getDefaultRetentionTime() { |
145 | return 0; |
146 | } |
147 | |
148 | } |