Home | History | Annotate | Download | only in utils
      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 
     50 int32_t Asset::getGlobalCount()
     51 {
     52     AutoMutex _l(gAssetLock);
     53     return gCount;
     54 }
     55 
     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 
     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 
     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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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 
    606 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 
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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