sl@0
|
1 |
/* Portions Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
|
sl@0
|
2 |
* All rights reserved.
|
sl@0
|
3 |
*/
|
sl@0
|
4 |
|
sl@0
|
5 |
/* zran.c -- example of zlib/gzip stream indexing and random access
|
sl@0
|
6 |
* Copyright (C) 2005 Mark Adler
|
sl@0
|
7 |
* For conditions of distribution and use, see copyright notice in zlib.h
|
sl@0
|
8 |
Version 1.0 29 May 2005 Mark Adler */
|
sl@0
|
9 |
|
sl@0
|
10 |
/* Illustrate the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary()
|
sl@0
|
11 |
for random access of a compressed file. A file containing a zlib or gzip
|
sl@0
|
12 |
stream is provided on the command line. The compressed stream is decoded in
|
sl@0
|
13 |
its entirety, and an index built with access points about every SPAN bytes
|
sl@0
|
14 |
in the uncompressed output. The compressed file is left open, and can then
|
sl@0
|
15 |
be read randomly, having to decompress on the average SPAN/2 uncompressed
|
sl@0
|
16 |
bytes before getting to the desired block of data.
|
sl@0
|
17 |
|
sl@0
|
18 |
An access point can be created at the start of any deflate block, by saving
|
sl@0
|
19 |
the starting file offset and bit of that block, and the 32K bytes of
|
sl@0
|
20 |
uncompressed data that precede that block. Also the uncompressed offset of
|
sl@0
|
21 |
that block is saved to provide a referece for locating a desired starting
|
sl@0
|
22 |
point in the uncompressed stream. build_index() works by decompressing the
|
sl@0
|
23 |
input zlib or gzip stream a block at a time, and at the end of each block
|
sl@0
|
24 |
deciding if enough uncompressed data has gone by to justify the creation of
|
sl@0
|
25 |
a new access point. If so, that point is saved in a data structure that
|
sl@0
|
26 |
grows as needed to accommodate the points.
|
sl@0
|
27 |
|
sl@0
|
28 |
To use the index, an offset in the uncompressed data is provided, for which
|
sl@0
|
29 |
the latest access point at or preceding that offset is located in the index.
|
sl@0
|
30 |
The input file is positioned to the specified location in the index, and if
|
sl@0
|
31 |
necessary the first few bits of the compressed data is read from the file.
|
sl@0
|
32 |
inflate is initialized with those bits and the 32K of uncompressed data, and
|
sl@0
|
33 |
the decompression then proceeds until the desired offset in the file is
|
sl@0
|
34 |
reached. Then the decompression continues to read the desired uncompressed
|
sl@0
|
35 |
data from the file.
|
sl@0
|
36 |
|
sl@0
|
37 |
Another approach would be to generate the index on demand. In that case,
|
sl@0
|
38 |
requests for random access reads from the compressed data would try to use
|
sl@0
|
39 |
the index, but if a read far enough past the end of the index is required,
|
sl@0
|
40 |
then further index entries would be generated and added.
|
sl@0
|
41 |
|
sl@0
|
42 |
There is some fair bit of overhead to starting inflation for the random
|
sl@0
|
43 |
access, mainly copying the 32K byte dictionary. So if small pieces of the
|
sl@0
|
44 |
file are being accessed, it would make sense to implement a cache to hold
|
sl@0
|
45 |
some lookahead and avoid many calls to extract() for small lengths.
|
sl@0
|
46 |
|
sl@0
|
47 |
Another way to build an index would be to use inflateCopy(). That would
|
sl@0
|
48 |
not be constrained to have access points at block boundaries, but requires
|
sl@0
|
49 |
more memory per access point, and also cannot be saved to file due to the
|
sl@0
|
50 |
use of pointers in the state. The approach here allows for storage of the
|
sl@0
|
51 |
index in a file.
|
sl@0
|
52 |
*/
|
sl@0
|
53 |
|
sl@0
|
54 |
#include <e32test.h>
|
sl@0
|
55 |
#include <stdio.h>
|
sl@0
|
56 |
#include <stdlib.h>
|
sl@0
|
57 |
#include <string.h>
|
sl@0
|
58 |
#include <fcntl.h>
|
sl@0
|
59 |
#include <zlib.h>
|
sl@0
|
60 |
|
sl@0
|
61 |
_LIT(KTestTitle, "inflatePrime() Test.");
|
sl@0
|
62 |
|
sl@0
|
63 |
RTest test(_L("inflateprimetest.exe"));
|
sl@0
|
64 |
const int numTestFiles = 2;
|
sl@0
|
65 |
const char *filePath = "z:\\test\\inflateprimetest\\\0";
|
sl@0
|
66 |
const char *testFile[numTestFiles] = {"gzipped.gz\0", "zipped.zip\0"};
|
sl@0
|
67 |
|
sl@0
|
68 |
/* Test macro and function */
|
sl@0
|
69 |
void Check(TInt aValue, TInt aExpected, TInt aLine)
|
sl@0
|
70 |
{
|
sl@0
|
71 |
if (aValue != aExpected)
|
sl@0
|
72 |
{
|
sl@0
|
73 |
test.Printf(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue);
|
sl@0
|
74 |
test.operator()(EFalse, aLine);
|
sl@0
|
75 |
}
|
sl@0
|
76 |
}
|
sl@0
|
77 |
#define test2(a, b) Check(a, b, __LINE__)
|
sl@0
|
78 |
|
sl@0
|
79 |
#define SPAN 1048576L /* desired distance between access points */
|
sl@0
|
80 |
#define WINSIZE 32768U /* sliding window size */
|
sl@0
|
81 |
#define CHUNK 128 /* file input buffer size */
|
sl@0
|
82 |
|
sl@0
|
83 |
/* access point entry */
|
sl@0
|
84 |
struct point {
|
sl@0
|
85 |
off_t out; /* corresponding offset in uncompressed data */
|
sl@0
|
86 |
off_t in; /* offset in input file of first full byte */
|
sl@0
|
87 |
int bits; /* number of bits (1-7) from byte at in - 1, or 0 */
|
sl@0
|
88 |
unsigned char window[WINSIZE]; /* preceding 32K of uncompressed data */
|
sl@0
|
89 |
};
|
sl@0
|
90 |
|
sl@0
|
91 |
/* access point list */
|
sl@0
|
92 |
struct access {
|
sl@0
|
93 |
int have; /* number of list entries filled in */
|
sl@0
|
94 |
int size; /* number of list entries allocated */
|
sl@0
|
95 |
struct point *list; /* allocated list */
|
sl@0
|
96 |
};
|
sl@0
|
97 |
|
sl@0
|
98 |
/* Deallocate an index built by build_index() */
|
sl@0
|
99 |
void free_index(struct access *index)
|
sl@0
|
100 |
{
|
sl@0
|
101 |
if (index != NULL) {
|
sl@0
|
102 |
free(index->list);
|
sl@0
|
103 |
free(index);
|
sl@0
|
104 |
}
|
sl@0
|
105 |
}
|
sl@0
|
106 |
|
sl@0
|
107 |
/* Add an entry to the access point list. If out of memory, deallocate the
|
sl@0
|
108 |
existing list and return NULL. */
|
sl@0
|
109 |
struct access *addpoint(struct access *index, int bits,
|
sl@0
|
110 |
off_t in, off_t out, unsigned left, unsigned char *window)
|
sl@0
|
111 |
{
|
sl@0
|
112 |
struct point *next;
|
sl@0
|
113 |
|
sl@0
|
114 |
// if list is empty, create it (start with eight points)
|
sl@0
|
115 |
if (index == NULL) {
|
sl@0
|
116 |
index = (struct access *)malloc(sizeof(struct access));
|
sl@0
|
117 |
if (index == NULL) return NULL;
|
sl@0
|
118 |
index->list = (struct point *)malloc(sizeof(struct point) << 3);
|
sl@0
|
119 |
if (index->list == NULL) {
|
sl@0
|
120 |
free(index);
|
sl@0
|
121 |
return NULL;
|
sl@0
|
122 |
}
|
sl@0
|
123 |
index->size = 8;
|
sl@0
|
124 |
index->have = 0;
|
sl@0
|
125 |
}
|
sl@0
|
126 |
|
sl@0
|
127 |
// if list is full, make it bigger
|
sl@0
|
128 |
else if (index->have == index->size) {
|
sl@0
|
129 |
index->size <<= 1;
|
sl@0
|
130 |
next = (struct point *)realloc(index->list, sizeof(struct point) * index->size);
|
sl@0
|
131 |
if (next == NULL) {
|
sl@0
|
132 |
free_index(index);
|
sl@0
|
133 |
return NULL;
|
sl@0
|
134 |
}
|
sl@0
|
135 |
index->list = next;
|
sl@0
|
136 |
}
|
sl@0
|
137 |
|
sl@0
|
138 |
// fill in entry and increment how many we have
|
sl@0
|
139 |
next = index->list + index->have;
|
sl@0
|
140 |
next->bits = bits;
|
sl@0
|
141 |
next->in = in;
|
sl@0
|
142 |
next->out = out;
|
sl@0
|
143 |
if (left)
|
sl@0
|
144 |
memcpy(next->window, window + WINSIZE - left, left);
|
sl@0
|
145 |
if (left < WINSIZE)
|
sl@0
|
146 |
memcpy(next->window + left, window, WINSIZE - left);
|
sl@0
|
147 |
index->have++;
|
sl@0
|
148 |
|
sl@0
|
149 |
/* return list, possibly reallocated */
|
sl@0
|
150 |
return index;
|
sl@0
|
151 |
}
|
sl@0
|
152 |
|
sl@0
|
153 |
/* Make one entire pass through the compressed stream and build an index, with
|
sl@0
|
154 |
access points about every span bytes of uncompressed output -- span is
|
sl@0
|
155 |
chosen to balance the speed of random access against the memory requirements
|
sl@0
|
156 |
of the list, about 32K bytes per access point. Note that data after the end
|
sl@0
|
157 |
of the first zlib or gzip stream in the file is ignored. build_index()
|
sl@0
|
158 |
returns the number of access points on success (>= 1), Z_MEM_ERROR for out
|
sl@0
|
159 |
of memory, Z_DATA_ERROR for an error in the input file, or Z_ERRNO for a
|
sl@0
|
160 |
file read error. On success, *built points to the resulting index. */
|
sl@0
|
161 |
int build_index(FILE *in, off_t span, struct access **built)
|
sl@0
|
162 |
{
|
sl@0
|
163 |
int ret;
|
sl@0
|
164 |
off_t totin, totout; /* our own total counters to avoid 4GB limit */
|
sl@0
|
165 |
off_t last; /* totout value of last access point */
|
sl@0
|
166 |
struct access *index; /* access points being generated */
|
sl@0
|
167 |
z_stream strm;
|
sl@0
|
168 |
unsigned char input[CHUNK];
|
sl@0
|
169 |
unsigned char window[WINSIZE];
|
sl@0
|
170 |
struct point *next = NULL;
|
sl@0
|
171 |
|
sl@0
|
172 |
/* initialize inflate */
|
sl@0
|
173 |
strm.zalloc = Z_NULL;
|
sl@0
|
174 |
strm.zfree = Z_NULL;
|
sl@0
|
175 |
strm.opaque = Z_NULL;
|
sl@0
|
176 |
strm.avail_in = 0;
|
sl@0
|
177 |
strm.next_in = Z_NULL;
|
sl@0
|
178 |
ret = inflateInit2(&strm, 47); /* automatic zlib or gzip decoding */
|
sl@0
|
179 |
if (ret != Z_OK)
|
sl@0
|
180 |
return ret;
|
sl@0
|
181 |
|
sl@0
|
182 |
/* inflate the input, maintain a sliding window, and build an index -- this
|
sl@0
|
183 |
also validates the integrity of the compressed data using the check
|
sl@0
|
184 |
information at the end of the gzip or zlib stream */
|
sl@0
|
185 |
totin = totout = last = 0;
|
sl@0
|
186 |
index = NULL; /* will be allocated by first addpoint() */
|
sl@0
|
187 |
strm.avail_out = 0;
|
sl@0
|
188 |
do {
|
sl@0
|
189 |
/* get some compressed data from input file */
|
sl@0
|
190 |
strm.avail_in = fread(input, 1, CHUNK, in);
|
sl@0
|
191 |
if (ferror(in)) {
|
sl@0
|
192 |
ret = Z_ERRNO;
|
sl@0
|
193 |
goto build_index_error;
|
sl@0
|
194 |
}
|
sl@0
|
195 |
if (strm.avail_in == 0) {
|
sl@0
|
196 |
ret = Z_DATA_ERROR;
|
sl@0
|
197 |
goto build_index_error;
|
sl@0
|
198 |
}
|
sl@0
|
199 |
strm.next_in = input;
|
sl@0
|
200 |
|
sl@0
|
201 |
/* process all of that, or until end of stream */
|
sl@0
|
202 |
do {
|
sl@0
|
203 |
/* reset sliding window if necessary */
|
sl@0
|
204 |
if (strm.avail_out == 0) {
|
sl@0
|
205 |
strm.avail_out = WINSIZE;
|
sl@0
|
206 |
strm.next_out = window;
|
sl@0
|
207 |
}
|
sl@0
|
208 |
|
sl@0
|
209 |
/* inflate until out of input, output, or at end of block --
|
sl@0
|
210 |
update the total input and output counters */
|
sl@0
|
211 |
totin += strm.avail_in;
|
sl@0
|
212 |
totout += strm.avail_out;
|
sl@0
|
213 |
ret = inflate(&strm, Z_BLOCK); /* return at end of block */
|
sl@0
|
214 |
totin -= strm.avail_in;
|
sl@0
|
215 |
totout -= strm.avail_out;
|
sl@0
|
216 |
if (ret == Z_NEED_DICT)
|
sl@0
|
217 |
ret = Z_DATA_ERROR;
|
sl@0
|
218 |
if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
|
sl@0
|
219 |
goto build_index_error;
|
sl@0
|
220 |
if (ret == Z_STREAM_END)
|
sl@0
|
221 |
break;
|
sl@0
|
222 |
|
sl@0
|
223 |
/* if at end of block, consider adding an index entry (note that if
|
sl@0
|
224 |
data_type indicates an end-of-block, then all of the
|
sl@0
|
225 |
uncompressed data from that block has been delivered, and none
|
sl@0
|
226 |
of the compressed data after that block has been consumed,
|
sl@0
|
227 |
except for up to seven bits) -- the totout == 0 provides an
|
sl@0
|
228 |
entry point after the zlib or gzip header, and assures that the
|
sl@0
|
229 |
index always has at least one access point; we avoid creating an
|
sl@0
|
230 |
access point after the last block by checking bit 6 of data_type
|
sl@0
|
231 |
*/
|
sl@0
|
232 |
if ((strm.data_type & 128) && !(strm.data_type & 64) &&
|
sl@0
|
233 |
(totout == 0 || totout - last > span)) {
|
sl@0
|
234 |
index = addpoint(index, strm.data_type & 7, totin,
|
sl@0
|
235 |
totout, strm.avail_out, window);
|
sl@0
|
236 |
if (index == NULL) {
|
sl@0
|
237 |
ret = Z_MEM_ERROR;
|
sl@0
|
238 |
goto build_index_error;
|
sl@0
|
239 |
}
|
sl@0
|
240 |
last = totout;
|
sl@0
|
241 |
}
|
sl@0
|
242 |
} while (strm.avail_in != 0);
|
sl@0
|
243 |
} while (ret != Z_STREAM_END);
|
sl@0
|
244 |
|
sl@0
|
245 |
/* clean up and return index (release unused entries in list) */
|
sl@0
|
246 |
(void)inflateEnd(&strm);
|
sl@0
|
247 |
|
sl@0
|
248 |
next = (struct point *)realloc(index->list, sizeof(struct point) * index->have);
|
sl@0
|
249 |
if (next == NULL) {
|
sl@0
|
250 |
free_index(index);
|
sl@0
|
251 |
return Z_MEM_ERROR;
|
sl@0
|
252 |
}
|
sl@0
|
253 |
index->list = next;
|
sl@0
|
254 |
index->size = index->have;
|
sl@0
|
255 |
*built = index;
|
sl@0
|
256 |
return index->size;
|
sl@0
|
257 |
|
sl@0
|
258 |
/* return error */
|
sl@0
|
259 |
build_index_error:
|
sl@0
|
260 |
(void)inflateEnd(&strm);
|
sl@0
|
261 |
if (index != NULL)
|
sl@0
|
262 |
free_index(index);
|
sl@0
|
263 |
return ret;
|
sl@0
|
264 |
}
|
sl@0
|
265 |
|
sl@0
|
266 |
/* Use the index to read len bytes from offset into buf, return bytes read or
|
sl@0
|
267 |
negative for error (Z_DATA_ERROR or Z_MEM_ERROR). If data is requested past
|
sl@0
|
268 |
the end of the uncompressed data, then extract() will return a value less
|
sl@0
|
269 |
than len, indicating how much as actually read into buf. This function
|
sl@0
|
270 |
should not return a data error unless the file was modified since the index
|
sl@0
|
271 |
was generated. extract() may also return Z_ERRNO if there is an error on
|
sl@0
|
272 |
reading or seeking the input file. */
|
sl@0
|
273 |
int extract(FILE *in, struct access *index, off_t offset,
|
sl@0
|
274 |
unsigned char *buf, int len)
|
sl@0
|
275 |
{
|
sl@0
|
276 |
int ret, skip, value;
|
sl@0
|
277 |
z_stream strm;
|
sl@0
|
278 |
struct point *here;
|
sl@0
|
279 |
unsigned char input[CHUNK];
|
sl@0
|
280 |
//unsigned char discard[WINSIZE]; /* No longer required. See comments below. */
|
sl@0
|
281 |
|
sl@0
|
282 |
/* proceed only if something reasonable to do */
|
sl@0
|
283 |
if (len < 0)
|
sl@0
|
284 |
return 0;
|
sl@0
|
285 |
|
sl@0
|
286 |
/* find where in stream to start */
|
sl@0
|
287 |
here = index->list;
|
sl@0
|
288 |
ret = index->have;
|
sl@0
|
289 |
while (--ret && here[1].out <= offset)
|
sl@0
|
290 |
here++;
|
sl@0
|
291 |
|
sl@0
|
292 |
/* initialize file and inflate state to start there */
|
sl@0
|
293 |
strm.zalloc = Z_NULL;
|
sl@0
|
294 |
strm.zfree = Z_NULL;
|
sl@0
|
295 |
strm.opaque = Z_NULL;
|
sl@0
|
296 |
strm.avail_in = 0;
|
sl@0
|
297 |
strm.next_in = Z_NULL;
|
sl@0
|
298 |
ret = inflateInit2(&strm, -15); /* raw inflate */
|
sl@0
|
299 |
if (ret != Z_OK)
|
sl@0
|
300 |
return ret;
|
sl@0
|
301 |
ret = fseek(in, here->in - (here->bits ? 1 : 0), SEEK_SET);
|
sl@0
|
302 |
if (ret == -1)
|
sl@0
|
303 |
goto extract_ret;
|
sl@0
|
304 |
|
sl@0
|
305 |
ret = getc(in);
|
sl@0
|
306 |
if (ret == -1) {
|
sl@0
|
307 |
ret = ferror(in) ? Z_ERRNO : Z_DATA_ERROR;
|
sl@0
|
308 |
goto extract_ret;
|
sl@0
|
309 |
}
|
sl@0
|
310 |
|
sl@0
|
311 |
// If bits is > 0 set the value as done in the original zran.c
|
sl@0
|
312 |
// else set the value to the next byte to prove that inflatePrime
|
sl@0
|
313 |
// is not adding anything to the start of the stream when bits is
|
sl@0
|
314 |
// set to 0. It is then necessary to unget the byte.
|
sl@0
|
315 |
if(here->bits) {
|
sl@0
|
316 |
value = ret >> (8 - here->bits);
|
sl@0
|
317 |
}
|
sl@0
|
318 |
else {
|
sl@0
|
319 |
value = ret;
|
sl@0
|
320 |
ungetc(ret, in);
|
sl@0
|
321 |
}
|
sl@0
|
322 |
|
sl@0
|
323 |
ret = inflatePrime(&strm, here->bits, value);
|
sl@0
|
324 |
if(ret != Z_OK) {
|
sl@0
|
325 |
goto extract_ret;
|
sl@0
|
326 |
}
|
sl@0
|
327 |
test.Printf(_L("zran: bits = %d\n"), here->bits);
|
sl@0
|
328 |
test.Printf(_L("zran: value = %d\n"), value);
|
sl@0
|
329 |
|
sl@0
|
330 |
(void)inflateSetDictionary(&strm, here->window, WINSIZE);
|
sl@0
|
331 |
|
sl@0
|
332 |
/* No longer required. See comment below.
|
sl@0
|
333 |
*
|
sl@0
|
334 |
* skip uncompressed bytes until offset reached, then satisfy request
|
sl@0
|
335 |
offset -= here->out;
|
sl@0
|
336 |
*/
|
sl@0
|
337 |
strm.avail_in = 0;
|
sl@0
|
338 |
skip = 1; /* while skipping to offset */
|
sl@0
|
339 |
do {
|
sl@0
|
340 |
/* define where to put uncompressed data, and how much */
|
sl@0
|
341 |
if (skip) { /* at offset now */
|
sl@0
|
342 |
strm.avail_out = len;
|
sl@0
|
343 |
strm.next_out = buf;
|
sl@0
|
344 |
skip = 0; /* only do this once */
|
sl@0
|
345 |
}
|
sl@0
|
346 |
|
sl@0
|
347 |
/* This code is not required in this test as it is used
|
sl@0
|
348 |
* to discard decompressed data between the current
|
sl@0
|
349 |
* access point and the offset(place in the file from
|
sl@0
|
350 |
* which we wish to decompress data).
|
sl@0
|
351 |
*
|
sl@0
|
352 |
if (offset > WINSIZE) { // skip WINSIZE bytes
|
sl@0
|
353 |
strm.avail_out = WINSIZE;
|
sl@0
|
354 |
strm.next_out = discard;
|
sl@0
|
355 |
offset -= WINSIZE;
|
sl@0
|
356 |
}
|
sl@0
|
357 |
else if (offset != 0) { // last skip
|
sl@0
|
358 |
strm.avail_out = (unsigned)offset;
|
sl@0
|
359 |
strm.next_out = discard;
|
sl@0
|
360 |
offset = 0;
|
sl@0
|
361 |
}
|
sl@0
|
362 |
*/
|
sl@0
|
363 |
|
sl@0
|
364 |
/* uncompress until avail_out filled, or end of stream */
|
sl@0
|
365 |
do {
|
sl@0
|
366 |
if (strm.avail_in == 0) {
|
sl@0
|
367 |
strm.avail_in = fread(input, 1, CHUNK, in);
|
sl@0
|
368 |
if (ferror(in)) {
|
sl@0
|
369 |
ret = Z_ERRNO;
|
sl@0
|
370 |
goto extract_ret;
|
sl@0
|
371 |
}
|
sl@0
|
372 |
if (strm.avail_in == 0) {
|
sl@0
|
373 |
ret = Z_DATA_ERROR;
|
sl@0
|
374 |
goto extract_ret;
|
sl@0
|
375 |
}
|
sl@0
|
376 |
strm.next_in = input;
|
sl@0
|
377 |
}
|
sl@0
|
378 |
ret = inflate(&strm, Z_NO_FLUSH); /* normal inflate */
|
sl@0
|
379 |
if (ret == Z_NEED_DICT)
|
sl@0
|
380 |
ret = Z_DATA_ERROR;
|
sl@0
|
381 |
if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
|
sl@0
|
382 |
goto extract_ret;
|
sl@0
|
383 |
if (ret == Z_STREAM_END)
|
sl@0
|
384 |
break;
|
sl@0
|
385 |
} while (strm.avail_out != 0);
|
sl@0
|
386 |
|
sl@0
|
387 |
/* if reach end of stream, then don't keep trying to get more */
|
sl@0
|
388 |
if (ret == Z_STREAM_END)
|
sl@0
|
389 |
break;
|
sl@0
|
390 |
|
sl@0
|
391 |
/* do until offset reached and requested data read, or stream ends */
|
sl@0
|
392 |
} while (skip);
|
sl@0
|
393 |
|
sl@0
|
394 |
/* compute number of uncompressed bytes read after offset */
|
sl@0
|
395 |
ret = skip ? 0 : len - strm.avail_out;
|
sl@0
|
396 |
|
sl@0
|
397 |
/* clean up and return bytes read or error */
|
sl@0
|
398 |
extract_ret:
|
sl@0
|
399 |
(void)inflateEnd(&strm);
|
sl@0
|
400 |
return ret;
|
sl@0
|
401 |
}
|
sl@0
|
402 |
|
sl@0
|
403 |
/* Demonstrate the use of build_index() and extract() by processing the file
|
sl@0
|
404 |
provided and then extracting CHUNK bytes at each access point. */
|
sl@0
|
405 |
int TestInflatePrime(char *file)
|
sl@0
|
406 |
{
|
sl@0
|
407 |
int len;
|
sl@0
|
408 |
FILE *in;
|
sl@0
|
409 |
struct access *index;
|
sl@0
|
410 |
unsigned char buf[CHUNK];
|
sl@0
|
411 |
|
sl@0
|
412 |
in = fopen(file, "rb");
|
sl@0
|
413 |
if (in == NULL)
|
sl@0
|
414 |
{
|
sl@0
|
415 |
return KErrPathNotFound;
|
sl@0
|
416 |
}
|
sl@0
|
417 |
|
sl@0
|
418 |
// build index
|
sl@0
|
419 |
len = build_index(in, SPAN, &index);
|
sl@0
|
420 |
if (len < 0)
|
sl@0
|
421 |
{
|
sl@0
|
422 |
fclose(in);
|
sl@0
|
423 |
test.Printf(_L("error: %d\n"), len);
|
sl@0
|
424 |
return KErrGeneral;
|
sl@0
|
425 |
}
|
sl@0
|
426 |
test.Printf(_L("zran: built index with %d access points\n"), len);
|
sl@0
|
427 |
|
sl@0
|
428 |
// Extract some data at the start of each access point. This is done
|
sl@0
|
429 |
// so that we can try extracting some data that does not necessarily
|
sl@0
|
430 |
// start at a byte boundary ie it might start mid byte.
|
sl@0
|
431 |
for(int i = 0; i < index->have; i++)
|
sl@0
|
432 |
{
|
sl@0
|
433 |
len = extract(in, index, index->list[i].out, buf, CHUNK);
|
sl@0
|
434 |
if (len < 0)
|
sl@0
|
435 |
{
|
sl@0
|
436 |
test.Printf(_L("zran: extraction failed: "));
|
sl@0
|
437 |
|
sl@0
|
438 |
if(len == Z_MEM_ERROR)
|
sl@0
|
439 |
{
|
sl@0
|
440 |
test.Printf(_L("out of memory error\n"));
|
sl@0
|
441 |
}
|
sl@0
|
442 |
else
|
sl@0
|
443 |
{
|
sl@0
|
444 |
test.Printf(_L("input corrupted error\n"));
|
sl@0
|
445 |
}
|
sl@0
|
446 |
}
|
sl@0
|
447 |
else
|
sl@0
|
448 |
{
|
sl@0
|
449 |
test.Printf(_L("zran: extracted %d bytes at %Lu\n"), len, index->list[i].out);
|
sl@0
|
450 |
}
|
sl@0
|
451 |
}
|
sl@0
|
452 |
|
sl@0
|
453 |
// clean up and exit
|
sl@0
|
454 |
free_index(index);
|
sl@0
|
455 |
fclose(in);
|
sl@0
|
456 |
|
sl@0
|
457 |
return KErrNone;
|
sl@0
|
458 |
}
|
sl@0
|
459 |
|
sl@0
|
460 |
/**
|
sl@0
|
461 |
@SYMTestCaseID SYSLIB-EZLIB2-UT-4273
|
sl@0
|
462 |
@SYMTestCaseDesc To check that data can be decompressed at various points in a
|
sl@0
|
463 |
compressed file (i.e. decompression may start part of the way
|
sl@0
|
464 |
through a byte) via the use of inflatePrime().
|
sl@0
|
465 |
@SYMTestPriority Low
|
sl@0
|
466 |
@SYMTestActions 1. Open a compressed file for reading.
|
sl@0
|
467 |
2. Create an inflate stream and initialise it using inflateInit2(),
|
sl@0
|
468 |
setting windowBits to 47 (automatic gzip/zip header detection).
|
sl@0
|
469 |
3. Inflate the data in the file using inflate(). During inflation
|
sl@0
|
470 |
create access points using structure Point which maps points
|
sl@0
|
471 |
in the uncompressed data with points in the compressed data.
|
sl@0
|
472 |
The first access point should be at the start of the data
|
sl@0
|
473 |
i.e. after the header.
|
sl@0
|
474 |
|
sl@0
|
475 |
Structure Point consist of :
|
sl@0
|
476 |
• UPoint(in bytes) – this is the point in the uncompressed data
|
sl@0
|
477 |
• CPoint(in bytes) – this is the point in the compressed data
|
sl@0
|
478 |
• bits(in bits) – this is the point in the compressed data
|
sl@0
|
479 |
4. Cleanup the inflate stream using inflateEnd().
|
sl@0
|
480 |
5. For each access point do the following:
|
sl@0
|
481 |
a. Initialise the inflate stream using inflateInit2(),
|
sl@0
|
482 |
setting windowBits to -15.
|
sl@0
|
483 |
b. Move the file pointer to CPoint - 1 in the input file.
|
sl@0
|
484 |
c. Calculate the value which will be passed to inflatePrime().
|
sl@0
|
485 |
The algorithm used to calculate value can be seen in the
|
sl@0
|
486 |
attached diagram (in the test spec).
|
sl@0
|
487 |
d. Call inflatePrime() with the bits and value.
|
sl@0
|
488 |
e. Inflate a small section of in the input file using inflate().
|
sl@0
|
489 |
f. Cleanup the inflate stream using inflateEnd().
|
sl@0
|
490 |
6. Close the compressed file and cleanup any allocated memory.
|
sl@0
|
491 |
|
sl@0
|
492 |
Note: This test should be completed using a zlib file and a gzip
|
sl@0
|
493 |
file. These files should be 500 – 1000KB in size.
|
sl@0
|
494 |
@SYMTestExpectedResults inflatePrime() should return Z_OK and the data should be
|
sl@0
|
495 |
decompressed with no errors.
|
sl@0
|
496 |
@SYMDEF REQ7362
|
sl@0
|
497 |
*/
|
sl@0
|
498 |
void RunTestL()
|
sl@0
|
499 |
{
|
sl@0
|
500 |
test.Next(_L(" @SYMTestCaseID:SYSLIB-EZLIB2-UT-4273 "));
|
sl@0
|
501 |
int err;
|
sl@0
|
502 |
char file[KMaxFileName];
|
sl@0
|
503 |
|
sl@0
|
504 |
for(int i = 0; i < numTestFiles; i++)
|
sl@0
|
505 |
{
|
sl@0
|
506 |
TBuf<40> testName(_L("inflatePrime test using file "));
|
sl@0
|
507 |
testName.AppendNum(i);
|
sl@0
|
508 |
test.Next(testName);
|
sl@0
|
509 |
|
sl@0
|
510 |
strcpy(file, filePath);
|
sl@0
|
511 |
strcat(file, testFile[i]);
|
sl@0
|
512 |
|
sl@0
|
513 |
err = TestInflatePrime(file);
|
sl@0
|
514 |
|
sl@0
|
515 |
if(err == KErrPathNotFound)
|
sl@0
|
516 |
{
|
sl@0
|
517 |
test.Printf(_L("zran: could not open file number %d for reading\n"), i);
|
sl@0
|
518 |
User::Leave(err);
|
sl@0
|
519 |
}
|
sl@0
|
520 |
else if(err != KErrNone)
|
sl@0
|
521 |
{
|
sl@0
|
522 |
User::Leave(err);
|
sl@0
|
523 |
}
|
sl@0
|
524 |
|
sl@0
|
525 |
test.Printf(_L("\n"));
|
sl@0
|
526 |
}
|
sl@0
|
527 |
}
|
sl@0
|
528 |
|
sl@0
|
529 |
TInt E32Main()
|
sl@0
|
530 |
{
|
sl@0
|
531 |
__UHEAP_MARK;
|
sl@0
|
532 |
|
sl@0
|
533 |
test.Printf(_L("\n"));
|
sl@0
|
534 |
test.Title();
|
sl@0
|
535 |
test.Start(KTestTitle);
|
sl@0
|
536 |
|
sl@0
|
537 |
CTrapCleanup* cleanup = CTrapCleanup::New();
|
sl@0
|
538 |
|
sl@0
|
539 |
TRAPD(err, RunTestL());
|
sl@0
|
540 |
test2(err, KErrNone);
|
sl@0
|
541 |
|
sl@0
|
542 |
test.End();
|
sl@0
|
543 |
test.Close();
|
sl@0
|
544 |
delete cleanup;
|
sl@0
|
545 |
|
sl@0
|
546 |
__UHEAP_MARKEND;
|
sl@0
|
547 |
return KErrNone;
|
sl@0
|
548 |
}
|