1
2 #include "upbc/message_layout.h"
3 #include "google/protobuf/descriptor.pb.h"
4
5 namespace upbc {
6
7 namespace protobuf = ::google::protobuf;
8
DivRoundUp(int64_t a,int64_t b)9 static int64_t DivRoundUp(int64_t a, int64_t b) {
10 ABSL_ASSERT(a >= 0);
11 ABSL_ASSERT(b > 0);
12 return (a + b - 1) / b;
13 }
14
Place(MessageLayout::SizeAndAlign size_and_align)15 MessageLayout::Size MessageLayout::Place(
16 MessageLayout::SizeAndAlign size_and_align) {
17 Size offset = size_;
18 offset.AlignUp(size_and_align.align);
19 size_ = offset;
20 size_.Add(size_and_align.size);
21 //maxalign_.MaxFrom(size_and_align.align);
22 maxalign_.MaxFrom(size_and_align.size);
23 return offset;
24 }
25
HasHasbit(const protobuf::FieldDescriptor * field)26 bool MessageLayout::HasHasbit(const protobuf::FieldDescriptor* field) {
27 return field->has_presence() && !field->real_containing_oneof() &&
28 !field->containing_type()->options().map_entry();
29 }
30
SizeOf(const protobuf::FieldDescriptor * field)31 MessageLayout::SizeAndAlign MessageLayout::SizeOf(
32 const protobuf::FieldDescriptor* field) {
33 if (field->is_repeated()) {
34 return {{4, 8}, {4, 8}}; // Pointer to array object.
35 } else {
36 return SizeOfUnwrapped(field);
37 }
38 }
39
SizeOfUnwrapped(const protobuf::FieldDescriptor * field)40 MessageLayout::SizeAndAlign MessageLayout::SizeOfUnwrapped(
41 const protobuf::FieldDescriptor* field) {
42 switch (field->cpp_type()) {
43 case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
44 return {{4, 8}, {4, 8}}; // Pointer to message.
45 case protobuf::FieldDescriptor::CPPTYPE_STRING:
46 return {{8, 16}, {4, 8}}; // upb_strview
47 case protobuf::FieldDescriptor::CPPTYPE_BOOL:
48 return {{1, 1}, {1, 1}};
49 case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
50 case protobuf::FieldDescriptor::CPPTYPE_INT32:
51 case protobuf::FieldDescriptor::CPPTYPE_UINT32:
52 case protobuf::FieldDescriptor::CPPTYPE_ENUM:
53 return {{4, 4}, {4, 4}};
54 case protobuf::FieldDescriptor::CPPTYPE_INT64:
55 case protobuf::FieldDescriptor::CPPTYPE_UINT64:
56 case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
57 return {{8, 8}, {8, 8}};
58 }
59 assert(false);
60 return {{-1, -1}, {-1, -1}};
61 }
62
FieldLayoutRank(const protobuf::FieldDescriptor * field)63 int64_t MessageLayout::FieldLayoutRank(const protobuf::FieldDescriptor* field) {
64 // Order:
65 // 1, 2, 3. primitive fields (8, 4, 1 byte)
66 // 4. string fields
67 // 5. submessage fields
68 // 6. repeated fields
69 //
70 // This has the following nice properties:
71 //
72 // 1. padding alignment is (nearly) minimized.
73 // 2. fields that might have defaults (1-4) are segregated
74 // from fields that are always zero-initialized (5-7).
75 //
76 // We skip oneof fields, because they are emitted in a separate pass.
77 int64_t rank;
78 if (field->containing_oneof()) {
79 fprintf(stderr, "shouldn't have oneofs here.\n");
80 abort();
81 } else if (field->label() == protobuf::FieldDescriptor::LABEL_REPEATED) {
82 rank = 6;
83 } else {
84 switch (field->cpp_type()) {
85 case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
86 rank = 5;
87 break;
88 case protobuf::FieldDescriptor::CPPTYPE_STRING:
89 rank = 4;
90 break;
91 case protobuf::FieldDescriptor::CPPTYPE_BOOL:
92 rank = 3;
93 break;
94 case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
95 case protobuf::FieldDescriptor::CPPTYPE_INT32:
96 case protobuf::FieldDescriptor::CPPTYPE_UINT32:
97 rank = 2;
98 break;
99 default:
100 rank = 1;
101 break;
102 }
103 }
104
105 // Break ties with field number.
106 return (rank << 29) | field->number();
107 }
108
ComputeLayout(const protobuf::Descriptor * descriptor)109 void MessageLayout::ComputeLayout(const protobuf::Descriptor* descriptor) {
110 size_ = Size{0, 0};
111 maxalign_ = Size{8, 8};
112
113 if (descriptor->options().map_entry()) {
114 // Map entries aren't actually stored, they are only used during parsing.
115 // For parsing, it helps a lot if all map entry messages have the same
116 // layout.
117 SizeAndAlign size{{8, 16}, {4, 8}}; // upb_strview
118 field_offsets_[descriptor->FindFieldByNumber(1)] = Place(size);
119 field_offsets_[descriptor->FindFieldByNumber(2)] = Place(size);
120 } else {
121 PlaceNonOneofFields(descriptor);
122 PlaceOneofFields(descriptor);
123 }
124
125 // Align overall size up to max size.
126 size_.AlignUp(maxalign_);
127 }
128
PlaceNonOneofFields(const protobuf::Descriptor * descriptor)129 void MessageLayout::PlaceNonOneofFields(
130 const protobuf::Descriptor* descriptor) {
131 std::vector<const protobuf::FieldDescriptor*> field_order;
132 for (int i = 0; i < descriptor->field_count(); i++) {
133 const protobuf::FieldDescriptor* field = descriptor->field(i);
134 if (!field->containing_oneof()) {
135 field_order.push_back(descriptor->field(i));
136 }
137 }
138 std::sort(field_order.begin(), field_order.end(),
139 [](const protobuf::FieldDescriptor* a,
140 const protobuf::FieldDescriptor* b) {
141 return FieldLayoutRank(a) < FieldLayoutRank(b);
142 });
143
144 // Place/count hasbits.
145 int hasbit_count = 0;
146 for (auto field : FieldHotnessOrder(descriptor)) {
147 if (HasHasbit(field)) {
148 // We don't use hasbit 0, so that 0 can indicate "no presence" in the
149 // table. This wastes one hasbit, but we don't worry about it for now.
150 hasbit_indexes_[field] = ++hasbit_count;
151 }
152 }
153
154 // Place hasbits at the beginning.
155 int64_t hasbit_bytes = DivRoundUp(hasbit_count, 8);
156 Place(SizeAndAlign{{hasbit_bytes, hasbit_bytes}, {1, 1}});
157
158 // Place non-oneof fields.
159 for (auto field : field_order) {
160 field_offsets_[field] = Place(SizeOf(field));
161 }
162 }
163
PlaceOneofFields(const protobuf::Descriptor * descriptor)164 void MessageLayout::PlaceOneofFields(const protobuf::Descriptor* descriptor) {
165 std::vector<const protobuf::OneofDescriptor*> oneof_order;
166 for (int i = 0; i < descriptor->oneof_decl_count(); i++) {
167 oneof_order.push_back(descriptor->oneof_decl(i));
168 }
169 std::sort(oneof_order.begin(), oneof_order.end(),
170 [](const protobuf::OneofDescriptor* a,
171 const protobuf::OneofDescriptor* b) {
172 return a->full_name() < b->full_name();
173 });
174
175 for (auto oneof : oneof_order) {
176 SizeAndAlign oneof_maxsize{{0, 0}, {0, 0}};
177 // Calculate max size.
178 for (int i = 0; i < oneof->field_count(); i++) {
179 oneof_maxsize.MaxFrom(SizeOf(oneof->field(i)));
180 }
181
182 // Place discriminator enum and data.
183 Size data = Place(oneof_maxsize);
184 Size discriminator = Place(SizeAndAlign{{4, 4}, {4, 4}});
185
186 oneof_case_offsets_[oneof] = discriminator;
187
188 for (int i = 0; i < oneof->field_count(); i++) {
189 field_offsets_[oneof->field(i)] = data;
190 }
191 }
192 }
193
194 } // namespace upbc
195