Skip to content
Merged
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
11 changes: 11 additions & 0 deletions hpb/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ cc_library(
":arena",
":extension",
":multibackend",
":options",
":ptr",
":status",
"//hpb/internal",
Expand Down Expand Up @@ -194,3 +195,13 @@ cc_library(
"@abseil-cpp//absl/base:core_headers",
],
)

cc_library(
name = "options",
hdrs = ["options.h"],
visibility = ["//visibility:public"],
deps = [
"//hpb:extension",
"//hpb:multibackend",
],
)
2 changes: 2 additions & 0 deletions hpb/backend/upb/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ cc_library(
":interop",
"//hpb:arena",
"//hpb:extension",
"//hpb:options",
"//hpb:ptr",
"//hpb:status",
"//hpb/backend:types",
"//hpb/internal",
"//hpb/internal:message_lock",
"//hpb/internal:template_help",
Expand Down
20 changes: 20 additions & 0 deletions hpb/backend/upb/upb.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "hpb/arena.h"
#include "hpb/backend/types.h"
#include "hpb/backend/upb/interop.h"
#include "hpb/extension.h"
#include "hpb/internal/internal.h"
#include "hpb/internal/message_lock.h"
#include "hpb/internal/template_help.h"
#include "hpb/options.h"
#include "hpb/ptr.h"
#include "hpb/status.h"
#include "upb/mem/arena.h"
Expand Down Expand Up @@ -106,6 +108,24 @@ absl::StatusOr<T> Parse(absl::string_view bytes,
return MessageDecodeError(status);
}

template <typename T>
hpb::StatusOr<T> Parse(absl::string_view bytes, ParseOptions options) {
int flags = 0;
if (options.alias_string) {
flags |= kUpb_DecodeOption_AliasString;
}
T message;
auto* arena = interop::upb::GetArena(&message);
upb_DecodeStatus status = upb_Decode(
bytes.data(), bytes.size(), interop::upb::GetMessage(&message),
interop::upb::GetMiniTable(&message),
internal::GetUpbExtensions(options.extension_registry), flags, arena);
if (status == kUpb_DecodeStatus_Ok) {
return hpb::StatusOr<T>(std::move(message));
}
return hpb::StatusOr<T>(internal::backend::Error(status));
}

} // namespace hpb::internal::backend::upb

#include "upb/port/undef.inc"
Expand Down
11 changes: 11 additions & 0 deletions hpb/hpb.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
#include "hpb/extension.h"
#include "hpb/internal/template_help.h"
#include "hpb/multibackend.h"
#include "hpb/options.h"
#include "hpb/ptr.h"
#include "hpb/status.h"

#if HPB_INTERNAL_BACKEND == HPB_INTERNAL_BACKEND_UPB
#include "hpb/backend/upb/upb.h"
Expand Down Expand Up @@ -70,7 +72,16 @@ ABSL_MUST_USE_RESULT bool Parse(internal::PtrOrRaw<T> message,
return backend::Parse(message, bytes, extension_registry);
}

// Note that the default extension registry is the the generated registry.
template <typename T>
hpb::StatusOr<T> Parse(absl::string_view bytes, ParseOptions options) {
return backend::Parse<T>(bytes, options);
}

