1 
2 #include "upb/msgfactory.h"
3 
4 #include "upb/port_def.inc"
5 
is_power_of_two(size_t val)6 static bool is_power_of_two(size_t val) {
7   return (val & (val - 1)) == 0;
8 }
9 
10 /* Align up to the given power of 2. */
align_up(size_t val,size_t align)11 static size_t align_up(size_t val, size_t align) {
12   UPB_ASSERT(is_power_of_two(align));
13   return (val + align - 1) & ~(align - 1);
14 }
15 
div_round_up(size_t n,size_t d)16 static size_t div_round_up(size_t n, size_t d) {
17   return (n + d - 1) / d;
18 }
19 
upb_msgval_sizeof2(upb_fieldtype_t type)20 static size_t upb_msgval_sizeof2(upb_fieldtype_t type) {
21   switch (type) {
22     case UPB_TYPE_DOUBLE:
23     case UPB_TYPE_INT64:
24     case UPB_TYPE_UINT64:
25       return 8;
26     case UPB_TYPE_ENUM:
27     case UPB_TYPE_INT32:
28     case UPB_TYPE_UINT32:
29     case UPB_TYPE_FLOAT:
30       return 4;
31     case UPB_TYPE_BOOL:
32       return 1;
33     case UPB_TYPE_MESSAGE:
34       return sizeof(void*);
35     case UPB_TYPE_BYTES:
36     case UPB_TYPE_STRING:
37       return sizeof(upb_strview);
38   }
39   UPB_UNREACHABLE();
40 }
41 
upb_msg_fielddefsize(const upb_fielddef * f)42 static uint8_t upb_msg_fielddefsize(const upb_fielddef *f) {
43   if (upb_fielddef_isseq(f)) {
44     return sizeof(void*);
45   } else {
46     return upb_msgval_sizeof2(upb_fielddef_type(f));
47   }
48 }
49 
50 
51 /** upb_msglayout *************************************************************/
52 
upb_msglayout_free(upb_msglayout * l)53 static void upb_msglayout_free(upb_msglayout *l) {
54   upb_gfree(l);
55 }
56 
upb_msglayout_place(upb_msglayout * l,size_t size)57 static size_t upb_msglayout_place(upb_msglayout *l, size_t size) {
58   size_t ret;
59 
60   l->size = align_up(l->size, size);
61   ret = l->size;
62   l->size += size;
63   return ret;
64 }
65 
upb_msglayout_init(const upb_msgdef * m,upb_msglayout * l,upb_msgfactory * factory)66 static bool upb_msglayout_init(const upb_msgdef *m,
67                                upb_msglayout *l,
68                                upb_msgfactory *factory) {
69   upb_msg_field_iter it;
70   upb_msg_oneof_iter oit;
71   size_t hasbit;
72   size_t submsg_count = 0;
73   const upb_msglayout **submsgs;
74   upb_msglayout_field *fields;
75 
76   for (upb_msg_field_begin(&it, m);
77        !upb_msg_field_done(&it);
78        upb_msg_field_next(&it)) {
79     const upb_fielddef* f = upb_msg_iter_field(&it);
80     if (upb_fielddef_issubmsg(f)) {
81       submsg_count++;
82     }
83   }
84 
85   memset(l, 0, sizeof(*l));
86 
87   fields = upb_gmalloc(upb_msgdef_numfields(m) * sizeof(*fields));
88   submsgs = upb_gmalloc(submsg_count * sizeof(*submsgs));
89 
90   if ((!fields && upb_msgdef_numfields(m)) ||
91       (!submsgs && submsg_count)) {
92     /* OOM. */
93     upb_gfree(fields);
94     upb_gfree(submsgs);
95     return false;
96   }
97 
98   l->field_count = upb_msgdef_numfields(m);
99   l->fields = fields;
100   l->submsgs = submsgs;
101 
102   /* Allocate data offsets in three stages:
103    *
104    * 1. hasbits.
105    * 2. regular fields.
106    * 3. oneof fields.
107    *
108    * OPT: There is a lot of room for optimization here to minimize the size.
109    */
110 
111   /* Allocate hasbits and set basic field attributes. */
112   submsg_count = 0;
113   for (upb_msg_field_begin(&it, m), hasbit = 0;
114        !upb_msg_field_done(&it);
115        upb_msg_field_next(&it)) {
116     const upb_fielddef* f = upb_msg_iter_field(&it);
117     upb_msglayout_field *field = &fields[upb_fielddef_index(f)];
118 
119     field->number = upb_fielddef_number(f);
120     field->descriptortype = upb_fielddef_descriptortype(f);
121     field->label = upb_fielddef_label(f);
122 
123     if (upb_fielddef_issubmsg(f)) {
124       const upb_msglayout *sub_layout =
125           upb_msgfactory_getlayout(factory, upb_fielddef_msgsubdef(f));
126       field->submsg_index = submsg_count++;
127       submsgs[field->submsg_index] = sub_layout;
128     }
129 
130     if (upb_fielddef_haspresence(f) && !upb_fielddef_containingoneof(f)) {
131       field->presence = (hasbit++);
132     } else {
133       field->presence = 0;
134     }
135   }
136 
137   /* Account for space used by hasbits. */
138   l->size = div_round_up(hasbit, 8);
139 
140   /* Allocate non-oneof fields. */
141   for (upb_msg_field_begin(&it, m); !upb_msg_field_done(&it);
142        upb_msg_field_next(&it)) {
143     const upb_fielddef* f = upb_msg_iter_field(&it);
144     size_t field_size = upb_msg_fielddefsize(f);
145     size_t index = upb_fielddef_index(f);
146 
147     if (upb_fielddef_containingoneof(f)) {
148       /* Oneofs are handled separately below. */
149       continue;
150     }
151 
152     fields[index].offset = upb_msglayout_place(l, field_size);
153   }
154 
155   /* Allocate oneof fields.  Each oneof field consists of a uint32 for the case
156    * and space for the actual data. */
157   for (upb_msg_oneof_begin(&oit, m); !upb_msg_oneof_done(&oit);
158        upb_msg_oneof_next(&oit)) {
159     const upb_oneofdef* o = upb_msg_iter_oneof(&oit);
160     upb_oneof_iter fit;
161 
162     size_t case_size = sizeof(uint32_t);  /* Could potentially optimize this. */
163     size_t field_size = 0;
164     uint32_t case_offset;
165     uint32_t data_offset;
166 
167     /* Calculate field size: the max of all field sizes. */
168     for (upb_oneof_begin(&fit, o);
169          !upb_oneof_done(&fit);
170          upb_oneof_next(&fit)) {
171       const upb_fielddef* f = upb_oneof_iter_field(&fit);
172       field_size = UPB_MAX(field_size, upb_msg_fielddefsize(f));
173     }
174 
175     /* Align and allocate case offset. */
176     case_offset = upb_msglayout_place(l, case_size);
177     data_offset = upb_msglayout_place(l, field_size);
178 
179     for (upb_oneof_begin(&fit, o);
180          !upb_oneof_done(&fit);
181          upb_oneof_next(&fit)) {
182       const upb_fielddef* f = upb_oneof_iter_field(&fit);
183       fields[upb_fielddef_index(f)].offset = data_offset;
184       fields[upb_fielddef_index(f)].presence = ~case_offset;
185     }
186   }
187 
188   /* Size of the entire structure should be a multiple of its greatest
189    * alignment.  TODO: track overall alignment for real? */
190   l->size = align_up(l->size, 8);
191 
192   return true;
193 }
194 
195 
196 /** upb_msgfactory ************************************************************/
197 
198 struct upb_msgfactory {
199   const upb_symtab *symtab;  /* We own a ref. */
200   upb_inttable layouts;
201 };
202 
upb_msgfactory_new(const upb_symtab * symtab)203 upb_msgfactory *upb_msgfactory_new(const upb_symtab *symtab) {
204   upb_msgfactory *ret = upb_gmalloc(sizeof(*ret));
205 
206   ret->symtab = symtab;
207   upb_inttable_init(&ret->layouts, UPB_CTYPE_PTR);
208 
209   return ret;
210 }
211 
upb_msgfactory_free(upb_msgfactory * f)212 void upb_msgfactory_free(upb_msgfactory *f) {
213   upb_inttable_iter i;
214   upb_inttable_begin(&i, &f->layouts);
215   for(; !upb_inttable_done(&i); upb_inttable_next(&i)) {
216     upb_msglayout *l = upb_value_getptr(upb_inttable_iter_value(&i));
217     upb_msglayout_free(l);
218   }
219 
220   upb_inttable_uninit(&f->layouts);
221   upb_gfree(f);
222 }
223 
upb_msgfactory_symtab(const upb_msgfactory * f)224 const upb_symtab *upb_msgfactory_symtab(const upb_msgfactory *f) {
225   return f->symtab;
226 }
227 
upb_msgfactory_getlayout(upb_msgfactory * f,const upb_msgdef * m)228 const upb_msglayout *upb_msgfactory_getlayout(upb_msgfactory *f,
229                                               const upb_msgdef *m) {
230   upb_value v;
231   UPB_ASSERT(upb_symtab_lookupmsg(f->symtab, upb_msgdef_fullname(m)) == m);
232   UPB_ASSERT(!upb_msgdef_mapentry(m));
233 
234   if (upb_inttable_lookupptr(&f->layouts, m, &v)) {
235     UPB_ASSERT(upb_value_getptr(v));
236     return upb_value_getptr(v);
237   } else {
238     /* In case of circular dependency, layout has to be inserted first. */
239     upb_msglayout *l = upb_gmalloc(sizeof(*l));
240     upb_msgfactory *mutable_f = (void*)f;
241     upb_inttable_insertptr(&mutable_f->layouts, m, upb_value_ptr(l));
242     UPB_ASSERT(l);
243     if (!upb_msglayout_init(m, l, f)) {
244       upb_msglayout_free(l);
245     }
246     return l;
247   }
248 }
249