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.IOException; |
9 | import java.io.InputStream; |
10 | import org.h2.engine.Constants; |
11 | import org.h2.message.DbException; |
12 | import org.h2.mvstore.DataUtils; |
13 | import org.h2.tools.CompressTool; |
14 | |
15 | /** |
16 | * An input stream that is backed by a file store. |
17 | */ |
18 | public class FileStoreInputStream extends InputStream { |
19 | |
20 | private FileStore store; |
21 | private final Data page; |
22 | private int remainingInBuffer; |
23 | private final CompressTool compress; |
24 | private boolean endOfFile; |
25 | private final boolean alwaysClose; |
26 | |
27 | public FileStoreInputStream(FileStore store, DataHandler handler, |
28 | boolean compression, boolean alwaysClose) { |
29 | this.store = store; |
30 | this.alwaysClose = alwaysClose; |
31 | if (compression) { |
32 | compress = CompressTool.getInstance(); |
33 | } else { |
34 | compress = null; |
35 | } |
36 | page = Data.create(handler, Constants.FILE_BLOCK_SIZE); |
37 | try { |
38 | if (store.length() <= FileStore.HEADER_LENGTH) { |
39 | close(); |
40 | } else { |
41 | fillBuffer(); |
42 | } |
43 | } catch (IOException e) { |
44 | throw DbException.convertIOException(e, store.name); |
45 | } |
46 | } |
47 | |
48 | @Override |
49 | public int available() { |
50 | return remainingInBuffer <= 0 ? 0 : remainingInBuffer; |
51 | } |
52 | |
53 | @Override |
54 | public int read(byte[] buff) throws IOException { |
55 | return read(buff, 0, buff.length); |
56 | } |
57 | |
58 | @Override |
59 | public int read(byte[] b, int off, int len) throws IOException { |
60 | if (len == 0) { |
61 | return 0; |
62 | } |
63 | int read = 0; |
64 | while (len > 0) { |
65 | int r = readBlock(b, off, len); |
66 | if (r < 0) { |
67 | break; |
68 | } |
69 | read += r; |
70 | off += r; |
71 | len -= r; |
72 | } |
73 | return read == 0 ? -1 : read; |
74 | } |
75 | |
76 | private int readBlock(byte[] buff, int off, int len) throws IOException { |
77 | fillBuffer(); |
78 | if (endOfFile) { |
79 | return -1; |
80 | } |
81 | int l = Math.min(remainingInBuffer, len); |
82 | page.read(buff, off, l); |
83 | remainingInBuffer -= l; |
84 | return l; |
85 | } |
86 | |
87 | private void fillBuffer() throws IOException { |
88 | if (remainingInBuffer > 0 || endOfFile) { |
89 | return; |
90 | } |
91 | page.reset(); |
92 | store.openFile(); |
93 | if (store.length() == store.getFilePointer()) { |
94 | close(); |
95 | return; |
96 | } |
97 | store.readFully(page.getBytes(), 0, Constants.FILE_BLOCK_SIZE); |
98 | page.reset(); |
99 | remainingInBuffer = page.readInt(); |
100 | if (remainingInBuffer < 0) { |
101 | close(); |
102 | return; |
103 | } |
104 | page.checkCapacity(remainingInBuffer); |
105 | // get the length to read |
106 | if (compress != null) { |
107 | page.checkCapacity(Data.LENGTH_INT); |
108 | page.readInt(); |
109 | } |
110 | page.setPos(page.length() + remainingInBuffer); |
111 | page.fillAligned(); |
112 | int len = page.length() - Constants.FILE_BLOCK_SIZE; |
113 | page.reset(); |
114 | page.readInt(); |
115 | store.readFully(page.getBytes(), Constants.FILE_BLOCK_SIZE, len); |
116 | page.reset(); |
117 | page.readInt(); |
118 | if (compress != null) { |
119 | int uncompressed = page.readInt(); |
120 | byte[] buff = DataUtils.newBytes(remainingInBuffer); |
121 | page.read(buff, 0, remainingInBuffer); |
122 | page.reset(); |
123 | page.checkCapacity(uncompressed); |
124 | CompressTool.expand(buff, page.getBytes(), 0); |
125 | remainingInBuffer = uncompressed; |
126 | } |
127 | if (alwaysClose) { |
128 | store.closeFile(); |
129 | } |
130 | } |
131 | |
132 | @Override |
133 | public void close() { |
134 | if (store != null) { |
135 | try { |
136 | store.close(); |
137 | endOfFile = true; |
138 | } finally { |
139 | store = null; |
140 | } |
141 | } |
142 | } |
143 | |
144 | @Override |
145 | protected void finalize() { |
146 | close(); |
147 | } |
148 | |
149 | @Override |
150 | public int read() throws IOException { |
151 | fillBuffer(); |
152 | if (endOfFile) { |
153 | return -1; |
154 | } |
155 | int i = page.readByte() & 0xff; |
156 | remainingInBuffer--; |
157 | return i; |
158 | } |
159 | |
160 | } |