• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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>
27 #include <utils/StreamingZipInflater.h>
28 #include <utils/ZipUtils.h>
29 #include <utils/ZipFileRO.h>
30 #include <utils/Log.h>
31 #include <utils/threads.h>
32 
33 #include <string.h>
34 #include <memory.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <assert.h>
38 
39 using namespace android;
40 
41 #ifndef O_BINARY
42 # define O_BINARY 0
43 #endif
44 
45 static Mutex gAssetLock;
46 static int32_t gCount = 0;
47 static Asset* gHead = NULL;
48 static Asset* gTail = NULL;
49 
getGlobalCount()50 int32_t Asset::getGlobalCount()
51 {
52     AutoMutex _l(gAssetLock);
53     return gCount;
54 }
55 
getAssetAllocations()56 String8 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 
Asset(void)76 Asset::Asset(void)
77     : mAccessMode(ACCESS_UNKNOWN)
78 {
79     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);
90 }
91 
~Asset(void)92 Asset::~Asset(void)
93 {
94     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);
110 }
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  */
createFromFile(const char * fileName,AccessMode mode)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  */
createFromCompressedFile(const char * fileName,AccessMode mode)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  */
createFromUncompressedMap(FileMap * dataMap,AccessMode mode)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  */
createFromCompressedMap(FileMap * dataMap,int method,size_t uncompressedLen,AccessMode mode)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  */
handleSeek(off_t offset,int whence,off_t curPosn,off_t maxPosn)298 off_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  */
_FileAsset(void)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  */
~_FileAsset(void)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  */
openChunk(const char * fileName,int fd,off_t offset,size_t length)356 status_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  */
openChunk(FileMap * dataMap)402 status_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  */
read(void * buf,size_t count)419 ssize_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  */
seek(off_t offset,int whence)485 off_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  */
close(void)509 void _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  */
getBuffer(bool wordAligned)541 const 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 
openFileDescriptor(off_t * outStart,off_t * outLength) const606 int _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 
ensureAlignment(FileMap * map)628 const 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.
634         LOGV("Returning aligned FileAsset %p (%s).", this,
635                 getAssetSource());
636         return data;
637     }
638     // If not aligned on a word boundary, then we need to copy it into
639     // our own buffer.
640     LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
641             getAssetSource(), (int)mLength);
642     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  */
_CompressedAsset(void)661 _CompressedAsset::_CompressedAsset(void)
662     : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
663       mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
664 {
665 }
666 
667 /*
668  * Destructor.  Release resources.
669  */
~_CompressedAsset(void)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  */
openChunk(int fd,off_t offset,int compressionMethod,size_t uncompressedLen,size_t compressedLen)681 status_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 
702     if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
703         mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
704     }
705 
706     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  */
openChunk(FileMap * dataMap,int compressionMethod,size_t uncompressedLen)714 status_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 
732     if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
733         mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
734     }
735     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  */
read(void * buf,size_t count)743 ssize_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 
750     /* 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);
759 
760         /* 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;
772     }
773 
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  */
seek(off_t offset,int whence)785 off_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 
794     if (mZipInflater) {
795         mZipInflater->seekAbsolute(newPosn);
796     }
797     mOffset = newPosn;
798     return mOffset;
799 }
800 
801 /*
802  * Close the asset.
803  */
close(void)804 void _CompressedAsset::close(void)
805 {
806     if (mMap != NULL) {
807         mMap->release();
808         mMap = NULL;
809     }
810 
811     delete[] mBuf;
812     mBuf = NULL;
813 
814     delete mZipInflater;
815     mZipInflater = NULL;
816 
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  */
getBuffer(bool wordAligned)829 const void* _CompressedAsset::getBuffer(bool wordAligned)
830 {
831     unsigned char* buf = NULL;
832 
833     if (mBuf != NULL)
834         return mBuf;
835 
836     /*
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 
866     /*
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 
873     mBuf = buf;
874     buf = NULL;
875 
876 bail:
877     delete[] buf;
878     return mBuf;
879 }
880 
881