static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) { // If no fields in the oneof, always nil. if (upb_oneofdef_numfields(o) == 0) { return Qnil; } // Grab the first field in the oneof so we can get its layout info to find the // oneof_case field. upb_oneof_iter it; upb_oneof_begin(&it, o); assert(!upb_oneof_done(&it)); const upb_fielddef* first_field = upb_oneof_iter_field(&it); assert(upb_fielddef_containingoneof(first_field) != NULL); size_t case_ofs = self->descriptor->layout-> fields[upb_fielddef_index(first_field)].case_offset; uint32_t oneof_case = *((uint32_t*)(Message_data(self) + case_ofs)); if (oneof_case == ONEOF_CASE_NONE) { return Qnil; } // oneof_case is a field index, so find that field. const upb_fielddef* f = upb_oneofdef_itof(o, oneof_case); assert(f != NULL); return ID2SYM(rb_intern(upb_fielddef_name(f))); }
MessageLayout* create_layout(const upb_msgdef* msgdef) { MessageLayout* layout = ALLOC(MessageLayout); int nfields = upb_msgdef_numfields(msgdef); upb_msg_field_iter it; upb_msg_oneof_iter oit; size_t off = 0; layout->fields = ALLOC_N(MessageField, nfields); size_t hasbit = 0; for (upb_msg_field_begin(&it, msgdef); !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); if (upb_fielddef_haspresence(field)) { layout->fields[upb_fielddef_index(field)].hasbit = hasbit++; } else { layout->fields[upb_fielddef_index(field)].hasbit = MESSAGE_FIELD_NO_HASBIT; } } if (hasbit != 0) { off += (hasbit + 8 - 1) / 8; } for (upb_msg_field_begin(&it, msgdef); !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); size_t field_size; if (upb_fielddef_containingoneof(field)) { // Oneofs are handled separately below. continue; } // Allocate |field_size| bytes for this field in the layout. field_size = 0; if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { field_size = sizeof(VALUE); } else { field_size = native_slot_size(upb_fielddef_type(field)); } // Align current offset up to |size| granularity. off = align_up_to(off, field_size); layout->fields[upb_fielddef_index(field)].offset = off; layout->fields[upb_fielddef_index(field)].case_offset = MESSAGE_FIELD_NO_CASE; off += field_size; } // Handle oneofs now -- we iterate over oneofs specifically and allocate only // one slot per oneof. // // We assign all value slots first, then pack the 'case' fields at the end, // since in the common case (modern 64-bit platform) these are 8 bytes and 4 // bytes respectively and we want to avoid alignment overhead. // // Note that we reserve 4 bytes (a uint32) per 'case' slot because the value // space for oneof cases is conceptually as wide as field tag numbers. In // practice, it's unlikely that a oneof would have more than e.g. 256 or 64K // members (8 or 16 bits respectively), so conceivably we could assign // consecutive case numbers and then pick a smaller oneof case slot size, but // the complexity to implement this indirection is probably not worthwhile. for (upb_msg_oneof_begin(&oit, msgdef); !upb_msg_oneof_done(&oit); upb_msg_oneof_next(&oit)) { const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit); upb_oneof_iter fit; // Always allocate NATIVE_SLOT_MAX_SIZE bytes, but share the slot between // all fields. size_t field_size = NATIVE_SLOT_MAX_SIZE; // Align the offset. off = align_up_to(off, field_size); // Assign all fields in the oneof this same offset. for (upb_oneof_begin(&fit, oneof); !upb_oneof_done(&fit); upb_oneof_next(&fit)) { const upb_fielddef* field = upb_oneof_iter_field(&fit); layout->fields[upb_fielddef_index(field)].offset = off; } off += field_size; } // Now the case fields. for (upb_msg_oneof_begin(&oit, msgdef); !upb_msg_oneof_done(&oit); upb_msg_oneof_next(&oit)) { const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit); upb_oneof_iter fit; size_t field_size = sizeof(uint32_t); // Align the offset. off = (off + field_size - 1) & ~(field_size - 1); // Assign all fields in the oneof this same offset. for (upb_oneof_begin(&fit, oneof); !upb_oneof_done(&fit); upb_oneof_next(&fit)) { const upb_fielddef* field = upb_oneof_iter_field(&fit); layout->fields[upb_fielddef_index(field)].case_offset = off; } off += field_size; } layout->size = off; layout->msgdef = msgdef; upb_msgdef_ref(layout->msgdef, &layout->msgdef); return layout; }
static bool upb_msglayout_init(const upb_msgdef *m, upb_msglayout *l, upb_msgfactory *factory) { upb_msg_field_iter it; upb_msg_oneof_iter oit; size_t hasbit; size_t submsg_count = 0; const upb_msglayout **submsgs; upb_msglayout_field *fields; for (upb_msg_field_begin(&it, m); !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* f = upb_msg_iter_field(&it); if (upb_fielddef_issubmsg(f)) { submsg_count++; } } memset(l, 0, sizeof(*l)); fields = upb_gmalloc(upb_msgdef_numfields(m) * sizeof(*fields)); submsgs = upb_gmalloc(submsg_count * sizeof(*submsgs)); if ((!fields && upb_msgdef_numfields(m)) || (!submsgs && submsg_count)) { /* OOM. */ upb_gfree(fields); upb_gfree(submsgs); return false; } l->field_count = upb_msgdef_numfields(m); l->fields = fields; l->submsgs = submsgs; /* Allocate data offsets in three stages: * * 1. hasbits. * 2. regular fields. * 3. oneof fields. * * OPT: There is a lot of room for optimization here to minimize the size. */ /* Allocate hasbits and set basic field attributes. */ submsg_count = 0; for (upb_msg_field_begin(&it, m), hasbit = 0; !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* f = upb_msg_iter_field(&it); upb_msglayout_field *field = &fields[upb_fielddef_index(f)]; field->number = upb_fielddef_number(f); field->descriptortype = upb_fielddef_descriptortype(f); field->label = upb_fielddef_label(f); if (upb_fielddef_issubmsg(f)) { const upb_msglayout *sub_layout = upb_msgfactory_getlayout(factory, upb_fielddef_msgsubdef(f)); field->submsg_index = submsg_count++; submsgs[field->submsg_index] = sub_layout; } if (upb_fielddef_haspresence(f) && !upb_fielddef_containingoneof(f)) { field->presence = (hasbit++); } else { field->presence = 0; } } /* Account for space used by hasbits. */ l->size = div_round_up(hasbit, 8); /* Allocate non-oneof fields. */ for (upb_msg_field_begin(&it, m); !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* f = upb_msg_iter_field(&it); size_t field_size = upb_msg_fielddefsize(f); size_t index = upb_fielddef_index(f); if (upb_fielddef_containingoneof(f)) { /* Oneofs are handled separately below. */ continue; } fields[index].offset = upb_msglayout_place(l, field_size); } /* Allocate oneof fields. Each oneof field consists of a uint32 for the case * and space for the actual data. */ for (upb_msg_oneof_begin(&oit, m); !upb_msg_oneof_done(&oit); upb_msg_oneof_next(&oit)) { const upb_oneofdef* o = upb_msg_iter_oneof(&oit); upb_oneof_iter fit; size_t case_size = sizeof(uint32_t); /* Could potentially optimize this. */ size_t field_size = 0; uint32_t case_offset; uint32_t data_offset; /* Calculate field size: the max of all field sizes. */ for (upb_oneof_begin(&fit, o); !upb_oneof_done(&fit); upb_oneof_next(&fit)) { const upb_fielddef* f = upb_oneof_iter_field(&fit); field_size = UPB_MAX(field_size, upb_msg_fielddefsize(f)); } /* Align and allocate case offset. */ case_offset = upb_msglayout_place(l, case_size); data_offset = upb_msglayout_place(l, field_size); for (upb_oneof_begin(&fit, o); !upb_oneof_done(&fit); upb_oneof_next(&fit)) { const upb_fielddef* f = upb_oneof_iter_field(&fit); fields[upb_fielddef_index(f)].offset = data_offset; fields[upb_fielddef_index(f)].presence = ~case_offset; } } /* Size of the entire structure should be a multiple of its greatest * alignment. TODO: track overall alignment for real? */ l->size = align_up(l->size, 8); return true; }