1 //===-- HashedNameToDIE.cpp -------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "HashedNameToDIE.h" 11 #include "llvm/ADT/StringRef.h" 12 13 void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, 14 DIEArray &die_offsets) { 15 const size_t count = die_info_array.size(); 16 for (size_t i = 0; i < count; ++i) 17 die_offsets.emplace_back(die_info_array[i].cu_offset, 18 die_info_array[i].offset); 19 } 20 21 void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, 22 const dw_tag_t tag, 23 DIEArray &die_offsets) { 24 if (tag == 0) { 25 ExtractDIEArray(die_info_array, die_offsets); 26 } else { 27 const size_t count = die_info_array.size(); 28 for (size_t i = 0; i < count; ++i) { 29 const dw_tag_t die_tag = die_info_array[i].tag; 30 bool tag_matches = die_tag == 0 || tag == die_tag; 31 if (!tag_matches) { 32 if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) 33 tag_matches = 34 tag == DW_TAG_structure_type || tag == DW_TAG_class_type; 35 } 36 if (tag_matches) 37 die_offsets.emplace_back(die_info_array[i].cu_offset, 38 die_info_array[i].offset); 39 } 40 } 41 } 42 43 void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, 44 const dw_tag_t tag, 45 const uint32_t qualified_name_hash, 46 DIEArray &die_offsets) { 47 if (tag == 0) { 48 ExtractDIEArray(die_info_array, die_offsets); 49 } else { 50 const size_t count = die_info_array.size(); 51 for (size_t i = 0; i < count; ++i) { 52 if (qualified_name_hash != die_info_array[i].qualified_name_hash) 53 continue; 54 const dw_tag_t die_tag = die_info_array[i].tag; 55 bool tag_matches = die_tag == 0 || tag == die_tag; 56 if (!tag_matches) { 57 if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) 58 tag_matches = 59 tag == DW_TAG_structure_type || tag == DW_TAG_class_type; 60 } 61 if (tag_matches) 62 die_offsets.emplace_back(die_info_array[i].cu_offset, 63 die_info_array[i].offset); 64 } 65 } 66 } 67 68 void DWARFMappedHash::ExtractClassOrStructDIEArray( 69 const DIEInfoArray &die_info_array, 70 bool return_implementation_only_if_available, DIEArray &die_offsets) { 71 const size_t count = die_info_array.size(); 72 for (size_t i = 0; i < count; ++i) { 73 const dw_tag_t die_tag = die_info_array[i].tag; 74 if (die_tag == 0 || die_tag == DW_TAG_class_type || 75 die_tag == DW_TAG_structure_type) { 76 if (die_info_array[i].type_flags & eTypeFlagClassIsImplementation) { 77 if (return_implementation_only_if_available) { 78 // We found the one true definition for this class, so 79 // only return that 80 die_offsets.clear(); 81 die_offsets.emplace_back(die_info_array[i].cu_offset, 82 die_info_array[i].offset); 83 return; 84 } else { 85 // Put the one true definition as the first entry so it 86 // matches first 87 die_offsets.emplace(die_offsets.begin(), die_info_array[i].cu_offset, 88 die_info_array[i].offset); 89 } 90 } else { 91 die_offsets.emplace_back(die_info_array[i].cu_offset, 92 die_info_array[i].offset); 93 } 94 } 95 } 96 } 97 98 void DWARFMappedHash::ExtractTypesFromDIEArray( 99 const DIEInfoArray &die_info_array, uint32_t type_flag_mask, 100 uint32_t type_flag_value, DIEArray &die_offsets) { 101 const size_t count = die_info_array.size(); 102 for (size_t i = 0; i < count; ++i) { 103 if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value) 104 die_offsets.emplace_back(die_info_array[i].cu_offset, 105 die_info_array[i].offset); 106 } 107 } 108 109 const char *DWARFMappedHash::GetAtomTypeName(uint16_t atom) { 110 switch (atom) { 111 case eAtomTypeNULL: 112 return "NULL"; 113 case eAtomTypeDIEOffset: 114 return "die-offset"; 115 case eAtomTypeCUOffset: 116 return "cu-offset"; 117 case eAtomTypeTag: 118 return "die-tag"; 119 case eAtomTypeNameFlags: 120 return "name-flags"; 121 case eAtomTypeTypeFlags: 122 return "type-flags"; 123 case eAtomTypeQualNameHash: 124 return "qualified-name-hash"; 125 } 126 return "<invalid>"; 127 } 128 129 DWARFMappedHash::DIEInfo::DIEInfo() 130 : cu_offset(DW_INVALID_OFFSET), offset(DW_INVALID_OFFSET), tag(0), 131 type_flags(0), qualified_name_hash(0) {} 132 133 DWARFMappedHash::DIEInfo::DIEInfo(dw_offset_t c, dw_offset_t o, dw_tag_t t, 134 uint32_t f, uint32_t h) 135 : cu_offset(c), offset(o), tag(t), type_flags(f), qualified_name_hash(h) {} 136 137 DWARFMappedHash::Prologue::Prologue(dw_offset_t _die_base_offset) 138 : die_base_offset(_die_base_offset), atoms(), atom_mask(0), 139 min_hash_data_byte_size(0), hash_data_has_fixed_byte_size(true) { 140 // Define an array of DIE offsets by first defining an array, 141 // and then define the atom type for the array, in this case 142 // we have an array of DIE offsets 143 AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4); 144 } 145 146 void DWARFMappedHash::Prologue::ClearAtoms() { 147 hash_data_has_fixed_byte_size = true; 148 min_hash_data_byte_size = 0; 149 atom_mask = 0; 150 atoms.clear(); 151 } 152 153 bool DWARFMappedHash::Prologue::ContainsAtom(AtomType atom_type) const { 154 return (atom_mask & (1u << atom_type)) != 0; 155 } 156 157 void DWARFMappedHash::Prologue::Clear() { 158 die_base_offset = 0; 159 ClearAtoms(); 160 } 161 162 void DWARFMappedHash::Prologue::AppendAtom(AtomType type, dw_form_t form) { 163 atoms.push_back({type, form}); 164 atom_mask |= 1u << type; 165 switch (form) { 166 case DW_FORM_indirect: 167 case DW_FORM_exprloc: 168 case DW_FORM_flag_present: 169 case DW_FORM_ref_sig8: 170 llvm_unreachable("Unhandled atom form"); 171 172 case DW_FORM_string: 173 case DW_FORM_block: 174 case DW_FORM_block1: 175 case DW_FORM_sdata: 176 case DW_FORM_udata: 177 case DW_FORM_ref_udata: 178 case DW_FORM_GNU_addr_index: 179 case DW_FORM_GNU_str_index: 180 hash_data_has_fixed_byte_size = false; 181 LLVM_FALLTHROUGH; 182 case DW_FORM_flag: 183 case DW_FORM_data1: 184 case DW_FORM_ref1: 185 case DW_FORM_sec_offset: 186 min_hash_data_byte_size += 1; 187 break; 188 189 case DW_FORM_block2: 190 hash_data_has_fixed_byte_size = false; 191 LLVM_FALLTHROUGH; 192 case DW_FORM_data2: 193 case DW_FORM_ref2: 194 min_hash_data_byte_size += 2; 195 break; 196 197 case DW_FORM_block4: 198 hash_data_has_fixed_byte_size = false; 199 LLVM_FALLTHROUGH; 200 case DW_FORM_data4: 201 case DW_FORM_ref4: 202 case DW_FORM_addr: 203 case DW_FORM_ref_addr: 204 case DW_FORM_strp: 205 min_hash_data_byte_size += 4; 206 break; 207 208 case DW_FORM_data8: 209 case DW_FORM_ref8: 210 min_hash_data_byte_size += 8; 211 break; 212 } 213 } 214 215 lldb::offset_t 216 DWARFMappedHash::Prologue::Read(const lldb_private::DataExtractor &data, 217 lldb::offset_t offset) { 218 ClearAtoms(); 219 220 die_base_offset = data.GetU32(&offset); 221 222 const uint32_t atom_count = data.GetU32(&offset); 223 if (atom_count == 0x00060003u) { 224 // Old format, deal with contents of old pre-release format 225 while (data.GetU32(&offset)) 226 /* do nothing */; 227 228 // Hardcode to the only known value for now. 229 AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4); 230 } else { 231 for (uint32_t i = 0; i < atom_count; ++i) { 232 AtomType type = (AtomType)data.GetU16(&offset); 233 dw_form_t form = (dw_form_t)data.GetU16(&offset); 234 AppendAtom(type, form); 235 } 236 } 237 return offset; 238 } 239 240 size_t DWARFMappedHash::Prologue::GetByteSize() const { 241 // Add an extra count to the atoms size for the zero termination Atom that 242 // gets 243 // written to disk 244 return sizeof(die_base_offset) + sizeof(uint32_t) + 245 atoms.size() * sizeof(Atom); 246 } 247 248 size_t DWARFMappedHash::Prologue::GetMinimumHashDataByteSize() const { 249 return min_hash_data_byte_size; 250 } 251 252 bool DWARFMappedHash::Prologue::HashDataHasFixedByteSize() const { 253 return hash_data_has_fixed_byte_size; 254 } 255 256 size_t DWARFMappedHash::Header::GetByteSize(const HeaderData &header_data) { 257 return header_data.GetByteSize(); 258 } 259 260 lldb::offset_t DWARFMappedHash::Header::Read(lldb_private::DataExtractor &data, 261 lldb::offset_t offset) { 262 offset = MappedHash::Header<Prologue>::Read(data, offset); 263 if (offset != UINT32_MAX) { 264 offset = header_data.Read(data, offset); 265 } 266 return offset; 267 } 268 269 bool DWARFMappedHash::Header::Read(const lldb_private::DWARFDataExtractor &data, 270 lldb::offset_t *offset_ptr, 271 DIEInfo &hash_data) const { 272 const size_t num_atoms = header_data.atoms.size(); 273 if (num_atoms == 0) 274 return false; 275 276 for (size_t i = 0; i < num_atoms; ++i) { 277 DWARFFormValue form_value(NULL, header_data.atoms[i].form); 278 279 if (!form_value.ExtractValue(data, offset_ptr)) 280 return false; 281 282 switch (header_data.atoms[i].type) { 283 case eAtomTypeDIEOffset: // DIE offset, check form for encoding 284 hash_data.offset = 285 (dw_offset_t)form_value.Reference(header_data.die_base_offset); 286 break; 287 288 case eAtomTypeTag: // DW_TAG value for the DIE 289 hash_data.tag = (dw_tag_t)form_value.Unsigned(); 290 break; 291 292 case eAtomTypeTypeFlags: // Flags from enum TypeFlags 293 hash_data.type_flags = (uint32_t)form_value.Unsigned(); 294 break; 295 296 case eAtomTypeQualNameHash: // Flags from enum TypeFlags 297 hash_data.qualified_name_hash = form_value.Unsigned(); 298 break; 299 300 default: 301 // We can always skip atoms we don't know about 302 break; 303 } 304 } 305 return true; 306 } 307 308 void DWARFMappedHash::Header::Dump(lldb_private::Stream &strm, 309 const DIEInfo &hash_data) const { 310 const size_t num_atoms = header_data.atoms.size(); 311 for (size_t i = 0; i < num_atoms; ++i) { 312 if (i > 0) 313 strm.PutCString(", "); 314 315 DWARFFormValue form_value(NULL, header_data.atoms[i].form); 316 switch (header_data.atoms[i].type) { 317 case eAtomTypeDIEOffset: // DIE offset, check form for encoding 318 strm.Printf("{0x%8.8x}", hash_data.offset); 319 break; 320 321 case eAtomTypeTag: // DW_TAG value for the DIE 322 { 323 const char *tag_cstr = lldb_private::DW_TAG_value_to_name(hash_data.tag); 324 if (tag_cstr) 325 strm.PutCString(tag_cstr); 326 else 327 strm.Printf("DW_TAG_(0x%4.4x)", hash_data.tag); 328 } break; 329 330 case eAtomTypeTypeFlags: // Flags from enum TypeFlags 331 strm.Printf("0x%2.2x", hash_data.type_flags); 332 if (hash_data.type_flags) { 333 strm.PutCString(" ("); 334 if (hash_data.type_flags & eTypeFlagClassIsImplementation) 335 strm.PutCString(" implementation"); 336 strm.PutCString(" )"); 337 } 338 break; 339 340 case eAtomTypeQualNameHash: // Flags from enum TypeFlags 341 strm.Printf("0x%8.8x", hash_data.qualified_name_hash); 342 break; 343 344 default: 345 strm.Printf("AtomType(0x%x)", header_data.atoms[i].type); 346 break; 347 } 348 } 349 } 350 351 DWARFMappedHash::MemoryTable::MemoryTable( 352 lldb_private::DWARFDataExtractor &table_data, 353 const lldb_private::DWARFDataExtractor &string_table, const char *name) 354 : MappedHash::MemoryTable<uint32_t, Header, DIEInfoArray>(table_data), 355 m_data(table_data), m_string_table(string_table), m_name(name) {} 356 357 const char * 358 DWARFMappedHash::MemoryTable::GetStringForKeyType(KeyType key) const { 359 // The key in the DWARF table is the .debug_str offset for the string 360 return m_string_table.PeekCStr(key); 361 } 362 363 bool DWARFMappedHash::MemoryTable::ReadHashData(uint32_t hash_data_offset, 364 HashData &hash_data) const { 365 lldb::offset_t offset = hash_data_offset; 366 offset += 4; // Skip string table offset that contains offset of hash name in 367 // .debug_str 368 const uint32_t count = m_data.GetU32(&offset); 369 if (count > 0) { 370 hash_data.resize(count); 371 for (uint32_t i = 0; i < count; ++i) { 372 if (!m_header.Read(m_data, &offset, hash_data[i])) 373 return false; 374 } 375 } else 376 hash_data.clear(); 377 return true; 378 } 379 380 DWARFMappedHash::MemoryTable::Result 381 DWARFMappedHash::MemoryTable::GetHashDataForName( 382 llvm::StringRef name, lldb::offset_t *hash_data_offset_ptr, 383 Pair &pair) const { 384 pair.key = m_data.GetU32(hash_data_offset_ptr); 385 pair.value.clear(); 386 387 // If the key is zero, this terminates our chain of HashData objects 388 // for this hash value. 389 if (pair.key == 0) 390 return eResultEndOfHashData; 391 392 // There definitely should be a string for this string offset, if 393 // there isn't, there is something wrong, return and error 394 const char *strp_cstr = m_string_table.PeekCStr(pair.key); 395 if (strp_cstr == NULL) { 396 *hash_data_offset_ptr = UINT32_MAX; 397 return eResultError; 398 } 399 400 const uint32_t count = m_data.GetU32(hash_data_offset_ptr); 401 const size_t min_total_hash_data_size = 402 count * m_header.header_data.GetMinimumHashDataByteSize(); 403 if (count > 0 && 404 m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr, 405 min_total_hash_data_size)) { 406 // We have at least one HashData entry, and we have enough 407 // data to parse at least "count" HashData entries. 408 409 // First make sure the entire C string matches... 410 const bool match = name == strp_cstr; 411 412 if (!match && m_header.header_data.HashDataHasFixedByteSize()) { 413 // If the string doesn't match and we have fixed size data, 414 // we can just add the total byte size of all HashData objects 415 // to the hash data offset and be done... 416 *hash_data_offset_ptr += min_total_hash_data_size; 417 } else { 418 // If the string does match, or we don't have fixed size data 419 // then we need to read the hash data as a stream. If the 420 // string matches we also append all HashData objects to the 421 // value array. 422 for (uint32_t i = 0; i < count; ++i) { 423 DIEInfo die_info; 424 if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) { 425 // Only happened if the HashData of the string matched... 426 if (match) 427 pair.value.push_back(die_info); 428 } else { 429 // Something went wrong while reading the data 430 *hash_data_offset_ptr = UINT32_MAX; 431 return eResultError; 432 } 433 } 434 } 435 // Return the correct response depending on if the string matched 436 // or not... 437 if (match) 438 return eResultKeyMatch; // The key (cstring) matches and we have lookup 439 // results! 440 else 441 return eResultKeyMismatch; // The key doesn't match, this function will 442 // get called 443 // again for the next key/value or the key terminator 444 // which in our case is a zero .debug_str offset. 445 } else { 446 *hash_data_offset_ptr = UINT32_MAX; 447 return eResultError; 448 } 449 } 450 451 DWARFMappedHash::MemoryTable::Result 452 DWARFMappedHash::MemoryTable::AppendHashDataForRegularExpression( 453 const lldb_private::RegularExpression ®ex, 454 lldb::offset_t *hash_data_offset_ptr, Pair &pair) const { 455 pair.key = m_data.GetU32(hash_data_offset_ptr); 456 // If the key is zero, this terminates our chain of HashData objects 457 // for this hash value. 458 if (pair.key == 0) 459 return eResultEndOfHashData; 460 461 // There definitely should be a string for this string offset, if 462 // there isn't, there is something wrong, return and error 463 const char *strp_cstr = m_string_table.PeekCStr(pair.key); 464 if (strp_cstr == NULL) 465 return eResultError; 466 467 const uint32_t count = m_data.GetU32(hash_data_offset_ptr); 468 const size_t min_total_hash_data_size = 469 count * m_header.header_data.GetMinimumHashDataByteSize(); 470 if (count > 0 && 471 m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr, 472 min_total_hash_data_size)) { 473 const bool match = regex.Execute(llvm::StringRef(strp_cstr)); 474 475 if (!match && m_header.header_data.HashDataHasFixedByteSize()) { 476 // If the regex doesn't match and we have fixed size data, 477 // we can just add the total byte size of all HashData objects 478 // to the hash data offset and be done... 479 *hash_data_offset_ptr += min_total_hash_data_size; 480 } else { 481 // If the string does match, or we don't have fixed size data 482 // then we need to read the hash data as a stream. If the 483 // string matches we also append all HashData objects to the 484 // value array. 485 for (uint32_t i = 0; i < count; ++i) { 486 DIEInfo die_info; 487 if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) { 488 // Only happened if the HashData of the string matched... 489 if (match) 490 pair.value.push_back(die_info); 491 } else { 492 // Something went wrong while reading the data 493 *hash_data_offset_ptr = UINT32_MAX; 494 return eResultError; 495 } 496 } 497 } 498 // Return the correct response depending on if the string matched 499 // or not... 500 if (match) 501 return eResultKeyMatch; // The key (cstring) matches and we have lookup 502 // results! 503 else 504 return eResultKeyMismatch; // The key doesn't match, this function will 505 // get called 506 // again for the next key/value or the key terminator 507 // which in our case is a zero .debug_str offset. 508 } else { 509 *hash_data_offset_ptr = UINT32_MAX; 510 return eResultError; 511 } 512 } 513 514 size_t DWARFMappedHash::MemoryTable::AppendAllDIEsThatMatchingRegex( 515 const lldb_private::RegularExpression ®ex, 516 DIEInfoArray &die_info_array) const { 517 const uint32_t hash_count = m_header.hashes_count; 518 Pair pair; 519 for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) { 520 lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx); 521 while (hash_data_offset != UINT32_MAX) { 522 const lldb::offset_t prev_hash_data_offset = hash_data_offset; 523 Result hash_result = 524 AppendHashDataForRegularExpression(regex, &hash_data_offset, pair); 525 if (prev_hash_data_offset == hash_data_offset) 526 break; 527 528 // Check the result of getting our hash data 529 switch (hash_result) { 530 case eResultKeyMatch: 531 case eResultKeyMismatch: 532 // Whether we matches or not, it doesn't matter, we 533 // keep looking. 534 break; 535 536 case eResultEndOfHashData: 537 case eResultError: 538 hash_data_offset = UINT32_MAX; 539 break; 540 } 541 } 542 } 543 die_info_array.swap(pair.value); 544 return die_info_array.size(); 545 } 546 547 size_t DWARFMappedHash::MemoryTable::AppendAllDIEsInRange( 548 const uint32_t die_offset_start, const uint32_t die_offset_end, 549 DIEInfoArray &die_info_array) const { 550 const uint32_t hash_count = m_header.hashes_count; 551 for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) { 552 bool done = false; 553 lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx); 554 while (!done && hash_data_offset != UINT32_MAX) { 555 KeyType key = m_data.GetU32(&hash_data_offset); 556 // If the key is zero, this terminates our chain of HashData objects 557 // for this hash value. 558 if (key == 0) 559 break; 560 561 const uint32_t count = m_data.GetU32(&hash_data_offset); 562 for (uint32_t i = 0; i < count; ++i) { 563 DIEInfo die_info; 564 if (m_header.Read(m_data, &hash_data_offset, die_info)) { 565 if (die_info.offset == 0) 566 done = true; 567 if (die_offset_start <= die_info.offset && 568 die_info.offset < die_offset_end) 569 die_info_array.push_back(die_info); 570 } 571 } 572 } 573 } 574 return die_info_array.size(); 575 } 576 577 size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name, 578 DIEArray &die_offsets) { 579 if (name.empty()) 580 return 0; 581 582 DIEInfoArray die_info_array; 583 if (FindByName(name, die_info_array)) 584 DWARFMappedHash::ExtractDIEArray(die_info_array, die_offsets); 585 return die_info_array.size(); 586 } 587 588 size_t DWARFMappedHash::MemoryTable::FindByNameAndTag(llvm::StringRef name, 589 const dw_tag_t tag, 590 DIEArray &die_offsets) { 591 DIEInfoArray die_info_array; 592 if (FindByName(name, die_info_array)) 593 DWARFMappedHash::ExtractDIEArray(die_info_array, tag, die_offsets); 594 return die_info_array.size(); 595 } 596 597 size_t DWARFMappedHash::MemoryTable::FindByNameAndTagAndQualifiedNameHash( 598 llvm::StringRef name, const dw_tag_t tag, 599 const uint32_t qualified_name_hash, DIEArray &die_offsets) { 600 DIEInfoArray die_info_array; 601 if (FindByName(name, die_info_array)) 602 DWARFMappedHash::ExtractDIEArray(die_info_array, tag, qualified_name_hash, 603 die_offsets); 604 return die_info_array.size(); 605 } 606 607 size_t DWARFMappedHash::MemoryTable::FindCompleteObjCClassByName( 608 llvm::StringRef name, DIEArray &die_offsets, bool must_be_implementation) { 609 DIEInfoArray die_info_array; 610 if (FindByName(name, die_info_array)) { 611 if (must_be_implementation && 612 GetHeader().header_data.ContainsAtom(eAtomTypeTypeFlags)) { 613 // If we have two atoms, then we have the DIE offset and 614 // the type flags so we can find the objective C class 615 // efficiently. 616 DWARFMappedHash::ExtractTypesFromDIEArray(die_info_array, UINT32_MAX, 617 eTypeFlagClassIsImplementation, 618 die_offsets); 619 } else { 620 // We don't only want the one true definition, so try and see 621 // what we can find, and only return class or struct DIEs. 622 // If we do have the full implementation, then return it alone, 623 // else return all possible matches. 624 const bool return_implementation_only_if_available = true; 625 DWARFMappedHash::ExtractClassOrStructDIEArray( 626 die_info_array, return_implementation_only_if_available, die_offsets); 627 } 628 } 629 return die_offsets.size(); 630 } 631 632 size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name, 633 DIEInfoArray &die_info_array) { 634 if (name.empty()) 635 return 0; 636 637 Pair kv_pair; 638 size_t old_size = die_info_array.size(); 639 if (Find(name, kv_pair)) { 640 die_info_array.swap(kv_pair.value); 641 return die_info_array.size() - old_size; 642 } 643 return 0; 644 } 645