Internal change PiperOrigin-RevId: 970815405
diff --git a/upb/message/BUILD b/upb/message/BUILD index c3a7004..9780a08 100644 --- a/upb/message/BUILD +++ b/upb/message/BUILD
@@ -93,6 +93,8 @@ "//upb/mem:internal", "//upb/mini_table", "//upb/port", + "//upb/wire:encode_extension", + "//upb/wire:encoder", "//upb/wire:eps_copy_input_stream", "//upb/wire:reader", ],
diff --git a/upb/message/unknown_fields.c b/upb/message/unknown_fields.c index 7d11276..159f0d5 100644 --- a/upb/message/unknown_fields.c +++ b/upb/message/unknown_fields.c
@@ -18,12 +18,41 @@ #include "upb/message/internal/message.h" #include "upb/message/internal/types.h" #include "upb/mini_table/extension.h" +#include "upb/wire/encode.h" +#include "upb/wire/encode_extension.h" #include "upb/wire/eps_copy_input_stream.h" #include "upb/wire/reader.h" // Must be last. #include "upb/port/def.inc" +bool upb_Message_NextWireFormatUnknown(const struct upb_Message* msg, + struct upb_Arena** arena, + upb_StringView* data, uintptr_t* iter) { + upb_MessageUnknown unknown; + if (!upb_Message_NextUnknown2(msg, &unknown, iter)) { + return false; + } + if (unknown.type == kUpb_MessageUnknownType_StringView) { + *data = unknown.value.bytes; + return true; + } + UPB_ASSERT(unknown.type == kUpb_MessageUnknownType_NonCanonicalExtension); + if (!*arena) { + *arena = upb_Arena_New(); + if (!*arena) { + return false; + } + } + upb_EncodeStatus status = + upb_EncodeExtension(unknown.value.extension, *arena, data, + /*encode_options=*/0); + if (status != kUpb_EncodeStatus_Ok) { + return false; + } + return true; +} + static upb_FindUnknownRet2 upb_FindUnknownRet2_ParseError(void) { return (upb_FindUnknownRet2){.status = kUpb_FindUnknown_ParseError}; }
diff --git a/upb/message/unknown_fields.h b/upb/message/unknown_fields.h index 94c28f5..5a0b8bc 100644 --- a/upb/message/unknown_fields.h +++ b/upb/message/unknown_fields.h
@@ -70,6 +70,24 @@ return false; } +// Iterates over unknown fields in wire format (upb_StringView). +// If an unknown field is a non-canonical extension, it is automatically +// encoded into wire format into `*arena` using default encode options (0). +// +// `arena` is a pointer to `upb_Arena*`. If `*arena` is NULL when a +// non-canonical extension is encountered, an arena will be lazily created via +// `upb_Arena_New()`. The caller is responsible for freeing `*arena` (if +// non-NULL) using `upb_Arena_Free(*arena)` after iteration completes. +// +// NOTE: Automatically encoding non-canonical extensions into wire format may +// incur a performance penalty if non-canonical extensions are present, as +// encoding requires allocating temporary buffers in `*arena`. Use +// `upb_Message_NextUnknown2` if you want to inspect non-canonical extensions +// directly without encoding them. +UPB_NODISCARD bool upb_Message_NextWireFormatUnknown( + const struct upb_Message* msg, struct upb_Arena** arena, + upb_StringView* data, uintptr_t* iter); + typedef enum { kUpb_FindUnknown_Ok, kUpb_FindUnknown_NotPresent,
diff --git a/upb/message/unknown_fields_test.cc b/upb/message/unknown_fields_test.cc index e03a32a..5c0b8b3 100644 --- a/upb/message/unknown_fields_test.cc +++ b/upb/message/unknown_fields_test.cc
@@ -303,4 +303,49 @@ upb_Arena_Free(arena); } +TEST(GeneratedCode, NextWireFormatUnknown) { + upb_Arena* arena = upb_Arena_New(); + + upb_test_ModelWithExtensions* msg = upb_test_ModelWithExtensions_new(arena); + + // Add a raw unknown field string view + const char raw_bytes[] = + "\x08\x96\x01"; // tag 1 (field 1, varint), value 150 + UPB_PRIVATE(_upb_Message_AddUnknown)(UPB_UPCAST(msg), raw_bytes, 3, arena, + kUpb_AddUnknown_Copy); + + // Add non-canonical extension + upb_test_ModelExtension2* extension2 = upb_test_ModelExtension2_new(arena); + upb_test_ModelExtension2_set_i(extension2, 42); + bool set_ext_ok = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)( + UPB_UPCAST(msg), upb_test_ModelExtension2_model_ext_ext, &extension2, + arena); + EXPECT_TRUE(set_ext_ok); + + uintptr_t iter = kUpb_Message_UnknownBegin; + upb_StringView view; + upb_Arena* enc_arena = nullptr; + + // First unknown should be raw string view (enc_arena remains nullptr) + EXPECT_TRUE(upb_Message_NextWireFormatUnknown(UPB_UPCAST(msg), &enc_arena, + &view, &iter)); + EXPECT_EQ(view.size, 3); + EXPECT_EQ(memcmp(view.data, raw_bytes, 3), 0); + EXPECT_EQ(enc_arena, nullptr); + + // Second unknown should be auto-encoded non-canonical extension (enc_arena + // lazily created) + EXPECT_TRUE(upb_Message_NextWireFormatUnknown(UPB_UPCAST(msg), &enc_arena, + &view, &iter)); + EXPECT_GT(view.size, 0); + EXPECT_NE(enc_arena, nullptr); + + // No more unknowns + EXPECT_FALSE(upb_Message_NextWireFormatUnknown(UPB_UPCAST(msg), &enc_arena, + &view, &iter)); + + if (enc_arena) upb_Arena_Free(enc_arena); + upb_Arena_Free(arena); +} + } // namespace