Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b002395 authored by Bidhya's avatar Bidhya
Browse files

Utility class to compute packet dependencies

Test: atest bluetooth_test_gd
Bug: 204206485
Tag: #refactor
Change-Id: I8ba9a45b4a0dca50aa88e789e7cc42b37446b5e6
parent 1be34ede
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ cc_binary_host {
        "fields/variable_length_struct_field.cc",
        "checksum_def.cc",
        "custom_field_def.cc",
        "packet_dependency.cc",
        "enum_def.cc",
        "enum_gen.cc",
        "packet_def.cc",
+150 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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 "packet_dependency.h"

#include <list>
#include <set>

PacketDependency::PacketDependency(const ParentDef* root) {
  std::map<std::string, std::set<std::string>> initial_parse_and_match_fields;
  std::vector<std::string> available_fields;
  CollectInitialParseAndMatchFields(root, initial_parse_and_match_fields);
  FinalizeParseAndMatchFields(root, initial_parse_and_match_fields, available_fields);
}

std::vector<std::string>& PacketDependency::GetDependencies(const std::string& packet_name) {
  return dependencies[packet_name];
}

std::vector<std::string>& PacketDependency::GetChildrenDependencies(
    const std::string& packet_name) {
  return children_dependencies[packet_name];
}

std::set<std::string> PacketDependency::CollectInitialParseAndMatchFields(
    const ParentDef* parent,
    std::map<std::string, std::set<std::string>>& initial_parse_and_match_fields) const {
  // Case Leaf Packet: Return all of its constraints
  if (parent->children_.empty()) {
    auto constraints = parent->GetAllConstraints();
    auto constraints_set = std::set<std::string>{};
    for (auto& c : constraints) {
      constraints_set.insert(c.first);
    }
    return constraints_set;
  }

  auto children_constraints = std::set<std::string>{};
  auto parent_constraints = parent->GetAllConstraints();
  auto parent_fields = parent->fields_;

  for (const auto child : parent->children_) {
    auto constraints = CollectInitialParseAndMatchFields(child, initial_parse_and_match_fields);
    auto child_only_constraints = std::set<std::string>{};
    for (auto c : constraints) {
      //             __PARENT__
      //          c1/   c2|     \c3
      //           /      |      \.
      //         CH1     CH2     CH3
      //        c4|
      //          |
      //        CH11
      // GetAllConstraints on leaf packet CH11 will return (C4, c1)
      // GetAllConstraints on packet CH1 will return C1
      // Thus CH11 only constraint is: (C4, C1) - (C1) => (C4)
      if (parent_constraints.find(c) == parent_constraints.end()) {
        child_only_constraints.insert(c);
      }
      // If the constraint can be satisfied by the immediate parent, no need to accumulate it
      if (parent_fields.GetField(c) != nullptr) {
        continue;
      }
      // Accumulate constraints from all the children so parent packet can accurately
      // figure out which constraints it should be getting from its parents.
      children_constraints.insert(c);
    }
    // child_only_constraints contains the variables required to be passed in when calling parse
    initial_parse_and_match_fields[child->name_] = child_only_constraints;
  }
  return children_constraints;
}

void PacketDependency::FinalizeParseAndMatchFields(
    const ParentDef* parent,
    std::map<std::string, std::set<std::string>>& initial_parse_and_match_fields,
    std::vector<std::string>& available_fields) {
  // Root does not have any constraints on anything
  if (parent->parent_ == nullptr) {
    dependencies[parent->name_] = std::vector<std::string>{};
    children_dependencies[parent->name_] = std::vector<std::string>{};
  }

  // Collect the available fields, required to fix the order of pass and match vectors
  for (auto& pf : parent->fields_) {
    available_fields.push_back(pf->GetName());
  }

  auto children_constraints_to_me = std::set<std::string>{};
  children_dependencies[parent->name_] = std::vector<std::string>{};

  // Accumulate direct constraints from all the children to parent
  //             __PARENT__
  //          c1/   c2|     \c3
  //           /      |      \.
  //         CH1     CH2     CH3
  //        c4|
  //          |
  //        CH11
  // For this case: children_constraints_to_me = (c1, c2, c3)
  for (auto& child : parent->children_) {
    for (auto pcons : child->parent_constraints_) {
      children_constraints_to_me.insert(pcons.first);
    }
  }

  // If children constraints to the parent are (c1, c2, c3) and so far parent has
  // fields (c1, c2) available, then parent will match its children on (c1, c2)
  for (auto avf : available_fields) {
    if (children_constraints_to_me.find(avf) != children_constraints_to_me.end()) {
      auto& match_variables = children_dependencies[parent->name_];
      if (std::find(match_variables.begin(), match_variables.end(), avf) == match_variables.end()) {
        match_variables.push_back(avf);
      }
    }
  }

  for (auto& child : parent->children_) {
    auto child_initial_parse_params = initial_parse_and_match_fields[child->name_];
    auto child_actual_parse_params = std::vector<std::string>{};

    // Remove all the params from parse method of this child
    // if these variables are the ones parent will match its children.
    for (auto pcons : child->parent_constraints_) {
      child_initial_parse_params.erase(pcons.first);
    }

    // Store unique vars from child_initial_parse_params to child_actual_parse_params
    // in the same order as fields are defined in the packets
    for (auto avf : available_fields) {
      if (child_initial_parse_params.find(avf) != child_initial_parse_params.end()) {
        child_actual_parse_params.push_back(avf);
      }
    }
    dependencies[child->name_] = child_actual_parse_params;
    FinalizeParseAndMatchFields(child, initial_parse_and_match_fields, available_fields);
  }
}
+40 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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.
 */

#pragma once

#include <map>

#include "parent_def.h"

class PacketDependency {
 private:
  std::map<std::string, std::vector<std::string>> dependencies;
  std::map<std::string, std::vector<std::string>> children_dependencies;

  std::set<std::string> CollectInitialParseAndMatchFields(
      const ParentDef* parent,
      std::map<std::string, std::set<std::string>>& initial_parse_and_match_fields) const;
  void FinalizeParseAndMatchFields(
      const ParentDef* parent,
      std::map<std::string, std::set<std::string>>& initial_parse_and_match_fields,
      std::vector<std::string>& available_fields);

 public:
  PacketDependency(const ParentDef* root);
  std::vector<std::string>& GetDependencies(const std::string& packet_name);
  std::vector<std::string>& GetChildrenDependencies(const std::string& packet_name);
};