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