• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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