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