blob: 7e78bc838f242ec87dbd50e89d71a0448c71f8e7 [file] [log] [blame]
/*
* Copyright (C) 2025 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 "src/protovm/ro_cursor.h"
#include "perfetto/base/logging.h"
#include "perfetto/protozero/proto_utils.h"
namespace perfetto {
namespace protovm {
RoCursor::RepeatedFieldIterator::RepeatedFieldIterator()
: decoder_{protozero::ConstBytes{}}, field_id_{0}, field_{Advance()} {
PERFETTO_DCHECK(!field_.valid());
}
RoCursor::RepeatedFieldIterator::RepeatedFieldIterator(
protozero::ProtoDecoder decoder,
uint32_t field_id)
: decoder_(decoder), field_id_(field_id), field_{Advance()} {}
RoCursor::RepeatedFieldIterator& RoCursor::RepeatedFieldIterator::operator++() {
field_ = Advance();
return *this;
}
RoCursor RoCursor::RepeatedFieldIterator::operator*() {
return RoCursor(field_);
}
RoCursor::RepeatedFieldIterator::operator bool() const {
return field_.valid();
}
protozero::Field RoCursor::RepeatedFieldIterator::Advance() {
auto field = decoder_.ReadField();
while (field) {
if (field.id() == field_id_) {
break;
}
field = decoder_.ReadField();
}
return field;
}
RoCursor::RoCursor() = default;
RoCursor::RoCursor(protozero::ConstBytes data) : data_{data} {}
RoCursor::RoCursor(protozero::Field data) : data_{data} {}
StatusOr<void> RoCursor::EnterField(uint32_t field_id) {
auto status_or_data = GetLengthDelimitedData();
PROTOVM_RETURN_IF_NOT_OK(status_or_data);
protozero::ProtoDecoder decoder(*status_or_data);
for (auto field = decoder.ReadField(); field.valid();
field = decoder.ReadField()) {
if (field.id() == field_id) {
data_ = field;
return StatusOr<void>::Ok();
}
}
return StatusOr<void>::Error();
}
StatusOr<void> RoCursor::EnterRepeatedFieldAt(uint32_t field_id,
uint32_t index) {
auto status_or_data = GetLengthDelimitedData();
PROTOVM_RETURN_IF_NOT_OK(status_or_data);
protozero::ProtoDecoder decoder(*status_or_data);
uint32_t current_index = 0;
for (auto field = decoder.ReadField(); field.valid();
field = decoder.ReadField()) {
if (field.id() != field_id) {
continue;
}
if (current_index == index) {
data_ = field.as_bytes();
return StatusOr<void>::Ok();
}
++current_index;
}
return StatusOr<void>::Error();
}
StatusOr<RoCursor::RepeatedFieldIterator> RoCursor::IterateRepeatedField(
uint32_t field_id) const {
auto status_or_data = GetLengthDelimitedData();
PROTOVM_RETURN_IF_NOT_OK(status_or_data);
protozero::ProtoDecoder decoder(*status_or_data);
return RepeatedFieldIterator(decoder, field_id);
}
bool RoCursor::IsScalar() const {
auto* field = std::get_if<protozero::Field>(&data_);
if (!field) {
return false;
}
return field->type() == protozero::proto_utils::ProtoWireType::kVarInt ||
field->type() == protozero::proto_utils::ProtoWireType::kFixed32 ||
field->type() == protozero::proto_utils::ProtoWireType::kFixed64;
}
bool RoCursor::IsBytes() const {
if (std::holds_alternative<protozero::ConstBytes>(data_)) {
return true;
}
auto& field = std::get<protozero::Field>(data_);
return field.type() ==
protozero::proto_utils::ProtoWireType::kLengthDelimited;
}
StatusOr<Scalar> RoCursor::GetScalar() const {
if (!IsScalar()) {
PROTOVM_ABORT("Attempted to access length-delimited field as a scalar");
}
auto& field = std::get<protozero::Field>(data_);
return Scalar{field.type(), field.as_uint64()};
}
StatusOr<protozero::ConstBytes> RoCursor::GetBytes() const {
if (std::holds_alternative<protozero::ConstBytes>(data_)) {
return std::get<protozero::ConstBytes>(data_);
}
auto& field = std::get<protozero::Field>(data_);
if (field.type() != protozero::proto_utils::ProtoWireType::kLengthDelimited) {
PROTOVM_ABORT(
"Attempted to access field as length-delimited but actual wire "
"type is "
"%u",
static_cast<uint32_t>(field.type()));
}
return field.as_bytes();
}
StatusOr<protozero::ConstBytes> RoCursor::GetLengthDelimitedData() const {
if (std::holds_alternative<protozero::Field>(data_)) {
auto& field = std::get<protozero::Field>(data_);
if (field.type() !=
protozero::proto_utils::ProtoWireType::kLengthDelimited) {
PROTOVM_ABORT(
"Attempted to access field as length-delimited, but actual "
"wire type is %u",
static_cast<uint32_t>(field.type()));
}
return field.as_bytes();
}
return std::get<protozero::ConstBytes>(data_);
}
} // namespace protovm
} // namespace perfetto