|
| 1 | +// Copyright 2026 Google LLC |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +#include "p4_pdpi/action_profile_modes.h" |
| 16 | + |
| 17 | +#include <cstddef> |
| 18 | +#include <cstdint> |
| 19 | +#include <string> |
| 20 | +#include <vector> |
| 21 | + |
| 22 | +#include "absl/status/statusor.h" |
| 23 | +#include "absl/strings/ascii.h" |
| 24 | +#include "absl/strings/numbers.h" |
| 25 | +#include "absl/strings/str_replace.h" |
| 26 | +#include "absl/strings/string_view.h" |
| 27 | +#include "google/protobuf/util/message_differencer.h" |
| 28 | +#include "p4/config/v1/p4info.pb.h" |
| 29 | +#include "p4/config/v1/p4types.pb.h" |
| 30 | +#include "p4_pdpi/action_profile_mode.pb.h" |
| 31 | +#include "p4_pdpi/annotation_parser.h" |
| 32 | + |
| 33 | +namespace pdpi { |
| 34 | +namespace { |
| 35 | + |
| 36 | +std::string CleanToken(absl::string_view s) { |
| 37 | + s = absl::StripAsciiWhitespace(s); |
| 38 | + while (s.size() >= 2 && ((s.front() == '"' && s.back() == '"') || |
| 39 | + (s.front() == '{' && s.back() == '}') || |
| 40 | + (s.front() == '\'' && s.back() == '\''))) { |
| 41 | + s = s.substr(1, s.size() - 2); |
| 42 | + s = absl::StripAsciiWhitespace(s); |
| 43 | + } |
| 44 | + return std::string(s); |
| 45 | +} |
| 46 | + |
| 47 | +void ParseSizeSemantics(absl::string_view raw_val, ActionProfileMode& mode) { |
| 48 | + std::string cleaned = CleanToken(raw_val); |
| 49 | + if (cleaned.empty()) return; |
| 50 | + |
| 51 | + absl::StatusOr<std::vector<std::string>> parts = |
| 52 | + pdpi::annotation::ParseAsArgList( |
| 53 | + absl::StrReplaceAll(cleaned, {{":", ","}})); |
| 54 | + if (!parts.ok() || parts->empty()) { |
| 55 | + parts = std::vector<std::string>{cleaned}; |
| 56 | + } |
| 57 | + |
| 58 | + for (size_t i = 0; i < parts->size(); ++i) { |
| 59 | + std::string part = std::string(absl::StripAsciiWhitespace((*parts)[i])); |
| 60 | + size_t eq_pos = part.find('='); |
| 61 | + if (eq_pos != std::string::npos) { |
| 62 | + std::string key = CleanToken(part.substr(0, eq_pos)); |
| 63 | + std::string val = CleanToken(part.substr(eq_pos + 1)); |
| 64 | + if (key == "semantics" || key == "size_semantics") { |
| 65 | + std::string upper_val = absl::AsciiStrToUpper(val); |
| 66 | + if (upper_val == "SUM_OF_WEIGHTS") { |
| 67 | + mode.mutable_sum_of_weights(); |
| 68 | + } else if (upper_val == "SUM_OF_MEMBERS") { |
| 69 | + mode.mutable_sum_of_members(); |
| 70 | + } |
| 71 | + } else if (key == "max_weight" || key == "max_member_weight") { |
| 72 | + int64_t w; |
| 73 | + if (absl::SimpleAtoi(val, &w)) { |
| 74 | + if (!mode.has_sum_of_weights() && !mode.has_sum_of_members()) { |
| 75 | + mode.mutable_sum_of_members(); |
| 76 | + } |
| 77 | + if (mode.has_sum_of_members()) { |
| 78 | + mode.mutable_sum_of_members()->set_max_member_weight(w); |
| 79 | + } |
| 80 | + } |
| 81 | + } else if (key == "member_multiplier" || |
| 82 | + key == "member_usage_multiplier") { |
| 83 | + int64_t mult; |
| 84 | + if (absl::SimpleAtoi(val, &mult)) { |
| 85 | + mode.mutable_resource_usage_multipliers() |
| 86 | + ->set_member_usage_multiplier(mult); |
| 87 | + } |
| 88 | + } |
| 89 | + } else { |
| 90 | + std::string token = CleanToken(part); |
| 91 | + std::string upper_token = absl::AsciiStrToUpper(token); |
| 92 | + if (upper_token == "SUM_OF_WEIGHTS") { |
| 93 | + mode.mutable_sum_of_weights(); |
| 94 | + } else if (upper_token == "SUM_OF_MEMBERS") { |
| 95 | + mode.mutable_sum_of_members(); |
| 96 | + } else if (i > 0) { |
| 97 | + int64_t val; |
| 98 | + if (absl::SimpleAtoi(token, &val)) { |
| 99 | + if (mode.has_sum_of_members()) { |
| 100 | + if (val > 100) { |
| 101 | + mode.mutable_sum_of_members()->set_max_member_weight(val); |
| 102 | + } else { |
| 103 | + mode.mutable_resource_usage_multipliers() |
| 104 | + ->set_member_usage_multiplier(val); |
| 105 | + } |
| 106 | + } |
| 107 | + } |
| 108 | + } |
| 109 | + } |
| 110 | + } |
| 111 | +} |
| 112 | + |
| 113 | +ActionProfileMode ParseActionProfileModeFromKvList( |
| 114 | + const p4::config::v1::KeyValuePairList& kv_list) { |
| 115 | + ActionProfileMode mode; |
| 116 | + for (const auto& kv : kv_list.kv_pairs()) { |
| 117 | + if (kv.key() == "action_selection_mode") { |
| 118 | + std::string val = |
| 119 | + absl::AsciiStrToUpper(CleanToken(kv.value().string_value())); |
| 120 | + if (val == "HASH") { |
| 121 | + mode.set_action_selection_mode(ActionProfileMode::HASH); |
| 122 | + } else if (val == "RANDOM") { |
| 123 | + mode.set_action_selection_mode(ActionProfileMode::RANDOM); |
| 124 | + } |
| 125 | + } else if (kv.key() == "size_semantics") { |
| 126 | + ParseSizeSemantics(kv.value().string_value(), mode); |
| 127 | + } else if (kv.key() == "sum_of_weights") { |
| 128 | + mode.mutable_sum_of_weights(); |
| 129 | + } else if (kv.key() == "sum_of_members") { |
| 130 | + mode.mutable_sum_of_members(); |
| 131 | + } else if (kv.key() == "member_multiplier" || |
| 132 | + kv.key() == "member_usage_multiplier") { |
| 133 | + if (kv.value().has_int64_value()) { |
| 134 | + mode.mutable_resource_usage_multipliers()->set_member_usage_multiplier( |
| 135 | + kv.value().int64_value()); |
| 136 | + } |
| 137 | + } else if (kv.key() == "max_member_weight" || kv.key() == "max_weight") { |
| 138 | + if (kv.value().has_int64_value()) { |
| 139 | + if (!mode.has_sum_of_weights() && !mode.has_sum_of_members()) { |
| 140 | + mode.mutable_sum_of_members(); |
| 141 | + } |
| 142 | + if (mode.has_sum_of_members()) { |
| 143 | + mode.mutable_sum_of_members()->set_max_member_weight( |
| 144 | + kv.value().int64_value()); |
| 145 | + } |
| 146 | + } |
| 147 | + } |
| 148 | + } |
| 149 | + return mode; |
| 150 | +} |
| 151 | + |
| 152 | +std::vector<ActionProfileMode> ParseActionProfileModesFromExpressionList( |
| 153 | + const p4::config::v1::ExpressionList& expression_list) { |
| 154 | + std::vector<ActionProfileMode> modes; |
| 155 | + const auto& exprs = expression_list.expressions(); |
| 156 | + |
| 157 | + ActionProfileMode mode; |
| 158 | + for (size_t i = 0; i < exprs.size(); i += 3) { |
| 159 | + mode.Clear(); |
| 160 | + |
| 161 | + std::string action_selection_mode = |
| 162 | + absl::AsciiStrToUpper(CleanToken(exprs[i].string_value())); |
| 163 | + if (action_selection_mode == "HASH") { |
| 164 | + mode.set_action_selection_mode(ActionProfileMode::HASH); |
| 165 | + } else if (action_selection_mode == "RANDOM") { |
| 166 | + mode.set_action_selection_mode(ActionProfileMode::RANDOM); |
| 167 | + } |
| 168 | + |
| 169 | + if (i + 1 < exprs.size()) { |
| 170 | + ParseSizeSemantics(exprs[i + 1].string_value(), mode); |
| 171 | + } |
| 172 | + |
| 173 | + if (i + 2 < exprs.size()) { |
| 174 | + int64_t val; |
| 175 | + if (absl::SimpleAtoi(CleanToken(exprs[i + 2].string_value()), &val)) { |
| 176 | + if (mode.has_sum_of_members()) { |
| 177 | + mode.mutable_sum_of_members()->set_max_member_weight(val); |
| 178 | + } else if (mode.has_sum_of_weights()) { |
| 179 | + mode.mutable_resource_usage_multipliers() |
| 180 | + ->set_member_usage_multiplier(val); |
| 181 | + } |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + modes.push_back(mode); |
| 186 | + } |
| 187 | + |
| 188 | + return modes; |
| 189 | +} |
| 190 | + |
| 191 | +} // namespace |
| 192 | + |
| 193 | +absl::StatusOr<std::vector<ActionProfileMode>> |
| 194 | +ParseRequiredModesFromActionProfile( |
| 195 | + const p4::config::v1::ActionProfile& action_profile) { |
| 196 | + std::vector<ActionProfileMode> modes; |
| 197 | + |
| 198 | + for (const auto& sa : action_profile.preamble().structured_annotations()) { |
| 199 | + if (sa.name() != "group_mode") { |
| 200 | + continue; |
| 201 | + } |
| 202 | + if (sa.has_kv_pair_list()) { |
| 203 | + modes.push_back(ParseActionProfileModeFromKvList(sa.kv_pair_list())); |
| 204 | + } else if (sa.has_expression_list()) { |
| 205 | + std::vector<ActionProfileMode> parsed_modes = |
| 206 | + ParseActionProfileModesFromExpressionList(sa.expression_list()); |
| 207 | + modes.insert(modes.end(), parsed_modes.begin(), parsed_modes.end()); |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + if (auto bodies = GetAllAnnotationBodies( |
| 212 | + "group_mode", action_profile.preamble().annotations()); |
| 213 | + bodies.ok()) { |
| 214 | + p4::config::v1::KeyValuePairList kv_list; |
| 215 | + for (const auto& raw_body : *bodies) { |
| 216 | + kv_list.Clear(); |
| 217 | + std::string body = CleanToken(raw_body); |
| 218 | + auto parts = annotation::ParseAsArgList(body); |
| 219 | + if (!parts.ok()) continue; |
| 220 | + for (const auto& part : *parts) { |
| 221 | + size_t eq_pos = part.find('='); |
| 222 | + if (eq_pos != std::string::npos) { |
| 223 | + std::string key = CleanToken(part.substr(0, eq_pos)); |
| 224 | + std::string val = CleanToken(part.substr(eq_pos + 1)); |
| 225 | + auto* kv = kv_list.add_kv_pairs(); |
| 226 | + kv->set_key(key); |
| 227 | + int64_t int_val; |
| 228 | + if (absl::SimpleAtoi(val, &int_val)) { |
| 229 | + kv->mutable_value()->set_int64_value(int_val); |
| 230 | + } else { |
| 231 | + kv->mutable_value()->set_string_value(val); |
| 232 | + } |
| 233 | + } |
| 234 | + } |
| 235 | + modes.push_back(ParseActionProfileModeFromKvList(kv_list)); |
| 236 | + } |
| 237 | + } |
| 238 | + |
| 239 | + std::vector<ActionProfileMode> deduplicated_modes; |
| 240 | + for (const auto& mode : modes) { |
| 241 | + bool exists = false; |
| 242 | + for (const auto& existing : deduplicated_modes) { |
| 243 | + if (google::protobuf::util::MessageDifferencer::Equals(mode, existing)) { |
| 244 | + exists = true; |
| 245 | + break; |
| 246 | + } |
| 247 | + } |
| 248 | + if (!exists) { |
| 249 | + deduplicated_modes.push_back(mode); |
| 250 | + } |
| 251 | + } |
| 252 | + |
| 253 | + return deduplicated_modes; |
| 254 | +} |
| 255 | + |
| 256 | +} // namespace pdpi |
0 commit comments