// Deprecated. Use the overload that returns hpb::StatusOr<T> instead.
// Note that the default extension registry is the empty registry.
template <typename T>
ABSL_DEPRECATED("Prefer the overload that returns hpb::StatusOr<T>")
absl::StatusOr<T> Parse(absl::string_view bytes,
const ExtensionRegistry& extension_registry =
ExtensionRegistry::empty_registry()) {
Expand Down
50 changes: 50 additions & 0 deletions hpb/options.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2025 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#ifndef GOOGLE_PROTOBUF_HPB_OPTIONS_H__
#define GOOGLE_PROTOBUF_HPB_OPTIONS_H__

#include "hpb/extension.h"
#include "hpb/multibackend.h"

namespace hpb {

struct ParseOptions {
// If true, the parsed proto may alias the input string instead of copying.
// Aliased data could include string fields, unknown fields, and possibly
// other data.
//
// REQUIRES: the input string outlives the resulting proto.
bool alias_string = false;

#if HPB_INTERNAL_BACKEND == HPB_INTERNAL_BACKEND_UPB
// For the upb backend, the user can determine which extension registry
// they wish to use. Unless there are compelling reasons to do otherwise,
// we recommend using the generated registry, which uses linker arrays
// and intelligently performs tree shaking when possible.
const ExtensionRegistry& extension_registry =
ExtensionRegistry::generated_registry();
#endif
};

inline ParseOptions ParseOptionsDefault() { return ParseOptions(); }

#if HPB_INTERNAL_BACKEND == HPB_INTERNAL_BACKEND_UPB
// The empty registry is provided here as a convenience for extant users.
// Prefer the generated registry.
inline ParseOptions ParseOptionsWithEmptyRegistry() {
ParseOptions options{
.alias_string = false,
.extension_registry = ExtensionRegistry::empty_registry(),
};
return options;
}
#endif

} // namespace hpb

#endif // GOOGLE_PROTOBUF_HPB_OPTIONS_H__
4 changes: 4 additions & 0 deletions hpb_generator/tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,11 @@ cc_test(
"//hpb/backend/upb:interop",
"//hpb:arena",
"//hpb:extension",
"//hpb:options",
"//hpb:requires",
"//hpb:ptr",
"//hpb:repeated_field",
"//hpb:status",
"//upb/mem",
"//upb/mini_table",
],
Expand All @@ -180,7 +182,9 @@ cc_test(
"//hpb",
"//hpb:arena",
"//hpb:extension",
"//hpb:options",
"//hpb:requires",
"//hpb:status",
"//hpb/backend/upb:interop",
"//upb/mem",
"@abseil-cpp//absl/status:status_matchers",
Expand Down
26 changes: 26 additions & 0 deletions hpb_generator/tests/extension_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
#include "hpb/arena.h"
#include "hpb/backend/upb/interop.h"
#include "hpb/hpb.h"
#include "hpb/options.h"
#include "hpb/requires.h"
#include "hpb/status.h"
#include "upb/mem/arena.h"

namespace {
Expand Down Expand Up @@ -488,6 +490,30 @@ TEST(CppGeneratedCode, ParseWithExtensionRegistry) {
->ext_name());
}

TEST(CppGeneratedCode, HpbStatusGeneratedRegistry) {
TestModel model;
ThemeExtension extension1;
extension1.set_ext_name("Hello World");
EXPECT_EQ(true, ::hpb::SetExtension(&model, ThemeExtension::theme_extension,
extension1)
.ok());
hpb::Arena arena;
auto bytes = ::hpb::Serialize(&model, arena);
EXPECT_EQ(true, bytes.ok());

// By default, hpb::ParseOptionsDefault uses the generated registry.
hpb::StatusOr<TestModel> parsed_model =
::hpb::Parse<TestModel>(bytes.value(), hpb::ParseOptionsDefault());
EXPECT_EQ(true, parsed_model.ok());
EXPECT_EQ(true, hpb::GetExtension(&parsed_model.value(),
ThemeExtension::theme_extension)
.ok());
EXPECT_EQ("Hello World", hpb::GetExtension(&parsed_model.value(),
ThemeExtension::theme_extension)
.value()
->ext_name());
}

TEST(CppGeneratedCode, ClearSubMessage) {
// Fill model.
TestModel model;
Expand Down
26 changes: 26 additions & 0 deletions hpb_generator/tests/test_generated.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
#include "hpb/arena.h"
#include "hpb/backend/upb/interop.h"
#include "hpb/hpb.h"
#include "hpb/options.h"
#include "hpb/ptr.h"
#include "hpb/requires.h"
#include "hpb/status.h"

namespace {

Expand Down Expand Up @@ -405,6 +407,30 @@ TEST(CppGeneratedCode, MessageMapStringKeyAndInt32Value) {
EXPECT_EQ(false, result_after_delete.ok());
}

TEST(CppGeneratedCode, HpbStatus) {
TestModel model;
model.set_str1("lightweight status");
hpb::Arena arena;
absl::StatusOr<absl::string_view> bytes = ::hpb::Serialize(&model, arena);
EXPECT_EQ(true, bytes.ok());

hpb::StatusOr<TestModel> parsed_model = ::hpb::Parse<TestModel>(
bytes.value(), hpb::ParseOptionsWithEmptyRegistry());
EXPECT_EQ(true, parsed_model.ok());
EXPECT_EQ("lightweight status", parsed_model.value().str1());
}

TEST(CppGeneratedCode, HpbStatusFail) {
hpb::StatusOr<TestModel> status = ::hpb::Parse<TestModel>(
"definitely not a proto", hpb::ParseOptionsWithEmptyRegistry());
EXPECT_EQ(false, status.ok());
EXPECT_EQ(status.error(), "Wire format was corrupt");

absl::StatusOr<TestModel> to_absl_status = status.ToAbslStatusOr();
EXPECT_EQ(false, to_absl_status.ok());
EXPECT_EQ(to_absl_status.status().message(), "Wire format was corrupt");
}

TEST(CppGeneratedCode, SerializeUsingArena) {
TestModel model;
model.set_str1("Hello World");
Expand Down
Loading