OSDN Git Service

Merge/Ignore 653fd9cf
[android-x86/frameworks-native.git] / libs / utils / Asset.cpp
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 #include <unistd.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41
42 using namespace android;
43
44 #ifndef O_BINARY
45 # define O_BINARY 0
46 #endif
47
48 static Mutex gAssetLock;
49 static int32_t gCount = 0;
50 static Asset* gHead = NULL;
51 static Asset* gTail = NULL;
52
53 int32_t Asset::getGlobalCount()
54 {
55     AutoMutex _l(gAssetLock);
56     return gCount;
57 }
58
59 String8 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());
68             off64_t size = (cur->getLength()+512)/1024;
69             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
79 Asset::Asset(void)
80     : mAccessMode(ACCESS_UNKNOWN)
81 {
82     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);
93 }
94
95 Asset::~Asset(void)
96 {
97     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);
113 }
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;
125     off64_t length;
126     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      */
138     // TODO(kroot): replace this with fstat despite the plea above.
139 #if 1
140     length = lseek64(fd, 0, SEEK_END);
141     if (length < 0) {
142         ::close(fd);
143         return NULL;
144     }
145     (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
158
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;
182     off64_t fileLen;
183     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) {
213         LOGD("File '%s' is not in gzip format\n", fileName);
214         ::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  */
235 /*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset,
236     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  */
253 /*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset,
254     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  */
315 off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
316 {
317     off64_t newOffset;
318
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);
331         // this was happening due to an off64_t size mismatch
332         assert(false);
333         return (off64_t) -1;
334     }
335
336     if (newOffset < 0 || newOffset > maxPosn) {
337         LOGW("seek out of range: want %ld, end=%ld\n",
338             (long) newOffset, (long) maxPosn);
339         return (off64_t) -1;
340     }
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  */
373 status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
374 {
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      */
383     off64_t fileLength;
384     fileLength = lseek64(fd, 0, SEEK_END);
385     if (fileLength == (off64_t) -1) {
386         // probably a bad file descriptor
387         LOGD("failed lseek (errno=%d)\n", errno);
388         return UNKNOWN_ERROR;
389     }
390
391     if ((off64_t) (offset + length) > fileLength) {
392         LOGD("start (%ld) + len (%ld) > end (%ld)\n",
393             (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  */
419 status_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  */
436 ssize_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  */
502 off64_t _FileAsset::seek(off64_t offset, int whence)
503 {
504     off64_t newPosn;
505     off64_t actualOffset;
506
507     // compute new position within chunk
508     newPosn = handleSeek(offset, whence, mOffset, mLength);
509     if (newPosn == (off64_t) -1)
510         return newPosn;
511
512     actualOffset = mStart + newPosn;
513
514     if (mFp != NULL) {
515         if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
516             return (off64_t) -1;
517     }
518
519     mOffset = actualOffset - mStart;
520     return mOffset;
521 }
522
523 /*
524  * Close the asset.
525  */
526 void _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  */
558 const 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
588         LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
589         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
600         LOGV(" getBuffer: loaded into buffer\n");
601
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
613         LOGV(" getBuffer: mapped\n");
614
615         mMap = map;
616         if (!wordAligned) {
617             return  mMap->getDataPtr();
618         }
619         return ensureAlignment(mMap);
620     }
621 }
622
623 int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
624 {
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
645 const 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.
651         LOGV("Returning aligned FileAsset %p (%s).", this,
652                 getAssetSource());
653         return data;
654     }
655     // If not aligned on a word boundary, then we need to copy it into
656     // our own buffer.
657     LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
658             getAssetSource(), (int)mLength);
659     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),
680       mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
681 {
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  */
698 status_t _CompressedAsset::openChunk(int fd, off64_t offset,
699     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
719     if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
720         mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
721     }
722
723     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  */
731 status_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
749     if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
750         mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
751     }
752     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  */
760 ssize_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
767     /* 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);
776
777         /* 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;
789     }
790
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  */
802 off64_t _CompressedAsset::seek(off64_t offset, int whence)
803 {
804     off64_t newPosn;
805
806     // compute new position within chunk
807     newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
808     if (newPosn == (off64_t) -1)
809         return newPosn;
810
811     if (mZipInflater) {
812         mZipInflater->seekAbsolute(newPosn);
813     }
814     mOffset = newPosn;
815     return mOffset;
816 }
817
818 /*
819  * Close the asset.
820  */
821 void _CompressedAsset::close(void)
822 {
823     if (mMap != NULL) {
824         mMap->release();
825         mMap = NULL;
826     }
827
828     delete[] mBuf;
829     mBuf = NULL;
830
831     delete mZipInflater;
832     mZipInflater = NULL;
833
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  */
846 const void* _CompressedAsset::getBuffer(bool wordAligned)
847 {
848     unsigned char* buf = NULL;
849
850     if (mBuf != NULL)
851         return mBuf;
852
853     /*
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
883     /*
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
890     mBuf = buf;
891     buf = NULL;
892
893 bail:
894     delete[] buf;
895     return mBuf;
896 }
897