1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
/*
* Copyright (C) 2011 Arunesh Mathur
*
* This file is a part of zimreader-java.
*
* zimreader-java is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3.0 as
* published by the Free Software Foundation.
*
* zimreader-java is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with zimreader-java. If not, see <http://www.gnu.org/licenses/>.
*/
package org.openzim;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
/**
* This is an implementation of RandomAccessFile to ensure that it is an
* InputStream as well, specifically designed for reading a ZIM file. Ad-Hoc
* implementation, can be improved.
*
* @author Arunesh Mathur <aruneshmathur1990 at gmail.com>
* @author Michael Christen
* bugfix to long parsing (return value was int),
* moved conditions for exceptions to asserts,
* refactoring and merge with Utilities
*/
public class RandomAccessFileZIMInputStream extends InputStream {
private final RandomAccessFile mRAFReader;
private long mMarked = -1;
private final byte[] buffer2 = new byte[2];
private final byte[] buffer4 = new byte[4];
private final byte[] buffer8 = new byte[8];
public RandomAccessFileZIMInputStream(final RandomAccessFile reader) {
this.mRAFReader = reader;
}
public int readTwoLittleEndianBytesInt() throws IOException {
this.mRAFReader.read(buffer2, 0, 2);
return toTwoLittleEndianInteger(buffer2);
}
public int readFourLittleEndianBytesInt() throws IOException {
this.mRAFReader.read(buffer4, 0, 4);
return toFourLittleEndianInteger(buffer4);
}
public long readEightLittleEndianBytesLong() throws IOException {
this.mRAFReader.read(buffer8, 0, 8);
return toEightLittleEndianLong(buffer8);
}
private static int toTwoLittleEndianInteger(final byte[] buffer) {
return ((buffer[0] & 0xFF) | ((buffer[1] & 0xFF) << 8));
}
public static int toFourLittleEndianInteger(final byte[] buffer) {
return
((buffer[0] & 0xFF) | ((buffer[1] & 0xFF) << 8)
| ((buffer[2] & 0xFF) << 16) | ((buffer[3] & 0xFF) << 24));
}
public static int toFourLittleEndianInteger(final byte[] buffer, int pos) {
return
((buffer[pos ] & 0xFF) | ((buffer[pos + 1] & 0xFF) << 8)
| ((buffer[pos + 2] & 0xFF) << 16) | ((buffer[pos + 3] & 0xFF) << 24));
}
public static long toEightLittleEndianLong(final byte[] buffer) {
return // cast to long required otherwise this is again an integer
((long)(buffer[0] & 0xFF) | ((long)(buffer[1] & 0xFF) << 8)
| ((long)(buffer[2] & 0xFF) << 16) | ((long)(buffer[3] & 0xFF) << 24)
| ((long)(buffer[4] & 0xFF) << 32) | ((long)(buffer[5] & 0xFF) << 40)
| ((long)(buffer[6] & 0xFF) << 48) | ((long)(buffer[7] & 0xFF) << 56));
}
public static long toEightLittleEndianLong(final byte[] buffer, int pos) {
return // cast to long required otherwise this is again an integer
((long)(buffer[pos ] & 0xFF) | ((long)(buffer[pos + 1] & 0xFF) << 8)
| ((long)(buffer[pos + 2] & 0xFF) << 16) | ((long)(buffer[pos + 3] & 0xFF) << 24)
| ((long)(buffer[pos + 4] & 0xFF) << 32) | ((long)(buffer[pos + 5] & 0xFF) << 40)
| ((long)(buffer[pos + 6] & 0xFF) << 48) | ((long)(buffer[pos + 7] & 0xFF) << 56));
}
public static void skipFully(final InputStream stream, final long bytes) throws IOException {
for (long i = stream.skip(bytes); i < bytes; i += stream.skip(bytes - i));
}
// Reads characters from the current position into a String and stops when a
// '\0' is encountered
public String readZeroTerminatedString() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int b;
while ((b = this.mRAFReader.read()) != '\0' && b != -1) {
buffer.write(b);
}
return buffer.toString("UTF-8");
}
@Override
public int read() throws IOException {
return this.mRAFReader.read();
}
@Override
public int read(byte b[], int off, int len) throws IOException {
return this.mRAFReader.read(b, off, len);
}
public static byte[] readFully(final InputStream is, final int len) throws IOException {
byte[] b = new byte[len];
int c = 0;
while (c < len) {
c = c + is.read(b, c, len - c);
}
return b;
}
public static void readFully(final InputStream is, final byte[] b) throws IOException {
int c = 0;
while (c < b.length) {
c = c + is.read(b, c, b.length - c);
}
}
public RandomAccessFile getRandomAccessFile() {
return this.mRAFReader;
}
public void seek(final long pos) throws IOException {
this.mRAFReader.seek(pos);
}
public long getFilePointer() throws IOException {
return this.mRAFReader.getFilePointer();
}
public void mark() throws IOException {
this.mMarked = this.mRAFReader.getFilePointer();
}
@Override
public void reset() throws IOException {
if (this.mMarked == -1) {
return;
} else {
this.mRAFReader.seek(this.mMarked);
this.mMarked = -1;
}
}
}
|