| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef TOOLS_FIDLCAT_LIB_MESSAGE_DECODER_H_ |
| #define TOOLS_FIDLCAT_LIB_MESSAGE_DECODER_H_ |
| |
| #include <lib/fidl/cpp/message.h> |
| #include <src/lib/fxl/logging.h> |
| |
| #include <memory> |
| #include <string_view> |
| #include <vector> |
| |
| #include "tools/fidlcat/lib/memory_helpers.h" |
| |
| namespace fidlcat { |
| |
| class Field; |
| class Object; |
| class Struct; |
| class Type; |
| |
| // Helper to decode a message (request or response). It generates an Object. |
| class MessageDecoder { |
| public: |
| MessageDecoder(const fidl::Message& message, bool output_errors = true); |
| MessageDecoder(const MessageDecoder* container, uint64_t num_bytes, |
| uint64_t num_handles); |
| |
| const uint8_t* byte_pos() const { return byte_pos_; } |
| |
| size_t current_offset() const { return byte_pos_ - start_byte_pos_; } |
| |
| bool output_errors() const { return output_errors_; } |
| |
| bool HasError() const { return error_count_ > 0; } |
| |
| // Adds a secondary object. That is data which can't be inlined within an |
| // object and which is decoded later. |
| void AddSecondaryObject(Field* secondary_object) { |
| secondary_objects_.push_back(secondary_object); |
| } |
| |
| // Used by numeric types to retrieve a numeric value. If there is not enough |
| // data, returns false and value is not modified. |
| template <typename T> |
| bool GetValueAt(uint64_t offset, T* value); |
| |
| // Gets the address of some data of |size| at |offset|. If there is not enough |
| // data, returns null. |
| const uint8_t* GetAddress(uint64_t offset, uint64_t size) { |
| if (byte_pos_ + offset + size > end_byte_pos_) { |
| if (output_errors_) { |
| FXL_LOG(ERROR) << "not enough data to decode (needs " << size |
| << " at offset " |
| << ((byte_pos_ - start_byte_pos_) + offset) |
| << ", remains " << (end_byte_pos_ - byte_pos_) << ")"; |
| } |
| ++error_count_; |
| return nullptr; |
| } |
| return byte_pos_ + offset; |
| } |
| |
| // Sets the offset to the next object offset. The current object may or may |
| // not have been decoded. The offset of the next object is the current |
| // object's offset + the current object's size. The new offset is 8 byte |
| // aligned. |
| void GotoNextObjectOffset(uint64_t size) { |
| byte_pos_ += size; |
| size_t offset = byte_pos_ - start_byte_pos_; |
| byte_pos_ += (8 - (offset & 7)) & 7; |
| if (byte_pos_ > end_byte_pos_) { |
| if (output_errors_) { |
| FXL_LOG(ERROR) << "not enough data at the end of object"; |
| } |
| ++error_count_; |
| } |
| } |
| |
| // Skips the handles we just decoded (used by envelopes). |
| void SkipHandles(uint64_t size) { |
| handle_pos_ += size; |
| if (handle_pos_ > end_handle_pos_) { |
| if (output_errors_) { |
| FXL_LOG(ERROR) << "not enough handles"; |
| } |
| ++error_count_; |
| } |
| } |
| |
| // Consumes a handle. Returns FIDL_HANDLE_ABSENT if there is no handle |
| // available. |
| zx_handle_t GetNextHandle() { |
| if (handle_pos_ == end_handle_pos_) { |
| if (output_errors_) { |
| FXL_LOG(ERROR) << "not enough handles"; |
| } |
| ++error_count_; |
| return FIDL_HANDLE_ABSENT; |
| } |
| return *handle_pos_++; |
| } |
| |
| // Decodes a whole message (request or response) and return an Object. |
| std::unique_ptr<Object> DecodeMessage(const Struct& message_format); |
| |
| // Decodes a field. Used by envelopes. |
| std::unique_ptr<Field> DecodeField(std::string_view name, const Type* type); |
| |
| private: |
| // The start of the message. |
| const uint8_t* const start_byte_pos_; |
| const zx_handle_t* const start_handle_pos_; |
| |
| // The end of the message. |
| const uint8_t* const end_byte_pos_; |
| const zx_handle_t* const end_handle_pos_; |
| |
| // The current decoding position in the message. |
| const uint8_t* byte_pos_; |
| const zx_handle_t* handle_pos_; |
| |
| // All the values which are not defined within the object they belong to. |
| // It is the case, for example, of string, nullable structs, ... |
| std::vector<Field*> secondary_objects_; |
| |
| // True if we display the errors we find. |
| bool output_errors_; |
| |
| // Errors found during the message decoding. |
| int error_count_ = 0; |
| }; |
| |
| // Used by numeric types to retrieve a numeric value. If there is not enough |
| // data, returns false and value is not modified. |
| template <typename T> |
| bool MessageDecoder::GetValueAt(uint64_t offset, T* value) { |
| if (byte_pos_ + offset + sizeof(T) > end_byte_pos_) { |
| if (output_errors_) { |
| FXL_LOG(ERROR) << "not enough data to decode (needs " << sizeof(T) |
| << " at offset " |
| << ((byte_pos_ - start_byte_pos_) + offset) << ", remains " |
| << (end_byte_pos_ - byte_pos_) << ")"; |
| } |
| ++error_count_; |
| return false; |
| } |
| *value = internal::MemoryFrom<T>(byte_pos_ + offset); |
| return true; |
| } |
| |
| } // namespace fidlcat |
| |
| #endif // TOOLS_FIDLCAT_LIB_MESSAGE_DECODER_H_ |