| 693 | |
| 694 | |
| 695 | bool CAICHHashSet::SaveHashSet() |
| 696 | { |
| 697 | if (m_eStatus != AICH_HASHSETCOMPLETE) { |
| 698 | wxFAIL; |
| 699 | return false; |
| 700 | } |
| 701 | if ( !m_pHashTree.m_bHashValid || m_pHashTree.m_nDataSize != m_pOwner->GetFileSize()) { |
| 702 | wxFAIL; |
| 703 | return false; |
| 704 | } |
| 705 | |
| 706 | wxMutexLocker cacheLock(s_rootHashCacheMutex); |
| 707 | |
| 708 | if (!s_rootHashCacheLoaded) { |
| 709 | LoadRootHashCacheLocked(); |
| 710 | } |
| 711 | |
| 712 | // O(1) dedup — replaces the linear file walk that used to make this |
| 713 | // O(N) per call and O(N^2) over a bulk-hashing batch. |
| 714 | if (s_rootHashCache.find(m_pHashTree.m_Hash) != s_rootHashCache.end()) { |
| 715 | return true; |
| 716 | } |
| 717 | |
| 718 | try { |
| 719 | const wxString fullpath = thePrefs::GetConfigDir() + KNOWN2_MET_FILENAME; |
| 720 | const bool exists = wxFile::Exists(fullpath); |
| 721 | |
| 722 | CFile file(fullpath, exists ? CFile::read_write : CFile::write); |
| 723 | if (!file.IsOpened()) { |
| 724 | AddDebugLogLineC(logSHAHashSet, "Failed to save HashSet: opening met file failed!"); |
| 725 | return false; |
| 726 | } |
| 727 | |
| 728 | uint64 nExistingSize = file.GetLength(); |
| 729 | if (nExistingSize) { |
| 730 | uint8 header = file.ReadUInt8(); |
| 731 | if (header != KNOWN2_MET_VERSION) { |
| 732 | AddDebugLogLineC(logSHAHashSet, "Saving failed: Current file is not a met-file!"); |
| 733 | return false; |
| 734 | } |
| 735 | // Skip the in-file dedup walk; the cache already confirmed |
| 736 | // our root hash isn't present. |
| 737 | file.Seek(static_cast<wxFileOffset>(nExistingSize), wxFromStart); |
| 738 | } else { |
| 739 | file.WriteUInt8(KNOWN2_MET_VERSION); |
| 740 | // Update the recorded size, in order for the sanity check below to work. |
| 741 | nExistingSize += 1; |
| 742 | } |
| 743 | |
| 744 | // write hashset |
| 745 | m_pHashTree.m_Hash.Write(&file); |
| 746 | uint32 nHashCount = (PARTSIZE/EMBLOCKSIZE + ((PARTSIZE % EMBLOCKSIZE != 0)? 1 : 0)) * (m_pHashTree.m_nDataSize/PARTSIZE); |
| 747 | if (m_pHashTree.m_nDataSize % PARTSIZE != 0) { |
| 748 | nHashCount += (m_pHashTree.m_nDataSize % PARTSIZE)/EMBLOCKSIZE + (((m_pHashTree.m_nDataSize % PARTSIZE) % EMBLOCKSIZE != 0)? 1 : 0); |
| 749 | } |
| 750 | file.WriteUInt32(nHashCount); |
| 751 | if (!m_pHashTree.WriteLowestLevelHashs(&file, 0, true, true)) { |
| 752 | // that's bad... really |
no test coverage detected