| // 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 |
| // 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 UPBC_FILE_LAYOUT_H |
| #define UPBC_FILE_LAYOUT_H |
| |
| #include <string> |
| |
| #include "google/protobuf/descriptor.pb.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/strings/substitute.h" |
| #include "upb/mini_table/decode.h" |
| #include "upb/mini_table/encode_internal.hpp" |
| #include "upb/upb.hpp" |
| |
| namespace upbc { |
| |
| namespace protoc = ::google::protobuf::compiler; |
| namespace protobuf = ::google::protobuf; |
| |
| std::vector<const protobuf::EnumDescriptor*> SortedEnums( |
| const protobuf::FileDescriptor* file); |
| |
| // Ordering must match upb/def.c! |
| // |
| // The ordering is significant because each upb_MessageDef* will point at the |
| // corresponding upb_MiniTable and we just iterate through the list without |
| // any search or lookup. |
| std::vector<const protobuf::Descriptor*> SortedMessages( |
| const protobuf::FileDescriptor* file); |
| |
| // Ordering must match upb/def.c! |
| // |
| // The ordering is significant because each upb_FieldDef* will point at the |
| // corresponding upb_MiniTable_Extension and we just iterate through the list |
| // without any search or lookup. |
| std::vector<const protobuf::FieldDescriptor*> SortedExtensions( |
| const protobuf::FileDescriptor* file); |
| |
| std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder( |
| const protobuf::Descriptor* message); |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // FilePlatformLayout |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // FilePlatformLayout builds and vends upb MiniTables for a given platform (32 |
| // or 64 bit). |
| class FilePlatformLayout { |
| public: |
| FilePlatformLayout(const protobuf::FileDescriptor* fd, |
| upb_MiniTablePlatform platform) |
| : platform_(platform) { |
| BuildMiniTables(fd); |
| BuildExtensions(fd); |
| } |
| |
| // Retrieves a upb MiniTable or Extension given a protobuf descriptor. The |
| // descriptor must be from this layout's file. |
| upb_MiniTable* GetMiniTable(const protobuf::Descriptor* m) const; |
| upb_MiniTable_Enum* GetEnumTable(const protobuf::EnumDescriptor* d) const; |
| const upb_MiniTable_Extension* GetExtension( |
| const protobuf::FieldDescriptor* fd) const; |
| |
| // Get the initializer for the given sub-message/sub-enum link. |
| static std::string GetSub(upb_MiniTable_Sub sub); |
| |
| private: |
| // Functions to build mini-tables for this file's messages and extensions. |
| void BuildMiniTables(const protobuf::FileDescriptor* fd); |
| void BuildExtensions(const protobuf::FileDescriptor* fd); |
| upb_MiniTable* MakeMiniTable(const protobuf::Descriptor* m); |
| upb_MiniTable* MakeMapMiniTable(const protobuf::Descriptor* m); |
| upb_MiniTable* MakeMessageSetMiniTable(const protobuf::Descriptor* m); |
| upb_MiniTable* MakeRegularMiniTable(const protobuf::Descriptor* m); |
| upb_MiniTable_Enum* MakeMiniTableEnum(const protobuf::EnumDescriptor* d); |
| uint64_t GetMessageModifiers(const protobuf::Descriptor* m); |
| uint64_t GetFieldModifiers(const protobuf::FieldDescriptor* f); |
| void ResolveIntraFileReferences(); |
| |
| // When we are generating code, tables are linked to sub-tables via name (ie. |
| // a string) rather than by pointer. We need to emit an initializer like |
| // `&foo_sub_table`. To do this, we store `const char*` strings in all the |
| // links that would normally be pointers: |
| // field -> sub-message |
| // field -> enum table (proto2 only) |
| // extension -> extendee |
| // |
| // This requires a bit of reinterpret_cast<>(), but it's confined to a few |
| // functions. We tag the pointer so we know which member of the union to |
| // initialize. |
| enum SubTag { |
| kNull = 0, |
| kMessage = 1, |
| kEnum = 2, |
| kMask = 3, |
| }; |
| |
| static upb_MiniTable_Sub PackSub(const char* data, SubTag tag); |
| static bool IsNull(upb_MiniTable_Sub sub); |
| void SetSubTableStrings(); |
| upb_MiniTable_Sub PackSubForField(const protobuf::FieldDescriptor* f, |
| const upb_MiniTable_Field* mt_f); |
| const char* AllocStr(absl::string_view str); |
| |
| private: |
| using TableMap = |
| absl::flat_hash_map<const protobuf::Descriptor*, upb_MiniTable*>; |
| using EnumMap = |
| absl::flat_hash_map<const protobuf::EnumDescriptor*, upb_MiniTable_Enum*>; |
| using ExtensionMap = absl::flat_hash_map<const protobuf::FieldDescriptor*, |
| upb_MiniTable_Extension>; |
| upb::Arena arena_; |
| TableMap table_map_; |
| EnumMap enum_map_; |
| ExtensionMap extension_map_; |
| upb_MiniTablePlatform platform_; |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // FileLayout |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // FileLayout is a pair of platform layouts: one for 32-bit and one for 64-bit. |
| class FileLayout { |
| public: |
| FileLayout(const protobuf::FileDescriptor* fd) |
| : descriptor_(fd), |
| layout32_(fd, kUpb_MiniTablePlatform_32Bit), |
| layout64_(fd, kUpb_MiniTablePlatform_64Bit) {} |
| |
| const protobuf::FileDescriptor* descriptor() const { return descriptor_; } |
| |
| const upb_MiniTable* GetMiniTable32(const protobuf::Descriptor* m) const { |
| return layout32_.GetMiniTable(m); |
| } |
| |
| const upb_MiniTable* GetMiniTable64(const protobuf::Descriptor* m) const { |
| return layout64_.GetMiniTable(m); |
| } |
| |
| const upb_MiniTable_Enum* GetEnumTable( |
| const protobuf::EnumDescriptor* d) const { |
| return layout64_.GetEnumTable(d); |
| } |
| |
| std::string GetFieldOffset(const protobuf::FieldDescriptor* f) const { |
| const upb_MiniTable_Field* f_32 = upb_MiniTable_FindFieldByNumber( |
| GetMiniTable32(f->containing_type()), f->number()); |
| const upb_MiniTable_Field* f_64 = upb_MiniTable_FindFieldByNumber( |
| GetMiniTable64(f->containing_type()), f->number()); |
| return absl::Substitute("UPB_SIZE($0, $1)", f_32->offset, f_64->offset); |
| } |
| |
| std::string GetOneofCaseOffset(const protobuf::OneofDescriptor* o) const { |
| const protobuf::FieldDescriptor* f = o->field(0); |
| const upb_MiniTable_Field* f_32 = upb_MiniTable_FindFieldByNumber( |
| GetMiniTable32(f->containing_type()), f->number()); |
| const upb_MiniTable_Field* f_64 = upb_MiniTable_FindFieldByNumber( |
| GetMiniTable64(f->containing_type()), f->number()); |
| return absl::Substitute("UPB_SIZE($0, $1)", ~f_32->presence, |
| ~f_64->presence); |
| } |
| |
| std::string GetMessageSize(const protobuf::Descriptor* d) const { |
| return absl::Substitute("UPB_SIZE($0, $1)", GetMiniTable32(d)->size, |
| GetMiniTable64(d)->size); |
| } |
| |
| int GetHasbitIndex(const protobuf::FieldDescriptor* f) const { |
| const upb_MiniTable_Field* f_64 = upb_MiniTable_FindFieldByNumber( |
| GetMiniTable64(f->containing_type()), f->number()); |
| return f_64->presence; |
| } |
| |
| bool HasHasbit(const protobuf::FieldDescriptor* f) const { |
| return GetHasbitIndex(f) > 0; |
| } |
| |
| const upb_MiniTable_Extension* GetExtension( |
| const protobuf::FieldDescriptor* f) const { |
| return layout64_.GetExtension(f); |
| } |
| |
| private: |
| const protobuf::FileDescriptor* descriptor_; |
| FilePlatformLayout layout32_; |
| FilePlatformLayout layout64_; |
| }; |
| |
| } // namespace upbc |
| |
| #endif // UPBC_FILE_LAYOUT_H |