1 /*
2  * Copyright (C) 2017 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 <stdlib.h>
19 #include <sys/mman.h>
20 #include <unistd.h>
21 
22 #include <android/util/EncodedBuffer.h>
23 #include <android/util/protobuf.h>
24 #include <cutils/log.h>
25 
26 namespace android {
27 namespace util {
28 
29 constexpr size_t BUFFER_SIZE = 8 * 1024; // 8 KB
30 const size_t kPageSize = getpagesize();
31 
Pointer()32 EncodedBuffer::Pointer::Pointer() : Pointer(BUFFER_SIZE)
33 {
34 }
35 
Pointer(size_t chunkSize)36 EncodedBuffer::Pointer::Pointer(size_t chunkSize)
37         :mIndex(0),
38          mOffset(0)
39 {
40     mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
41 }
42 
43 size_t
pos() const44 EncodedBuffer::Pointer::pos() const
45 {
46     return mIndex * mChunkSize + mOffset;
47 }
48 
49 size_t
index() const50 EncodedBuffer::Pointer::index() const
51 {
52     return mIndex;
53 }
54 
55 size_t
offset() const56 EncodedBuffer::Pointer::offset() const
57 {
58     return mOffset;
59 }
60 
61 EncodedBuffer::Pointer*
move(size_t amt)62 EncodedBuffer::Pointer::move(size_t amt)
63 {
64     size_t newOffset = mOffset + amt;
65     mIndex += newOffset / mChunkSize;
66     mOffset = newOffset % mChunkSize;
67     return this;
68 }
69 
70 EncodedBuffer::Pointer*
rewind()71 EncodedBuffer::Pointer::rewind()
72 {
73     mIndex = 0;
74     mOffset = 0;
75     return this;
76 }
77 
78 EncodedBuffer::Pointer
copy() const79 EncodedBuffer::Pointer::copy() const
80 {
81     Pointer p = Pointer(mChunkSize);
82     p.mIndex = mIndex;
83     p.mOffset = mOffset;
84     return p;
85 }
86 
87 // ===========================================================
EncodedBuffer()88 EncodedBuffer::EncodedBuffer() : EncodedBuffer(BUFFER_SIZE)
89 {
90 }
91 
EncodedBuffer(size_t chunkSize)92 EncodedBuffer::EncodedBuffer(size_t chunkSize)
93         :mBuffers()
94 {
95     // Align chunkSize to memory page size
96     chunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
97     mChunkSize = (chunkSize + (kPageSize - 1)) & ~(kPageSize - 1);
98     mWp = Pointer(mChunkSize);
99     mEp = Pointer(mChunkSize);
100 }
101 
~EncodedBuffer()102 EncodedBuffer::~EncodedBuffer()
103 {
104     for (size_t i=0; i<mBuffers.size(); i++) {
105         uint8_t* buf = mBuffers[i];
106         munmap(buf, mChunkSize);
107     }
108 }
109 
110 inline uint8_t*
at(const Pointer & p) const111 EncodedBuffer::at(const Pointer& p) const
112 {
113     return mBuffers[p.index()] + p.offset();
114 }
115 
116 void
clear()117 EncodedBuffer::clear()
118 {
119     mWp.rewind();
120     mEp.rewind();
121 }
122 
123 /******************************** Write APIs ************************************************/
124 size_t
size() const125 EncodedBuffer::size() const
126 {
127     return mWp.pos();
128 }
129 
130 EncodedBuffer::Pointer*
wp()131 EncodedBuffer::wp()
132 {
133     return &mWp;
134 }
135 
136 uint8_t*
writeBuffer()137 EncodedBuffer::writeBuffer()
138 {
139     // This prevents write pointer move too fast than allocating the buffer.
140     if (mWp.index() > mBuffers.size()) return NULL;
141     uint8_t* buf = NULL;
142     if (mWp.index() == mBuffers.size()) {
143         // Use mmap instead of malloc to ensure memory alignment i.e. no fragmentation so that
144         // the mem region can be immediately reused by the allocator after calling munmap()
145         buf = (uint8_t*)mmap(NULL, mChunkSize, PROT_READ | PROT_WRITE,
146                 MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
147 
148         if (buf == NULL) return NULL; // This indicates NO_MEMORY
149 
150         mBuffers.push_back(buf);
151     }
152     return at(mWp);
153 }
154 
155 size_t
currentToWrite()156 EncodedBuffer::currentToWrite()
157 {
158     return mChunkSize - mWp.offset();
159 }
160 
161 void
writeRawByte(uint8_t val)162 EncodedBuffer::writeRawByte(uint8_t val)
163 {
164     *writeBuffer() = val;
165     mWp.move();
166 }
167 
168 size_t
writeRawVarint64(uint64_t val)169 EncodedBuffer::writeRawVarint64(uint64_t val)
170 {
171     size_t size = 0;
172     while (true) {
173         size++;
174         if ((val & ~0x7F) == 0) {
175             writeRawByte((uint8_t) val);
176             return size;
177         } else {
178             writeRawByte((uint8_t)((val & 0x7F) | 0x80));
179             val >>= 7;
180         }
181     }
182 }
183 
184 size_t
writeRawVarint32(uint32_t val)185 EncodedBuffer::writeRawVarint32(uint32_t val)
186 {
187     uint64_t v =(uint64_t)val;
188     return writeRawVarint64(v);
189 }
190 
191 void
writeRawFixed32(uint32_t val)192 EncodedBuffer::writeRawFixed32(uint32_t val)
193 {
194     writeRawByte((uint8_t) val);
195     writeRawByte((uint8_t) (val>>8));
196     writeRawByte((uint8_t) (val>>16));
197     writeRawByte((uint8_t) (val>>24));
198 }
199 
200 void
writeRawFixed64(uint64_t val)201 EncodedBuffer::writeRawFixed64(uint64_t val)
202 {
203     writeRawByte((uint8_t) val);
204     writeRawByte((uint8_t) (val>>8));
205     writeRawByte((uint8_t) (val>>16));
206     writeRawByte((uint8_t) (val>>24));
207     writeRawByte((uint8_t) (val>>32));
208     writeRawByte((uint8_t) (val>>40));
209     writeRawByte((uint8_t) (val>>48));
210     writeRawByte((uint8_t) (val>>56));
211 }
212 
213 size_t
writeHeader(uint32_t fieldId,uint8_t wireType)214 EncodedBuffer::writeHeader(uint32_t fieldId, uint8_t wireType)
215 {
216     return writeRawVarint32((fieldId << FIELD_ID_SHIFT) | wireType);
217 }
218 
219 status_t
writeRaw(uint8_t const * buf,size_t size)220 EncodedBuffer::writeRaw(uint8_t const* buf, size_t size)
221 {
222     while (size > 0) {
223         uint8_t* target = writeBuffer();
224         if (target == NULL) {
225             return -ENOMEM;
226         }
227         size_t chunk = currentToWrite();
228         if (chunk > size) {
229             chunk = size;
230         }
231         memcpy(target, buf, chunk);
232         size -= chunk;
233         buf += chunk;
234         mWp.move(chunk);
235     }
236     return NO_ERROR;
237 }
238 
239 status_t
writeRaw(const sp<ProtoReader> & reader)240 EncodedBuffer::writeRaw(const sp<ProtoReader>& reader)
241 {
242     status_t err;
243     uint8_t const* buf;
244     while ((buf = reader->readBuffer()) != nullptr) {
245         size_t amt = reader->currentToRead();
246         err = writeRaw(buf, amt);
247         reader->move(amt);
248         if (err != NO_ERROR) {
249             return err;
250         }
251     }
252     return NO_ERROR;
253 }
254 
255 status_t
writeRaw(const sp<ProtoReader> & reader,size_t size)256 EncodedBuffer::writeRaw(const sp<ProtoReader>& reader, size_t size)
257 {
258     status_t err;
259     uint8_t const* buf;
260     while (size > 0 && (buf = reader->readBuffer()) != nullptr) {
261         size_t amt = reader->currentToRead();
262         if (size < amt) {
263             amt = size;
264         }
265         err = writeRaw(buf, amt);
266         reader->move(amt);
267         size -= amt;
268         if (err != NO_ERROR) {
269             return err;
270         }
271     }
272     return size == 0 ? NO_ERROR : NOT_ENOUGH_DATA;
273 }
274 
275 
276 /******************************** Edit APIs ************************************************/
277 EncodedBuffer::Pointer*
ep()278 EncodedBuffer::ep()
279 {
280     return &mEp;
281 }
282 
283 uint8_t
readRawByte()284 EncodedBuffer::readRawByte()
285 {
286     uint8_t val = *at(mEp);
287     mEp.move();
288     return val;
289 }
290 
291 uint64_t
readRawVarint()292 EncodedBuffer::readRawVarint()
293 {
294     uint64_t val = 0, shift = 0;
295     size_t start = mEp.pos();
296     while (true) {
297         uint8_t byte = readRawByte();
298         val |= (UINT64_C(0x7F) & byte) << shift;
299         if ((byte & 0x80) == 0) break;
300         shift += 7;
301     }
302     return val;
303 }
304 
305 uint32_t
readRawFixed32()306 EncodedBuffer::readRawFixed32()
307 {
308     uint32_t val = 0;
309     for (auto i=0; i<32; i+=8) {
310         val += (uint32_t)readRawByte() << i;
311     }
312     return val;
313 }
314 
315 uint64_t
readRawFixed64()316 EncodedBuffer::readRawFixed64()
317 {
318     uint64_t val = 0;
319     for (auto i=0; i<64; i+=8) {
320         val += (uint64_t)readRawByte() << i;
321     }
322     return val;
323 }
324 
325 void
editRawFixed32(size_t pos,uint32_t val)326 EncodedBuffer::editRawFixed32(size_t pos, uint32_t val)
327 {
328     size_t oldPos = mEp.pos();
329     mEp.rewind()->move(pos);
330     for (auto i=0; i<32; i+=8) {
331         *at(mEp) = (uint8_t) (val >> i);
332         mEp.move();
333     }
334     mEp.rewind()->move(oldPos);
335 }
336 
337 void
copy(size_t srcPos,size_t size)338 EncodedBuffer::copy(size_t srcPos, size_t size)
339 {
340     if (size == 0) return;
341     Pointer cp(mChunkSize);
342     cp.move(srcPos);
343 
344     while (cp.pos() < srcPos + size) {
345         writeRawByte(*at(cp));
346         cp.move();
347     }
348 }
349 
350 /********************************* Read APIs ************************************************/
351 sp<ProtoReader>
read()352 EncodedBuffer::read()
353 {
354     return new EncodedBuffer::Reader(this);
355 }
356 
Reader(const sp<EncodedBuffer> & buffer)357 EncodedBuffer::Reader::Reader(const sp<EncodedBuffer>& buffer)
358         :mData(buffer),
359          mRp(buffer->mChunkSize)
360 {
361 }
362 
~Reader()363 EncodedBuffer::Reader::~Reader() {
364 }
365 
366 ssize_t
size() const367 EncodedBuffer::Reader::size() const
368 {
369     return (ssize_t)mData->size();
370 }
371 
372 size_t
bytesRead() const373 EncodedBuffer::Reader::bytesRead() const
374 {
375     return mRp.pos();
376 }
377 
378 uint8_t const*
readBuffer()379 EncodedBuffer::Reader::readBuffer()
380 {
381     return hasNext() ? const_cast<uint8_t const*>(mData->at(mRp)) : NULL;
382 }
383 
384 size_t
currentToRead()385 EncodedBuffer::Reader::currentToRead()
386 {
387     return (mData->mWp.index() > mRp.index()) ?
388             mData->mChunkSize - mRp.offset() :
389             mData->mWp.offset() - mRp.offset();
390 }
391 
392 bool
hasNext()393 EncodedBuffer::Reader::hasNext()
394 {
395     return mRp.pos() < mData->mWp.pos();
396 }
397 
398 uint8_t
next()399 EncodedBuffer::Reader::next()
400 {
401     uint8_t res = *(mData->at(mRp));
402     mRp.move();
403     return res;
404 }
405 
406 uint64_t
readRawVarint()407 EncodedBuffer::Reader::readRawVarint()
408 {
409     uint64_t val = 0, shift = 0;
410     while (true) {
411         uint8_t byte = next();
412         val |= (INT64_C(0x7F) & byte) << shift;
413         if ((byte & 0x80) == 0) break;
414         shift += 7;
415     }
416     return val;
417 }
418 
419 void
move(size_t amt)420 EncodedBuffer::Reader::move(size_t amt)
421 {
422     mRp.move(amt);
423 }
424 
425 } // util
426 } // android
427