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 java.io.Reader; |
11 | import java.nio.ByteBuffer; |
12 | import java.nio.CharBuffer; |
13 | import java.nio.charset.CharsetEncoder; |
14 | import java.nio.charset.CodingErrorAction; |
15 | |
16 | import org.h2.engine.Constants; |
17 | |
18 | /** |
19 | * An input stream that reads the data from a reader. |
20 | */ |
21 | public class CountingReaderInputStream extends InputStream { |
22 | |
23 | private final Reader reader; |
24 | |
25 | private final CharBuffer charBuffer = |
26 | CharBuffer.allocate(Constants.IO_BUFFER_SIZE); |
27 | |
28 | private final CharsetEncoder encoder = Constants.UTF8.newEncoder(). |
29 | onMalformedInput(CodingErrorAction.REPLACE). |
30 | onUnmappableCharacter(CodingErrorAction.REPLACE); |
31 | |
32 | private ByteBuffer byteBuffer = ByteBuffer.allocate(0); |
33 | private long length; |
34 | private long remaining; |
35 | |
36 | CountingReaderInputStream(Reader reader, long maxLength) { |
37 | this.reader = reader; |
38 | this.remaining = maxLength; |
39 | } |
40 | |
41 | @Override |
42 | public int read(byte[] buff, int offset, int len) throws IOException { |
43 | if (!fetch()) { |
44 | return -1; |
45 | } |
46 | len = Math.min(len, byteBuffer.remaining()); |
47 | byteBuffer.get(buff, offset, len); |
48 | return len; |
49 | } |
50 | |
51 | @Override |
52 | public int read() throws IOException { |
53 | if (!fetch()) { |
54 | return -1; |
55 | } |
56 | return byteBuffer.get() & 255; |
57 | } |
58 | |
59 | private boolean fetch() throws IOException { |
60 | if (byteBuffer != null && byteBuffer.remaining() == 0) { |
61 | fillBuffer(); |
62 | } |
63 | return byteBuffer != null; |
64 | } |
65 | |
66 | private void fillBuffer() throws IOException { |
67 | int len = (int) Math.min(charBuffer.capacity() - charBuffer.position(), |
68 | remaining); |
69 | if (len > 0) { |
70 | len = reader.read(charBuffer.array(), charBuffer.position(), len); |
71 | } |
72 | if (len > 0) { |
73 | remaining -= len; |
74 | } else { |
75 | len = 0; |
76 | remaining = 0; |
77 | } |
78 | length += len; |
79 | charBuffer.limit(charBuffer.position() + len); |
80 | charBuffer.rewind(); |
81 | byteBuffer = ByteBuffer.allocate(Constants.IO_BUFFER_SIZE); |
82 | boolean end = remaining == 0; |
83 | encoder.encode(charBuffer, byteBuffer, end); |
84 | if (end && byteBuffer.position() == 0) { |
85 | // EOF |
86 | byteBuffer = null; |
87 | return; |
88 | } |
89 | byteBuffer.flip(); |
90 | charBuffer.compact(); |
91 | charBuffer.flip(); |
92 | charBuffer.position(charBuffer.limit()); |
93 | } |
94 | |
95 | /** |
96 | * The number of characters read so far (but there might still be some bytes |
97 | * in the buffer). |
98 | * |
99 | * @return the number of characters |
100 | */ |
101 | public long getLength() { |
102 | return length; |
103 | } |
104 | |
105 | @Override |
106 | public void close() throws IOException { |
107 | reader.close(); |
108 | } |
109 | |
110 | } |