/* * 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 extern "C" { #include } #include namespace nvram { namespace { template T min(T x, T y) { return x < y ? x : y; } template T max(T x, T y) { return x > y ? x : y; } // Encodes |value| in varint format and writes the result to |stream|. bool EncodeVarint(OutputStreamBuffer* stream, uint64_t value) { do { uint8_t byte = (value & 0x7f) | (((value >> 7) == 0) ? 0x00 : 0x80); if (!stream->WriteByte(byte)) { return false; } value >>= 7; } while (value != 0); return true; } // Read a varint-encoded number from stream, decode it and store the result in // |value|. bool DecodeVarint(InputStreamBuffer* stream_buffer, uint64_t* value) { // Maximum number of bytes required to encode an |uint64_t| as varint. Each // byte in a varint has 7 payload bytes, so encoding 64 bits yields at most 10 // bytes. static constexpr int kMaxVarintBytes = 10; *value = 0; for (int i = 0; i < kMaxVarintBytes; ++i) { uint8_t byte = 0; if (!stream_buffer->ReadByte(&byte)) { return false; } *value |= static_cast(byte & 0x7f) << (i * 7); if ((byte & 0x80) == 0) { return true; } } return false; } } // namespace InputStreamBuffer::InputStreamBuffer(const void* data, size_t size) : InputStreamBuffer(data, static_cast(data) + size) {} InputStreamBuffer::InputStreamBuffer(const void* start, const void* end) : pos_(static_cast(start)), end_(static_cast(end)) { NVRAM_CHECK(pos_ <= end_); } bool InputStreamBuffer::Done() { return pos_ >= end_ && !Advance(); } bool InputStreamBuffer::Read(void* data, size_t size) { uint8_t* buffer = static_cast(data); NVRAM_CHECK(pos_ <= end_); while (size > static_cast(end_ - pos_)) { memcpy(buffer, pos_, end_ - pos_); buffer += end_ - pos_; size -= end_ - pos_; pos_ = end_; if (!Advance()) { return false; } NVRAM_CHECK(pos_ < end_); } memcpy(buffer, pos_, size); pos_ += size; return true; } bool InputStreamBuffer::ReadByte(uint8_t* byte) { if (pos_ >= end_) { if (!Advance()) { return false; } NVRAM_CHECK(pos_ < end_); } *byte = *pos_; ++pos_; return true; } bool InputStreamBuffer::Skip(size_t size) { NVRAM_CHECK(pos_ <= end_); while (size > static_cast(end_ - pos_)) { size -= end_ - pos_; pos_ = end_; if (!Advance()) { return false; } NVRAM_CHECK(pos_ < end_); } pos_ += size; return true; } bool InputStreamBuffer::Advance() { return false; } NestedInputStreamBuffer::NestedInputStreamBuffer(InputStreamBuffer* delegate, size_t size) : InputStreamBuffer(delegate->pos_, ClampEnd(delegate, size)), delegate_(delegate), remaining_(size) {} bool NestedInputStreamBuffer::Advance() { remaining_ -= end_ - delegate_->pos_; if (remaining_ == 0) { delegate_->pos_ = end_; return false; } bool status = delegate_->Advance(); pos_ = delegate_->pos_; end_ = ClampEnd(delegate_, remaining_); return status; } // static const uint8_t* NestedInputStreamBuffer::ClampEnd(InputStreamBuffer* delegate, size_t size) { NVRAM_CHECK(delegate->pos_ <= delegate->end_); return size < static_cast(delegate->end_ - delegate->pos_) ? delegate->pos_ + size : delegate->end_; } OutputStreamBuffer::OutputStreamBuffer(void* data, size_t size) : OutputStreamBuffer(data, static_cast(data) + size) {} OutputStreamBuffer::OutputStreamBuffer(void* start, void* end) : pos_(static_cast(start)), end_(static_cast(end)) { NVRAM_CHECK(pos_ <= end_); } bool OutputStreamBuffer::Done() { return pos_ >= end_ && !Advance(); } bool OutputStreamBuffer::Write(const void* data, size_t size) { const uint8_t* buffer = static_cast(data); NVRAM_CHECK(pos_ <= end_); while (size > static_cast(end_ - pos_)) { memcpy(pos_, buffer, end_ - pos_); buffer += end_ - pos_; size -= end_ - pos_; pos_ = end_; if (!Advance()) { return false; } NVRAM_CHECK(pos_ < end_); } memcpy(pos_, buffer, size); pos_ += size; return true; } bool OutputStreamBuffer::WriteByte(uint8_t byte) { if (pos_ >= end_) { if (!Advance()) { return false; } NVRAM_CHECK(pos_ < end_); } *pos_ = byte; ++pos_; return true; } bool OutputStreamBuffer::Advance() { return false; } CountingOutputStreamBuffer::CountingOutputStreamBuffer() : OutputStreamBuffer(scratch_space_, kScratchSpaceSize) {} bool CountingOutputStreamBuffer::Advance() { bytes_written_ += pos_ - scratch_space_; pos_ = scratch_space_; end_ = scratch_space_ + kScratchSpaceSize; return true; } uint8_t CountingOutputStreamBuffer::scratch_space_[kScratchSpaceSize]; BlobOutputStreamBuffer::BlobOutputStreamBuffer(Blob* blob) : OutputStreamBuffer(blob->data(), blob->size()), blob_(blob) {} bool BlobOutputStreamBuffer::Advance() { ptrdiff_t offset = pos_ - blob_->data(); if (!blob_->Resize(max(blob_->size() * 2, 32))) { return false; } pos_ = blob_->data() + offset; end_ = blob_->data() + blob_->size(); return true; } bool BlobOutputStreamBuffer::Truncate() { if (!blob_->Resize(pos_ - blob_->data())) { return false; } end_ = blob_->data() + blob_->size(); pos_ = end_; return true; } ProtoReader::ProtoReader(InputStreamBuffer* stream_buffer) : stream_buffer_(stream_buffer) {} bool ProtoReader::ReadWireTag() { uint64_t wire_tag; if (!DecodeVarint(stream_buffer_, &wire_tag)) { return false; } wire_type_ = wire_tag & 0x7; field_number_ = wire_tag >> 3; switch (wire_type()) { case WireType::kLengthDelimited: { uint64_t size; if (!DecodeVarint(stream_buffer_, &size)) { return false; } field_size_ = static_cast(size); if (static_cast(field_size_) != size) { return false; } break; } case WireType::kFixed64: field_size_ = sizeof(uint64_t); break; case WireType::kFixed32: field_size_ = sizeof(uint32_t); break; case WireType::kVarint: case WireType::kStartGroup: case WireType::kEndGroup: field_size_ = 0; break; } return true; } bool ProtoReader::ReadVarint(uint64_t* value) { NVRAM_CHECK(wire_type() == WireType::kVarint); return DecodeVarint(stream_buffer_, value); } bool ProtoReader::ReadLengthDelimited(void* data, size_t size) { NVRAM_CHECK(wire_type() == WireType::kLengthDelimited); return stream_buffer_->Read(data, size); } bool ProtoReader::SkipField() { if (wire_type() == WireType::kVarint) { uint64_t dummy; return DecodeVarint(stream_buffer_, &dummy); } else if (field_size_ > 0) { return stream_buffer_->Skip(field_size_); } return true; } ProtoWriter::ProtoWriter(OutputStreamBuffer* stream_buffer) : stream_buffer_(stream_buffer) {} bool ProtoWriter::WriteVarint(uint64_t value) { return WriteWireTag(WireType::kVarint) && EncodeVarint(stream_buffer_, value); } bool ProtoWriter::WriteLengthDelimited(const void* data, size_t size) { return WriteWireTag(WireType::kLengthDelimited) && EncodeVarint(stream_buffer_, size) && stream_buffer_->Write(data, size); } bool ProtoWriter::WriteLengthHeader(size_t size) { return WriteWireTag(WireType::kLengthDelimited) && EncodeVarint(stream_buffer_, size); } bool ProtoWriter::WriteWireTag(WireType wire_type) { return EncodeVarint(stream_buffer_, (field_number_ << 3) | static_cast(wire_type)); } } // namespace nvram