Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2018 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 #define LOG_TAG "libprotoutil"
     17 
     18 #include <android/util/ProtoFileReader.h>
     19 #include <cutils/log.h>
     20 
     21 #include <cinttypes>
     22 #include <type_traits>
     23 
     24 #include <unistd.h>
     25 
     26 namespace android {
     27 namespace util {
     28 
     29 /**
     30  * Get the amount of data remaining in the file in fd, or -1 if the file size can't be measured.
     31  * It's not the whole file, but this allows us to skip any preamble that might have already
     32  * been passed over.
     33  */
     34 ssize_t get_file_size(int fd) {
     35     off_t current = lseek(fd, 0, SEEK_CUR);
     36     if (current < 0) {
     37         return -1;
     38     }
     39     off_t end = lseek(fd, 0, SEEK_END);
     40     if (end < 0) {
     41         return -1;
     42     }
     43     off_t err = lseek(fd, current, SEEK_SET);
     44     if (err < 0) {
     45         ALOGW("get_file_size could do SEEK_END but not SEEK_SET. We might have skipped data.");
     46         return -1;
     47     }
     48     return (ssize_t)(end-current);
     49 }
     50 
     51 // =========================================================================
     52 ProtoFileReader::ProtoFileReader(int fd)
     53         :mFd(fd),
     54          mStatus(NO_ERROR),
     55          mSize(get_file_size(fd)),
     56          mPos(0),
     57          mOffset(0),
     58          mMaxOffset(0),
     59          mChunkSize(sizeof(mBuffer)) {
     60 }
     61 
     62 ProtoFileReader::~ProtoFileReader() {
     63 }
     64 
     65 ssize_t
     66 ProtoFileReader::size() const
     67 {
     68     return (ssize_t)mSize;
     69 }
     70 
     71 size_t
     72 ProtoFileReader::bytesRead() const
     73 {
     74     return mPos;
     75 }
     76 
     77 uint8_t const*
     78 ProtoFileReader::readBuffer()
     79 {
     80     return hasNext() ? mBuffer + mOffset : NULL;
     81 }
     82 
     83 size_t
     84 ProtoFileReader::currentToRead()
     85 {
     86     return mMaxOffset - mOffset;
     87 }
     88 
     89 bool
     90 ProtoFileReader::hasNext()
     91 {
     92     return ensure_data();
     93 }
     94 
     95 uint8_t
     96 ProtoFileReader::next()
     97 {
     98     if (!ensure_data()) {
     99         // Shouldn't get to here.  Always call hasNext() before calling next().
    100         return 0;
    101     }
    102     return mBuffer[mOffset++];
    103 }
    104 
    105 uint64_t
    106 ProtoFileReader::readRawVarint()
    107 {
    108     uint64_t val = 0, shift = 0;
    109     while (true) {
    110         if (!hasNext()) {
    111             ALOGW("readRawVarint() called without hasNext() called first.");
    112             mStatus = NOT_ENOUGH_DATA;
    113             return 0;
    114         }
    115         uint8_t byte = next();
    116         val |= (INT64_C(0x7F) & byte) << shift;
    117         if ((byte & 0x80) == 0) break;
    118         shift += 7;
    119     }
    120     return val;
    121 }
    122 
    123 void
    124 ProtoFileReader::move(size_t amt)
    125 {
    126     while (mStatus == NO_ERROR && amt > 0) {
    127         if (!ensure_data()) {
    128             return;
    129         }
    130         const size_t chunk =
    131                 mMaxOffset - mOffset > amt ? amt : mMaxOffset - mOffset;
    132         mOffset += chunk;
    133         amt -= chunk;
    134     }
    135 }
    136 
    137 status_t
    138 ProtoFileReader::getError() const {
    139     return mStatus;
    140 }
    141 
    142 bool
    143 ProtoFileReader::ensure_data() {
    144     if (mStatus != NO_ERROR) {
    145         return false;
    146     }
    147     if (mOffset < mMaxOffset) {
    148         return true;
    149     }
    150     ssize_t amt = TEMP_FAILURE_RETRY(read(mFd, mBuffer, mChunkSize));
    151     if (amt == 0) {
    152         return false;
    153     } else if (amt < 0) {
    154         mStatus = -errno;
    155         return false;
    156     } else {
    157         mOffset = 0;
    158         mMaxOffset = amt;
    159         return true;
    160     }
    161 }
    162 
    163 
    164 } // util
    165 } // android
    166 
    167