/* * call-seq: * Map.inspect => string * * Returns a string representing this map's elements. It will be formatted as * "{key => value, key => value, ...}", with each key and value string * representation computed by its own #inspect method. */ VALUE Map_inspect(VALUE _self) { Map* self = ruby_to_Map(_self); VALUE str = rb_str_new2("{"); bool first = true; VALUE inspect_sym = rb_intern("inspect"); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { VALUE key = table_key_to_ruby( self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); if (!first) { str = rb_str_cat2(str, ", "); } else { first = false; } str = rb_str_append(str, rb_funcall(key, inspect_sym, 0)); str = rb_str_cat2(str, "=>"); str = rb_str_append(str, rb_funcall(value, inspect_sym, 0)); } str = rb_str_cat2(str, "}"); return str; }
/* * call-seq: * Map.hash => hash_value * * Returns a hash value based on this map's contents. */ VALUE Map_hash(VALUE _self) { Map* self = ruby_to_Map(_self); st_index_t h = rb_hash_start(0); VALUE hash_sym = rb_intern("hash"); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { VALUE key = table_key_to_ruby( self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); h = rb_hash_uint(h, NUM2LONG(rb_funcall(key, hash_sym, 0))); h = rb_hash_uint(h, NUM2LONG(rb_funcall(value, hash_sym, 0))); } return INT2FIX(h); }
// Used by Google::Protobuf.deep_copy but not exposed directly. VALUE Map_deep_copy(VALUE _self) { Map* self = ruby_to_Map(_self); VALUE new_map = Map_new_this_type(_self); Map* new_self = ruby_to_Map(new_map); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); upb_value dup; void* dup_mem = value_memory(&dup); native_slot_deep_copy(self->value_type, dup_mem, mem); if (!upb_strtable_insert2(&new_self->table, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it), dup)) { rb_raise(rb_eRuntimeError, "Error inserting value into new table"); } } return new_map; }
static void _upb_symtab_free(upb_strtable *t) { upb_strtable_iter i; upb_strtable_begin(&i, t); for (; !upb_strtable_done(&i); upb_strtable_next(&i)) { const upb_symtab_ent *e = upb_strtable_iter_value(&i); assert(upb_atomic_read(&e->def->refcount) == 0); upb_def_free(e->def); } upb_strtable_free(t); }
/* * call-seq: * Map.==(other) => boolean * * Compares this map to another. Maps are equal if they have identical key sets, * and for each key, the values in both maps compare equal. Elements are * compared as per normal Ruby semantics, by calling their :== methods (or * performing a more efficient comparison for primitive types). * * Maps with dissimilar key types or value types/typeclasses are never equal, * even if value comparison (for example, between integers and floats) would * have otherwise indicated that every element has equal value. */ VALUE Map_eq(VALUE _self, VALUE _other) { Map* self = ruby_to_Map(_self); Map* other; upb_strtable_iter it; // Allow comparisons to Ruby hashmaps by converting to a temporary Map // instance. Slow, but workable. if (TYPE(_other) == T_HASH) { VALUE other_map = Map_new_this_type(_self); Map_merge_into_self(other_map, _other); _other = other_map; } other = ruby_to_Map(_other); if (self == other) { return Qtrue; } if (self->key_type != other->key_type || self->value_type != other->value_type || self->value_type_class != other->value_type_class) { return Qfalse; } if (upb_strtable_count(&self->table) != upb_strtable_count(&other->table)) { return Qfalse; } // For each member of self, check that an equal member exists at the same key // in other. for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); upb_value other_v; void* other_mem = value_memory(&other_v); if (!upb_strtable_lookup2(&other->table, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it), &other_v)) { // Not present in other map. return Qfalse; } if (!native_slot_eq(self->value_type, mem, other_mem)) { // Present, but value not equal. return Qfalse; } } return Qtrue; }
static void upb_symtab_free(upb_refcounted *r) { upb_symtab *s = (upb_symtab*)r; upb_strtable_iter i; upb_strtable_begin(&i, &s->symtab); for (; !upb_strtable_done(&i); upb_strtable_next(&i)) { const upb_def *def = upb_value_getptr(upb_strtable_iter_value(&i)); upb_def_unref(def, s); } upb_strtable_uninit(&s->symtab); free(s); }
/* * call-seq: * Map.keys => [list_of_keys] * * Returns the list of keys contained in the map, in unspecified order. */ VALUE Map_keys(VALUE _self) { Map* self = ruby_to_Map(_self); VALUE ret = rb_ary_new(); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { VALUE key = table_key_to_ruby( self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); rb_ary_push(ret, key); } return ret; }
void Map_mark(void* _self) { Map* self = _self; rb_gc_mark(self->value_type_class); if (self->value_type == UPB_TYPE_STRING || self->value_type == UPB_TYPE_BYTES || self->value_type == UPB_TYPE_MESSAGE) { upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); native_slot_mark(self->value_type, mem); } } }
/* * call-seq: * Map.values => [list_of_values] * * Returns the list of values contained in the map, in unspecified order. */ VALUE Map_values(VALUE _self) { Map* self = ruby_to_Map(_self); VALUE ret = rb_ary_new(); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); rb_ary_push(ret, value); } return ret; }
/* * call-seq: * Map.to_h => {} * * Returns a Ruby Hash object containing all the values within the map */ VALUE Map_to_h(VALUE _self) { Map* self = ruby_to_Map(_self); VALUE hash = rb_hash_new(); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { VALUE key = table_key_to_ruby( self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); if (self->value_type == UPB_TYPE_MESSAGE) { value = Message_to_h(value); } rb_hash_aset(hash, key, value); } return hash; }
/* * call-seq: * Map.each(&block) * * Invokes &block on each |key, value| pair in the map, in unspecified order. * Note that Map also includes Enumerable; map thus acts like a normal Ruby * sequence. */ VALUE Map_each(VALUE _self) { Map* self = ruby_to_Map(_self); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { VALUE key = table_key_to_ruby( self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); rb_yield_values(2, key, value); } return Qnil; }
// Used only internally -- shared by #merge and #initialize. VALUE Map_merge_into_self(VALUE _self, VALUE hashmap) { if (TYPE(hashmap) == T_HASH) { rb_hash_foreach(hashmap, merge_into_self_callback, _self); } else if (RB_TYPE_P(hashmap, T_DATA) && RTYPEDDATA_P(hashmap) && RTYPEDDATA_TYPE(hashmap) == &Map_type) { Map* self = ruby_to_Map(_self); Map* other = ruby_to_Map(hashmap); upb_strtable_iter it; if (self->key_type != other->key_type || self->value_type != other->value_type || self->value_type_class != other->value_type_class) { rb_raise(rb_eArgError, "Attempt to merge Map with mismatching types"); } for (upb_strtable_begin(&it, &other->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { // Replace any existing value by issuing a 'remove' operation first. upb_value v; upb_value oldv; upb_strtable_remove2(&self->table, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it), &oldv); v = upb_strtable_iter_value(&it); upb_strtable_insert2(&self->table, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it), v); } } else { rb_raise(rb_eArgError, "Unknown type merging into Map"); } return _self; }
void upb_symtab_begin(upb_symtab_iter *iter, const upb_symtab *s, upb_deftype_t type) { upb_strtable_begin(&iter->iter, &s->symtab); iter->type = type; advance_to_matching(iter); }
void upb_mapiter_begin(upb_mapiter *i, const upb_map *map) { upb_strtable_begin(&i->iter, &map->strtab); i->key_type = map->key_type; }
void upb_enum_begin(upb_enum_iter *i, const upb_enumdef *e) { // We iterate over the ntoi table, to account for duplicate numbers. upb_strtable_begin(i, &e->ntoi); }
bool upb_symtab_add(upb_symtab *s, upb_def **defs, int n, upb_status *status) { upb_rwlock_wrlock(&s->lock); // Add all defs to a table for resolution. upb_strtable addtab; upb_strtable_init(&addtab, n, sizeof(upb_symtab_ent)); for (int i = 0; i < n; i++) { upb_def *def = defs[i]; if (upb_strtable_lookup(&addtab, def->fqname)) { upb_status_seterrf(status, "Conflicting defs named '%s'", def->fqname); upb_strtable_free(&addtab); return false; } upb_strtable_insert(&addtab, def->fqname, &def); } // All existing defs that can reach defs that are being replaced must // themselves be replaced with versions that will point to the new defs. // Do a DFS -- any path that finds a new def must replace all ancestors. upb_strtable *symtab = &s->symtab; upb_strtable_iter i; upb_strtable_begin(&i, symtab); for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { upb_def *open_defs[UPB_MAX_TYPE_DEPTH]; const upb_symtab_ent *e = upb_strtable_iter_value(&i); upb_symtab_dfs(e->def, open_defs, 0, &addtab); } // Resolve all refs. upb_strtable_begin(&i, &addtab); for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { const upb_symtab_ent *e = upb_strtable_iter_value(&i); upb_msgdef *m = upb_dyncast_msgdef(e->def); if(!m) continue; // Type names are resolved relative to the message in which they appear. const char *base = m->base.fqname; upb_msg_iter j; for(j = upb_msg_begin(m); !upb_msg_done(j); j = upb_msg_next(m, j)) { upb_fielddef *f = upb_msg_iter_field(j); if (f->type == 0) { upb_status_seterrf(status, "Field type was not set."); return false; } if (!upb_hassubdef(f)) continue; // No resolving necessary. upb_downcast_unresolveddef(f->def); // Type check. const char *name = f->def->fqname; // Resolve from either the addtab (pending adds) or symtab (existing // defs). If both exist, prefer the pending add, because it will be // overwriting the existing def. upb_symtab_ent *found; if(!(found = upb_resolve(&addtab, base, name)) && !(found = upb_resolve(symtab, base, name))) { upb_status_seterrf(status, "could not resolve symbol '%s' " "in context '%s'", name, base); return false; } // Check the type of the found def. upb_fieldtype_t expected = upb_issubmsg(f) ? UPB_DEF_MSG : UPB_DEF_ENUM; if(found->def->type != expected) { upb_status_seterrliteral(status, "Unexpected type"); return false; } if (!upb_fielddef_resolve(f, found->def, status)) return false; } } // The defs in the transaction have been vetted, and can be moved to the // symtab without causing errors. upb_strtable_begin(&i, &addtab); for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { const upb_symtab_ent *tmptab_e = upb_strtable_iter_value(&i); upb_def_movetosymtab(tmptab_e->def, s); upb_symtab_ent *symtab_e = upb_strtable_lookup(&s->symtab, tmptab_e->def->fqname); if(symtab_e) { upb_deflist_push(&s->olddefs, symtab_e->def); symtab_e->def = tmptab_e->def; } else { upb_strtable_insert(&s->symtab, tmptab_e->def->fqname, tmptab_e); } } upb_strtable_free(&addtab); upb_rwlock_unlock(&s->lock); upb_symtab_gc(s); return true; }
// Internal method: map iterator initialization (used for serialization). void Map_begin(VALUE _self, Map_iter* iter) { Map* self = ruby_to_Map(_self); iter->self = self; upb_strtable_begin(&iter->it, &self->table); }
/* TODO(haberman): we need a lot more testing of error conditions. * The came_from_user stuff in particular is not tested. */ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, upb_status *status) { int i; upb_strtable_iter iter; upb_def **add_defs = NULL; upb_strtable addtab; upb_inttable seen; assert(!upb_symtab_isfrozen(s)); if (!upb_strtable_init(&addtab, UPB_CTYPE_PTR)) { upb_status_seterrmsg(status, "out of memory"); return false; } /* Add new defs to our "add" set. */ for (i = 0; i < n; i++) { upb_def *def = defs[i]; const char *fullname; upb_fielddef *f; if (upb_def_isfrozen(def)) { upb_status_seterrmsg(status, "added defs must be mutable"); goto err; } assert(!upb_def_isfrozen(def)); fullname = upb_def_fullname(def); if (!fullname) { upb_status_seterrmsg( status, "Anonymous defs cannot be added to a symtab"); goto err; } f = upb_dyncast_fielddef_mutable(def); if (f) { if (!upb_fielddef_containingtypename(f)) { upb_status_seterrmsg(status, "Standalone fielddefs must have a containing type " "(extendee) name set"); goto err; } } else { if (upb_strtable_lookup(&addtab, fullname, NULL)) { upb_status_seterrf(status, "Conflicting defs named '%s'", fullname); goto err; } /* We need this to back out properly, because if there is a failure we * need to donate the ref back to the caller. */ def->came_from_user = true; upb_def_donateref(def, ref_donor, s); if (!upb_strtable_insert(&addtab, fullname, upb_value_ptr(def))) goto oom_err; } } /* Add standalone fielddefs (ie. extensions) to the appropriate messages. * If the appropriate message only exists in the existing symtab, duplicate * it so we have a mutable copy we can add the fields to. */ for (i = 0; i < n; i++) { upb_def *def = defs[i]; upb_fielddef *f = upb_dyncast_fielddef_mutable(def); const char *msgname; upb_value v; upb_msgdef *m; if (!f) continue; msgname = upb_fielddef_containingtypename(f); /* We validated this earlier in this function. */ assert(msgname); /* If the extendee name is absolutely qualified, move past the initial ".". * TODO(haberman): it is not obvious what it would mean if this was not * absolutely qualified. */ if (msgname[0] == '.') { msgname++; } if (upb_strtable_lookup(&addtab, msgname, &v)) { /* Extendee is in the set of defs the user asked us to add. */ m = upb_value_getptr(v); } else { /* Need to find and dup the extendee from the existing symtab. */ const upb_msgdef *frozen_m = upb_symtab_lookupmsg(s, msgname); if (!frozen_m) { upb_status_seterrf(status, "Tried to extend message %s that does not exist " "in this SymbolTable.", msgname); goto err; } m = upb_msgdef_dup(frozen_m, s); if (!m) goto oom_err; if (!upb_strtable_insert(&addtab, msgname, upb_value_ptr(m))) { upb_msgdef_unref(m, s); goto oom_err; } } if (!upb_msgdef_addfield(m, f, ref_donor, status)) { goto err; } } /* Add dups of any existing def that can reach a def with the same name as * anything in our "add" set. */ if (!upb_inttable_init(&seen, UPB_CTYPE_BOOL)) goto oom_err; upb_strtable_begin(&iter, &s->symtab); for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); upb_resolve_dfs(def, &addtab, s, &seen, status); if (!upb_ok(status)) goto err; } upb_inttable_uninit(&seen); /* Now using the table, resolve symbolic references for subdefs. */ upb_strtable_begin(&iter, &addtab); for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { const char *base; upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); upb_msgdef *m = upb_dyncast_msgdef_mutable(def); upb_msg_field_iter j; if (!m) continue; /* Type names are resolved relative to the message in which they appear. */ base = upb_msgdef_fullname(m); for(upb_msg_field_begin(&j, m); !upb_msg_field_done(&j); upb_msg_field_next(&j)) { upb_fielddef *f = upb_msg_iter_field(&j); const char *name = upb_fielddef_subdefname(f); if (name && !upb_fielddef_subdef(f)) { /* Try the lookup in the current set of to-be-added defs first. If not * there, try existing defs. */ upb_def *subdef = upb_resolvename(&addtab, base, name); if (subdef == NULL) { subdef = upb_resolvename(&s->symtab, base, name); } if (subdef == NULL) { upb_status_seterrf( status, "couldn't resolve name '%s' in message '%s'", name, base); goto err; } else if (!upb_fielddef_setsubdef(f, subdef, status)) { goto err; } } } } /* We need an array of the defs in addtab, for passing to upb_def_freeze. */ add_defs = malloc(sizeof(void*) * upb_strtable_count(&addtab)); if (add_defs == NULL) goto oom_err; upb_strtable_begin(&iter, &addtab); for (n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { add_defs[n++] = upb_value_getptr(upb_strtable_iter_value(&iter)); } if (!upb_def_freeze(add_defs, n, status)) goto err; /* This must be delayed until all errors have been detected, since error * recovery code uses this table to cleanup defs. */ upb_strtable_uninit(&addtab); /* TODO(haberman) we don't properly handle errors after this point (like * OOM in upb_strtable_insert() below). */ for (i = 0; i < n; i++) { upb_def *def = add_defs[i]; const char *name = upb_def_fullname(def); upb_value v; bool success; if (upb_strtable_remove(&s->symtab, name, &v)) { const upb_def *def = upb_value_getptr(v); upb_def_unref(def, s); } success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def)); UPB_ASSERT_VAR(success, success == true); } free(add_defs); return true; oom_err: upb_status_seterrmsg(status, "out of memory"); err: { /* For defs the user passed in, we need to donate the refs back. For defs * we dup'd, we need to just unref them. */ upb_strtable_begin(&iter, &addtab); for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); bool came_from_user = def->came_from_user; def->came_from_user = false; if (came_from_user) { upb_def_donateref(def, s, ref_donor); } else { upb_def_unref(def, s); } } } upb_strtable_uninit(&addtab); free(add_defs); assert(!upb_ok(status)); return false; }