blob: f3d92f4f7b646044a18a973ba706d47592c83ff6 [file] [log] [blame]
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_PROTOZERO_PROTO_DECODER_H_
#define INCLUDE_PERFETTO_PROTOZERO_PROTO_DECODER_H_
#include <stdint.h>
#include <memory>
#include "perfetto/base/logging.h"
#include "perfetto/base/string_view.h"
#include "perfetto/protozero/proto_utils.h"
namespace protozero {
// Reads and decodes protobuf messages from a fixed length buffer. This class
// does not allocate and does no more work than necessary so can be used in
// performance sensitive contexts.
class ProtoDecoder {
public:
using StringView = ::perfetto::base::StringView;
// The field of a protobuf message. |id| == 0 if the tag is not valid (e.g.
// because the full tag was unable to be read etc.).
struct Field {
struct LengthDelimited {
const uint8_t* data;
size_t length;
};
uint32_t id = 0;
protozero::proto_utils::FieldType type;
union {
uint64_t int_value;
LengthDelimited length_limited;
};
inline uint32_t as_uint32() const {
PERFETTO_DCHECK(type == proto_utils::FieldType::kFieldTypeVarInt ||
type == proto_utils::FieldType::kFieldTypeFixed32);
return static_cast<uint32_t>(int_value);
}
inline uint64_t as_uint64() const {
PERFETTO_DCHECK(type == proto_utils::FieldType::kFieldTypeVarInt ||
type == proto_utils::FieldType::kFieldTypeFixed64);
return int_value;
}
inline int64_t as_int64() const {
PERFETTO_DCHECK(type == proto_utils::FieldType::kFieldTypeVarInt ||
type == proto_utils::FieldType::kFieldTypeFixed64);
return static_cast<int64_t>(int_value);
}
inline float as_float() const {
PERFETTO_DCHECK(type == proto_utils::FieldType::kFieldTypeFixed32);
float res;
uint32_t value32 = static_cast<uint32_t>(int_value);
memcpy(&res, &value32, sizeof(res));
return res;
}
inline StringView as_string() const {
PERFETTO_DCHECK(type ==
proto_utils::FieldType::kFieldTypeLengthDelimited);
return StringView(reinterpret_cast<const char*>(length_limited.data),
length_limited.length);
}
inline const uint8_t* data() const {
PERFETTO_DCHECK(type ==
proto_utils::FieldType::kFieldTypeLengthDelimited);
return length_limited.data;
}
inline size_t size() const {
PERFETTO_DCHECK(type ==
proto_utils::FieldType::kFieldTypeLengthDelimited);
return static_cast<size_t>(length_limited.length);
}
};
// Creates a ProtoDecoder using the given |buffer| with size |length| bytes.
inline ProtoDecoder(const uint8_t* buffer, uint64_t length)
: buffer_(buffer), length_(length), current_position_(buffer) {}
// Reads the next field from the buffer. If the full field cannot be read,
// the returned struct will have id 0 which is an invalid field id.
Field ReadField();
template <int field_id>
inline bool FindIntField(uint64_t* field_value) {
bool res = false;
for (auto f = ReadField(); f.id != 0; f = ReadField()) {
if (f.id == field_id) {
*field_value = f.int_value;
res = true;
break;
}
}
Reset();
return res;
}
// Returns true if |length_| == |current_position_| - |buffer| and false
// otherwise.
inline bool IsEndOfBuffer() {
PERFETTO_DCHECK(current_position_ >= buffer_);
return length_ == static_cast<uint64_t>(current_position_ - buffer_);
}
// Resets the current position to the start of the buffer.
inline void Reset() { current_position_ = buffer_; }
// Resets to the given position (must be within the buffer).
inline void Reset(const uint8_t* pos) {
PERFETTO_DCHECK(pos >= buffer_ && pos < buffer_ + length_);
current_position_ = pos;
}
// Return's offset inside the buffer.
inline uint64_t offset() const {
return static_cast<uint64_t>(current_position_ - buffer_);
}
inline const uint8_t* buffer() const { return buffer_; }
inline uint64_t length() const { return length_; }
private:
const uint8_t* const buffer_;
const uint64_t length_; // The outer buffer can be larger than 4GB.
const uint8_t* current_position_ = nullptr;
};
} // namespace protozero
#endif // INCLUDE_PERFETTO_PROTOZERO_PROTO_DECODER_H_