blob: cef7db492e55b6fd7bef03a6beccbbdbb6bd25a4 [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>
38
39using namespace android;
40
41#ifndef O_BINARY
42# define O_BINARY 0
43#endif
44
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070045static Mutex gAssetLock;
46static int32_t gCount = 0;
47static Asset* gHead = NULL;
48static Asset* gTail = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
50int32_t Asset::getGlobalCount()
51{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070052 AutoMutex _l(gAssetLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 return gCount;
54}
55
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070056String8 Asset::getAssetAllocations()
57{
58 AutoMutex _l(gAssetLock);
59 String8 res;
60 Asset* cur = gHead;
61 while (cur != NULL) {
62 if (cur->isAllocated()) {
63 res.append(" ");
64 res.append(cur->getAssetSource());
65 off_t size = (cur->getLength()+512)/1024;
66 char buf[64];
67 sprintf(buf, ": %dK\n", (int)size);
68 res.append(buf);
69 }
70 cur = cur->mNext;
71 }
72
73 return res;
74}
75
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076Asset::Asset(void)
77 : mAccessMode(ACCESS_UNKNOWN)
78{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070079 AutoMutex _l(gAssetLock);
80 gCount++;
81 mNext = mPrev = NULL;
82 if (gTail == NULL) {
83 gHead = gTail = this;
84 } else {
85 mPrev = gTail;
86 gTail->mNext = this;
87 gTail = this;
88 }
89 //LOGI("Creating Asset %p #%d\n", this, gCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090}
91
92Asset::~Asset(void)
93{
Dianne Hackborn82e1ee92009-08-11 18:56:41 -070094 AutoMutex _l(gAssetLock);
95 gCount--;
96 if (gHead == this) {
97 gHead = mNext;
98 }
99 if (gTail == this) {
100 gTail = mPrev;
101 }
102 if (mNext != NULL) {
103 mNext->mPrev = mPrev;
104 }
105 if (mPrev != NULL) {
106 mPrev->mNext = mNext;
107 }
108 mNext = mPrev = NULL;
109 //LOGI("Destroying Asset in %p #%d\n", this, gCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110}
111
112/*
113 * Create a new Asset from a file on disk. There is a fair chance that
114 * the file doesn't actually exist.
115 *
116 * We can use "mode" to decide how we want to go about it.
117 */
118/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
119{
120 _FileAsset* pAsset;
121 status_t result;
122 off_t length;
123 int fd;
124
125 fd = open(fileName, O_RDONLY | O_BINARY);
126 if (fd < 0)
127 return NULL;
128
129 /*
130 * Under Linux, the lseek fails if we actually opened a directory. To
131 * be correct we should test the file type explicitly, but since we
132 * always open things read-only it doesn't really matter, so there's
133 * no value in incurring the extra overhead of an fstat() call.
134 */
135 length = lseek(fd, 0, SEEK_END);
136 if (length < 0) {
137 ::close(fd);
138 return NULL;
139 }
140 (void) lseek(fd, 0, SEEK_SET);
141
142 pAsset = new _FileAsset;
143 result = pAsset->openChunk(fileName, fd, 0, length);
144 if (result != NO_ERROR) {
145 delete pAsset;
146 return NULL;
147 }
148
149 pAsset->mAccessMode = mode;
150 return pAsset;
151}
152
153
154/*
155 * Create a new Asset from a compressed file on disk. There is a fair chance
156 * that the file doesn't actually exist.
157 *
158 * We currently support gzip files. We might want to handle .bz2 someday.
159 */
160/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
161 AccessMode mode)
162{
163 _CompressedAsset* pAsset;
164 status_t result;
165 off_t fileLen;
166 bool scanResult;
167 long offset;
168 int method;
169 long uncompressedLen, compressedLen;
170 int fd;
171
172 fd = open(fileName, O_RDONLY | O_BINARY);
173 if (fd < 0)
174 return NULL;
175
176 fileLen = lseek(fd, 0, SEEK_END);
177 if (fileLen < 0) {
178 ::close(fd);
179 return NULL;
180 }
181 (void) lseek(fd, 0, SEEK_SET);
182
183 /* want buffered I/O for the file scan; must dup so fclose() is safe */
184 FILE* fp = fdopen(dup(fd), "rb");
185 if (fp == NULL) {
186 ::close(fd);
187 return NULL;
188 }
189
190 unsigned long crc32;
191 scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen,
192 &compressedLen, &crc32);
193 offset = ftell(fp);
194 fclose(fp);
195 if (!scanResult) {
196 LOGD("File '%s' is not in gzip format\n", fileName);
197 ::close(fd);
198 return NULL;
199 }
200
201 pAsset = new _CompressedAsset;
202 result = pAsset->openChunk(fd, offset, method, uncompressedLen,
203 compressedLen);
204 if (result != NO_ERROR) {
205 delete pAsset;
206 return NULL;
207 }
208
209 pAsset->mAccessMode = mode;
210 return pAsset;
211}
212
213
214#if 0
215/*
216 * Create a new Asset from part of an open file.
217 */
218/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset,
219 size_t length, AccessMode mode)
220{
221 _FileAsset* pAsset;
222 status_t result;
223
224 pAsset = new _FileAsset;
225 result = pAsset->openChunk(NULL, fd, offset, length);
226 if (result != NO_ERROR)
227 return NULL;
228
229 pAsset->mAccessMode = mode;
230 return pAsset;
231}
232
233/*
234 * Create a new Asset from compressed data in an open file.
235 */
236/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset,
237 int compressionMethod, size_t uncompressedLen, size_t compressedLen,
238 AccessMode mode)
239{
240 _CompressedAsset* pAsset;
241 status_t result;
242
243 pAsset = new _CompressedAsset;
244 result = pAsset->openChunk(fd, offset, compressionMethod,
245 uncompressedLen, compressedLen);
246 if (result != NO_ERROR)
247 return NULL;
248
249 pAsset->mAccessMode = mode;
250 return pAsset;
251}
252#endif
253
254/*
255 * Create a new Asset from a memory mapping.
256 */
257/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
258 AccessMode mode)
259{
260 _FileAsset* pAsset;
261 status_t result;
262
263 pAsset = new _FileAsset;
264 result = pAsset->openChunk(dataMap);
265 if (result != NO_ERROR)
266 return NULL;
267
268 pAsset->mAccessMode = mode;
269 return pAsset;
270}
271
272/*
273 * Create a new Asset from compressed data in a memory mapping.
274 */
275/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
276 int method, size_t uncompressedLen, AccessMode mode)
277{
278 _CompressedAsset* pAsset;
279 status_t result;
280
281 pAsset = new _CompressedAsset;
282 result = pAsset->openChunk(dataMap, method, uncompressedLen);
283 if (result != NO_ERROR)
284 return NULL;
285
286 pAsset->mAccessMode = mode;
287 return pAsset;
288}
289
290
291/*
292 * Do generic seek() housekeeping. Pass in the offset/whence values from
293 * the seek request, along with the current chunk offset and the chunk
294 * length.
295 *
296 * Returns the new chunk offset, or -1 if the seek is illegal.
297 */
298off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn)
299{
300 off_t newOffset;
301
302 switch (whence) {
303 case SEEK_SET:
304 newOffset = offset;
305 break;
306 case SEEK_CUR:
307 newOffset = curPosn + offset;
308 break;
309 case SEEK_END:
310 newOffset = maxPosn + offset;
311 break;
312 default:
313 LOGW("unexpected whence %d\n", whence);
314 // this was happening due to an off_t size mismatch
315 assert(false);
316 return (off_t) -1;
317 }
318
319 if (newOffset < 0 || newOffset > maxPosn) {
320 LOGW("seek out of range: want %ld, end=%ld\n",
321 (long) newOffset, (long) maxPosn);
322 return (off_t) -1;
323 }
324
325 return newOffset;
326}
327
328
329/*
330 * ===========================================================================
331 * _FileAsset
332 * ===========================================================================
333 */
334
335/*
336 * Constructor.
337 */
338_FileAsset::_FileAsset(void)
339 : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
340{
341}
342
343/*
344 * Destructor. Release resources.
345 */
346_FileAsset::~_FileAsset(void)
347{
348 close();
349}
350
351/*
352 * Operate on a chunk of an uncompressed file.
353 *
354 * Zero-length chunks are allowed.
355 */
356status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length)
357{
358 assert(mFp == NULL); // no reopen
359 assert(mMap == NULL);
360 assert(fd >= 0);
361 assert(offset >= 0);
362
363 /*
364 * Seek to end to get file length.
365 */
366 off_t fileLength;
367 fileLength = lseek(fd, 0, SEEK_END);
368 if (fileLength == (off_t) -1) {
369 // probably a bad file descriptor
370 LOGD("failed lseek (errno=%d)\n", errno);
371 return UNKNOWN_ERROR;
372 }
373
374 if ((off_t) (offset + length) > fileLength) {
375 LOGD("start (%ld) + len (%ld) > end (%ld)\n",
376 (long) offset, (long) length, (long) fileLength);
377 return BAD_INDEX;
378 }
379
380 /* after fdopen, the fd will be closed on fclose() */
381 mFp = fdopen(fd, "rb");
382 if (mFp == NULL)
383 return UNKNOWN_ERROR;
384
385 mStart = offset;
386 mLength = length;
387 assert(mOffset == 0);
388
389 /* seek the FILE* to the start of chunk */
390 if (fseek(mFp, mStart, SEEK_SET) != 0) {
391 assert(false);
392 }
393
394 mFileName = fileName != NULL ? strdup(fileName) : NULL;
395
396 return NO_ERROR;
397}
398
399/*
400 * Create the chunk from the map.
401 */
402status_t _FileAsset::openChunk(FileMap* dataMap)
403{
404 assert(mFp == NULL); // no reopen
405 assert(mMap == NULL);
406 assert(dataMap != NULL);
407
408 mMap = dataMap;
409 mStart = -1; // not used
410 mLength = dataMap->getDataLength();
411 assert(mOffset == 0);
412
413 return NO_ERROR;
414}
415
416/*
417 * Read a chunk of data.
418 */
419ssize_t _FileAsset::read(void* buf, size_t count)
420{
421 size_t maxLen;
422 size_t actual;
423
424 assert(mOffset >= 0 && mOffset <= mLength);
425
426 if (getAccessMode() == ACCESS_BUFFER) {
427 /*
428 * On first access, read or map the entire file. The caller has
429 * requested buffer access, either because they're going to be
430 * using the buffer or because what they're doing has appropriate
431 * performance needs and access patterns.
432 */
433 if (mBuf == NULL)
434 getBuffer(false);
435 }
436
437 /* adjust count if we're near EOF */
438 maxLen = mLength - mOffset;
439 if (count > maxLen)
440 count = maxLen;
441
442 if (!count)
443 return 0;
444
445 if (mMap != NULL) {
446 /* copy from mapped area */
447 //printf("map read\n");
448 memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
449 actual = count;
450 } else if (mBuf != NULL) {
451 /* copy from buffer */
452 //printf("buf read\n");
453 memcpy(buf, (char*)mBuf + mOffset, count);
454 actual = count;
455 } else {
456 /* read from the file */
457 //printf("file read\n");
458 if (ftell(mFp) != mStart + mOffset) {
459 LOGE("Hosed: %ld != %ld+%ld\n",
460 ftell(mFp), (long) mStart, (long) mOffset);
461 assert(false);
462 }
463
464 /*
465 * This returns 0 on error or eof. We need to use ferror() or feof()
466 * to tell the difference, but we don't currently have those on the
467 * device. However, we know how much data is *supposed* to be in the
468 * file, so if we don't read the full amount we know something is
469 * hosed.
470 */
471 actual = fread(buf, 1, count, mFp);
472 if (actual == 0) // something failed -- I/O error?
473 return -1;
474
475 assert(actual == count);
476 }
477
478 mOffset += actual;
479 return actual;
480}
481
482/*
483 * Seek to a new position.
484 */
485off_t _FileAsset::seek(off_t offset, int whence)
486{
487 off_t newPosn;
488 long actualOffset;
489
490 // compute new position within chunk
491 newPosn = handleSeek(offset, whence, mOffset, mLength);
492 if (newPosn == (off_t) -1)
493 return newPosn;
494
495 actualOffset = (long) (mStart + newPosn);
496
497 if (mFp != NULL) {
498 if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
499 return (off_t) -1;
500 }
501
502 mOffset = actualOffset - mStart;
503 return mOffset;
504}
505
506/*
507 * Close the asset.
508 */
509void _FileAsset::close(void)
510{
511 if (mMap != NULL) {
512 mMap->release();
513 mMap = NULL;
514 }
515 if (mBuf != NULL) {
516 delete[] mBuf;
517 mBuf = NULL;
518 }
519
520 if (mFileName != NULL) {
521 free(mFileName);
522 mFileName = NULL;
523 }
524
525 if (mFp != NULL) {
526 // can only be NULL when called from destructor
527 // (otherwise we would never return this object)
528 fclose(mFp);
529 mFp = NULL;
530 }
531}
532
533/*
534 * Return a read-only pointer to a buffer.
535 *
536 * We can either read the whole thing in or map the relevant piece of
537 * the source file. Ideally a map would be established at a higher
538 * level and we'd be using a different object, but we didn't, so we
539 * deal with it here.
540 */
541const void* _FileAsset::getBuffer(bool wordAligned)
542{
543 /* subsequent requests just use what we did previously */
544 if (mBuf != NULL)
545 return mBuf;
546 if (mMap != NULL) {
547 if (!wordAligned) {
548 return mMap->getDataPtr();
549 }
550 return ensureAlignment(mMap);
551 }
552
553 assert(mFp != NULL);
554
555 if (mLength < kReadVsMapThreshold) {
556 unsigned char* buf;
557 long allocLen;
558
559 /* zero-length files are allowed; not sure about zero-len allocs */
560 /* (works fine with gcc + x86linux) */
561 allocLen = mLength;
562 if (mLength == 0)
563 allocLen = 1;
564
565 buf = new unsigned char[allocLen];
566 if (buf == NULL) {
567 LOGE("alloc of %ld bytes failed\n", (long) allocLen);
568 return NULL;
569 }
570
571 LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
572 if (mLength > 0) {
573 long oldPosn = ftell(mFp);
574 fseek(mFp, mStart, SEEK_SET);
575 if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
576 LOGE("failed reading %ld bytes\n", (long) mLength);
577 delete[] buf;
578 return NULL;
579 }
580 fseek(mFp, oldPosn, SEEK_SET);
581 }
582
583 LOGV(" getBuffer: loaded into buffer\n");
584
585 mBuf = buf;
586 return mBuf;
587 } else {
588 FileMap* map;
589
590 map = new FileMap;
591 if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
592 map->release();
593 return NULL;
594 }
595
596 LOGV(" getBuffer: mapped\n");
597
598 mMap = map;
599 if (!wordAligned) {
600 return mMap->getDataPtr();
601 }
602 return ensureAlignment(mMap);
603 }
604}
605
606int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const
607{
608 if (mMap != NULL) {
609 const char* fname = mMap->getFileName();
610 if (fname == NULL) {
611 fname = mFileName;
612 }
613 if (fname == NULL) {
614 return -1;
615 }
616 *outStart = mMap->getDataOffset();
617 *outLength = mMap->getDataLength();
618 return open(fname, O_RDONLY | O_BINARY);
619 }
620 if (mFileName == NULL) {
621 return -1;
622 }
623 *outStart = mStart;
624 *outLength = mLength;
625 return open(mFileName, O_RDONLY | O_BINARY);
626}
627
628const void* _FileAsset::ensureAlignment(FileMap* map)
629{
630 void* data = map->getDataPtr();
631 if ((((size_t)data)&0x3) == 0) {
632 // We can return this directly if it is aligned on a word
633 // boundary.
Dianne Hackborn78c40512009-07-06 11:07:40 -0700634 LOGV("Returning aligned FileAsset %p (%s).", this,
635 getAssetSource());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 return data;
637 }
638 // If not aligned on a word boundary, then we need to copy it into
639 // our own buffer.
Dianne Hackborn78c40512009-07-06 11:07:40 -0700640 LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
641 getAssetSource(), (int)mLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 unsigned char* buf = new unsigned char[mLength];
643 if (buf == NULL) {
644 LOGE("alloc of %ld bytes failed\n", (long) mLength);
645 return NULL;
646 }
647 memcpy(buf, data, mLength);
648 mBuf = buf;
649 return buf;
650}
651
652/*
653 * ===========================================================================
654 * _CompressedAsset
655 * ===========================================================================
656 */
657
658/*
659 * Constructor.
660 */
661_CompressedAsset::_CompressedAsset(void)
662 : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
Christopher Tateb100cbf2010-07-26 11:24:18 -0700663 mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664{
665}
666
667/*
668 * Destructor. Release resources.
669 */
670_CompressedAsset::~_CompressedAsset(void)
671{
672 close();
673}
674
675/*
676 * Open a chunk of compressed data inside a file.
677 *
678 * This currently just sets up some values and returns. On the first
679 * read, we expand the entire file into a buffer and return data from it.
680 */
681status_t _CompressedAsset::openChunk(int fd, off_t offset,
682 int compressionMethod, size_t uncompressedLen, size_t compressedLen)
683{
684 assert(mFd < 0); // no re-open
685 assert(mMap == NULL);
686 assert(fd >= 0);
687 assert(offset >= 0);
688 assert(compressedLen > 0);
689
690 if (compressionMethod != ZipFileRO::kCompressDeflated) {
691 assert(false);
692 return UNKNOWN_ERROR;
693 }
694
695 mStart = offset;
696 mCompressedLen = compressedLen;
697 mUncompressedLen = uncompressedLen;
698 assert(mOffset == 0);
699 mFd = fd;
700 assert(mBuf == NULL);
701
Christopher Tateb100cbf2010-07-26 11:24:18 -0700702 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
703 mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
704 }
705
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 return NO_ERROR;
707}
708
709/*
710 * Open a chunk of compressed data in a mapped region.
711 *
712 * Nothing is expanded until the first read call.
713 */
714status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
715 size_t uncompressedLen)
716{
717 assert(mFd < 0); // no re-open
718 assert(mMap == NULL);
719 assert(dataMap != NULL);
720
721 if (compressionMethod != ZipFileRO::kCompressDeflated) {
722 assert(false);
723 return UNKNOWN_ERROR;
724 }
725
726 mMap = dataMap;
727 mStart = -1; // not used
728 mCompressedLen = dataMap->getDataLength();
729 mUncompressedLen = uncompressedLen;
730 assert(mOffset == 0);
731
Christopher Tateb100cbf2010-07-26 11:24:18 -0700732 if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
733 mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
734 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 return NO_ERROR;
736}
737
738/*
739 * Read data from a chunk of compressed data.
740 *
741 * [For now, that's just copying data out of a buffer.]
742 */
743ssize_t _CompressedAsset::read(void* buf, size_t count)
744{
745 size_t maxLen;
746 size_t actual;
747
748 assert(mOffset >= 0 && mOffset <= mUncompressedLen);
749
Christopher Tateb100cbf2010-07-26 11:24:18 -0700750 /* If we're relying on a streaming inflater, go through that */
751 if (mZipInflater) {
752 actual = mZipInflater->read(buf, count);
753 } else {
754 if (mBuf == NULL) {
755 if (getBuffer(false) == NULL)
756 return -1;
757 }
758 assert(mBuf != NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759
Christopher Tateb100cbf2010-07-26 11:24:18 -0700760 /* adjust count if we're near EOF */
761 maxLen = mUncompressedLen - mOffset;
762 if (count > maxLen)
763 count = maxLen;
764
765 if (!count)
766 return 0;
767
768 /* copy from buffer */
769 //printf("comp buf read\n");
770 memcpy(buf, (char*)mBuf + mOffset, count);
771 actual = count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773
774 mOffset += actual;
775 return actual;
776}
777
778/*
779 * Handle a seek request.
780 *
781 * If we're working in a streaming mode, this is going to be fairly
782 * expensive, because it requires plowing through a bunch of compressed
783 * data.
784 */
785off_t _CompressedAsset::seek(off_t offset, int whence)
786{
787 off_t newPosn;
788
789 // compute new position within chunk
790 newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
791 if (newPosn == (off_t) -1)
792 return newPosn;
793
Christopher Tateb100cbf2010-07-26 11:24:18 -0700794 if (mZipInflater) {
795 mZipInflater->seekAbsolute(newPosn);
796 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 mOffset = newPosn;
798 return mOffset;
799}
800
801/*
802 * Close the asset.
803 */
804void _CompressedAsset::close(void)
805{
806 if (mMap != NULL) {
807 mMap->release();
808 mMap = NULL;
809 }
Christopher Tateb100cbf2010-07-26 11:24:18 -0700810
811 delete[] mBuf;
812 mBuf = NULL;
813
814 delete mZipInflater;
815 mZipInflater = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816
817 if (mFd > 0) {
818 ::close(mFd);
819 mFd = -1;
820 }
821}
822
823/*
824 * Get a pointer to a read-only buffer of data.
825 *
826 * The first time this is called, we expand the compressed data into a
827 * buffer.
828 */
829const void* _CompressedAsset::getBuffer(bool wordAligned)
830{
831 unsigned char* buf = NULL;
832
833 if (mBuf != NULL)
834 return mBuf;
835
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 /*
837 * Allocate a buffer and read the file into it.
838 */
839 buf = new unsigned char[mUncompressedLen];
840 if (buf == NULL) {
841 LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
842 goto bail;
843 }
844
845 if (mMap != NULL) {
846 if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(),
847 mUncompressedLen, mCompressedLen))
848 goto bail;
849 } else {
850 assert(mFd >= 0);
851
852 /*
853 * Seek to the start of the compressed data.
854 */
855 if (lseek(mFd, mStart, SEEK_SET) != mStart)
856 goto bail;
857
858 /*
859 * Expand the data into it.
860 */
861 if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
862 mCompressedLen))
863 goto bail;
864 }
865
Christopher Tateb100cbf2010-07-26 11:24:18 -0700866 /*
867 * Success - now that we have the full asset in RAM we
868 * no longer need the streaming inflater
869 */
870 delete mZipInflater;
871 mZipInflater = NULL;
872
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 mBuf = buf;
874 buf = NULL;
875
876bail:
877 delete[] buf;
878 return mBuf;
879}
880