| /* |
| * Copyright (C) 2017 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. |
| */ |
| |
| #include <android/util/EncodedBuffer.h> |
| #include <android/util/protobuf.h> |
| |
| #include <stdlib.h> |
| |
| namespace android { |
| namespace util { |
| |
| const size_t BUFFER_SIZE = 8 * 1024; // 8 KB |
| |
| EncodedBuffer::Pointer::Pointer() : Pointer(BUFFER_SIZE) |
| { |
| } |
| |
| EncodedBuffer::Pointer::Pointer(size_t chunkSize) |
| :mIndex(0), |
| mOffset(0) |
| { |
| mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize; |
| } |
| |
| size_t |
| EncodedBuffer::Pointer::pos() const |
| { |
| return mIndex * mChunkSize + mOffset; |
| } |
| |
| size_t |
| EncodedBuffer::Pointer::index() const |
| { |
| return mIndex; |
| } |
| |
| size_t |
| EncodedBuffer::Pointer::offset() const |
| { |
| return mOffset; |
| } |
| |
| EncodedBuffer::Pointer* |
| EncodedBuffer::Pointer::move(size_t amt) |
| { |
| size_t newOffset = mOffset + amt; |
| mIndex += newOffset / mChunkSize; |
| mOffset = newOffset % mChunkSize; |
| return this; |
| } |
| |
| EncodedBuffer::Pointer* |
| EncodedBuffer::Pointer::rewind() |
| { |
| mIndex = 0; |
| mOffset = 0; |
| return this; |
| } |
| |
| EncodedBuffer::Pointer |
| EncodedBuffer::Pointer::copy() const |
| { |
| Pointer p = Pointer(mChunkSize); |
| p.mIndex = mIndex; |
| p.mOffset = mOffset; |
| return p; |
| } |
| |
| // =========================================================== |
| EncodedBuffer::EncodedBuffer() : EncodedBuffer(0) |
| { |
| } |
| |
| EncodedBuffer::EncodedBuffer(size_t chunkSize) |
| :mBuffers() |
| { |
| mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize; |
| mWp = Pointer(mChunkSize); |
| mEp = Pointer(mChunkSize); |
| } |
| |
| EncodedBuffer::~EncodedBuffer() |
| { |
| for (size_t i=0; i<mBuffers.size(); i++) { |
| uint8_t* buf = mBuffers[i]; |
| free(buf); |
| } |
| } |
| |
| inline uint8_t* |
| EncodedBuffer::at(const Pointer& p) const |
| { |
| return mBuffers[p.index()] + p.offset(); |
| } |
| |
| /******************************** Write APIs ************************************************/ |
| size_t |
| EncodedBuffer::size() const |
| { |
| return mWp.pos(); |
| } |
| |
| EncodedBuffer::Pointer* |
| EncodedBuffer::wp() |
| { |
| return &mWp; |
| } |
| |
| uint8_t* |
| EncodedBuffer::writeBuffer() |
| { |
| // This prevents write pointer move too fast than allocating the buffer. |
| if (mWp.index() > mBuffers.size()) return NULL; |
| uint8_t* buf = NULL; |
| if (mWp.index() == mBuffers.size()) { |
| buf = (uint8_t*)malloc(mChunkSize); |
| |
| if (buf == NULL) return NULL; // This indicates NO_MEMORY |
| |
| mBuffers.push_back(buf); |
| } |
| return at(mWp); |
| } |
| |
| size_t |
| EncodedBuffer::currentToWrite() |
| { |
| return mChunkSize - mWp.offset(); |
| } |
| |
| void |
| EncodedBuffer::writeRawByte(uint8_t val) |
| { |
| *writeBuffer() = val; |
| mWp.move(); |
| } |
| |
| size_t |
| EncodedBuffer::writeRawVarint64(uint64_t val) |
| { |
| size_t size = 0; |
| while (true) { |
| size++; |
| if ((val & ~0x7F) == 0) { |
| writeRawByte((uint8_t) val); |
| return size; |
| } else { |
| writeRawByte((uint8_t)((val & 0x7F) | 0x80)); |
| val >>= 7; |
| } |
| } |
| } |
| |
| size_t |
| EncodedBuffer::writeRawVarint32(uint32_t val) |
| { |
| uint64_t v =(uint64_t)val; |
| return writeRawVarint64(v); |
| } |
| |
| void |
| EncodedBuffer::writeRawFixed32(uint32_t val) |
| { |
| writeRawByte((uint8_t) val); |
| writeRawByte((uint8_t) (val>>8)); |
| writeRawByte((uint8_t) (val>>16)); |
| writeRawByte((uint8_t) (val>>24)); |
| } |
| |
| void |
| EncodedBuffer::writeRawFixed64(uint64_t val) |
| { |
| writeRawByte((uint8_t) val); |
| writeRawByte((uint8_t) (val>>8)); |
| writeRawByte((uint8_t) (val>>16)); |
| writeRawByte((uint8_t) (val>>24)); |
| writeRawByte((uint8_t) (val>>32)); |
| writeRawByte((uint8_t) (val>>40)); |
| writeRawByte((uint8_t) (val>>48)); |
| writeRawByte((uint8_t) (val>>56)); |
| } |
| |
| size_t |
| EncodedBuffer::writeHeader(uint32_t fieldId, uint8_t wireType) |
| { |
| return writeRawVarint32((fieldId << FIELD_ID_SHIFT) | wireType); |
| } |
| |
| /******************************** Edit APIs ************************************************/ |
| EncodedBuffer::Pointer* |
| EncodedBuffer::ep() |
| { |
| return &mEp; |
| } |
| |
| uint8_t |
| EncodedBuffer::readRawByte() |
| { |
| uint8_t val = *at(mEp); |
| mEp.move(); |
| return val; |
| } |
| |
| uint64_t |
| EncodedBuffer::readRawVarint() |
| { |
| uint64_t val = 0, shift = 0; |
| size_t start = mEp.pos(); |
| while (true) { |
| uint8_t byte = readRawByte(); |
| val += (byte & 0x7F) << shift; |
| if ((byte & 0x80) == 0) break; |
| shift += 7; |
| } |
| return val; |
| } |
| |
| uint32_t |
| EncodedBuffer::readRawFixed32() |
| { |
| uint32_t val = 0; |
| for (auto i=0; i<32; i+=8) { |
| val += (uint32_t)readRawByte() << i; |
| } |
| return val; |
| } |
| |
| uint64_t |
| EncodedBuffer::readRawFixed64() |
| { |
| uint64_t val = 0; |
| for (auto i=0; i<64; i+=8) { |
| val += (uint64_t)readRawByte() << i; |
| } |
| return val; |
| } |
| |
| void |
| EncodedBuffer::editRawFixed32(size_t pos, uint32_t val) |
| { |
| size_t oldPos = mEp.pos(); |
| mEp.rewind()->move(pos); |
| for (auto i=0; i<32; i+=8) { |
| *at(mEp) = (uint8_t) (val >> i); |
| mEp.move(); |
| } |
| mEp.rewind()->move(oldPos); |
| } |
| |
| void |
| EncodedBuffer::copy(size_t srcPos, size_t size) |
| { |
| if (size == 0) return; |
| Pointer cp(mChunkSize); |
| cp.move(srcPos); |
| |
| while (cp.pos() < srcPos + size) { |
| writeRawByte(*at(cp)); |
| cp.move(); |
| } |
| } |
| |
| /********************************* Read APIs ************************************************/ |
| EncodedBuffer::iterator |
| EncodedBuffer::begin() const |
| { |
| return EncodedBuffer::iterator(*this); |
| } |
| |
| EncodedBuffer::iterator::iterator(const EncodedBuffer& buffer) |
| :mData(buffer), |
| mRp(buffer.mChunkSize) |
| { |
| } |
| |
| size_t |
| EncodedBuffer::iterator::size() const |
| { |
| return mData.size(); |
| } |
| |
| size_t |
| EncodedBuffer::iterator::bytesRead() const |
| { |
| return mRp.pos(); |
| } |
| |
| EncodedBuffer::Pointer* |
| EncodedBuffer::iterator::rp() |
| { |
| return &mRp; |
| } |
| |
| uint8_t const* |
| EncodedBuffer::iterator::readBuffer() |
| { |
| return hasNext() ? const_cast<uint8_t const*>(mData.at(mRp)) : NULL; |
| } |
| |
| size_t |
| EncodedBuffer::iterator::currentToRead() |
| { |
| return (mData.mWp.index() > mRp.index()) ? |
| mData.mChunkSize - mRp.offset() : |
| mData.mWp.offset() - mRp.offset(); |
| } |
| |
| bool |
| EncodedBuffer::iterator::hasNext() |
| { |
| return mRp.pos() < mData.mWp.pos(); |
| } |
| |
| uint8_t |
| EncodedBuffer::iterator::next() |
| { |
| uint8_t res = *(mData.at(mRp)); |
| mRp.move(); |
| return res; |
| } |
| |
| uint64_t |
| EncodedBuffer::iterator::readRawVarint() |
| { |
| uint64_t val = 0, shift = 0; |
| while (true) { |
| uint8_t byte = next(); |
| val += (byte & 0x7F) << shift; |
| if ((byte & 0x80) == 0) break; |
| shift += 7; |
| } |
| return val; |
| } |
| |
| } // util |
| } // android |