comparison src/org/tmatesoft/hg/internal/InflaterDataAccess.java @ 157:d5268ca7715b

Merged branch wrap-data-access into default for resource-friendly data access. Updated API to promote that friendliness to clients (channels, not byte[]). More exceptions
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 09 Mar 2011 05:22:17 +0100
parents src/com/tmate/hgkit/fs/InflaterDataAccess.java@9429c7bd1920
children b413b16d10a5
comparison
equal deleted inserted replaced
156:643ddec3be36 157:d5268ca7715b
1 /*
2 * Copyright (c) 2011 TMate Software Ltd
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * For information on how to redistribute this software under
14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@hg4j.com
16 */
17 package org.tmatesoft.hg.internal;
18
19 import java.io.EOFException;
20 import java.io.IOException;
21 import java.util.zip.DataFormatException;
22 import java.util.zip.Inflater;
23 import java.util.zip.ZipException;
24
25
26 /**
27 * DataAccess counterpart for InflaterInputStream.
28 * XXX is it really needed to be subclass of FilterDataAccess?
29 *
30 * @author Artem Tikhomirov
31 * @author TMate Software Ltd.
32 */
33 public class InflaterDataAccess extends FilterDataAccess {
34
35 private final Inflater inflater;
36 private final byte[] buffer;
37 private final byte[] singleByte = new byte[1];
38 private int decompressedPos = 0;
39 private int decompressedLength = -1;
40
41 public InflaterDataAccess(DataAccess dataAccess, long offset, int length) {
42 this(dataAccess, offset, length, new Inflater(), 512);
43 }
44
45 public InflaterDataAccess(DataAccess dataAccess, long offset, int length, Inflater inflater, int bufSize) {
46 super(dataAccess, offset, length);
47 this.inflater = inflater;
48 buffer = new byte[bufSize];
49 }
50
51 @Override
52 public InflaterDataAccess reset() throws IOException {
53 super.reset();
54 inflater.reset();
55 decompressedPos = 0;
56 return this;
57 }
58
59 @Override
60 protected int available() {
61 throw new IllegalStateException("Can't tell how much uncompressed data left");
62 }
63
64 @Override
65 public boolean isEmpty() {
66 return super.available() <= 0 && inflater.finished(); // and/or inflater.getRemaining() <= 0 ?
67 }
68
69 @Override
70 public long length() {
71 if (decompressedLength != -1) {
72 return decompressedLength;
73 }
74 int c = 0;
75 try {
76 int oldPos = decompressedPos;
77 while (!isEmpty()) {
78 readByte();
79 c++;
80 }
81 decompressedLength = c + oldPos;
82 reset();
83 seek(oldPos);
84 return decompressedLength;
85 } catch (IOException ex) {
86 ex.printStackTrace(); // FIXME log error
87 decompressedLength = -1; // better luck next time?
88 return 0;
89 }
90 }
91
92 @Override
93 public void seek(long localOffset) throws IOException {
94 if (localOffset < 0 /* || localOffset >= length() */) {
95 throw new IllegalArgumentException();
96 }
97 if (localOffset >= decompressedPos) {
98 skip((int) (localOffset - decompressedPos));
99 } else {
100 reset();
101 skip((int) localOffset);
102 }
103 }
104
105 @Override
106 public void skip(int bytes) throws IOException {
107 if (bytes < 0) {
108 bytes += decompressedPos;
109 if (bytes < 0) {
110 throw new IOException("Underflow. Rewind past start of the slice.");
111 }
112 reset();
113 // fall-through
114 }
115 while (!isEmpty() && bytes > 0) {
116 readByte();
117 bytes--;
118 }
119 if (bytes != 0) {
120 throw new IOException("Underflow. Rewind past end of the slice");
121 }
122 }
123
124 @Override
125 public byte readByte() throws IOException {
126 readBytes(singleByte, 0, 1);
127 return singleByte[0];
128 }
129
130 @Override
131 public void readBytes(byte[] b, int off, int len) throws IOException {
132 try {
133 int n;
134 while (len > 0) {
135 while ((n = inflater.inflate(b, off, len)) == 0) {
136 // FIXME few last bytes (checksum?) may be ignored by inflater, thus inflate may return 0 in
137 // perfectly legal conditions (when all data already expanded, but there are still some bytes
138 // in the input stream
139 if (inflater.finished() || inflater.needsDictionary()) {
140 throw new EOFException();
141 }
142 if (inflater.needsInput()) {
143 // fill:
144 int toRead = super.available();
145 if (toRead > buffer.length) {
146 toRead = buffer.length;
147 }
148 super.readBytes(buffer, 0, toRead);
149 inflater.setInput(buffer, 0, toRead);
150 }
151 }
152 off += n;
153 len -= n;
154 decompressedPos += n;
155 if (len == 0) {
156 return; // filled
157 }
158 }
159 } catch (DataFormatException e) {
160 String s = e.getMessage();
161 throw new ZipException(s != null ? s : "Invalid ZLIB data format");
162 }
163 }
164 }