blob: 7c34c6a35173ee420606ee00a304ad0ecdb3efec [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//
18// Provide access to a read-only asset.
19//
20
21#define LOG_TAG "asset"
22//#define NDEBUG 0
23
24#include <utils/Asset.h>
25#include <utils/Atomic.h>
26#include <utils/FileMap.h>
Christopher Tateb100cbf2010-07-26 11:24:18 -070027#include <utils/StreamingZipInflater.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028#include <utils/ZipUtils.h>
29#include <utils/ZipFileRO.h>
30#include <utils/Log.h>
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070031#include <utils/threads.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
33#include <string.h>
34#include <memory.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <assert.h>
Kenny Rootddb76c42010-11-24 12:56:06 -080038#include <unistd.h>
39#include <sys/stat.h>
40#include <sys/types.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
42using namespace android;
43
44#ifndef O_BINARY
45# define O_BINARY 0
46#endif
47
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070048static Mutex gAssetLock;
49static int32_t gCount = 0;
50static Asset* gHead = NULL;
51static Asset* gTail = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
53int32_t Asset::getGlobalCount()
54{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070055 AutoMutex _l(gAssetLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 return gCount;
57}
58
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070059String8 Asset::getAssetAllocations()
60{
61 AutoMutex _l(gAssetLock);
62 String8 res;
63 Asset* cur = gHead;
64 while (cur != NULL) {
65 if (cur->isAllocated()) {
66 res.append(" ");
67 res.append(cur->getAssetSource());
Kenny Rootddb76c42010-11-24 12:56:06 -080068 off64_t size = (cur->getLength()+512)/1024;
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070069 char buf[64];
70 sprintf(buf, ": %dK\n", (int)size);
71 res.append(buf);
72 }
73 cur = cur->mNext;
74 }
75
76 return res;
77}
78
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079Asset::Asset(void)
80 : mAccessMode(ACCESS_UNKNOWN)
81{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070082 AutoMutex _l(gAssetLock);
83 gCount++;
84 mNext = mPrev = NULL;
85 if (gTail == NULL) {
86 gHead = gTail = this;
87 } else {
88 mPrev = gTail;
89 gTail->mNext = this;
90 gTail = this;
91 }
92 //LOGI("Creating Asset %p #%d\n", this, gCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093}
94
95Asset::~Asset(void)
96{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070097 AutoMutex _l(gAssetLock);
98 gCount--;
99 if (gHead == this) {
100 gHead = mNext;
101 }
102 if (gTail == this) {
103 gTail = mPrev;
104 }
105 if (mNext != NULL) {
106 mNext->mPrev = mPrev;
107 }
108 if (mPrev != NULL) {
109 mPrev->mNext = mNext;
110 }
111 mNext = mPrev = NULL;
112 //LOGI("Destroying Asset in %p #%d\n", this, gCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113}
114
115/*
116 * Create a new Asset from a file on disk. There is a fair chance that
117 * the file doesn't actually exist.
118 *
119 * We can use "mode" to decide how we want to go about it.
120 */
121/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
122{
123 _FileAsset* pAsset;
124 status_t result;
Kenny Rootddb76c42010-11-24 12:56:06 -0800125 off64_t length;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 int fd;
127
128 fd = open(fileName, O_RDONLY | O_BINARY);
129 if (fd < 0)
130 return NULL;
131
132 /*
133 * Under Linux, the lseek fails if we actually opened a directory. To
134 * be correct we should test the file type explicitly, but since we
135 * always open things read-only it doesn't really matter, so there's
136 * no value in incurring the extra overhead of an fstat() call.
137 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800138 // TODO(kroot): replace this with fstat despite the plea above.
139#if 1
140 length = lseek64(fd, 0, SEEK_END);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 if (length < 0) {
142 ::close(fd);
143 return NULL;
144 }
Kenny Rootddb76c42010-11-24 12:56:06 -0800145 (void) lseek64(fd, 0, SEEK_SET);
146#else
147 struct stat st;
148 if (fstat(fd, &st) < 0) {
149 ::close(fd);
150 return NULL;
151 }
152
153 if (!S_ISREG(st.st_mode)) {
154 ::close(fd);
155 return NULL;
156 }
157#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158
159 pAsset = new _FileAsset;
160 result = pAsset->openChunk(fileName, fd, 0, length);
161 if (result != NO_ERROR) {
162 delete pAsset;
163 return NULL;
164 }
165
166 pAsset->mAccessMode = mode;
167 return pAsset;
168}
169
170
171/*
172 * Create a new Asset from a compressed file on disk. There is a fair chance
173 * that the file doesn't actually exist.
174 *
175 * We currently support gzip files. We might want to handle .bz2 someday.
176 */
177/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
178 AccessMode mode)
179{
180 _CompressedAsset* pAsset;
181 status_t result;
Kenny Rootddb76c42010-11-24 12:56:06 -0800182 off64_t fileLen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 bool scanResult;
184 long offset;
185 int method;
186 long uncompressedLen, compressedLen;
187 int fd;
188
189 fd = open(fileName, O_RDONLY | O_BINARY);
190 if (fd < 0)
191 return NULL;
192
193 fileLen = lseek(fd, 0, SEEK_END);
194 if (fileLen < 0) {
195 ::close(fd);
196 return NULL;
197 }
198 (void) lseek(fd, 0, SEEK_SET);
199
200 /* want buffered I/O for the file scan; must dup so fclose() is safe */
201 FILE* fp = fdopen(dup(fd), "rb");
202 if (fp == NULL) {
203 ::close(fd);
204 return NULL;
205 }
206
207 unsigned long crc32;
208 scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen,
209 &compressedLen, &crc32);
210 offset = ftell(fp);
211 fclose(fp);
212 if (!scanResult) {
Steve Block1afd5ba2011-12-20 16:23:08 +0000213 ALOGD("File '%s' is not in gzip format\n", fileName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 ::close(fd);
215 return NULL;
216 }
217
218 pAsset = new _CompressedAsset;
219 result = pAsset->openChunk(fd, offset, method, uncompressedLen,
220 compressedLen);
221 if (result != NO_ERROR) {
222 delete pAsset;
223 return NULL;
224 }
225
226 pAsset->mAccessMode = mode;
227 return pAsset;
228}
229
230
231#if 0
232/*
233 * Create a new Asset from part of an open file.
234 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800235/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 size_t length, AccessMode mode)
237{
238 _FileAsset* pAsset;
239 status_t result;
240
241 pAsset = new _FileAsset;
242 result = pAsset->openChunk(NULL, fd, offset, length);
243 if (result != NO_ERROR)
244 return NULL;
245
246 pAsset->mAccessMode = mode;
247 return pAsset;
248}
249
250/*
251 * Create a new Asset from compressed data in an open file.
252 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800253/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 int compressionMethod, size_t uncompressedLen, size_t compressedLen,
255 AccessMode mode)
256{
257 _CompressedAsset* pAsset;
258 status_t result;
259
260 pAsset = new _CompressedAsset;
261 result = pAsset->openChunk(fd, offset, compressionMethod,
262 uncompressedLen, compressedLen);
263 if (result != NO_ERROR)
264 return NULL;
265
266 pAsset->mAccessMode = mode;
267 return pAsset;
268}
269#endif
270
271/*
272 * Create a new Asset from a memory mapping.
273 */
274/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
275 AccessMode mode)
276{
277 _FileAsset* pAsset;
278 status_t result;
279
280 pAsset = new _FileAsset;
281 result = pAsset->openChunk(dataMap);
282 if (result != NO_ERROR)
283 return NULL;
284
285 pAsset->mAccessMode = mode;
286 return pAsset;
287}
288
289/*
290 * Create a new Asset from compressed data in a memory mapping.
291 */
292/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
293 int method, size_t uncompressedLen, AccessMode mode)
294{
295 _CompressedAsset* pAsset;
296 status_t result;
297
298 pAsset = new _CompressedAsset;
299 result = pAsset->openChunk(dataMap, method, uncompressedLen);
300 if (result != NO_ERROR)
301 return NULL;
302
303 pAsset->mAccessMode = mode;
304 return pAsset;
305}
306
307
308/*
309 * Do generic seek() housekeeping. Pass in the offset/whence values from
310 * the seek request, along with the current chunk offset and the chunk
311 * length.
312 *
313 * Returns the new chunk offset, or -1 if the seek is illegal.
314 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800315off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316{
Kenny Rootddb76c42010-11-24 12:56:06 -0800317 off64_t newOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318
319 switch (whence) {
320 case SEEK_SET:
321 newOffset = offset;
322 break;
323 case SEEK_CUR:
324 newOffset = curPosn + offset;
325 break;
326 case SEEK_END:
327 newOffset = maxPosn + offset;
328 break;
329 default:
330 LOGW("unexpected whence %d\n", whence);
Kenny Rootddb76c42010-11-24 12:56:06 -0800331 // this was happening due to an off64_t size mismatch
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 assert(false);
Kenny Rootddb76c42010-11-24 12:56:06 -0800333 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 }
335
336 if (newOffset < 0 || newOffset > maxPosn) {
337 LOGW("seek out of range: want %ld, end=%ld\n",
338 (long) newOffset, (long) maxPosn);
Kenny Rootddb76c42010-11-24 12:56:06 -0800339 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 }
341
342 return newOffset;
343}
344
345
346/*
347 * ===========================================================================
348 * _FileAsset
349 * ===========================================================================
350 */
351
352/*
353 * Constructor.
354 */
355_FileAsset::_FileAsset(void)
356 : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
357{
358}
359
360/*
361 * Destructor. Release resources.
362 */
363_FileAsset::~_FileAsset(void)
364{
365 close();
366}
367
368/*
369 * Operate on a chunk of an uncompressed file.
370 *
371 * Zero-length chunks are allowed.
372 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800373status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374{
375 assert(mFp == NULL); // no reopen
376 assert(mMap == NULL);
377 assert(fd >= 0);
378 assert(offset >= 0);
379
380 /*
381 * Seek to end to get file length.
382 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800383 off64_t fileLength;
384 fileLength = lseek64(fd, 0, SEEK_END);
385 if (fileLength == (off64_t) -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 // probably a bad file descriptor
Steve Block1afd5ba2011-12-20 16:23:08 +0000387 ALOGD("failed lseek (errno=%d)\n", errno);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 return UNKNOWN_ERROR;
389 }
390
Kenny Rootddb76c42010-11-24 12:56:06 -0800391 if ((off64_t) (offset + length) > fileLength) {
Steve Block1afd5ba2011-12-20 16:23:08 +0000392 ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 (long) offset, (long) length, (long) fileLength);
394 return BAD_INDEX;
395 }
396
397 /* after fdopen, the fd will be closed on fclose() */
398 mFp = fdopen(fd, "rb");
399 if (mFp == NULL)
400 return UNKNOWN_ERROR;
401
402 mStart = offset;
403 mLength = length;
404 assert(mOffset == 0);
405
406 /* seek the FILE* to the start of chunk */
407 if (fseek(mFp, mStart, SEEK_SET) != 0) {
408 assert(false);
409 }
410
411 mFileName = fileName != NULL ? strdup(fileName) : NULL;
412
413 return NO_ERROR;
414}
415
416/*
417 * Create the chunk from the map.
418 */
419status_t _FileAsset::openChunk(FileMap* dataMap)
420{
421 assert(mFp == NULL); // no reopen
422 assert(mMap == NULL);
423 assert(dataMap != NULL);
424
425 mMap = dataMap;
426 mStart = -1; // not used
427 mLength = dataMap->getDataLength();
428 assert(mOffset == 0);
429
430 return NO_ERROR;
431}
432
433/*
434 * Read a chunk of data.
435 */
436ssize_t _FileAsset::read(void* buf, size_t count)
437{
438 size_t maxLen;
439 size_t actual;
440
441 assert(mOffset >= 0 && mOffset <= mLength);
442
443 if (getAccessMode() == ACCESS_BUFFER) {
444 /*
445 * On first access, read or map the entire file. The caller has
446 * requested buffer access, either because they're going to be
447 * using the buffer or because what they're doing has appropriate
448 * performance needs and access patterns.
449 */
450 if (mBuf == NULL)
451 getBuffer(false);
452 }
453
454 /* adjust count if we're near EOF */
455 maxLen = mLength - mOffset;
456 if (count > maxLen)
457 count = maxLen;
458
459 if (!count)
460 return 0;
461
462 if (mMap != NULL) {
463 /* copy from mapped area */
464 //printf("map read\n");
465 memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
466 actual = count;
467 } else if (mBuf != NULL) {
468 /* copy from buffer */
469 //printf("buf read\n");
470 memcpy(buf, (char*)mBuf + mOffset, count);
471 actual = count;
472 } else {
473 /* read from the file */
474 //printf("file read\n");
475 if (ftell(mFp) != mStart + mOffset) {
476 LOGE("Hosed: %ld != %ld+%ld\n",
477 ftell(mFp), (long) mStart, (long) mOffset);
478 assert(false);
479 }
480
481 /*
482 * This returns 0 on error or eof. We need to use ferror() or feof()
483 * to tell the difference, but we don't currently have those on the
484 * device. However, we know how much data is *supposed* to be in the
485 * file, so if we don't read the full amount we know something is
486 * hosed.
487 */
488 actual = fread(buf, 1, count, mFp);
489 if (actual == 0) // something failed -- I/O error?
490 return -1;
491
492 assert(actual == count);
493 }
494
495 mOffset += actual;
496 return actual;
497}
498
499/*
500 * Seek to a new position.
501 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800502off64_t _FileAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503{
Kenny Rootddb76c42010-11-24 12:56:06 -0800504 off64_t newPosn;
505 off64_t actualOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506
507 // compute new position within chunk
508 newPosn = handleSeek(offset, whence, mOffset, mLength);
Kenny Rootddb76c42010-11-24 12:56:06 -0800509 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 return newPosn;
511
Kenny Rootddb76c42010-11-24 12:56:06 -0800512 actualOffset = mStart + newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513
514 if (mFp != NULL) {
515 if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
Kenny Rootddb76c42010-11-24 12:56:06 -0800516 return (off64_t) -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 }
518
519 mOffset = actualOffset - mStart;
520 return mOffset;
521}
522
523/*
524 * Close the asset.
525 */
526void _FileAsset::close(void)
527{
528 if (mMap != NULL) {
529 mMap->release();
530 mMap = NULL;
531 }
532 if (mBuf != NULL) {
533 delete[] mBuf;
534 mBuf = NULL;
535 }
536
537 if (mFileName != NULL) {
538 free(mFileName);
539 mFileName = NULL;
540 }
541
542 if (mFp != NULL) {
543 // can only be NULL when called from destructor
544 // (otherwise we would never return this object)
545 fclose(mFp);
546 mFp = NULL;
547 }
548}
549
550/*
551 * Return a read-only pointer to a buffer.
552 *
553 * We can either read the whole thing in or map the relevant piece of
554 * the source file. Ideally a map would be established at a higher
555 * level and we'd be using a different object, but we didn't, so we
556 * deal with it here.
557 */
558const void* _FileAsset::getBuffer(bool wordAligned)
559{
560 /* subsequent requests just use what we did previously */
561 if (mBuf != NULL)
562 return mBuf;
563 if (mMap != NULL) {
564 if (!wordAligned) {
565 return mMap->getDataPtr();
566 }
567 return ensureAlignment(mMap);
568 }
569
570 assert(mFp != NULL);
571
572 if (mLength < kReadVsMapThreshold) {
573 unsigned char* buf;
574 long allocLen;
575
576 /* zero-length files are allowed; not sure about zero-len allocs */
577 /* (works fine with gcc + x86linux) */
578 allocLen = mLength;
579 if (mLength == 0)
580 allocLen = 1;
581
582 buf = new unsigned char[allocLen];
583 if (buf == NULL) {
584 LOGE("alloc of %ld bytes failed\n", (long) allocLen);
585 return NULL;
586 }
587
Steve Block06ade6a2011-10-20 11:56:00 +0100588 ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 if (mLength > 0) {
590 long oldPosn = ftell(mFp);
591 fseek(mFp, mStart, SEEK_SET);
592 if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
593 LOGE("failed reading %ld bytes\n", (long) mLength);
594 delete[] buf;
595 return NULL;
596 }
597 fseek(mFp, oldPosn, SEEK_SET);
598 }
599
Steve Block06ade6a2011-10-20 11:56:00 +0100600 ALOGV(" getBuffer: loaded into buffer\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601
602 mBuf = buf;
603 return mBuf;
604 } else {
605 FileMap* map;
606
607 map = new FileMap;
608 if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
609 map->release();
610 return NULL;
611 }
612
Steve Block06ade6a2011-10-20 11:56:00 +0100613 ALOGV(" getBuffer: mapped\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614
615 mMap = map;
616 if (!wordAligned) {
617 return mMap->getDataPtr();
618 }
619 return ensureAlignment(mMap);
620 }
621}
622
Kenny Rootddb76c42010-11-24 12:56:06 -0800623int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624{
625 if (mMap != NULL) {
626 const char* fname = mMap->getFileName();
627 if (fname == NULL) {
628 fname = mFileName;
629 }
630 if (fname == NULL) {
631 return -1;
632 }
633 *outStart = mMap->getDataOffset();
634 *outLength = mMap->getDataLength();
635 return open(fname, O_RDONLY | O_BINARY);
636 }
637 if (mFileName == NULL) {
638 return -1;
639 }
640 *outStart = mStart;
641 *outLength = mLength;
642 return open(mFileName, O_RDONLY | O_BINARY);
643}
644
645const void* _FileAsset::ensureAlignment(FileMap* map)
646{
647 void* data = map->getDataPtr();
648 if ((((size_t)data)&0x3) == 0) {
649 // We can return this directly if it is aligned on a word
650 // boundary.
Steve Block06ade6a2011-10-20 11:56:00 +0100651 ALOGV("Returning aligned FileAsset %p (%s).", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700652 getAssetSource());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 return data;
654 }
655 // If not aligned on a word boundary, then we need to copy it into
656 // our own buffer.
Steve Block06ade6a2011-10-20 11:56:00 +0100657 ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
Dianne Hackborn78c40512009-07-06 11:07:40 -0700658 getAssetSource(), (int)mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 unsigned char* buf = new unsigned char[mLength];
660 if (buf == NULL) {
661 LOGE("alloc of %ld bytes failed\n", (long) mLength);
662 return NULL;
663 }
664 memcpy(buf, data, mLength);
665 mBuf = buf;
666 return buf;
667}
668
669/*
670 * ===========================================================================
671 * _CompressedAsset
672 * ===========================================================================
673 */
674
675/*
676 * Constructor.
677 */
678_CompressedAsset::_CompressedAsset(void)
679 : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
Christopher Tateb100cbf2010-07-26 11:24:18 -0700680 mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681{
682}
683
684/*
685 * Destructor. Release resources.
686 */
687_CompressedAsset::~_CompressedAsset(void)
688{
689 close();
690}
691
692/*
693 * Open a chunk of compressed data inside a file.
694 *
695 * This currently just sets up some values and returns. On the first
696 * read, we expand the entire file into a buffer and return data from it.
697 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800698status_t _CompressedAsset::openChunk(int fd, off64_t offset,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 int compressionMethod, size_t uncompressedLen, size_t compressedLen)
700{
701 assert(mFd < 0); // no re-open
702 assert(mMap == NULL);
703 assert(fd >= 0);
704 assert(offset >= 0);
705 assert(compressedLen > 0);
706
707 if (compressionMethod != ZipFileRO::kCompressDeflated) {
708 assert(false);
709 return UNKNOWN_ERROR;
710 }
711
712 mStart = offset;
713 mCompressedLen = compressedLen;
714 mUncompressedLen = uncompressedLen;
715 assert(mOffset == 0);
716 mFd = fd;
717 assert(mBuf == NULL);
718
Christopher Tateb100cbf2010-07-26 11:24:18 -0700719 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
720 mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
721 }
722
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 return NO_ERROR;
724}
725
726/*
727 * Open a chunk of compressed data in a mapped region.
728 *
729 * Nothing is expanded until the first read call.
730 */
731status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
732 size_t uncompressedLen)
733{
734 assert(mFd < 0); // no re-open
735 assert(mMap == NULL);
736 assert(dataMap != NULL);
737
738 if (compressionMethod != ZipFileRO::kCompressDeflated) {
739 assert(false);
740 return UNKNOWN_ERROR;
741 }
742
743 mMap = dataMap;
744 mStart = -1; // not used
745 mCompressedLen = dataMap->getDataLength();
746 mUncompressedLen = uncompressedLen;
747 assert(mOffset == 0);
748
Christopher Tateb100cbf2010-07-26 11:24:18 -0700749 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
750 mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 return NO_ERROR;
753}
754
755/*
756 * Read data from a chunk of compressed data.
757 *
758 * [For now, that's just copying data out of a buffer.]
759 */
760ssize_t _CompressedAsset::read(void* buf, size_t count)
761{
762 size_t maxLen;
763 size_t actual;
764
765 assert(mOffset >= 0 && mOffset <= mUncompressedLen);
766
Christopher Tateb100cbf2010-07-26 11:24:18 -0700767 /* If we're relying on a streaming inflater, go through that */
768 if (mZipInflater) {
769 actual = mZipInflater->read(buf, count);
770 } else {
771 if (mBuf == NULL) {
772 if (getBuffer(false) == NULL)
773 return -1;
774 }
775 assert(mBuf != NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776
Christopher Tateb100cbf2010-07-26 11:24:18 -0700777 /* adjust count if we're near EOF */
778 maxLen = mUncompressedLen - mOffset;
779 if (count > maxLen)
780 count = maxLen;
781
782 if (!count)
783 return 0;
784
785 /* copy from buffer */
786 //printf("comp buf read\n");
787 memcpy(buf, (char*)mBuf + mOffset, count);
788 actual = count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790
791 mOffset += actual;
792 return actual;
793}
794
795/*
796 * Handle a seek request.
797 *
798 * If we're working in a streaming mode, this is going to be fairly
799 * expensive, because it requires plowing through a bunch of compressed
800 * data.
801 */
Kenny Rootddb76c42010-11-24 12:56:06 -0800802off64_t _CompressedAsset::seek(off64_t offset, int whence)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803{
Kenny Rootddb76c42010-11-24 12:56:06 -0800804 off64_t newPosn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805
806 // compute new position within chunk
807 newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
Kenny Rootddb76c42010-11-24 12:56:06 -0800808 if (newPosn == (off64_t) -1)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 return newPosn;
810
Christopher Tateb100cbf2010-07-26 11:24:18 -0700811 if (mZipInflater) {
812 mZipInflater->seekAbsolute(newPosn);
813 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 mOffset = newPosn;
815 return mOffset;
816}
817
818/*
819 * Close the asset.
820 */
821void _CompressedAsset::close(void)
822{
823 if (mMap != NULL) {
824 mMap->release();
825 mMap = NULL;
826 }
Christopher Tateb100cbf2010-07-26 11:24:18 -0700827
828 delete[] mBuf;
829 mBuf = NULL;
830
831 delete mZipInflater;
832 mZipInflater = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833
834 if (mFd > 0) {
835 ::close(mFd);
836 mFd = -1;
837 }
838}
839
840/*
841 * Get a pointer to a read-only buffer of data.
842 *
843 * The first time this is called, we expand the compressed data into a
844 * buffer.
845 */
846const void* _CompressedAsset::getBuffer(bool wordAligned)
847{
848 unsigned char* buf = NULL;
849
850 if (mBuf != NULL)
851 return mBuf;
852
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 /*
854 * Allocate a buffer and read the file into it.
855 */
856 buf = new unsigned char[mUncompressedLen];
857 if (buf == NULL) {
858 LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
859 goto bail;
860 }
861
862 if (mMap != NULL) {
863 if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(),
864 mUncompressedLen, mCompressedLen))
865 goto bail;
866 } else {
867 assert(mFd >= 0);
868
869 /*
870 * Seek to the start of the compressed data.
871 */
872 if (lseek(mFd, mStart, SEEK_SET) != mStart)
873 goto bail;
874
875 /*
876 * Expand the data into it.
877 */
878 if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
879 mCompressedLen))
880 goto bail;
881 }
882
Christopher Tateb100cbf2010-07-26 11:24:18 -0700883 /*
884 * Success - now that we have the full asset in RAM we
885 * no longer need the streaming inflater
886 */
887 delete mZipInflater;
888 mZipInflater = NULL;
889
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 mBuf = buf;
891 buf = NULL;
892
893bail:
894 delete[] buf;
895 return mBuf;
896}
897