Skip to content

Commit

Permalink
Add MessageFieldWithPresence to allow surfacing info on whether a f…
Browse files Browse the repository at this point in the history
…ield has value in the proto

PiperOrigin-RevId: 718807317
Change-Id: I838f7f77c9143e875246991554d9e9f66ec3ad53
  • Loading branch information
morambro authored and copybara-github committed Jan 23, 2025
1 parent cc86fcb commit 6e35c83
Show file tree
Hide file tree
Showing 4 changed files with 505 additions and 0 deletions.
39 changes: 39 additions & 0 deletions tink/internal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -1979,3 +1979,42 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)

cc_library(
name = "proto_parser_message_field_with_presence",
hdrs = ["proto_parser_message_field_with_presence.h"],
include_prefix = "tink/internal",
deps = [
":proto_parser_fields",
":proto_parser_state",
":proto_parsing_helpers",
":proto_parsing_low_level_parser",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
],
)

cc_test(
name = "proto_parser_message_field_with_presence_test",
srcs = ["proto_parser_message_field_with_presence_test.cc"],
deps = [
":proto_parser_fields",
":proto_parser_message_field_with_presence",
":proto_parser_state",
":proto_parsing_low_level_parser",
"//tink/internal/testing:field_with_number",
"//tink/util:test_matchers",
"//tink/util:test_util",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/crc:crc32c",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
"@com_google_googletest//:gtest_main",
],
)
38 changes: 38 additions & 0 deletions tink/internal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1906,3 +1906,41 @@ tink_cc_test(
gmock
tink::util::test_util
)

tink_cc_library(
NAME proto_parser_message_field_with_presence
SRCS
proto_parser_message_field_with_presence.h
DEPS
tink::internal::proto_parser_fields
tink::internal::proto_parser_state
tink::internal::proto_parsing_helpers
tink::internal::proto_parsing_low_level_parser
absl::status
absl::statusor
absl::strings
absl::string_view
absl::optional
absl::span
)

tink_cc_test(
NAME proto_parser_message_field_with_presence_test
SRCS
proto_parser_message_field_with_presence_test.cc
DEPS
tink::internal::proto_parser_fields
tink::internal::proto_parser_message_field_with_presence
tink::internal::proto_parser_state
tink::internal::proto_parsing_low_level_parser
gmock
absl::btree
absl::crc32c
absl::strings
absl::string_view
absl::optional
absl::span
tink::internal::testing::field_with_number
tink::util::test_matchers
tink::util::test_util
)
126 changes: 126 additions & 0 deletions tink/internal/proto_parser_message_field_with_presence.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright 2025 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 TINK_INTERNAL_PROTO_PARSER_MESSAGE_FIELD_WITH_PRESENCE_H_
#define TINK_INTERNAL_PROTO_PARSER_MESSAGE_FIELD_WITH_PRESENCE_H_

#include <cstddef>
#include <cstdint>

#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "tink/internal/proto_parser_fields.h"
#include "tink/internal/proto_parser_state.h"
#include "tink/internal/proto_parsing_helpers.h"
#include "tink/internal/proto_parsing_low_level_parser.h"

namespace crypto {
namespace tink {
namespace internal {
namespace proto_parsing {

// A field in OuterStruct of type absl::optional<InnerStruct>.
// struct InnerStruct { /* omitted */ };
//
// struct OuterStruct {
// absl::optional<InnerStruct> inner_struct;
// };
template <typename OuterStruct, typename InnerStruct>
class MessageFieldWithPresence : public Field<OuterStruct> {
public:
explicit MessageFieldWithPresence(
int field_number, std::optional<InnerStruct> OuterStruct::* value,
LowLevelParser<InnerStruct> low_level_parser)
: value_(value),
field_number_(field_number),
low_level_parser_(std::move(low_level_parser)) {}
// Not copyable, not movable.
MessageFieldWithPresence(const MessageFieldWithPresence&) = delete;
MessageFieldWithPresence& operator=(const MessageFieldWithPresence&) = delete;
MessageFieldWithPresence(MessageFieldWithPresence&&) noexcept = delete;
MessageFieldWithPresence& operator=(MessageFieldWithPresence&&) noexcept =
delete;

void ClearMember(OuterStruct& s) const override { (s.*value_).reset(); }

absl::Status ConsumeIntoMember(ParsingState& serialized,
OuterStruct& s) const override {
absl::StatusOr<uint32_t> length = ConsumeVarintForSize(serialized);
if (!length.ok()) {
return length.status();
}
if (*length > serialized.RemainingData().size()) {
return absl::InvalidArgumentError(
absl::StrCat("Length ", *length, " exceeds remaining input size ",
serialized.RemainingData().size()));
}
ParsingState submessage_parsing_state =
serialized.SplitOffSubmessageState(*length);
if (!(s.*value_).has_value()) {
(s.*value_) = InnerStruct();
}
return low_level_parser_.ConsumeIntoAllFields(submessage_parsing_state,
*(s.*value_));
}

WireType GetWireType() const override { return WireType::kLengthDelimited; }
int GetFieldNumber() const override { return field_number_; }

absl::Status SerializeWithTagInto(SerializationState& out,
const OuterStruct& values) const override {
if (!(values.*value_).has_value()) {
return absl::OkStatus();
}
if (absl::Status result = SerializeWireTypeAndFieldNumber(
GetWireType(), GetFieldNumber(), out);
!result.ok()) {
return result;
}
size_t size = low_level_parser_.GetSerializedSize(*(values.*value_));
if (absl::Status result = SerializeVarint(size, out); !result.ok()) {
return result;
}
if (out.GetBuffer().size() < size) {
return absl::InvalidArgumentError(absl::StrCat(
"Output buffer too small: ", out.GetBuffer().size(), " < ", size));
}
return low_level_parser_.SerializeInto(out, *(values.*value_));
}

size_t GetSerializedSizeIncludingTag(
const OuterStruct& values) const override {
if (!(values.*value_).has_value()) {
return 0;
}
size_t size = low_level_parser_.GetSerializedSize(*(values.*value_));
return WireTypeAndFieldNumberLength(GetWireType(), GetFieldNumber()) +
VarintLength(size) + size;
}

private:
absl::optional<InnerStruct> OuterStruct::* value_;
int field_number_;
LowLevelParser<InnerStruct> low_level_parser_;
};

} // namespace proto_parsing
} // namespace internal
} // namespace tink
} // namespace crypto
#endif // TINK_INTERNAL_PROTO_PARSER_MESSAGE_FIELD_WITH_PRESENCE_H_
Loading

0 comments on commit 6e35c83

Please sign in to comment.