Mercurial > hg > Members > sugi > MessagePack-java
comparison src/main/java/org/msgpack/unpacker/MessagePackUnpacker.java @ 0:cb825acd883a
first commit
author | sugi |
---|---|
date | Sat, 18 Oct 2014 15:06:15 +0900 |
parents | |
children | 769ba8da0840 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:cb825acd883a |
---|---|
1 // | |
2 // MessagePack for Java | |
3 // | |
4 // Copyright (C) 2009 - 2013 FURUHASHI Sadayuki | |
5 // | |
6 // Licensed under the Apache License, Version 2.0 (the "License"); | |
7 // you may not use this file except in compliance with the License. | |
8 // You may obtain a copy of the License at | |
9 // | |
10 // http://www.apache.org/licenses/LICENSE-2.0 | |
11 // | |
12 // Unless required by applicable law or agreed to in writing, software | |
13 // distributed under the License is distributed on an "AS IS" BASIS, | |
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 // See the License for the specific language governing permissions and | |
16 // limitations under the License. | |
17 // | |
18 package org.msgpack.unpacker; | |
19 | |
20 import java.io.IOException; | |
21 import java.io.EOFException; | |
22 import java.io.InputStream; | |
23 import java.math.BigInteger; | |
24 import org.msgpack.io.Input; | |
25 import org.msgpack.io.StreamInput; | |
26 import org.msgpack.io.BufferReferer; | |
27 import org.msgpack.MessagePack; | |
28 import org.msgpack.MessageTypeException; | |
29 import org.msgpack.packer.Unconverter; | |
30 import org.msgpack.type.ValueType; | |
31 | |
32 public class MessagePackUnpacker extends AbstractUnpacker { | |
33 private static final byte REQUIRE_TO_READ_HEAD = (byte) 0xc1; | |
34 | |
35 protected final Input in; | |
36 private final UnpackerStack stack = new UnpackerStack(); | |
37 | |
38 private byte headByte = REQUIRE_TO_READ_HEAD; | |
39 | |
40 private byte[] raw; | |
41 private int rawFilled; | |
42 | |
43 private final IntAccept intAccept = new IntAccept(); | |
44 private final LongAccept longAccept = new LongAccept(); | |
45 private final BigIntegerAccept bigIntegerAccept = new BigIntegerAccept(); | |
46 private final DoubleAccept doubleAccept = new DoubleAccept(); | |
47 private final ByteArrayAccept byteArrayAccept = new ByteArrayAccept(); | |
48 private final StringAccept stringAccept = new StringAccept(); | |
49 private final ArrayAccept arrayAccept = new ArrayAccept(); | |
50 private final MapAccept mapAccept = new MapAccept(); | |
51 private final ValueAccept valueAccept = new ValueAccept(); | |
52 private final SkipAccept skipAccept = new SkipAccept(); | |
53 | |
54 public MessagePackUnpacker(MessagePack msgpack, InputStream stream) { | |
55 this(msgpack, new StreamInput(stream)); | |
56 } | |
57 | |
58 protected MessagePackUnpacker(MessagePack msgpack, Input in) { | |
59 super(msgpack); | |
60 this.in = in; | |
61 } | |
62 | |
63 private byte getHeadByte() throws IOException { | |
64 byte b = headByte; | |
65 if (b == REQUIRE_TO_READ_HEAD) { | |
66 b = headByte = in.readByte(); | |
67 } | |
68 return b; | |
69 } | |
70 | |
71 final void readOne(Accept a) throws IOException { | |
72 stack.checkCount(); | |
73 if (readOneWithoutStack(a)) { | |
74 stack.reduceCount(); | |
75 } | |
76 } | |
77 | |
78 final boolean readOneWithoutStack(Accept a) throws IOException { | |
79 if (raw != null) { | |
80 readRawBodyCont(); | |
81 a.acceptRaw(raw); | |
82 raw = null; | |
83 headByte = REQUIRE_TO_READ_HEAD; | |
84 return true; | |
85 } | |
86 | |
87 final int b = (int) getHeadByte(); | |
88 | |
89 if ((b & 0x80) == 0) { // Positive Fixnum | |
90 // System.out.println("positive fixnum "+b); | |
91 a.acceptInteger(b); | |
92 headByte = REQUIRE_TO_READ_HEAD; | |
93 return true; | |
94 } | |
95 | |
96 if ((b & 0xe0) == 0xe0) { // Negative Fixnum | |
97 // System.out.println("negative fixnum "+b); | |
98 a.acceptInteger(b); | |
99 headByte = REQUIRE_TO_READ_HEAD; | |
100 return true; | |
101 } | |
102 | |
103 if ((b & 0xe0) == 0xa0) { // FixRaw | |
104 int count = b & 0x1f; | |
105 if (count == 0) { | |
106 a.acceptEmptyRaw(); | |
107 headByte = REQUIRE_TO_READ_HEAD; | |
108 return true; | |
109 } | |
110 if (!tryReferRawBody(a, count)) { | |
111 readRawBody(count); | |
112 a.acceptRaw(raw); | |
113 raw = null; | |
114 } | |
115 headByte = REQUIRE_TO_READ_HEAD; | |
116 return true; | |
117 } | |
118 | |
119 if ((b & 0xf0) == 0x90) { // FixArray | |
120 int count = b & 0x0f; | |
121 // System.out.println("fixarray count:"+count); | |
122 a.acceptArray(count); | |
123 stack.reduceCount(); | |
124 stack.pushArray(count); | |
125 headByte = REQUIRE_TO_READ_HEAD; | |
126 return false; | |
127 } | |
128 | |
129 if ((b & 0xf0) == 0x80) { // FixMap | |
130 int count = b & 0x0f; | |
131 // System.out.println("fixmap count:"+count/2); | |
132 a.acceptMap(count); | |
133 stack.reduceCount(); | |
134 stack.pushMap(count); | |
135 headByte = REQUIRE_TO_READ_HEAD; | |
136 return false; | |
137 } | |
138 | |
139 return readOneWithoutStackLarge(a, b); | |
140 } | |
141 | |
142 private boolean readOneWithoutStackLarge(Accept a, final int b) | |
143 throws IOException { | |
144 switch (b & 0xff) { | |
145 case 0xc0: // nil | |
146 a.acceptNil(); | |
147 headByte = REQUIRE_TO_READ_HEAD; | |
148 return true; | |
149 case 0xc2: // false | |
150 a.acceptBoolean(false); | |
151 headByte = REQUIRE_TO_READ_HEAD; | |
152 return true; | |
153 case 0xc3: // true | |
154 a.acceptBoolean(true); | |
155 headByte = REQUIRE_TO_READ_HEAD; | |
156 return true; | |
157 //case 0xc4: // bin 8 -> see 0xd9 | |
158 //case 0xc5: // bin 16 -> see 0xda | |
159 //case 0xc6: // bin 32 -> see 0xdb | |
160 case 0xca: // float | |
161 a.acceptFloat(in.getFloat()); | |
162 in.advance(); | |
163 headByte = REQUIRE_TO_READ_HEAD; | |
164 return true; | |
165 case 0xcb: // double | |
166 a.acceptDouble(in.getDouble()); | |
167 in.advance(); | |
168 headByte = REQUIRE_TO_READ_HEAD; | |
169 return true; | |
170 case 0xcc: // unsigned int 8 | |
171 a.acceptUnsignedInteger(in.getByte()); | |
172 in.advance(); | |
173 headByte = REQUIRE_TO_READ_HEAD; | |
174 return true; | |
175 case 0xcd: // unsigned int 16 | |
176 a.acceptUnsignedInteger(in.getShort()); | |
177 in.advance(); | |
178 headByte = REQUIRE_TO_READ_HEAD; | |
179 return true; | |
180 case 0xce: // unsigned int 32 | |
181 a.acceptUnsignedInteger(in.getInt()); | |
182 in.advance(); | |
183 headByte = REQUIRE_TO_READ_HEAD; | |
184 return true; | |
185 case 0xcf: // unsigned int 64 | |
186 a.acceptUnsignedInteger(in.getLong()); | |
187 in.advance(); | |
188 headByte = REQUIRE_TO_READ_HEAD; | |
189 return true; | |
190 case 0xd0: // signed int 8 | |
191 a.acceptInteger(in.getByte()); | |
192 in.advance(); | |
193 headByte = REQUIRE_TO_READ_HEAD; | |
194 return true; | |
195 case 0xd1: // signed int 16 | |
196 a.acceptInteger(in.getShort()); | |
197 in.advance(); | |
198 headByte = REQUIRE_TO_READ_HEAD; | |
199 return true; | |
200 case 0xd2: // signed int 32 | |
201 a.acceptInteger(in.getInt()); | |
202 in.advance(); | |
203 headByte = REQUIRE_TO_READ_HEAD; | |
204 return true; | |
205 case 0xd3: // signed int 64 | |
206 a.acceptInteger(in.getLong()); | |
207 in.advance(); | |
208 headByte = REQUIRE_TO_READ_HEAD; | |
209 return true; | |
210 case 0xc4: // bin 8 | |
211 case 0xd9: // str 8 | |
212 { | |
213 int count = in.getByte() & 0xff; | |
214 if (count == 0) { | |
215 a.acceptEmptyRaw(); | |
216 in.advance(); | |
217 headByte = REQUIRE_TO_READ_HEAD; | |
218 return true; | |
219 } | |
220 if (count >= rawSizeLimit) { | |
221 String reason = String.format( | |
222 "Size of raw (%d) over limit at %d", | |
223 new Object[] { count, rawSizeLimit }); | |
224 throw new SizeLimitException(reason); | |
225 } | |
226 in.advance(); | |
227 if (!tryReferRawBody(a, count)) { | |
228 readRawBody(count); | |
229 a.acceptRaw(raw); | |
230 raw = null; | |
231 } | |
232 headByte = REQUIRE_TO_READ_HEAD; | |
233 return true; | |
234 } | |
235 case 0xc5: // bin 16 | |
236 case 0xda: // raw 16 | |
237 { | |
238 int count = in.getShort() & 0xffff; | |
239 if (count == 0) { | |
240 a.acceptEmptyRaw(); | |
241 in.advance(); | |
242 headByte = REQUIRE_TO_READ_HEAD; | |
243 return true; | |
244 } | |
245 if (count >= rawSizeLimit) { | |
246 String reason = String.format( | |
247 "Size of raw (%d) over limit at %d", | |
248 new Object[] { count, rawSizeLimit }); | |
249 throw new SizeLimitException(reason); | |
250 } | |
251 in.advance(); | |
252 if (!tryReferRawBody(a, count)) { | |
253 readRawBody(count); | |
254 a.acceptRaw(raw); | |
255 raw = null; | |
256 } | |
257 headByte = REQUIRE_TO_READ_HEAD; | |
258 return true; | |
259 } | |
260 case 0xc6: // bin 32 | |
261 case 0xdb: // raw 32 | |
262 { | |
263 int count = in.getInt(); | |
264 if (count == 0) { | |
265 a.acceptEmptyRaw(); | |
266 in.advance(); | |
267 headByte = REQUIRE_TO_READ_HEAD; | |
268 return true; | |
269 } | |
270 if (count < 0 || count >= rawSizeLimit) { | |
271 String reason = String.format( | |
272 "Size of raw (%d) over limit at %d", | |
273 new Object[] { count, rawSizeLimit }); | |
274 throw new SizeLimitException(reason); | |
275 } | |
276 in.advance(); | |
277 if (!tryReferRawBody(a, count)) { | |
278 readRawBody(count); | |
279 a.acceptRaw(raw); | |
280 raw = null; | |
281 } | |
282 headByte = REQUIRE_TO_READ_HEAD; | |
283 return true; | |
284 } | |
285 case 0xdc: // array 16 | |
286 { | |
287 int count = in.getShort() & 0xffff; | |
288 if (count >= arraySizeLimit) { | |
289 String reason = String.format( | |
290 "Size of array (%d) over limit at %d", | |
291 new Object[] { count, arraySizeLimit }); | |
292 throw new SizeLimitException(reason); | |
293 } | |
294 a.acceptArray(count); | |
295 stack.reduceCount(); | |
296 stack.pushArray(count); | |
297 in.advance(); | |
298 headByte = REQUIRE_TO_READ_HEAD; | |
299 return false; | |
300 } | |
301 case 0xdd: // array 32 | |
302 { | |
303 int count = in.getInt(); | |
304 if (count < 0 || count >= arraySizeLimit) { | |
305 String reason = String.format( | |
306 "Size of array (%d) over limit at %d", | |
307 new Object[] { count, arraySizeLimit }); | |
308 throw new SizeLimitException(reason); | |
309 } | |
310 a.acceptArray(count); | |
311 stack.reduceCount(); | |
312 stack.pushArray(count); | |
313 in.advance(); | |
314 headByte = REQUIRE_TO_READ_HEAD; | |
315 return false; | |
316 } | |
317 case 0xde: // map 16 | |
318 { | |
319 int count = in.getShort() & 0xffff; | |
320 if (count >= mapSizeLimit) { | |
321 String reason = String.format( | |
322 "Size of map (%d) over limit at %d", | |
323 new Object[] { count, mapSizeLimit }); | |
324 throw new SizeLimitException(reason); | |
325 } | |
326 a.acceptMap(count); | |
327 stack.reduceCount(); | |
328 stack.pushMap(count); | |
329 in.advance(); | |
330 headByte = REQUIRE_TO_READ_HEAD; | |
331 return false; | |
332 } | |
333 case 0xdf: // map 32 | |
334 { | |
335 int count = in.getInt(); | |
336 if (count < 0 || count >= mapSizeLimit) { | |
337 String reason = String.format( | |
338 "Size of map (%d) over limit at %d", | |
339 new Object[] { count, mapSizeLimit }); | |
340 throw new SizeLimitException(reason); | |
341 } | |
342 a.acceptMap(count); | |
343 stack.reduceCount(); | |
344 stack.pushMap(count); | |
345 in.advance(); | |
346 headByte = REQUIRE_TO_READ_HEAD; | |
347 return false; | |
348 } | |
349 default: | |
350 // System.out.println("unknown b "+(b&0xff)); | |
351 // headByte = CS_INVALID | |
352 headByte = REQUIRE_TO_READ_HEAD; | |
353 throw new IOException("Invalid byte: " + b); // TODO error FormatException | |
354 } | |
355 } | |
356 | |
357 private boolean tryReferRawBody(BufferReferer referer, int size) throws IOException { | |
358 return in.tryRefer(referer, size); | |
359 } | |
360 | |
361 private void readRawBody(int size) throws IOException { | |
362 raw = new byte[size]; | |
363 rawFilled = 0; | |
364 readRawBodyCont(); | |
365 } | |
366 | |
367 private void readRawBodyCont() throws IOException { | |
368 int len = in.read(raw, rawFilled, raw.length - rawFilled); | |
369 rawFilled += len; | |
370 if (rawFilled < raw.length) { | |
371 throw new EOFException(); | |
372 } | |
373 } | |
374 | |
375 @Override | |
376 protected boolean tryReadNil() throws IOException { | |
377 stack.checkCount(); | |
378 int b = getHeadByte() & 0xff; | |
379 if (b == 0xc0) { | |
380 // nil is read | |
381 stack.reduceCount(); | |
382 headByte = REQUIRE_TO_READ_HEAD; | |
383 return true; | |
384 } | |
385 // not nil | |
386 return false; | |
387 } | |
388 | |
389 @Override | |
390 public boolean trySkipNil() throws IOException { | |
391 if (stack.getDepth() > 0 && stack.getTopCount() <= 0) { | |
392 // end of array or map | |
393 return true; | |
394 } | |
395 | |
396 int b = getHeadByte() & 0xff; | |
397 if (b == 0xc0) { | |
398 // nil is skipped | |
399 stack.reduceCount(); | |
400 headByte = REQUIRE_TO_READ_HEAD; | |
401 return true; | |
402 } | |
403 // not nil | |
404 return false; | |
405 } | |
406 | |
407 @Override | |
408 public void readNil() throws IOException { | |
409 // optimized not to allocate nilAccept | |
410 stack.checkCount(); | |
411 int b = getHeadByte() & 0xff; | |
412 if (b == 0xc0) { | |
413 stack.reduceCount(); | |
414 headByte = REQUIRE_TO_READ_HEAD; | |
415 return; | |
416 } | |
417 throw new MessageTypeException("Expected nil but got not nil value"); | |
418 } | |
419 | |
420 @Override | |
421 public boolean readBoolean() throws IOException { | |
422 // optimized not to allocate booleanAccept | |
423 stack.checkCount(); | |
424 int b = getHeadByte() & 0xff; | |
425 if (b == 0xc2) { | |
426 stack.reduceCount(); | |
427 headByte = REQUIRE_TO_READ_HEAD; | |
428 return false; | |
429 } else if (b == 0xc3) { | |
430 stack.reduceCount(); | |
431 headByte = REQUIRE_TO_READ_HEAD; | |
432 return true; | |
433 } | |
434 throw new MessageTypeException( | |
435 "Expected Boolean but got not boolean value"); | |
436 } | |
437 | |
438 @Override | |
439 public byte readByte() throws IOException { | |
440 // optimized not to allocate byteAccept | |
441 stack.checkCount(); | |
442 readOneWithoutStack(intAccept); | |
443 int value = intAccept.value; | |
444 if (value < (int) Byte.MIN_VALUE || value > (int) Byte.MAX_VALUE) { | |
445 throw new MessageTypeException(); // TODO message | |
446 } | |
447 stack.reduceCount(); | |
448 return (byte) value; | |
449 } | |
450 | |
451 @Override | |
452 public short readShort() throws IOException { | |
453 // optimized not to allocate shortAccept | |
454 stack.checkCount(); | |
455 readOneWithoutStack(intAccept); | |
456 int value = intAccept.value; | |
457 if (value < (int) Short.MIN_VALUE || value > (int) Short.MAX_VALUE) { | |
458 throw new MessageTypeException(); // TODO message | |
459 } | |
460 stack.reduceCount(); | |
461 return (short) value; | |
462 } | |
463 | |
464 @Override | |
465 public int readInt() throws IOException { | |
466 readOne(intAccept); | |
467 return intAccept.value; | |
468 } | |
469 | |
470 @Override | |
471 public long readLong() throws IOException { | |
472 readOne(longAccept); | |
473 return longAccept.value; | |
474 } | |
475 | |
476 @Override | |
477 public BigInteger readBigInteger() throws IOException { | |
478 readOne(bigIntegerAccept); | |
479 return bigIntegerAccept.value; | |
480 } | |
481 | |
482 @Override | |
483 public float readFloat() throws IOException { | |
484 readOne(doubleAccept); | |
485 return (float) doubleAccept.value; | |
486 } | |
487 | |
488 @Override | |
489 public double readDouble() throws IOException { | |
490 readOne(doubleAccept); | |
491 return doubleAccept.value; | |
492 } | |
493 | |
494 @Override | |
495 public byte[] readByteArray() throws IOException { | |
496 readOne(byteArrayAccept); | |
497 return byteArrayAccept.value; | |
498 } | |
499 | |
500 @Override | |
501 public String readString() throws IOException { | |
502 readOne(stringAccept); | |
503 return stringAccept.value; | |
504 } | |
505 | |
506 @Override | |
507 public int readArrayBegin() throws IOException { | |
508 readOne(arrayAccept); | |
509 return arrayAccept.size; | |
510 } | |
511 | |
512 @Override | |
513 public void readArrayEnd(boolean check) throws IOException { | |
514 if (!stack.topIsArray()) { | |
515 throw new MessageTypeException( | |
516 "readArrayEnd() is called but readArrayBegin() is not called"); | |
517 } | |
518 | |
519 int remain = stack.getTopCount(); | |
520 if (remain > 0) { | |
521 if (check) { | |
522 throw new MessageTypeException( | |
523 "readArrayEnd(check=true) is called but the array is not end"); | |
524 } | |
525 for (int i = 0; i < remain; i++) { | |
526 skip(); | |
527 } | |
528 } | |
529 stack.pop(); | |
530 } | |
531 | |
532 @Override | |
533 public int readMapBegin() throws IOException { | |
534 readOne(mapAccept); | |
535 return mapAccept.size; | |
536 } | |
537 | |
538 @Override | |
539 public void readMapEnd(boolean check) throws IOException { | |
540 if (!stack.topIsMap()) { | |
541 throw new MessageTypeException( | |
542 "readMapEnd() is called but readMapBegin() is not called"); | |
543 } | |
544 | |
545 int remain = stack.getTopCount(); | |
546 if (remain > 0) { | |
547 if (check) { | |
548 throw new MessageTypeException( | |
549 "readMapEnd(check=true) is called but the map is not end"); | |
550 } | |
551 for (int i = 0; i < remain; i++) { | |
552 skip(); | |
553 } | |
554 } | |
555 stack.pop(); | |
556 } | |
557 | |
558 @Override | |
559 protected void readValue(Unconverter uc) throws IOException { | |
560 if (uc.getResult() != null) { | |
561 uc.resetResult(); | |
562 } | |
563 valueAccept.setUnconverter(uc); | |
564 | |
565 stack.checkCount(); | |
566 if (readOneWithoutStack(valueAccept)) { | |
567 stack.reduceCount(); | |
568 if (uc.getResult() != null) { | |
569 return; | |
570 } | |
571 } | |
572 while (true) { | |
573 while (stack.getTopCount() == 0) { | |
574 if (stack.topIsArray()) { | |
575 uc.writeArrayEnd(true); | |
576 stack.pop(); | |
577 // stack.reduceCount(); | |
578 } else if (stack.topIsMap()) { | |
579 uc.writeMapEnd(true); | |
580 stack.pop(); | |
581 // stack.reduceCount(); | |
582 } else { | |
583 throw new RuntimeException("invalid stack"); // FIXME error? | |
584 } | |
585 if (uc.getResult() != null) { | |
586 return; | |
587 } | |
588 } | |
589 readOne(valueAccept); | |
590 } | |
591 } | |
592 | |
593 @Override | |
594 public void skip() throws IOException { | |
595 stack.checkCount(); | |
596 if (readOneWithoutStack(skipAccept)) { | |
597 stack.reduceCount(); | |
598 return; | |
599 } | |
600 int targetDepth = stack.getDepth() - 1; | |
601 while (true) { | |
602 while (stack.getTopCount() == 0) { | |
603 stack.pop(); | |
604 if (stack.getDepth() <= targetDepth) { | |
605 return; | |
606 } | |
607 } | |
608 readOne(skipAccept); | |
609 } | |
610 } | |
611 | |
612 public ValueType getNextType() throws IOException { | |
613 final int b = (int) getHeadByte(); | |
614 if ((b & 0x80) == 0) { // Positive Fixnum | |
615 return ValueType.INTEGER; | |
616 } | |
617 if ((b & 0xe0) == 0xe0) { // Negative Fixnum | |
618 return ValueType.INTEGER; | |
619 } | |
620 if ((b & 0xe0) == 0xa0) { // FixRaw | |
621 return ValueType.RAW; | |
622 } | |
623 if ((b & 0xf0) == 0x90) { // FixArray | |
624 return ValueType.ARRAY; | |
625 } | |
626 if ((b & 0xf0) == 0x80) { // FixMap | |
627 return ValueType.MAP; | |
628 } | |
629 switch (b & 0xff) { | |
630 case 0xc0: // nil | |
631 return ValueType.NIL; | |
632 case 0xc2: // false | |
633 case 0xc3: // true | |
634 return ValueType.BOOLEAN; | |
635 case 0xca: // float | |
636 case 0xcb: // double | |
637 return ValueType.FLOAT; | |
638 case 0xcc: // unsigned int 8 | |
639 case 0xcd: // unsigned int 16 | |
640 case 0xce: // unsigned int 32 | |
641 case 0xcf: // unsigned int 64 | |
642 case 0xd0: // signed int 8 | |
643 case 0xd1: // signed int 16 | |
644 case 0xd2: // signed int 32 | |
645 case 0xd3: // signed int 64 | |
646 return ValueType.INTEGER; | |
647 // The definition based on a minor upgrade guide. | |
648 // https://github.com/msgpack/msgpack/blob/master/spec.md#impl-upgrade | |
649 case 0xc4: // bin 8 | |
650 case 0xc5: // bin 16 | |
651 case 0xc6: // bin 32 | |
652 case 0xd9: // str8 | |
653 case 0xda: // raw 16 | |
654 case 0xdb: // raw 32 | |
655 return ValueType.RAW; | |
656 case 0xdc: // array 16 | |
657 case 0xdd: // array 32 | |
658 return ValueType.ARRAY; | |
659 case 0xde: // map 16 | |
660 case 0xdf: // map 32 | |
661 return ValueType.MAP; | |
662 default: | |
663 throw new IOException("Invalid byte: " + b); // TODO error FormatException | |
664 } | |
665 } | |
666 | |
667 public void reset() { | |
668 raw = null; | |
669 stack.clear(); | |
670 } | |
671 | |
672 public void close() throws IOException { | |
673 in.close(); | |
674 } | |
675 | |
676 @Override | |
677 public int getReadByteCount() { | |
678 return in.getReadByteCount(); | |
679 } | |
680 | |
681 @Override | |
682 public void resetReadByteCount() { | |
683 in.resetReadByteCount(); | |
684 } | |
685 } |