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  */
get_file_size(int fd)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 // =========================================================================
ProtoFileReader(int fd)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 
~ProtoFileReader()62 ProtoFileReader::~ProtoFileReader() {
63 }
64 
65 ssize_t
size() const66 ProtoFileReader::size() const
67 {
68     return (ssize_t)mSize;
69 }
70 
71 size_t
bytesRead() const72 ProtoFileReader::bytesRead() const
73 {
74     return mPos;
75 }
76 
77 uint8_t const*
readBuffer()78 ProtoFileReader::readBuffer()
79 {
80     return hasNext() ? mBuffer + mOffset : NULL;
81 }
82 
83 size_t
currentToRead()84 ProtoFileReader::currentToRead()
85 {
86     return mMaxOffset - mOffset;
87 }
88 
89 bool
hasNext()90 ProtoFileReader::hasNext()
91 {
92     return ensure_data();
93 }
94 
95 uint8_t
next()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
readRawVarint()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
move(size_t amt)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
getError() const138 ProtoFileReader::getError() const {
139     return mStatus;
140 }
141 
142 bool
ensure_data()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