Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions p4_pdpi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -1082,3 +1082,24 @@ cc_test(
"@p4runtime//proto/p4/v1:p4runtime_cc_proto",
],
)

cc_library(
name = "ir_entry_builder",
srcs = ["ir_entry_builder.cc"],
hdrs = ["ir_entry_builder.h"],
deps = [
":ir_cc_proto",
"@abseil-cpp//absl/strings",
],
)

cc_test(
name = "ir_entry_builder_test",
srcs = ["ir_entry_builder_test.cc"],
deps = [
":ir_cc_proto",
":ir_entry_builder",
"@googletest//:gtest_main",
"@gutil//gutil:proto_matchers",
],
)
231 changes: 231 additions & 0 deletions p4_pdpi/ir_entry_builder.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
// Copyright 2026 Google LLC
//
// 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 "p4_pdpi/ir_entry_builder.h"

#include <cstdint>
#include <string>
#include <utility>

#include "absl/strings/string_view.h"
#include "p4_pdpi/ir.pb.h"

namespace pdpi {

IrTableEntryBuilder::IrTableEntryBuilder(absl::string_view table_name) {
entry_.set_table_name(std::string(table_name));
}

IrTableEntryBuilder& IrTableEntryBuilder::TableName(
absl::string_view table_name) {
entry_.set_table_name(std::string(table_name));
return *this;
}

IrTableEntryBuilder& IrTableEntryBuilder::Priority(int32_t priority) {
entry_.set_priority(priority);
return *this;
}

IrTableEntryBuilder& IrTableEntryBuilder::Exact(absl::string_view field_name,
IrValue value) {
// Append an exact match field with the provided IrValue.
IrMatch* match = entry_.add_matches();
match->set_name(std::string(field_name));
*match->mutable_exact() = std::move(value);
return *this;
}

IrTableEntryBuilder& IrTableEntryBuilder::ExactStr(absl::string_view field_name,
absl::string_view value) {
IrValue ir_value;
ir_value.set_str(std::string(value));
return Exact(field_name, std::move(ir_value));
}

IrTableEntryBuilder& IrTableEntryBuilder::ExactHexStr(
absl::string_view field_name, absl::string_view value) {
IrValue ir_value;
ir_value.set_hex_str(std::string(value));
return Exact(field_name, std::move(ir_value));
}

IrTableEntryBuilder& IrTableEntryBuilder::ExactIpv4(
absl::string_view field_name, absl::string_view value) {
IrValue ir_value;
ir_value.set_ipv4(std::string(value));
return Exact(field_name, std::move(ir_value));
}

IrTableEntryBuilder& IrTableEntryBuilder::ExactIpv6(
absl::string_view field_name, absl::string_view value) {
IrValue ir_value;
ir_value.set_ipv6(std::string(value));
return Exact(field_name, std::move(ir_value));
}

IrTableEntryBuilder& IrTableEntryBuilder::ExactMac(absl::string_view field_name,
absl::string_view value) {
IrValue ir_value;
ir_value.set_mac(std::string(value));
return Exact(field_name, std::move(ir_value));
}

IrTableEntryBuilder& IrTableEntryBuilder::Lpm(absl::string_view field_name,
IrValue value,
int32_t prefix_length) {
// Append an LPM match field with value and prefix length.
IrMatch* match = entry_.add_matches();
match->set_name(std::string(field_name));
*match->mutable_lpm()->mutable_value() = std::move(value);
match->mutable_lpm()->set_prefix_length(prefix_length);
return *this;
}

IrTableEntryBuilder& IrTableEntryBuilder::LpmIpv4(absl::string_view field_name,
absl::string_view value,
int32_t prefix_length) {
IrValue ir_value;
ir_value.set_ipv4(std::string(value));
return Lpm(field_name, std::move(ir_value), prefix_length);
}

IrTableEntryBuilder& IrTableEntryBuilder::LpmIpv6(absl::string_view field_name,
absl::string_view value,
int32_t prefix_length) {
IrValue ir_value;
ir_value.set_ipv6(std::string(value));
return Lpm(field_name, std::move(ir_value), prefix_length);
}

IrTableEntryBuilder& IrTableEntryBuilder::Ternary(absl::string_view field_name,
IrValue value, IrValue mask) {
// Append a ternary match field with value and bitmask.
IrMatch* match = entry_.add_matches();
match->set_name(std::string(field_name));
*match->mutable_ternary()->mutable_value() = std::move(value);
*match->mutable_ternary()->mutable_mask() = std::move(mask);
return *this;
}

IrTableEntryBuilder& IrTableEntryBuilder::Optional(absl::string_view field_name,
IrValue value) {
// Append an optional match field with value.
IrMatch* match = entry_.add_matches();
match->set_name(std::string(field_name));
*match->mutable_optional()->mutable_value() = std::move(value);
return *this;
}

IrTableEntryBuilder& IrTableEntryBuilder::Action(
absl::string_view action_name) {
entry_.mutable_action()->set_name(std::string(action_name));
return *this;
}

IrTableEntryBuilder& IrTableEntryBuilder::AddActionSetMember(
absl::string_view action_name, int32_t weight,
absl::string_view watch_port) {
// Append a new weighted member action to the WCMP action set.
IrActionSetInvocation* member = entry_.mutable_action_set()->add_actions();
member->mutable_action()->set_name(std::string(action_name));
member->set_weight(weight);
if (!watch_port.empty()) {
member->set_watch_port(std::string(watch_port));
}
return *this;
}

IrTableEntryBuilder& IrTableEntryBuilder::AddActionSetMember(
IrActionInvocation action, int32_t weight, absl::string_view watch_port) {
// Append a pre-constructed member action to the WCMP action set.
IrActionSetInvocation* member = entry_.mutable_action_set()->add_actions();
*member->mutable_action() = std::move(action);
member->set_weight(weight);
if (!watch_port.empty()) {
member->set_watch_port(std::string(watch_port));
}
return *this;
}

IrActionInvocation* IrTableEntryBuilder::MutableCurrentAction() {
// Target the last action set member if present, or the single action.
if (entry_.has_action_set() && !entry_.action_set().actions().empty()) {
return entry_.mutable_action_set()
->mutable_actions(entry_.action_set().actions_size() - 1)
->mutable_action();
}
return entry_.mutable_action();
}

IrTableEntryBuilder& IrTableEntryBuilder::Param(absl::string_view param_name,
IrValue value) {
// Append parameter name and value to the currently active action.
IrActionInvocation::IrActionParam* param =
MutableCurrentAction()->add_params();
param->set_name(std::string(param_name));
*param->mutable_value() = std::move(value);
return *this;
}

IrTableEntryBuilder& IrTableEntryBuilder::ParamStr(absl::string_view param_name,
absl::string_view value) {
IrValue ir_value;
ir_value.set_str(std::string(value));
return Param(param_name, std::move(ir_value));
}

IrTableEntryBuilder& IrTableEntryBuilder::ParamHexStr(
absl::string_view param_name, absl::string_view value) {
IrValue ir_value;
ir_value.set_hex_str(std::string(value));
return Param(param_name, std::move(ir_value));
}

IrTableEntryBuilder& IrTableEntryBuilder::ParamIpv4(
absl::string_view param_name, absl::string_view value) {
IrValue ir_value;
ir_value.set_ipv4(std::string(value));
return Param(param_name, std::move(ir_value));
}

IrTableEntryBuilder& IrTableEntryBuilder::ParamIpv6(
absl::string_view param_name, absl::string_view value) {
IrValue ir_value;
ir_value.set_ipv6(std::string(value));
return Param(param_name, std::move(ir_value));
}

IrTableEntryBuilder& IrTableEntryBuilder::ParamMac(absl::string_view param_name,
absl::string_view value) {
IrValue ir_value;
ir_value.set_mac(std::string(value));
return Param(param_name, std::move(ir_value));
}

IrEntity IrTableEntryBuilder::BuildEntity() const& {
// Wrap a copy of the constructed IrTableEntry into an IrEntity.
IrEntity entity;
*entity.mutable_table_entry() = entry_;
return entity;
}

IrEntity IrTableEntryBuilder::BuildEntity() && {
// Move the constructed IrTableEntry into an IrEntity.
IrEntity entity;
*entity.mutable_table_entry() = std::move(entry_);
return entity;
}

} // namespace pdpi
123 changes: 123 additions & 0 deletions p4_pdpi/ir_entry_builder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2026 Google LLC
//
// 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 P4_INFRA_P4_PDPI_IR_ENTRY_BUILDER_H_
#define P4_INFRA_P4_PDPI_IR_ENTRY_BUILDER_H_

