/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "libprotoutil" #include <android/util/ProtoFileReader.h> #include <cutils/log.h> #include <cinttypes> #include <type_traits> #include <unistd.h> namespace android { namespace util { /** * Get the amount of data remaining in the file in fd, or -1 if the file size can't be measured. * It's not the whole file, but this allows us to skip any preamble that might have already * been passed over. */ ssize_t get_file_size(int fd) { off_t current = lseek(fd, 0, SEEK_CUR); if (current < 0) { return -1; } off_t end = lseek(fd, 0, SEEK_END); if (end < 0) { return -1; } off_t err = lseek(fd, current, SEEK_SET); if (err < 0) { ALOGW("get_file_size could do SEEK_END but not SEEK_SET. We might have skipped data."); return -1; } return (ssize_t)(end-current); } // ========================================================================= ProtoFileReader::ProtoFileReader(int fd) :mFd(fd), mStatus(NO_ERROR), mSize(get_file_size(fd)), mPos(0), mOffset(0), mMaxOffset(0), mChunkSize(sizeof(mBuffer)) { } ProtoFileReader::~ProtoFileReader() { } ssize_t ProtoFileReader::size() const { return (ssize_t)mSize; } size_t ProtoFileReader::bytesRead() const { return mPos; } uint8_t const* ProtoFileReader::readBuffer() { return hasNext() ? mBuffer + mOffset : NULL; } size_t ProtoFileReader::currentToRead() { return mMaxOffset - mOffset; } bool ProtoFileReader::hasNext() { return ensure_data(); } uint8_t ProtoFileReader::next() { if (!ensure_data()) { // Shouldn't get to here. Always call hasNext() before calling next(). return 0; } return mBuffer[mOffset++]; } uint64_t ProtoFileReader::readRawVarint() { uint64_t val = 0, shift = 0; while (true) { if (!hasNext()) { ALOGW("readRawVarint() called without hasNext() called first."); mStatus = NOT_ENOUGH_DATA; return 0; } uint8_t byte = next(); val |= (INT64_C(0x7F) & byte) << shift; if ((byte & 0x80) == 0) break; shift += 7; } return val; } void ProtoFileReader::move(size_t amt) { while (mStatus == NO_ERROR && amt > 0) { if (!ensure_data()) { return; } const size_t chunk = mMaxOffset - mOffset > amt ? amt : mMaxOffset - mOffset; mOffset += chunk; amt -= chunk; } } status_t ProtoFileReader::getError() const { return mStatus; } bool ProtoFileReader::ensure_data() { if (mStatus != NO_ERROR) { return false; } if (mOffset < mMaxOffset) { return true; } ssize_t amt = TEMP_FAILURE_RETRY(read(mFd, mBuffer, mChunkSize)); if (amt == 0) { return false; } else if (amt < 0) { mStatus = -errno; return false; } else { mOffset = 0; mMaxOffset = amt; return true; } } } // util } // android