Skip to content

Commit 0ae50e6

Browse files
P4 Infra Teamcopybara-github
authored andcommitted
[Modular Group Programming] Parse ActionProfile @group_mode annotations and union preambles in PDPI.
PiperOrigin-RevId: 985334694
1 parent 15a4d2c commit 0ae50e6

6 files changed

Lines changed: 516 additions & 4 deletions

File tree

‎p4_pdpi/BUILD.bazel‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ cc_library(
304304
# Disable default arguments internally. Using them in PDPI itself is very likely a bug.
305305
local_defines = ["PDPI_DISABLE_TRANSLATION_OPTIONS_DEFAULT"],
306306
deps = [
307+
":action_profile_modes",
307308
"@abseil-cpp//absl/status",
308309
"@abseil-cpp//absl/status:statusor",
309310
"@abseil-cpp//absl/strings",
@@ -559,6 +560,39 @@ cc_proto_library(
559560
deps = [":action_profile_mode_proto"],
560561
)
561562

563+
cc_library(
564+
name = "action_profile_modes",
565+
srcs = [
566+
"action_profile_modes.cc",
567+
],
568+
hdrs = [
569+
"action_profile_modes.h",
570+
],
571+
deps = [
572+
":action_profile_mode_cc_proto",
573+
":annotation_parser",
574+
"@abseil-cpp//absl/status:statusor",
575+
"@abseil-cpp//absl/strings",
576+
"@abseil-cpp//absl/types:span",
577+
"@gutil//gutil:status",
578+
"@p4runtime//proto/p4/config/v1:p4info_cc_proto",
579+
],
580+
)
581+
582+
cc_test(
583+
name = "action_profile_modes_test",
584+
srcs = ["action_profile_modes_test.cc"],
585+
deps = [
586+
":action_profile_mode_cc_proto",
587+
":action_profile_modes",
588+
"@abseil-cpp//absl/status",
589+
"@googletest//:gtest_main",
590+
"@gutil//gutil:proto_matchers",
591+
"@gutil//gutil:status_matchers",
592+
"@p4runtime//proto/p4/config/v1:p4info_cc_proto",
593+
],
594+
)
595+
562596
cc_library(
563597
name = "annotation_parser",
564598
srcs = [

‎p4_pdpi/action_profile_modes.cc‎

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
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 <cstdint>
18+
#include <optional>
19+
#include <string>
20+
#include <utility>
21+
#include <vector>
22+
23+
#include "absl/status/statusor.h"
24+
#include "absl/strings/ascii.h"
25+
#include "absl/strings/numbers.h"
26+
#include "absl/strings/str_split.h"
27+
#include "absl/strings/string_view.h"
28+
#include "absl/types/span.h"
29+
#include "gutil/status.h"
30+
#include "p4/config/v1/p4info.pb.h"
31+
#include "p4_pdpi/action_profile_mode.pb.h"
32+
#include "p4_pdpi/annotation_parser.h"
33+
34+
namespace pdpi {
35+
namespace {
36+
37+
constexpr absl::string_view kAnnotationLabel = "group_mode_resource_guarantee";
38+
39+
// Keys of the annotation's key-value pairs. They mirror the field names of
40+
// `ActionProfileMode` and `p4::config::v1::ActionProfile`.
41+
enum class Key {
42+
kActionSelectionMode,
43+
kSizeSemantics,
44+
kMaxMemberWeight,
45+
kMemberUsageMultiplier,
46+
};
47+
48+
// All values of `Key`. Keep in sync with the enum above; only used so that
49+
// `ParseStringToKey` does not need a second list of key spellings.
50+
constexpr Key kAllKeys[] = {
51+
Key::kActionSelectionMode,
52+
Key::kSizeSemantics,
53+
Key::kMaxMemberWeight,
54+
Key::kMemberUsageMultiplier,
55+
};
56+
57+
// Returns the spelling of `key` as it appears in the annotation.
58+
constexpr absl::string_view KeyToString(Key key) {
59+
switch (key) {
60+
case Key::kActionSelectionMode:
61+
return "action_selection_mode";
62+
case Key::kSizeSemantics:
63+
return "size_semantics";
64+
case Key::kMaxMemberWeight:
65+
return "max_member_weight";
66+
case Key::kMemberUsageMultiplier:
67+
return "member_usage_multiplier";
68+
}
69+
// Unreachable: `Key` values are only ever produced by `ParseStringToKey`.
70+
return "";
71+
}
72+
73+
absl::StatusOr<Key> ParseStringToKey(absl::string_view key) {
74+
for (Key candidate : kAllKeys) {
75+
if (key == KeyToString(candidate)) return candidate;
76+
}
77+
return gutil::InvalidArgumentErrorBuilder() << "unknown key: '" << key << "'";
78+
}
79+
80+
// Values of the `size_semantics` key.
81+
constexpr absl::string_view kSumOfWeights = "sum_of_weights";
82+
constexpr absl::string_view kSumOfMembers = "sum_of_members";
83+
84+
// Parses a single `<key> = <value>` argument of the annotation body.
85+
absl::StatusOr<std::pair<Key, absl::string_view>> ParseKeyValue(
86+
absl::string_view arg) {
87+
std::vector<absl::string_view> key_and_value =
88+
absl::StrSplit(arg, absl::MaxSplits('=', 1));
89+
if (key_and_value.size() != 2) {
90+
return gutil::InvalidArgumentErrorBuilder()
91+
<< "expected argument of the form '<key> = <value>', but got '"
92+
<< arg << "'";
93+
}
94+
ASSIGN_OR_RETURN(
95+
const Key key,
96+
ParseStringToKey(absl::StripAsciiWhitespace(key_and_value[0])));
97+
return std::make_pair(key, absl::StripAsciiWhitespace(key_and_value[1]));
98+
}
99+
100+
absl::StatusOr<int32_t> ParseInt32(Key key, absl::string_view value) {
101+
int32_t parsed_value;
102+
if (!absl::SimpleAtoi(value, &parsed_value)) {
103+
return gutil::InvalidArgumentErrorBuilder()
104+
<< "expected an integer value for key '" << KeyToString(key)
105+
<< "', but got '" << value << "'";
106+
}
107+
return parsed_value;
108+
}
109+
110+
// Parses a single mode from the argument list of one annotation, e.g.
111+
// {"action_selection_mode = HASH", "size_semantics = sum_of_members",
112+
// "max_member_weight = 4095"}.
113+
absl::StatusOr<ActionProfileMode> ParseMode(
114+
absl::Span<const std::string> args) {
115+
ActionProfileMode mode;
116+
std::optional<int32_t> max_member_weight;
117+
for (absl::string_view arg : args) {
118+
// NOTE: The structured binding is declared outside of `ASSIGN_OR_RETURN`
119+
// because the open-source version of the macro does not support
120+
// parenthesized structured binding declarations.
121+
ASSIGN_OR_RETURN(const auto key_value, ParseKeyValue(arg));
122+
const auto& [key, value] = key_value;
123+
switch (key) {
124+
case Key::kActionSelectionMode: {
125+
if (value == "HASH") {
126+
mode.set_action_selection_mode(ActionProfileMode::HASH);
127+
} else if (value == "RANDOM") {
128+
mode.set_action_selection_mode(ActionProfileMode::RANDOM);
129+
} else {
130+
return gutil::InvalidArgumentErrorBuilder()
131+
<< "unknown '" << KeyToString(key) << "' value: '" << value
132+
<< "'; expected 'HASH' or 'RANDOM'";
133+
}
134+
break;
135+
}
136+
case Key::kSizeSemantics: {
137+
if (value == kSumOfWeights) {
138+
mode.mutable_sum_of_weights();
139+
} else if (value == kSumOfMembers) {
140+
mode.mutable_sum_of_members();
141+
} else {
142+
return gutil::InvalidArgumentErrorBuilder()
143+
<< "unknown '" << KeyToString(key) << "' value: '" << value
144+
<< "'; expected '" << kSumOfWeights << "' or '"
145+
<< kSumOfMembers << "'";
146+
}
147+
break;
148+
}
149+
case Key::kMaxMemberWeight: {
150+
ASSIGN_OR_RETURN(max_member_weight, ParseInt32(key, value));
151+
break;
152+
}
153+
case Key::kMemberUsageMultiplier: {
154+
ASSIGN_OR_RETURN(int32_t multiplier, ParseInt32(key, value));
155+
mode.mutable_resource_usage_multipliers()->set_member_usage_multiplier(
156+
multiplier);
157+
break;
158+
}
159+
}
160+
}
161+
162+
if (max_member_weight.has_value()) {
163+
if (!mode.has_sum_of_members()) {
164+
return gutil::InvalidArgumentErrorBuilder()
165+
<< "'" << KeyToString(Key::kMaxMemberWeight) << "' requires '"
166+
<< KeyToString(Key::kSizeSemantics) << " = " << kSumOfMembers
167+
<< "'";
168+
}
169+
mode.mutable_sum_of_members()->set_max_member_weight(*max_member_weight);
170+
}
171+
return mode;
172+
}
173+
174+
} // namespace
175+
176+
absl::StatusOr<std::vector<ActionProfileMode>>
177+
ParseRequiredModesFromActionProfile(
178+
const p4::config::v1::ActionProfile& action_profile) {
179+
ASSIGN_OR_RETURN(
180+
std::vector<std::vector<std::string>> annotation_arg_lists,
181+
GetAllAnnotationsAsArgList(kAnnotationLabel,
182+
action_profile.preamble().annotations()));
183+
184+
std::vector<ActionProfileMode> modes;
185+
modes.reserve(annotation_arg_lists.size());
186+
for (const std::vector<std::string>& args : annotation_arg_lists) {
187+
ASSIGN_OR_RETURN(ActionProfileMode mode, ParseMode(args),
188+
_.SetPrepend()
189+
<< "failed to parse @" << kAnnotationLabel
190+
<< " annotation of ActionProfile '"
191+
<< action_profile.preamble().name() << "': ");
192+
modes.push_back(mode);
193+
}
194+
195+
return modes;
196+
}
197+
198+
} // namespace pdpi

‎p4_pdpi/action_profile_modes.h‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
#ifndef P4_INFRA_P4_PDPI_ACTION_PROFILE_MODES_H_
16+
#define P4_INFRA_P4_PDPI_ACTION_PROFILE_MODES_H_
17+
18+
#include <vector>
19+
20+
#include "absl/status/statusor.h"
21+
#include "p4/config/v1/p4info.pb.h"
22+
#include "p4_pdpi/action_profile_mode.pb.h"
23+
24+
namespace pdpi {
25+
26+
// Parses the modes required by an ActionProfile from the
27+
// `@group_mode_resource_guarantee` annotations in its preamble. Each annotation
28+
// describes one mode as a flat list of key-value pairs, e.g.
29+
//
30+
// @group_mode_resource_guarantee(action_selection_mode = HASH,
31+
// size_semantics = sum_of_members,
32+
// max_member_weight = 4095,
33+
// member_usage_multiplier = 4)
34+
//
35+
// Supported keys are `action_selection_mode` (HASH or RANDOM),
36+
// `size_semantics` (sum_of_weights or sum_of_members),
37+
// `max_member_weight` (only valid with `size_semantics = sum_of_members`) and
38+
// `member_usage_multiplier`.
39+
//
40+
// Returns InvalidArgument if an annotation is malformed, uses an unknown key or
41+
// value, or if two annotations describe the same mode.
42+
absl::StatusOr<std::vector<ActionProfileMode>>
43+
ParseRequiredModesFromActionProfile(
44+
const p4::config::v1::ActionProfile& action_profile);
45+
46+
} // namespace pdpi
47+
48+
#endif // P4_INFRA_P4_PDPI_ACTION_PROFILE_MODES_H_

0 commit comments

Comments
 (0)