#include <cstdint>
#include <utility>

#include "absl/strings/string_view.h"
#include "p4_pdpi/ir.pb.h"

namespace pdpi {

// Fluent builder for constructing pdpi::IrTableEntry and pdpi::IrEntity
// objects with concise match, action, and action-set specifications.
class IrTableEntryBuilder {
public:
// Constructs an empty table entry builder.
IrTableEntryBuilder() = default;
// Constructs a builder targeting the specified P4 table name.
explicit IrTableEntryBuilder(absl::string_view table_name);

// Sets the table name.
IrTableEntryBuilder& TableName(absl::string_view table_name);

// Sets the entry priority (required for ternary/optional tables; 0 for
// exact/LPM).
IrTableEntryBuilder& Priority(int32_t priority);

// Appends an exact match with the specified IrValue format.
IrTableEntryBuilder& Exact(absl::string_view field_name, IrValue value);
IrTableEntryBuilder& ExactStr(absl::string_view field_name,
absl::string_view value);
IrTableEntryBuilder& ExactHexStr(absl::string_view field_name,
absl::string_view value);
IrTableEntryBuilder& ExactIpv4(absl::string_view field_name,
absl::string_view value);
IrTableEntryBuilder& ExactIpv6(absl::string_view field_name,
absl::string_view value);
IrTableEntryBuilder& ExactMac(absl::string_view field_name,
absl::string_view value);

// Appends an LPM match with the specified IrValue format and prefix length.
IrTableEntryBuilder& Lpm(absl::string_view field_name, IrValue value,
int32_t prefix_length);
IrTableEntryBuilder& LpmIpv4(absl::string_view field_name,
absl::string_view value, int32_t prefix_length);
IrTableEntryBuilder& LpmIpv6(absl::string_view field_name,
absl::string_view value, int32_t prefix_length);

// Appends a ternary match with value and mask.
IrTableEntryBuilder& Ternary(absl::string_view field_name, IrValue value,
IrValue mask);

// Appends an optional match.
IrTableEntryBuilder& Optional(absl::string_view field_name, IrValue value);

// Sets the single action name for this entry. Subsequent Param* calls will
// attach parameters to this action.
IrTableEntryBuilder& Action(absl::string_view action_name);

// Appends an action member to the entry's WCMP action set with the given
// weight and optional watch port. Subsequent Param* calls will attach
// parameters to this newly added member action.
IrTableEntryBuilder& AddActionSetMember(absl::string_view action_name,
int32_t weight,
absl::string_view watch_port = "");
IrTableEntryBuilder& AddActionSetMember(IrActionInvocation action,
int32_t weight,
absl::string_view watch_port = "");

// Appends a parameter to the currently active action (either the single
// entry action set by Action(), or the most recently added action set member
// from AddActionSetMember()).
IrTableEntryBuilder& Param(absl::string_view param_name, IrValue value);
IrTableEntryBuilder& ParamStr(absl::string_view param_name,
absl::string_view value);
IrTableEntryBuilder& ParamHexStr(absl::string_view param_name,
absl::string_view value);
IrTableEntryBuilder& ParamIpv4(absl::string_view param_name,
absl::string_view value);
IrTableEntryBuilder& ParamIpv6(absl::string_view param_name,
absl::string_view value);
IrTableEntryBuilder& ParamMac(absl::string_view param_name,
absl::string_view value);

// Builds and returns the constructed IrTableEntry.
IrTableEntry Build() const& { return entry_; }
IrTableEntry Build() && { return std::move(entry_); }
IrTableEntry BuildTableEntry() const& { return entry_; }
IrTableEntry BuildTableEntry() && { return std::move(entry_); }

// Builds and returns an IrEntity wrapping the constructed IrTableEntry.
IrEntity BuildEntity() const&;
IrEntity BuildEntity() &&;

private:
// === Internal Helpers ======================================================

// Returns a mutable pointer to the active action invocation (either the
// entry's single action, or the last member of action_set if present).
IrActionInvocation* MutableCurrentAction();

// === Mutable State =========================================================

IrTableEntry entry_;
};

} // namespace pdpi

#endif // P4_INFRA_P4_PDPI_IR_ENTRY_BUILDER_H_
Loading
Loading