1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.james.mime4j.io;
21
22 import org.apache.james.mime4j.util.ByteArrayBuffer;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26
27
28
29
30
31 public class BufferedLineReaderInputStream extends LineReaderInputStream {
32
33 private boolean truncated;
34
35 private byte[] buffer;
36
37 private int bufpos;
38 private int buflen;
39
40 private final int maxLineLen;
41
42 public BufferedLineReaderInputStream(
43 final InputStream instream,
44 int buffersize,
45 int maxLineLen) {
46 super(instream);
47 if (instream == null) {
48 throw new IllegalArgumentException("Input stream may not be null");
49 }
50 if (buffersize <= 0) {
51 throw new IllegalArgumentException("Buffer size may not be negative or zero");
52 }
53 this.buffer = new byte[buffersize];
54 this.bufpos = 0;
55 this.buflen = 0;
56 this.maxLineLen = maxLineLen;
57 this.truncated = false;
58 }
59
60 public BufferedLineReaderInputStream(
61 final InputStream instream,
62 int buffersize) {
63 this(instream, buffersize, -1);
64 }
65
66 private void expand(int newlen) {
67 byte newbuffer[] = new byte[newlen];
68 int len = this.buflen - this.bufpos;
69 if (len > 0) {
70 System.arraycopy(this.buffer, this.bufpos, newbuffer, this.bufpos, len);
71 }
72 this.buffer = newbuffer;
73 }
74
75 public void ensureCapacity(int len) {
76 if (len > this.buffer.length) {
77 expand(len);
78 }
79 }
80
81 public int fillBuffer() throws IOException {
82
83 if (this.bufpos > 0) {
84 int len = this.buflen - this.bufpos;
85 if (len > 0) {
86 System.arraycopy(this.buffer, this.bufpos, this.buffer, 0, len);
87 }
88 this.bufpos = 0;
89 this.buflen = len;
90 }
91 int l;
92 int off = this.buflen;
93 int len = this.buffer.length - off;
94 l = in.read(this.buffer, off, len);
95 if (l == -1) {
96 return -1;
97 } else {
98 this.buflen = off + l;
99 return l;
100 }
101 }
102
103 public boolean hasBufferedData() {
104 return this.bufpos < this.buflen;
105 }
106
107 public void truncate() {
108 clear();
109 this.truncated = true;
110 }
111
112 @Override
113 public int read() throws IOException {
114 if (this.truncated) {
115 return -1;
116 }
117 int noRead = 0;
118 while (!hasBufferedData()) {
119 noRead = fillBuffer();
120 if (noRead == -1) {
121 return -1;
122 }
123 }
124 return this.buffer[this.bufpos++] & 0xff;
125 }
126
127 @Override
128 public int read(final byte[] b, int off, int len) throws IOException {
129 if (this.truncated) {
130 return -1;
131 }
132 if (b == null) {
133 return 0;
134 }
135 int noRead = 0;
136 while (!hasBufferedData()) {
137 noRead = fillBuffer();
138 if (noRead == -1) {
139 return -1;
140 }
141 }
142 int chunk = this.buflen - this.bufpos;
143 if (chunk > len) {
144 chunk = len;
145 }
146 System.arraycopy(this.buffer, this.bufpos, b, off, chunk);
147 this.bufpos += chunk;
148 return chunk;
149 }
150
151 @Override
152 public int read(final byte[] b) throws IOException {
153 if (this.truncated) {
154 return -1;
155 }
156 if (b == null) {
157 return 0;
158 }
159 return read(b, 0, b.length);
160 }
161
162 @Override
163 public boolean markSupported() {
164 return false;
165 }
166
167
168 @Override
169 public int readLine(final ByteArrayBuffer dst) throws IOException {
170 if (dst == null) {
171 throw new IllegalArgumentException("Buffer may not be null");
172 }
173 if (this.truncated) {
174 return -1;
175 }
176 int total = 0;
177 boolean found = false;
178 int bytesRead = 0;
179 while (!found) {
180 if (!hasBufferedData()) {
181 bytesRead = fillBuffer();
182 if (bytesRead == -1) {
183 break;
184 }
185 }
186 int i = indexOf((byte)'\n');
187 int chunk;
188 if (i != -1) {
189 found = true;
190 chunk = i + 1 - pos();
191 } else {
192 chunk = length();
193 }
194 if (chunk > 0) {
195 dst.append(buf(), pos(), chunk);
196 skip(chunk);
197 total += chunk;
198 }
199 if (this.maxLineLen > 0 && dst.length() >= this.maxLineLen) {
200 throw new MaxLineLimitException("Maximum line length limit exceeded");
201 }
202 }
203 if (total == 0 && bytesRead == -1) {
204 return -1;
205 } else {
206 return total;
207 }
208 }
209
210
211
212
213
214
215
216
217
218 public int indexOf(final byte[] pattern, int off, int len) {
219 if (pattern == null) {
220 throw new IllegalArgumentException("Pattern may not be null");
221 }
222 if (off < this.bufpos || len < 0 || off + len > this.buflen) {
223 throw new IndexOutOfBoundsException();
224 }
225 if (len < pattern.length) {
226 return -1;
227 }
228
229 int[] shiftTable = new int[256];
230 for (int i = 0; i < shiftTable.length; i++) {
231 shiftTable[i] = pattern.length + 1;
232 }
233 for (int i = 0; i < pattern.length; i++) {
234 int x = pattern[i] & 0xff;
235 shiftTable[x] = pattern.length - i;
236 }
237
238 int j = 0;
239 while (j <= len - pattern.length) {
240 int cur = off + j;
241 boolean match = true;
242 for (int i = 0; i < pattern.length; i++) {
243 if (this.buffer[cur + i] != pattern[i]) {
244 match = false;
245 break;
246 }
247 }
248 if (match) {
249 return cur;
250 }
251
252 int pos = cur + pattern.length;
253 if (pos >= this.buffer.length) {
254 break;
255 }
256 int x = this.buffer[pos] & 0xff;
257 j += shiftTable[x];
258 }
259 return -1;
260 }
261
262
263
264
265
266
267
268
269
270 public int indexOf(final byte[] pattern) {
271 return indexOf(pattern, this.bufpos, this.buflen - this.bufpos);
272 }
273
274 public int indexOf(byte b, int off, int len) {
275 if (off < this.bufpos || len < 0 || off + len > this.buflen) {
276 throw new IndexOutOfBoundsException();
277 }
278 for (int i = off; i < off + len; i++) {
279 if (this.buffer[i] == b) {
280 return i;
281 }
282 }
283 return -1;
284 }
285
286 public int indexOf(byte b) {
287 return indexOf(b, this.bufpos, this.buflen - this.bufpos);
288 }
289
290 public byte charAt(int pos) {
291 if (pos < this.bufpos || pos > this.buflen) {
292 throw new IndexOutOfBoundsException();
293 }
294 return this.buffer[pos];
295 }
296
297 public byte[] buf() {
298 return this.buffer;
299 }
300
301 public int pos() {
302 return this.bufpos;
303 }
304
305 public int limit() {
306 return this.buflen;
307 }
308
309 public int length() {
310 return this.buflen - this.bufpos;
311 }
312
313 public int capacity() {
314 return this.buffer.length;
315 }
316
317 public int skip(int n) {
318 int chunk = Math.min(n, this.buflen - this.bufpos);
319 this.bufpos += chunk;
320 return chunk;
321 }
322
323 public void clear() {
324 this.bufpos = 0;
325 this.buflen = 0;
326 }
327
328 @Override
329 public String toString() {
330 StringBuilder buffer = new StringBuilder();
331 buffer.append("[pos: ");
332 buffer.append(this.bufpos);
333 buffer.append("]");
334 buffer.append("[limit: ");
335 buffer.append(this.buflen);
336 buffer.append("]");
337 buffer.append("[");
338 for (int i = this.bufpos; i < this.buflen; i++) {
339 buffer.append((char) this.buffer[i]);
340 }
341 buffer.append("]");
342 return buffer.toString();
343 }
344
345 }