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.store; |
7 | |
8 | import java.io.EOFException; |
9 | import java.io.IOException; |
10 | import java.io.InputStream; |
11 | import org.h2.message.DbException; |
12 | import org.h2.message.Trace; |
13 | import org.h2.util.BitField; |
14 | |
15 | /** |
16 | * An input stream that reads from a page store. |
17 | */ |
18 | public class PageInputStream extends InputStream { |
19 | |
20 | private final PageStore store; |
21 | private final Trace trace; |
22 | private final int firstTrunkPage; |
23 | private final PageStreamTrunk.Iterator trunkIterator; |
24 | private int dataPage; |
25 | private PageStreamTrunk trunk; |
26 | private int trunkIndex; |
27 | private PageStreamData data; |
28 | private int dataPos; |
29 | private boolean endOfFile; |
30 | private int remaining; |
31 | private final byte[] buffer = { 0 }; |
32 | private int logKey; |
33 | |
34 | PageInputStream(PageStore store, int logKey, int firstTrunkPage, int dataPage) { |
35 | this.store = store; |
36 | this.trace = store.getTrace(); |
37 | // minus one because we increment before comparing |
38 | this.logKey = logKey - 1; |
39 | this.firstTrunkPage = firstTrunkPage; |
40 | trunkIterator = new PageStreamTrunk.Iterator(store, firstTrunkPage); |
41 | this.dataPage = dataPage; |
42 | } |
43 | |
44 | @Override |
45 | public int read() throws IOException { |
46 | int len = read(buffer); |
47 | return len < 0 ? -1 : (buffer[0] & 255); |
48 | } |
49 | |
50 | @Override |
51 | public int read(byte[] b) throws IOException { |
52 | return read(b, 0, b.length); |
53 | } |
54 | |
55 | @Override |
56 | public int read(byte[] b, int off, int len) throws IOException { |
57 | if (len == 0) { |
58 | return 0; |
59 | } |
60 | int read = 0; |
61 | while (len > 0) { |
62 | int r = readBlock(b, off, len); |
63 | if (r < 0) { |
64 | break; |
65 | } |
66 | read += r; |
67 | off += r; |
68 | len -= r; |
69 | } |
70 | return read == 0 ? -1 : read; |
71 | } |
72 | |
73 | private int readBlock(byte[] buff, int off, int len) throws IOException { |
74 | try { |
75 | fillBuffer(); |
76 | if (endOfFile) { |
77 | return -1; |
78 | } |
79 | int l = Math.min(remaining, len); |
80 | data.read(dataPos, buff, off, l); |
81 | remaining -= l; |
82 | dataPos += l; |
83 | return l; |
84 | } catch (DbException e) { |
85 | throw new EOFException(); |
86 | } |
87 | } |
88 | |
89 | private void fillBuffer() { |
90 | if (remaining > 0 || endOfFile) { |
91 | return; |
92 | } |
93 | int next; |
94 | while (true) { |
95 | if (trunk == null) { |
96 | trunk = trunkIterator.next(); |
97 | trunkIndex = 0; |
98 | logKey++; |
99 | if (trunk == null || trunk.getLogKey() != logKey) { |
100 | endOfFile = true; |
101 | return; |
102 | } |
103 | } |
104 | if (trunk != null) { |
105 | next = trunk.getPageData(trunkIndex++); |
106 | if (next == -1) { |
107 | trunk = null; |
108 | } else if (dataPage == -1 || dataPage == next) { |
109 | break; |
110 | } |
111 | } |
112 | } |
113 | if (trace.isDebugEnabled()) { |
114 | trace.debug("pageIn.readPage " + next); |
115 | } |
116 | dataPage = -1; |
117 | data = null; |
118 | Page p = store.getPage(next); |
119 | if (p instanceof PageStreamData) { |
120 | data = (PageStreamData) p; |
121 | } |
122 | if (data == null || data.getLogKey() != logKey) { |
123 | endOfFile = true; |
124 | return; |
125 | } |
126 | dataPos = PageStreamData.getReadStart(); |
127 | remaining = store.getPageSize() - dataPos; |
128 | } |
129 | |
130 | /** |
131 | * Set all pages as 'allocated' in the page store. |
132 | * |
133 | * @return the bit set |
134 | */ |
135 | BitField allocateAllPages() { |
136 | BitField pages = new BitField(); |
137 | int key = logKey; |
138 | PageStreamTrunk.Iterator it = new PageStreamTrunk.Iterator( |
139 | store, firstTrunkPage); |
140 | while (true) { |
141 | PageStreamTrunk t = it.next(); |
142 | key++; |
143 | if (it.canDelete()) { |
144 | store.allocatePage(it.getCurrentPageId()); |
145 | } |
146 | if (t == null || t.getLogKey() != key) { |
147 | break; |
148 | } |
149 | pages.set(t.getPos()); |
150 | for (int i = 0;; i++) { |
151 | int n = t.getPageData(i); |
152 | if (n == -1) { |
153 | break; |
154 | } |
155 | pages.set(n); |
156 | store.allocatePage(n); |
157 | } |
158 | } |
159 | return pages; |
160 | } |
161 | |
162 | int getDataPage() { |
163 | return data.getPos(); |
164 | } |
165 | |
166 | @Override |
167 | public void close() { |
168 | // nothing to do |
169 | } |
170 | |
171 | } |