QuakeGod
2023-05-19 418cb35b936f21415807a2bcc21b2d75934bd4d2
提交 | 用户 | age
4b03ae 1 #pragma once
Q 2 /*
3 * HashTable File
4 * Version 1.22 2019-02-25
5 * Description:
6 *
7 *
8 */
9
10 #include <string>
11 #include <iostream>
12 #include <fstream>
13 #include "../Utils.h"
14
15 inline std::string trim(const std::string& s)
16 {
17     char p[] = " \t\r\n";
18     long sp = 0;
19     long ep = (long)s.length() - 1;
20     for (; sp <= ep; ++sp)
21         if (!strchr(p, s[sp])) break;
22     for (; ep >= 0; --ep)
23         if (!strchr(p, s[ep])) break;
24     return s.substr(sp, ep-sp+1);
25 }
26 //struct stCfgCtrl        //配置文件项和控件对应关系
27 //{
28 //    CStringA sKeyName;
29 //    UINT nCtrlId;
30 //};
31 enum enVtype        //变量类型
32 {
33     vtNone = 0,
34     vtbool =1,
35     vtInt = 2,
36     vtFloat = 3,
37     vtDouble = 4,
38     vtString = 5,
39 };
40 enum enCType        //控件类型 
41 {
42     ctNone =0,
43     ctStatic=1,
44     ctEdit=2,
45     ctCheckBox=3,
46     ctRadio=4,
47     ctComboBoxIndex=5,    //ComboBox Index;
48     ctComboBoxString = 6, // ComboBox String;
49     ctListBox = 6,
50     ctListCtrl=7,
51 };
52 struct stCfgValCtrl        //配置文件项和变量以及控件对应关系
53 {
54     CStringA sKeyName;
55     int vType;
56     void * var;
57     UINT nCtrlId;
58     int cType;
59 };
60 template <typename T, size_t N>
61 char(&_ArraySizeHelper(T(&array)[N]))[N];
62 #define countof(array) (sizeof(_ArraySizeHelper(array)))
63
64 #define MAX_HASH_KEY 3000
65 #define MAX_MHASH_SEGMENT 300
66 class Hashelement
67 {
68 public:
69     int HashValue;
70     CString * Key;
71     CString * Value;
72     Hashelement()
73     {
74         HashValue = 0;
75         Key = NULL;
76         Value = NULL;
77     }
78     ~Hashelement()
79     {
80         delete Key;
81         delete Value;
82         Key = NULL;
83         Value = NULL;
84     }
85     Hashelement(const Hashelement& elem)
86     {
87         *this = elem;
88     }
89     Hashelement& operator = (const Hashelement& elem)
90     {
91         if (this == &elem)
92         {
93             return *this;
94         }
95         HashValue = elem.HashValue;
96
97         delete Key;
98         delete Value;    
99         Key = new CString(*(elem.Key));
100         Value = new CString(*(elem.Value));
101         
102         return *this;
103     }
104     operator int ()
105     {
106         return _tstoi(*Value);
107     }
108     operator float()
109     {
110         return float(_tstof(*Value));
111     }
112     operator double()
113     {
114         return _tstof(*Value);
115     }
116     operator LPCTSTR()
117     {
118         return *Value;
119     }
120     //operator CString()
121     //{
122     //    return *Value;
123     //}
124
125     //operator TCHAR *()
126     //{
127     //    return *Value;
128     //}
129     int operator =(int i)
130     {
131         if (Value == nullptr) { Value = new CString; }
132         (*Value).Format(_T("%d"),i);
133         return i;
134     }
135     float operator =(float f)
136     {
137         if (Value == nullptr) { Value = new CString; }
138         (*Value).Format(_T("%f"), f);
139         return f;
140     }
141     double operator =(double d)
142     {
143         if (Value == nullptr) { Value = new CString; }
144         (*Value).Format(_T("%f"), d);
145         return d;
146     }
147     CString operator =(CString s)
148     {
149         if (Value == nullptr) { Value = new CString; }
150         *Value = s;
151         return s;
152     }
153
154 };
155
156 class Hash
157 {
158 public:
159
160     CString Name;
161     int TotalKey;
162     Hashelement elements[MAX_HASH_KEY];
163     
164     Hashelement OverFlow;
165 public:
166
167     Hash::~Hash()
168     {
169     }
170
171     Hash::Hash(void)
172     {
173         TotalKey=0;
174         OverFlow = _T("OverFlow");
175     };
176     Hash::Hash(CString thisName)
177     {
178         TotalKey=0;
179         this->Name=thisName;
180         OverFlow = _T("OverFlow");
181     };
182     Hash(const Hash& hash)
183     {
184         *this = hash;
185         OverFlow = _T("OverFlow");
186     }
187
188     Hash& operator = (const Hash& hash)
189     {
190         if (this == &hash)
191         {
192             return *this;
193         }
194         Name = hash.Name;
195         TotalKey = hash.TotalKey;
196         for (int i=0; i<TotalKey; ++i)
197         {
198             elements[i] = hash.elements[i];
199         }
200         return *this;
201     }
202     void Empty(void)
203     {
204         int i;
205         for (i=0;i<this->TotalKey;i++)
206         {
207             delete elements[i].Key;
208             delete elements[i].Value;
209         }
210         TotalKey=0;
211     }
418cb3 212     bool Exist(CString s)
Q 213     {
214         int left = 0;
215         int right = TotalKey - 1;
216         int mid;
4b03ae 217
418cb3 218         int index = -1;
Q 219         while (left <= right)
220         {
221             mid = (left + right) / 2;
222             int r = elements[mid].Key->CompareNoCase(s);
223             if (r < 0)
224             {//key在右边
225                 left = mid + 1;
226             }
227             else if (r > 0)
228             {//key在左边
229                 right = mid - 1;
230             }
231             else
232             {
233                 index = mid;
234                 break;
235             }
236         }
237         if (index == -1) return false;
238         else return true;
239     }
4b03ae 240     Hashelement & Hash::operator [] (const char * str)
Q 241     {
242         CString s1(str);
243         return this->operator [] (s1);
244     };
245     Hashelement & Hash::operator [] (CString s)
246     {
247         int left = 0;
248         int right = TotalKey - 1;
249         int mid;
250
251         int index = -1;
252         while (left <= right)
253         {
254             mid = (left + right) / 2;
255             int r = elements[mid].Key->CompareNoCase(s);
256             if (r < 0)
257             {//key在右边
258                 left = mid + 1;
259             } else if (r > 0)
260             {//key在左边
261                 right = mid - 1;
262             } else
263             {
264                 index = mid;
265                 break;
266             }
267         }
268
269         if (index == -1 && TotalKey < MAX_HASH_KEY)
270         {
271             index = left;
272             for (int i = TotalKey; i > index; --i)
273             {
274                 elements[i].Key = elements[i-1].Key;
275                 elements[i].Value = elements[i-1].Value;
276             }
277
278             elements[index].Key = new CString(s);
279             elements[index].Value = new CString(s);
280             TotalKey++;
281         }
282
283         if (index==-1)
284         {
285             return OverFlow;
286         }
287
288         return elements[index];
289     };
290     Hashelement & Hash::operator [] (int k)
291     {
292         return elements[k];
293     };
294
295     template <typename T, size_t N>
296     int inline SetVarList(T(&pCfgs)[N])
297     {
298         return SetVarList(pCfgs, N);
299     }
300
301     int SetVarList(stCfgValCtrl *pCfgs, int nCount)
302     {
303         for (int i = 0; i < nCount; i++)
304         {
305             int vtype = pCfgs[i].vType;
306             void * pvar = pCfgs[i].var;
307             int nCtrlId = pCfgs[i].nCtrlId;
308             if (pvar==nullptr) continue;
309
310             switch (vtype)
311             {
312             case vtbool:
313             case vtInt: *(int *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
314             case vtFloat:*(float *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
315             case vtDouble:*(double *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
316             case vtString:*(CString *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
317             default:
318                 break;
319             }
320         }
321         return nCount;
322     }
323     int SetCtrl(const char * str, CWnd * pWnd, int nCtrlId, int nCtrlType)
324     {
325         CString s1(str);
326         return SetCtrl(s1, pWnd, nCtrlId,nCtrlType);
327     }
328     int SetCtrl(CString str, CWnd * pWnd, int nCtrlId, int nCtrlType)
329     {
330         if (nCtrlId == 0) return 0;
331         CWnd * pCtrl = pWnd->GetDlgItem(nCtrlId);
332         if (pCtrl == nullptr) return -1;
333         switch (nCtrlType)
334         {
335         case ctEdit:    
336             pWnd->SetDlgItemText(nCtrlId, this->operator [] (str)); break;
337         case ctCheckBox:   
338             ((CButton *)pCtrl)->SetCheck(this->operator [] (str)); break;
339         case ctComboBoxIndex: 
340             ((CComboBox*)pCtrl)->SetCurSel(this->operator [] (str)); break;
341         case ctComboBoxString:
342             ((CComboBox*)pCtrl)->SelectString(0,this->operator [] (str));
343             pWnd->SetDlgItemText(nCtrlId, this->operator [] (str)); break;
344         default:
345             break;
346         }
347         return 0;
348     }
349
350     template <typename T,size_t N>
351     int inline SetCtrlList(CWnd * pWnd, T (&pCfgs)[N])
352     {
353         return SetCtrlList(pWnd, pCfgs, N);
354     }
355
356     int SetCtrlList(CWnd * pWnd, stCfgValCtrl * pCfgs, int nCount)
357     {
358         for (int i = 0; i < nCount; i++)
359         {
360
361             int vtype = pCfgs[i].vType;
362             void * pvar = pCfgs[i].var;
363             int nCtrlId = pCfgs[i].nCtrlId;
364             int  nCtrlType = pCfgs[i].cType;
365             SetCtrl(pCfgs[i].sKeyName, pWnd, nCtrlId,nCtrlType);
366
367             if (pvar == nullptr) continue;
368             switch (vtype)
369             {
370             case vtbool: 
371             case vtInt: *(int *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
372             case vtFloat:*(float *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
373             case vtDouble:*(double *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
374             case vtString:*(CString *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
375             default:
376                 break;
377             }
378
379         }
380         return nCount;
381     }
382     template <typename T, size_t N>
383     int inline GetCtrlList(CWnd * pWnd, T(&pCfgs)[N])
384     {
385         return GetCtrlList(pWnd, pCfgs, N);
386     }
387
388     int GetCtrl(const char * str, CWnd * pWnd, int nCtrlId, int nCtrlType)
389     {
390         CString s1(str);
391         return GetCtrl(s1, pWnd, nCtrlId, nCtrlType);
392     }
393     int GetCtrl(CString str, CWnd * pWnd, int nCtrlId, int nCtrlType)
394     {
395         if (nCtrlId == 0) return 0;
396         CString s1;
397         CWnd * pCtrl = pWnd->GetDlgItem(nCtrlId);
398         if (pCtrl == nullptr) return -1;
399
400         switch (nCtrlType)
401         {
402         case ctEdit:    
403             pWnd->GetDlgItemText(nCtrlId, s1); this->operator [] (str) = s1; break;
404         case ctCheckBox:   
405             this->operator [] (str) = ((CButton *)pCtrl)->GetCheck(); break;
406         case ctComboBoxIndex:
407             this->operator [] (str)=((CComboBox *)pCtrl)->GetCurSel();     break;
408         case ctComboBoxString:
409             pWnd->GetDlgItemText(nCtrlId, s1); this->operator [] (str) = s1; break;
410
411         default:
412             break;
413         }
414         return 0;
415     }
416
417     int GetCtrlList(CWnd * pWnd, stCfgValCtrl * pCfgs, int nCount)
418     {
419         for (int i = 0; i < nCount; i++)
420         {
421             int vtype = pCfgs[i].vType;
422             void * pvar = pCfgs[i].var;
423             int nCtrlId = pCfgs[i].nCtrlId;
424             int  nCtrlType = pCfgs[i].cType;
425             GetCtrl(pCfgs[i].sKeyName, pWnd, nCtrlId,nCtrlType);
426
427             if (pvar == nullptr) continue;
428             switch (vtype)
429             {
430             case vtbool:
431             case vtInt: *(int *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
432             case vtFloat:*(float *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
433             case vtDouble:*(double *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
434             case vtString:*(CString *)pvar = this->operator [] (pCfgs[i].sKeyName); break;
435             default:
436                 break;
437             }
438
439         }
440         return nCount;
441     }
442     int Hash::SaveToFile(CString FilePathName,CString Section)
443     {
444         int i;
445         CString Key,Value;
446         CString CurrentDirectory;
447
448         for (i=0;i<TotalKey;i++)
449         {
450             Key=*(elements[i].Key);
451             Value=*(elements[i].Value);
452             Value.Replace(_T("\r\n"),_T("<CR><LF>"));
453             Value.Replace(_T("\r"),_T("<CR>"));
454             Value.Replace(_T("\n"),_T("<LF>"));
455             WritePrivateProfileString(Section,Key,Value,FilePathName);
456         }
457         return 1;
458     };
459
460     int Hash::LoadFromFile(CString FilePathName,CString Section)
461     {
462         CString Key;
463         CString value,value1,value2;
464         CString Defaultstr;
465         Defaultstr=_T("");
466         int k,l;
467         CString s1,s2,s3;
468         k=GetPrivateProfileString(NULL,NULL,Defaultstr,value1.GetBuffer(40960),40960,FilePathName);
469         value1.ReleaseBuffer(k);
470         TCHAR * p2;
471         int n1,n2,n3;
472         n3=GetPrivateProfileString(Section,NULL,Defaultstr,value2.GetBuffer(40960),40960,FilePathName);
473         for (n1=0;n1<n3;n1++)
474         {
475             p2=s3.GetBuffer(40960);
476             for (n2=0;;n2++)
477             {
478                 if (value2.GetString()[n1+n2])
479                 {p2[n2]=(value2.GetString())[n1+n2];}
480                 else
481                 {break;}
482             }
483             s3.ReleaseBuffer(n2);
484
485             Key=s3;
486
487             l=GetPrivateProfileString(Section,Key,Defaultstr,value.GetBuffer(40960),40960,FilePathName);
488             value.ReleaseBuffer();
489             value.Replace(_T("<CR><LF>"),_T("\r\n"));
490             value.Replace(_T("<CR>"),_T("\r"));
491             value.Replace(_T("<LF>"),_T("\n"));
492             this->operator [](Key)=value;
493             n1+=n2;
494         }
495         return 1;
496     };
497 };
498
499 class MHashelement
500 {
501 public:
502     int HashValue;
503     CString SegmentName;
504     Hash * Segment;
505
506     MHashelement()
507     {
508         HashValue = 0;
509         SegmentName = _T("");
510         Segment = NULL;
511     }
512
513     ~MHashelement()
514     {
515         delete Segment;
516         Segment = NULL;
517     }
518
519     MHashelement(const MHashelement& elem)
520     {
521         *this = elem;
522     }
523     MHashelement& operator = (const MHashelement& elem)
524     {
525         if (this == &elem)
526         {
527             return *this;
528         }
529         HashValue = elem.HashValue;
530         SegmentName = elem.SegmentName;
531
532         delete Segment;
533         Segment = new Hash(*(elem.Segment));
534
535         return *this;
536     };
537 };
538 class MHash
539 {
540 public:
541     int TotalSegment;
542     MHashelement elements[MAX_MHASH_SEGMENT];
543
544     Hash OverFlow;
545     ~MHash()
546     {
547     };
548
549     MHash(void)
550     {
551         TotalSegment=0;
552         OverFlow = Hash(_T("OverFlow"));
553     };
554
555     MHash(const MHash& mhash)
556     {
557         *this = mhash;
558         OverFlow = Hash(_T("OverFlow"));
559     }
560
561     MHash& operator = (const MHash& mhash)
562     {
563         if (this == &mhash)
564         {
565             return *this;
566         }
567         TotalSegment=mhash.TotalSegment;
568         for (int i=0;i<TotalSegment;i++)
569         {
570             elements[i] = mhash.elements[i];
571         }
572         return *this;
573     }
574     void Empty()
575     {
576         int i;
577         for (i=0;i<TotalSegment;i++)
578         {
579             delete elements[i].Segment;
580         }
581         TotalSegment=0;
582     }
583     Hash & operator [] (const char * str)
584     {
585         CString s1(str);
586         return this->operator [] (s1);
587     };
588     
589     Hash & operator [] (CString s)
590     {
591         int left = 0;
592         int right = TotalSegment - 1;
593         int mid;
594
595         int index = -1;
596         while (left <= right)
597         {
598             mid = (left + right) / 2;
599             int r = elements[mid].SegmentName.CompareNoCase(s);
600             if (r < 0)
601             {//key在右边
602                 left = mid + 1;
603             } else if (r > 0)
604             {//key在左边
605                 right = mid - 1;
606             } else
607             {
608                 index = mid;
609                 break;
610             }
611         }
612
613         if (index == -1 && TotalSegment < MAX_MHASH_SEGMENT)
614         {
615             index = left;
616             for (int i = TotalSegment; i > index; --i)
617             {
618                 elements[i].Segment = elements[i-1].Segment;
619                 elements[i].SegmentName = elements[i-1].SegmentName;
620             }
621             elements[index].Segment= new Hash(s);
622             elements[index].SegmentName = s;
623             TotalSegment++;
624         }
625         
626         if (index==-1)
627         {
628             return OverFlow;
629         }
630         
631         return *(elements[index].Segment);
632     };
633     Hash & operator [] (int k)
634     {
635         return *(elements[k].Segment);
636     };
637
638     int SaveToFileOld(CString FilePathName)
639     {
640         int i,j;
641         CString Section;
642         CString Key,Value;
643         CString CurrentDirectory;
644
645         for (i=0;i<TotalSegment;i++)
646         {
647             Section=elements[i].SegmentName;
648             for (j=0;j<this->operator [](i).TotalKey;j++)
649             {
650                 Key=*(this->operator [](i).elements[j].Key) ;
651                 Value=*(this->operator [](i).elements[j].Value);
652                 Value.Replace(_T("\r\n"),_T("<CR><LF>"));
653                 Value.Replace(_T("\r"),_T("<CR>"));
654                 Value.Replace(_T("\n"),_T("<LF>"));
655                 WritePrivateProfileString(Section,Key,Value,FilePathName);
656             }
657         }
658         return 1;
659     };
660
661     int LoadFromFileOld(CString FilePathName)
662     {
663         CString Key;
664         CString Value,value1,value2;
665         CString Section;
666         CString Defaultstr;
667         Defaultstr=_T("");
668         this->Empty();
669         int i,j,k,l;
670         CString s1,s2,s3;
671         k=GetPrivateProfileString(NULL,NULL,Defaultstr,value1.GetBuffer(40960),40960,FilePathName);
672         value1.ReleaseBuffer(k);
673         TCHAR * p1;
674         TCHAR * p2;
675         int n1,n2,n3;
676         for (i=0;i<k;i++)
677         {
678             p1=s2.GetBuffer(40960);
679             for (j=0;;j++)
680             {
681                 if ((value1.GetString())[i+j])
682                 {p1[j]=((LPTSTR)value1.GetString())[i+j];}
683                 else
684                 {    break;}
685             }
686             s2.ReleaseBuffer(j);
687             Section=s2;
688             n3=GetPrivateProfileString(Section,NULL,Defaultstr,value2.GetBuffer(40960),40960,FilePathName);
689
690             for (n1=0;n1<n3;n1++)
691             {
692                 p2=s3.GetBuffer(40960);
693                 for (n2=0;;n2++)
694                 {
695                     if (value2.GetString()[n1+n2])
696                     {p2[n2]=(value2.GetString())[n1+n2];}
697                     else
698                     {break;}
699                 }
700                 s3.ReleaseBuffer(n2);
701
702                 Key=s3;
703
704                 l=GetPrivateProfileString(Section,Key,Defaultstr,Value.GetBuffer(40960),40960,FilePathName);
705                 Value.ReleaseBuffer();
706                 Value.Replace(_T("<CR><LF>"),_T("\r\n"));
707                 Value.Replace(_T("<CR>"),_T("\r"));
708                 Value.Replace(_T("<LF>"),_T("\n"));
709                 this->operator [](Section)[Key]=Value;
710                 n1+=n2;
711             }
712
713             i+=j;        
714         }
715
716         return k;
717     };
718
719     int SaveToFile(CString FilePathName)
720     {
721         std::string filename = CString2String(FilePathName);
722         std::ofstream ofs(filename);
723         if (!ofs.is_open())
724         {
725             std::cerr << "Save MHash error, open file" << filename << " fail." << std::endl;
726             return -1;
727         }
728
729         int i,j;
730         CString Section;
731         CString Key,Value;
732
733         for (i=0;i<TotalSegment;i++)
734         {
735             Section=elements[i].SegmentName;
736             ofs << "[" << CString2String(Section) << "]" << std::endl;
737
738             for (j=0;j<this->operator [](i).TotalKey;j++)
739             {
740                 Key=*(this->operator [](i).elements[j].Key) ;
741                 Value=*(this->operator [](i).elements[j].Value);
742                 Value.Replace(_T("\r\n"),_T("<CR><LF>"));
743                 Value.Replace(_T("\r"),_T("<CR>"));
744                 Value.Replace(_T("\n"),_T("<LF>"));
745
746                 ofs << CString2String(Key) << "=" << CString2String(Value) << std::endl;
747             }
748         }
749         ofs.close();
750
751         return 1;
752     };
753
754     int LoadFromFile(CString FilePathName)
755     {
756         this->Empty();
757
758         std::string filename = CString2String(FilePathName);
759         std::ifstream ifs(filename);
760         if(!ifs.is_open())
761         {
762             std::cerr << "Load MHash error, open file" << filename << " fail." << std::endl;
763             return -1;
764         }
765
766         std::string segment;
767         std::string line;
768         size_t line_count=0;
769         while (std::getline(ifs, line)) {
770             //to skip BOM
771             if (line_count++ == 0 && line.size() > 3) {
772                 if (static_cast<unsigned char>(line[0]) == 0xEF &&
773                     static_cast<unsigned char>(line[1]) == 0xBB &&
774                     static_cast<unsigned char>(line[2]) == 0xBF)
775                 {
776                     for (int i = 0; i < 3; i++)
777                     {
778                         line.erase(line.begin());
779                     }
780                 }
781             }
782
783             if (line[0] == '#' || line[0] == ';') continue;
784             line = trim(line);
785             if (line.empty()) continue;
786
787             if (line[0] == '[')
788             {
789                 segment = trim(line.substr(1,line.length()-2));
790             }
791             else if(segment.length()>0)
792             {
793                 size_t n = line.find('=');
794                 if (n == std::string::npos)
795                     continue;
796
797                 std::string k = line.substr(0,n);
798                 std::string v = trim(line.substr(n+1, line.length()-n-1));
799
800                 CString Value = String2CString(v);
801                 Value.Replace(_T("<CR><LF>"),_T("\r\n"));
802                 Value.Replace(_T("<CR>"),_T("\r"));
803                 Value.Replace(_T("<LF>"),_T("\n"));
804                 this->operator [](segment.c_str())[k.c_str()] = Value;
805             }
806         }
807         ifs.close();
808
809         return TotalSegment;
810     }
811 };
812
813 typedef void DiffCallBack(const CString& section, const CString& key, const CString oldValue, const CString newValue);
814
815 //比较两个Hash的区别,第二个可能在第一个的基础上有变更
816 inline void HashDiff(Hash& h1, Hash& h2, DiffCallBack callback)
817 {
818     CString s1;
819     
820     for (int i=0; i<h2.TotalKey; ++i)
821     {
822         CString key = *(h2.elements[i].Key);
823         CString value1 = h1[key];
824         CString value2 = *(h2.elements[i].Value);
825         if (value1.GetLength()>256 || value2.GetLength()>256)
826         {
827             continue;
828         }
829         if (0!=value1.CompareNoCase(value2))
830         {
831             callback(h2.Name, key, value1, value2);
832         }
833     }
834 }
835 //比较两个MHash的区别,第二个可能在第一个的基础上有变更
836 inline void MHashDiff(MHash& mh1, MHash& mh2, DiffCallBack callback)
837 {
838     for (int i=0; i<mh2.TotalSegment; ++i)
839     {
840         HashDiff(mh1[mh2.elements[i].SegmentName], *(mh2.elements[i].Segment), callback);
841     }
842 }
843