diff options
author | Yi Jin <jinyithu@google.com> | 2017-09-15 17:24:59 -0700 |
---|---|---|
committer | Yi Jin <jinyithu@google.com> | 2017-09-27 12:23:51 -0700 |
commit | c23fad2f9079f678eae15338f5e57e2a6bf7e391 (patch) | |
tree | acc978bb1862682ef8d7832b17aefd2ddc3be14d /libs/protoutil | |
parent | f39df68b7176fac3187e882e61145dcbfb1e96ac (diff) |
Implement c++ native lib for streaming proto, part 1
Extract protobuf class out and creates EncodedBuffer class
which holds protobuf data.
Next step is to create a ProtoOutputStream and let incident helper
adapt the change as well.
please see frameworks/base/core/java/android/util/proto
Bug: 65641021
Test: unit tested
Change-Id: I0dd343b2e62d60f091c8f857fae3452ec8da6b96
Diffstat (limited to 'libs/protoutil')
-rw-r--r-- | libs/protoutil/Android.mk | 39 | ||||
-rw-r--r-- | libs/protoutil/include/android/util/EncodedBuffer.h | 170 | ||||
-rw-r--r-- | libs/protoutil/include/android/util/protobuf.h | 58 | ||||
-rw-r--r-- | libs/protoutil/src/EncodedBuffer.cpp | 237 | ||||
-rw-r--r-- | libs/protoutil/src/protobuf.cpp | 58 |
5 files changed, 562 insertions, 0 deletions
diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk new file mode 100644 index 000000000000..a5348169513f --- /dev/null +++ b/libs/protoutil/Android.mk @@ -0,0 +1,39 @@ +# +# 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := libprotoutil + +LOCAL_CFLAGS := \ + -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + liblog \ + libutils + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include + +LOCAL_SRC_FILES := \ + src/EncodedBuffer.cpp \ + src/protobuf.cpp \ + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + +include $(BUILD_SHARED_LIBRARY) + diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h new file mode 100644 index 000000000000..cf096091c055 --- /dev/null +++ b/libs/protoutil/include/android/util/EncodedBuffer.h @@ -0,0 +1,170 @@ +/* + * 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. + */ + +#ifndef ANDROID_UTIL_ENCODED_BUFFER_H +#define ANDROID_UTIL_ENCODED_BUFFER_H + +#include <stdint.h> +#include <vector> + +namespace android { +namespace util { + +using namespace std; + +/** + * A stream of bytes containing a read pointer and a write pointer, + * backed by a set of fixed-size buffers. There are write functions for the + * primitive types stored by protocol buffers, but none of the logic + * for tags, inner objects, or any of that. + * + * Terminology: + * *Pos: Position in the whole data set (as if it were a single buffer). + * *Index: Index of a buffer within the mBuffers list. + * *Offset: Position within a buffer. + */ +class EncodedBuffer +{ +public: + EncodedBuffer(); + EncodedBuffer(size_t chunkSize); + ~EncodedBuffer(); + + class Pointer { + public: + Pointer(); + Pointer(size_t chunkSize); + + size_t pos() const; + size_t index() const; + size_t offset() const; + + void move(size_t amt); + inline void move() { move(1); }; + + void rewind(); + Pointer copy() const; + + private: + size_t mChunkSize; + size_t mIndex; + size_t mOffset; + }; + + /******************************** Write APIs ************************************************/ + + /** + * Returns the number of bytes written in the buffer + */ + size_t size() const; + + /** + * Returns the write pointer. + */ + Pointer* wp(); + + /** + * Returns the current position of write pointer, if the write buffer is full, it will automatically + * rotate to a new buffer with given chunkSize. If NULL is returned, it means NO_MEMORY + */ + uint8_t* writeBuffer(); + + /** + * Returns the writeable size in the current write buffer . + */ + size_t currentToWrite(); + + /** + * Write a varint into a vector. Return the size of the varint. + */ + size_t writeRawVarint(uint32_t val); + + /** + * Write a protobuf header. Return the size of the header. + */ + size_t writeHeader(uint32_t fieldId, uint8_t wireType); + + /********************************* Read APIs ************************************************/ + class iterator; + friend class iterator; + class iterator { + public: + iterator(const EncodedBuffer& buffer); + + /** + * Returns the number of bytes written in the buffer + */ + size_t size() const; + + /** + * Returns the size of total bytes read. + */ + size_t bytesRead() const; + + /** + * Returns the read pointer. + */ + Pointer* rp(); + + /** + * Returns the current position of read pointer, if NULL is returned, it reaches end of buffer. + */ + uint8_t const* readBuffer(); + + /** + * Returns the readable size in the current read buffer. + */ + size_t currentToRead(); + + /** + * Returns true if next bytes is available for read. + */ + bool hasNext(); + + /** + * Reads the current byte and moves pointer 1 bit. + */ + uint8_t next(); + + /** + * Read varint from iterator, the iterator will point to next available byte. + * Return the number of bytes of the varint. + */ + uint32_t readRawVarint(); + + private: + const EncodedBuffer& mData; + Pointer mRp; + }; + + /** + * Returns the iterator of EncodedBuffer so it guarantees consumers won't be able to modified the buffer. + */ + iterator begin() const; + +private: + size_t mChunkSize; + vector<uint8_t*> mBuffers; + + Pointer mWp; + + inline uint8_t* at(const Pointer& p) const; // helper function to get value +}; + +} // util +} // android + +#endif // ANDROID_UTIL_ENCODED_BUFFER_H
\ No newline at end of file diff --git a/libs/protoutil/include/android/util/protobuf.h b/libs/protoutil/include/android/util/protobuf.h new file mode 100644 index 000000000000..f4e8d092ba52 --- /dev/null +++ b/libs/protoutil/include/android/util/protobuf.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef ANDROID_UTIL_PROTOBUF_H +#define ANDROID_UTIL_PROTOBUF_H + +#include <stdint.h> + +namespace android { +namespace util { + +using namespace std; + +const uint8_t WIRE_TYPE_VARINT = 0; +const uint8_t WIRE_TYPE_FIXED64 = 1; +const uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2; +const uint8_t WIRE_TYPE_FIXED32 = 5; + +/** + * Read the wire type from varint, it is the smallest 3 bits. + */ +uint8_t read_wire_type(uint32_t varint); + +/** + * read field id from varint, it is varint >> 3; + */ +uint32_t read_field_id(uint32_t varint); + +/** + * Write a varint into the buffer. Return the next position to write at. + * There must be 10 bytes in the buffer. The same as + * EncodedBuffer.writeRawVarint32 + */ +uint8_t* write_raw_varint(uint8_t* buf, uint32_t val); + +/** + * Write a protobuf WIRE_TYPE_LENGTH_DELIMITED header. Return the next position + * to write at. There must be 20 bytes in the buffer. + */ +uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size); + +} // util +} // android + +#endif // ANDROID_UTIL_PROTOUBUF_H diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp new file mode 100644 index 000000000000..84dc5b6d7852 --- /dev/null +++ b/libs/protoutil/src/EncodedBuffer.cpp @@ -0,0 +1,237 @@ +/* + * 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 <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; +} + +void +EncodedBuffer::Pointer::move(size_t amt) +{ + size_t newOffset = mOffset + amt; + mIndex += newOffset / mChunkSize; + mOffset = newOffset % mChunkSize; +} + +void +EncodedBuffer::Pointer::rewind() +{ + mIndex = 0; + mOffset = 0; +} + +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); +} + +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(); +} + +size_t +EncodedBuffer::writeRawVarint(uint32_t val) +{ + size_t size = 0; + while (true) { + size++; + if ((val & ~0x7F) == 0) { + *writeBuffer() = (uint8_t) val; + mWp.move(); + return size; + } else { + *writeBuffer() = (uint8_t)((val & 0x7F) | 0x80); + mWp.move(); + val >>= 7; + } + } +} + +size_t +EncodedBuffer::writeHeader(uint32_t fieldId, uint8_t wireType) +{ + return writeRawVarint((fieldId << 3) | wireType); +} + +/********************************* 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; +} + +uint32_t +EncodedBuffer::iterator::readRawVarint() +{ + uint32_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 diff --git a/libs/protoutil/src/protobuf.cpp b/libs/protoutil/src/protobuf.cpp new file mode 100644 index 000000000000..ec5325c57bd1 --- /dev/null +++ b/libs/protoutil/src/protobuf.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 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/protobuf.h> + +namespace android { +namespace util { + +uint8_t +read_wire_type(uint32_t varint) +{ + return (uint8_t) (varint & 0x07); +} + +uint32_t +read_field_id(uint32_t varint) +{ + return varint >> 3; +} + +uint8_t* +write_raw_varint(uint8_t* buf, uint32_t val) +{ + uint8_t* p = buf; + while (true) { + if ((val & ~0x7F) == 0) { + *p++ = (uint8_t)val; + return p; + } else { + *p++ = (uint8_t)((val & 0x7F) | 0x80); + val >>= 7; + } + } +} + +uint8_t* +write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size) +{ + buf = write_raw_varint(buf, (fieldId << 3) | 2); + buf = write_raw_varint(buf, size); + return buf; +} + +} // util +} // android |