From e5468b47f84263aed50d2b56507cbe4a580766f5 Mon Sep 17 00:00:00 2001 From: Protobuf Team Date: Tue, 19 Apr 2022 14:09:14 -0700 Subject: [PATCH] Add mini table accessors - Add hazzer, ClearField - Add bool/int32 accessors PiperOrigin-RevId: 442906872 --- BUILD | 39 ++++++- upb/mini_table_accessors.c | 91 ++++++++++++++++ upb/mini_table_accessors.h | 87 ++++++++++++++++ upb/mini_table_accessors_internal.h | 59 +++++++++++ upb/mini_table_accessors_test.cc | 154 ++++++++++++++++++++++++++++ 5 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 upb/mini_table_accessors.c create mode 100644 upb/mini_table_accessors.h create mode 100644 upb/mini_table_accessors_internal.h create mode 100644 upb/mini_table_accessors_test.cc diff --git a/BUILD b/BUILD index 50ac8121fc..08eb27050d 100644 --- a/BUILD +++ b/BUILD @@ -132,7 +132,9 @@ cc_library( cc_library( name = "mini_table", - srcs = ["upb/mini_table.c"], + srcs = [ + "upb/mini_table.c", + ], hdrs = [ "upb/mini_table.h", "upb/mini_table.hpp", @@ -146,6 +148,25 @@ cc_library( ], ) +cc_library( + name = "mini_table_accessors", + srcs = [ + "upb/mini_table_accessors.c", + "upb/mini_table_accessors_internal.h", + ], + hdrs = [ + "upb/mini_table_accessors.h", + ], + copts = UPB_DEFAULT_COPTS, + visibility = ["//video/youtube/utils/elements/javascript/client/proto/upb/native:__pkg__"], + deps = [ + ":mini_table", + ":mini_table_internal", + ":port", + ":upb", + ], +) + cc_test( name = "mini_table_test", srcs = ["upb/mini_table_test.cc"], @@ -159,6 +180,22 @@ cc_test( ], ) +cc_test( + name = "mini_table_accessors_test", + srcs = ["upb/mini_table_accessors_test.cc"], + deps = [ + ":mini_table", + ":mini_table_accessors", + ":mini_table_internal", + ":test_messages_proto2_proto_upb", + ":test_messages_proto3_proto_upb", + ":upb", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_googletest//:gtest_main", + "@com_google_protobuf//:protobuf", + ], +) + cc_library( name = "fastdecode", srcs = [ diff --git a/upb/mini_table_accessors.c b/upb/mini_table_accessors.c new file mode 100644 index 0000000000..86f2f42d5f --- /dev/null +++ b/upb/mini_table_accessors.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "upb/mini_table_accessors.h" + +#include "upb/mini_table.h" +#include "upb/mini_table_accessors_internal.h" +#include "upb/msg_internal.h" + +// Must be last. +#include "upb/port_def.inc" + +size_t upb_MiniTable_Field_GetSize(const upb_MiniTable_Field* f) { + static unsigned char sizes[] = { + 0, /* 0 */ + 8, /* kUpb_FieldType_Double */ + 4, /* kUpb_FieldType_Float */ + 8, /* kUpb_FieldType_Int64 */ + 8, /* kUpb_FieldType_UInt64 */ + 4, /* kUpb_FieldType_Int32 */ + 8, /* kUpb_FieldType_Fixed64 */ + 4, /* kUpb_FieldType_Fixed32 */ + 1, /* kUpb_FieldType_Bool */ + sizeof(upb_StringView), /* kUpb_FieldType_String */ + sizeof(void*), /* kUpb_FieldType_Group */ + sizeof(void*), /* kUpb_FieldType_Message */ + sizeof(upb_StringView), /* kUpb_FieldType_Bytes */ + 4, /* kUpb_FieldType_UInt32 */ + 4, /* kUpb_FieldType_Enum */ + 4, /* kUpb_FieldType_SFixed32 */ + 8, /* kUpb_FieldType_SFixed64 */ + 4, /* kUpb_FieldType_SInt32 */ + 8, /* kUpb_FieldType_SInt64 */ + }; + return upb_IsRepeatedOrMap(f) ? sizeof(void*) : sizes[f->descriptortype]; +} + +UPB_INLINE upb_Message* upb_MiniTable_GetMessage( + const upb_Message* msg, const upb_MiniTable_Field* field) { + return UPB_PTR_AT(msg, field->offset, upb_Message); +} + +bool upb_MiniTable_HasField(const upb_Message* msg, + const upb_MiniTable_Field* field) { + if (_upb_MiniTable_Field_InOneOf(field)) { + return _upb_getoneofcase_field(msg, field) == field->number; + } else if (field->presence > 0) { + return _upb_hasbit_field(msg, field); + } else { + UPB_ASSERT(field->descriptortype == kUpb_FieldType_Message || + field->descriptortype == kUpb_FieldType_Group); + return upb_MiniTable_GetMessage(msg, field) != NULL; + } +} + +void upb_MiniTable_ClearField(upb_Message* msg, + const upb_MiniTable_Field* field) { + char* mem = UPB_PTR_AT(msg, field->offset, char); + if (field->presence > 0) { + _upb_clearhas_field(msg, field); + } else if (_upb_MiniTable_Field_InOneOf(field)) { + uint32_t* oneof_case = _upb_oneofcase_field(msg, field); + if (*oneof_case != field->number) return; + *oneof_case = 0; + } + memset(mem, 0, upb_MiniTable_Field_GetSize(field)); +} diff --git a/upb/mini_table_accessors.h b/upb/mini_table_accessors.h new file mode 100644 index 0000000000..d5279feee0 --- /dev/null +++ b/upb/mini_table_accessors.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2009-2022, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_MINI_TABLE_ACCESSORS_H_ +#define UPB_MINI_TABLE_ACCESSORS_H_ + +#include "upb/mini_table_accessors_internal.h" +#include "upb/msg_internal.h" + +// Must be last. +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +bool upb_MiniTable_HasField(const upb_Message *msg, + const upb_MiniTable_Field *field); + +void upb_MiniTable_ClearField(upb_Message *msg, + const upb_MiniTable_Field *field); + +UPB_INLINE bool upb_MiniTable_GetBool(const upb_Message *msg, + const upb_MiniTable_Field *field) { + UPB_ASSERT(field->descriptortype == kUpb_FieldType_Bool); + return *UPB_PTR_AT(msg, field->offset, bool); +} + +UPB_INLINE void upb_MiniTable_SetBool(upb_Message *msg, + const upb_MiniTable_Field *field, + bool value) { + UPB_ASSERT(field->descriptortype == kUpb_FieldType_Bool); + _upb_MiniTable_SetPresence(msg, field); + *UPB_PTR_AT(msg, field->offset, bool) = value; +} + +UPB_INLINE int32_t upb_MiniTable_GetInt32(const upb_Message *msg, + const upb_MiniTable_Field *field) { + UPB_ASSERT(field->descriptortype == kUpb_FieldType_Int32 || + field->descriptortype == kUpb_FieldType_SInt32 || + field->descriptortype == kUpb_FieldType_SFixed32); + return *UPB_PTR_AT(msg, field->offset, int32_t); +} + +UPB_INLINE void upb_MiniTable_SetInt32(upb_Message *msg, + const upb_MiniTable_Field *field, + int32_t value) { + UPB_ASSERT(field->descriptortype == kUpb_FieldType_Int32 || + field->descriptortype == kUpb_FieldType_SInt32 || + field->descriptortype == kUpb_FieldType_SFixed32); + _upb_MiniTable_SetPresence(msg, field); + *UPB_PTR_AT(msg, field->offset, int32_t) = value; +} + +// TODO(ferhat): Add accessors for all proto field types. + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port_undef.inc" + +#endif // UPB_MINI_TABLE_ACCESSORS_H_ diff --git a/upb/mini_table_accessors_internal.h b/upb/mini_table_accessors_internal.h new file mode 100644 index 0000000000..a2cfa44df8 --- /dev/null +++ b/upb/mini_table_accessors_internal.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2009-2022, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UPB_MINI_TABLE_ACCESSORS_INTERNAL_H_ +#define UPB_MINI_TABLE_ACCESSORS_INTERNAL_H_ + +#include "upb/msg_internal.h" + +// Must be last. +#include "upb/port_def.inc" + +#ifdef __cplusplus +extern "C" { +#endif + +UPB_INLINE bool _upb_MiniTable_Field_InOneOf(const upb_MiniTable_Field* field) { + return field->presence < 0; +} + +UPB_INLINE void _upb_MiniTable_SetPresence(upb_Message* msg, + const upb_MiniTable_Field* field) { + if (field->presence > 0) { + _upb_sethas_field(msg, field); + } else if (_upb_MiniTable_Field_InOneOf(field)) { + *_upb_oneofcase_field(msg, field) = field->number; + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include "upb/port_undef.inc" + +#endif // UPB_MINI_TABLE_ACCESSORS_INTERNAL_H_ diff --git a/upb/mini_table_accessors_test.cc b/upb/mini_table_accessors_test.cc new file mode 100644 index 0000000000..c2f84f4f56 --- /dev/null +++ b/upb/mini_table_accessors_test.cc @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Test of mini table accessors. + * + * Messages are created and mutated using generated code, and then + * accessed through reflective APIs exposed through mini table accessors. + */ + +#include "upb/mini_table_accessors.h" + +#include "gtest/gtest.h" +#include "src/google/protobuf/test_messages_proto2.upb.h" +#include "src/google/protobuf/test_messages_proto3.upb.h" +#include "upb/mini_table.h" + +namespace { + +// Proto Field numbers used for reflective access. +const uint32_t kFieldOptionalInt32 = 1; +const uint32_t kFieldOptionalBool = 13; +const uint32_t kFieldOptionalString = 14; +const uint32_t kFieldOptionalNestedMessage = 18; +const uint32_t kFieldOptionalOneOfUInt32 = 111; +const uint32_t kFieldOptionalOneOfString = 113; + +const char kTestStr1[] = "Hello"; +const int32_t kTestInt32 = 567; + +const upb_MiniTable_Field* find_proto2_field(int field_number) { + return upb_MiniTable_FindFieldByNumber( + &protobuf_test_messages_proto2_TestAllTypesProto2_msginit, field_number); +} + +TEST(GeneratedCode, HazzersProto2) { + upb_Arena* arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2* msg = + protobuf_test_messages_proto2_TestAllTypesProto2_new(arena); + + // Scalar/Boolean. + const upb_MiniTable_Field* optional_bool_field = + find_proto2_field(kFieldOptionalBool); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_bool_field)); + protobuf_test_messages_proto2_TestAllTypesProto2_set_optional_bool(msg, true); + EXPECT_EQ(true, upb_MiniTable_HasField(msg, optional_bool_field)); + upb_MiniTable_ClearField(msg, optional_bool_field); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_bool_field)); + EXPECT_EQ( + false, + protobuf_test_messages_proto2_TestAllTypesProto2_optional_bool(msg)); + + // String. + const upb_MiniTable_Field* optional_string_field = + find_proto2_field(kFieldOptionalString); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_string_field)); + protobuf_test_messages_proto2_TestAllTypesProto2_set_optional_string( + msg, upb_StringView_FromString(kTestStr1)); + EXPECT_EQ(true, upb_MiniTable_HasField(msg, optional_string_field)); + EXPECT_EQ( + strlen(kTestStr1), + protobuf_test_messages_proto2_TestAllTypesProto2_optional_string(msg) + .size); + upb_MiniTable_ClearField(msg, optional_string_field); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_string_field)); + EXPECT_EQ( + 0, protobuf_test_messages_proto2_TestAllTypesProto2_optional_string(msg) + .size); + + // Message. + const upb_MiniTable_Field* optional_message_field = + find_proto2_field(kFieldOptionalNestedMessage); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_message_field)); + protobuf_test_messages_proto2_TestAllTypesProto2_mutable_optional_nested_message( + msg, arena); + EXPECT_EQ(true, upb_MiniTable_HasField(msg, optional_message_field)); + upb_MiniTable_ClearField(msg, optional_message_field); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_message_field)); + EXPECT_EQ( + true, + protobuf_test_messages_proto2_TestAllTypesProto2_optional_nested_message( + msg) == nullptr); + + // One of. + const upb_MiniTable_Field* optional_oneof_uint32_field = + find_proto2_field(kFieldOptionalOneOfUInt32); + const upb_MiniTable_Field* optional_oneof_string_field = + find_proto2_field(kFieldOptionalOneOfString); + + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_oneof_uint32_field)); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_oneof_string_field)); + protobuf_test_messages_proto2_TestAllTypesProto2_set_oneof_uint32(msg, 123); + EXPECT_EQ(true, upb_MiniTable_HasField(msg, optional_oneof_uint32_field)); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_oneof_string_field)); + protobuf_test_messages_proto2_TestAllTypesProto2_set_oneof_string( + msg, upb_StringView_FromString(kTestStr1)); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_oneof_uint32_field)); + EXPECT_EQ(true, upb_MiniTable_HasField(msg, optional_oneof_string_field)); + upb_MiniTable_ClearField(msg, optional_oneof_uint32_field); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_oneof_uint32_field)); + EXPECT_EQ(true, upb_MiniTable_HasField(msg, optional_oneof_string_field)); + upb_MiniTable_ClearField(msg, optional_oneof_string_field); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_oneof_uint32_field)); + EXPECT_EQ(false, upb_MiniTable_HasField(msg, optional_oneof_string_field)); + + upb_Arena_Free(arena); +} + +TEST(GeneratedCode, ScalarsProto2) { + upb_Arena* arena = upb_Arena_New(); + protobuf_test_messages_proto2_TestAllTypesProto2* msg = + protobuf_test_messages_proto2_TestAllTypesProto2_new(arena); + + const upb_MiniTable_Field* optional_int32_field = + find_proto2_field(kFieldOptionalInt32); + + EXPECT_EQ( + 0, protobuf_test_messages_proto2_TestAllTypesProto2_optional_int32(msg)); + + EXPECT_EQ(0, upb_MiniTable_GetInt32(msg, optional_int32_field)); + upb_MiniTable_SetInt32(msg, optional_int32_field, kTestInt32); + EXPECT_EQ(true, upb_MiniTable_HasField(msg, optional_int32_field)); + EXPECT_EQ(kTestInt32, upb_MiniTable_GetInt32(msg, optional_int32_field)); + EXPECT_EQ( + kTestInt32, + protobuf_test_messages_proto2_TestAllTypesProto2_optional_int32(msg)); + + upb_Arena_Free(arena); +} + +} // namespace