Expand embed_edition_defaults encoding to byte types.

While there is support for base64, it requires a runtime cost to decode
back into the bytes, so adding support to write out either hex or decimal
byte values to the encoding. This allows the values to be directly used for
datatypes that are more "byte buffer" than a "string".

Also make the rule's implementation return the output file via the default file
provider.

PiperOrigin-RevId: 786338818
This commit is contained in:
Thomas Van Lenten 2025-07-23 10:38:46 -07:00 committed by Copybara-Service
parent c81a0c2467
commit b09d551636
6 changed files with 119 additions and 2 deletions

View file

@ -74,6 +74,26 @@ embed_edition_defaults(
template = "defaults_test_embedded_base64.h.template",
)
embed_edition_defaults(
name = "embed_test_defaults_decimal_array",
testonly = True,
defaults = ":test_defaults_2023",
encoding = "decimal_array",
output = "defaults_test_embedded_decimal_array.h",
placeholder = "DEFAULTS_VALUE",
template = "defaults_test_embedded_decimal_array.h.template",
)
embed_edition_defaults(
name = "embed_test_defaults_hex_array",
testonly = True,
defaults = ":test_defaults_2023",
encoding = "hex_array",
output = "defaults_test_embedded_hex_array.h",
placeholder = "DEFAULTS_VALUE",
template = "defaults_test_embedded_hex_array.h.template",
)
cc_binary(
name = "internal_defaults_escape",
srcs = ["internal_defaults_escape.cc"],
@ -94,6 +114,8 @@ cc_library(
hdrs = [
"defaults_test_embedded.h",
"defaults_test_embedded_base64.h",
"defaults_test_embedded_decimal_array.h",
"defaults_test_embedded_hex_array.h",
],
)

View file

@ -78,6 +78,8 @@ def _embed_edition_defaults_impl(ctx):
inputs = [ctx.file.defaults, ctx.file.template],
)
return DefaultInfo(files = depset([ctx.outputs.output]))
embed_edition_defaults = rule(
doc = "genrule to embed edition defaults binary data into a template file.",
attrs = {
@ -103,8 +105,8 @@ embed_edition_defaults = rule(
),
"encoding": attr.string(
default = "octal",
values = ["octal", "base64"],
doc = "The encoding format to use for the binary data (octal or base64)",
values = ["octal", "base64", "decimal_array", "hex_array"],
doc = "The encoding format to use for the binary data (octal, base64, decimal_array, hex_array)",
),
"_escape": attr.label(
default = "//editions:internal_defaults_escape",

View file

@ -16,6 +16,8 @@
#include "google/protobuf/cpp_features.pb.h"
#include "editions/defaults_test_embedded.h"
#include "editions/defaults_test_embedded_base64.h"
#include "editions/defaults_test_embedded_decimal_array.h"
#include "editions/defaults_test_embedded_hex_array.h"
#include "google/protobuf/extension_set.h"
#include "google/protobuf/message.h"
#include "google/protobuf/test_textproto.h"
@ -188,6 +190,48 @@ TEST(DefaultsTest, EmbeddedBase64) {
pb::VALUE3);
}
TEST(DefaultsTest, EmbeddedDecimalArray) {
FeatureSetDefaults defaults;
ASSERT_TRUE(defaults.ParseFromArray(kDefaultTestEmbeddedDecimalArray.data(),
kDefaultTestEmbeddedDecimalArray.size()))
<< "Could not parse embedded data";
ASSERT_EQ(defaults.defaults().size(), 3);
ASSERT_EQ(defaults.minimum_edition(), EDITION_2023);
ASSERT_EQ(defaults.maximum_edition(), EDITION_2023);
EXPECT_EQ(defaults.defaults()[0].edition(), EDITION_LEGACY);
EXPECT_EQ(defaults.defaults()[1].edition(), EDITION_PROTO3);
EXPECT_EQ(defaults.defaults()[2].edition(), EDITION_2023);
EXPECT_EQ(defaults.defaults()[2].overridable_features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults.defaults()[2]
.overridable_features()
.GetExtension(pb::test)
.file_feature(),
pb::VALUE3);
}
TEST(DefaultsTest, EmbeddedHexArray) {
FeatureSetDefaults defaults;
ASSERT_TRUE(defaults.ParseFromArray(kDefaultTestEmbeddedHexArray.data(),
kDefaultTestEmbeddedHexArray.size()))
<< "Could not parse embedded data";
ASSERT_EQ(defaults.defaults().size(), 3);
ASSERT_EQ(defaults.minimum_edition(), EDITION_2023);
ASSERT_EQ(defaults.maximum_edition(), EDITION_2023);
EXPECT_EQ(defaults.defaults()[0].edition(), EDITION_LEGACY);
EXPECT_EQ(defaults.defaults()[1].edition(), EDITION_PROTO3);
EXPECT_EQ(defaults.defaults()[2].edition(), EDITION_2023);
EXPECT_EQ(defaults.defaults()[2].overridable_features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults.defaults()[2]
.overridable_features()
.GetExtension(pb::test)
.file_feature(),
pb::VALUE3);
}
// Lock down that overridable defaults never change in released editions. After
// an edition has been released these tests should never need to be touched.
class OverridableDefaultsTest : public ::testing::Test {

View file

@ -0,0 +1,11 @@
#ifndef THIRD_PARTY_PROTOBUF_EDITIONS_DEFAULTS_TEST_EMBEDDED_DECIMAL_ARRAY_H_
#define THIRD_PARTY_PROTOBUF_EDITIONS_DEFAULTS_TEST_EMBEDDED_DECIMAL_ARRAY_H_
#include <cstdint>
#include <vector>
// Normally something like this would go into a .cc file, but using a header to
// simplify things.
static const std::vector<uint8_t> kDefaultTestEmbeddedDecimalArray = { DEFAULTS_VALUE };
#endif // THIRD_PARTY_PROTOBUF_EDITIONS_DEFAULTS_TEST_EMBEDDED_DECIMAL_ARRAY_H_

View file

@ -0,0 +1,13 @@
#ifndef THIRD_PARTY_PROTOBUF_EDITIONS_DEFAULTS_TEST_EMBEDDED_HEX_ARRAY_H_
#define THIRD_PARTY_PROTOBUF_EDITIONS_DEFAULTS_TEST_EMBEDDED_HEX_ARRAY_H_
#include <cstdint>
#include <vector>
// Normally something like this would go into a .cc file, but using a header to
// simplify things.
static const std::vector<uint8_t> kDefaultTestEmbeddedHexArray = {
DEFAULTS_VALUE
};
#endif // THIRD_PARTY_PROTOBUF_EDITIONS_DEFAULTS_TEST_EMBEDDED_HEX_ARRAY_H_

View file

@ -1,4 +1,5 @@
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <sstream>
#include <string>
@ -14,6 +15,8 @@
#include "absl/flags/parse.h"
#include "absl/log/absl_log.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#if defined(_WIN32)
#include "google/protobuf/io/io_win32.h"
@ -58,6 +61,28 @@ int defaults_escape(const std::string& defaults_path,
content = absl::Base64Escape(content);
} else if (encoding == "octal") {
content = absl::CEscape(content);
} else if (encoding == "decimal_array") {
std::string encoded;
bool first = true;
for (uint8_t c : content) {
if (first) {
first = false;
absl::StrAppend(&encoded, c);
} else {
absl::StrAppend(&encoded, ", ", c);
}
}
content = encoded;
} else if (encoding == "hex_array") {
std::string encoded = {};
size_t count = 0;
for (uint8_t c : content) {
absl::string_view prefix =
(count % 12 != 0) ? ", 0x" : (count == 0 ? " 0x" : ",\n 0x");
absl::StrAppend(&encoded, prefix, absl::Hex(c, absl::kZeroPad2));
++count;
}
content = encoded;
} else {
ABSL_LOG(FATAL) << "Unknown encoding: " << encoding;
return 1;