#include "unimdm.h" #include "mcxp.h" #if 0 #include #endif #include #include #include "mt.h" // #define SILENT #ifdef SILENT # define PRINTF0(_a) (0) # define PRINTF1(_a,_b) (0) # define PRINTF2(_a,_b,_c) (0) # define PRINTF3(_a,_b,_c,_d) (0) #else #ifdef CONSOLE # define PRINTF0(_a) printf(_a) # define PRINTF1(_a,_b) printf((_a),(_b)) # define PRINTF2(_a,_b,_c) printf((_a),(_b),(_c)) # define PRINTF3(_a,_b,_c,_d) printf((_a),(_b),(_c),(_d)) #else # define PRINTF0(_a) DPRINTF(_a) # define PRINTF1(_a,_b) DPRINTF1((_a),(_b)) # define PRINTF2(_a,_b,_c) DPRINTF2((_a),(_b),(_c)) # define PRINTF3(_a,_b,_c,_d) DPRINTF3((_a),(_b),(_c),(_d)) #endif //!CONSOLE #endif // !SILENT // ======================== PRIVATES =================================== #define ASSERT(_c) \ ((_c) ? 0: printf("Assertion failed in %s:%d\n", __FILE__, __LINE__)) typedef struct _MNODE { MATCHREC mr; struct _MNODE *pmnNext; struct _MNODE *pmnCh; } MNODE, *PMNODE; PMNODE mn_create_raw_tree(PMATCHREC pmr, DWORD dwcmr); BOOL mn_cook_tree (PMNODE pmn, DWORD dwMin); void mn_free_tree (PMNODE pmn); PMNODE mn_alloc (PMATCHREC pmr, PMNODE pmnNext); void mn_free (PMNODE pmn); void mn_dump_tree (PMNODE pmn, UINT uDepth, UINT uWidth); DWORD mn_find_smallest(PMNODE pmn); DWORD mn_find_match(PMNODE pmn, PMATCHREC pmr); // ======================== PRIVATES (end) =================================== HMATCHTREE mtCreateTree(PMATCHREC pmr, DWORD dwcmr) { PMNODE pmn = mn_create_raw_tree(pmr, dwcmr); if (!mn_cook_tree(pmn, MAXDWORD)) { mn_free_tree(pmn); pmn=NULL; } return (DWORD) pmn; } void mtFreeTree(HMATCHTREE hmt) { PMNODE pmn = (PMNODE) hmt; mn_free_tree(pmn); // NULL is a valid tree } // Returns one or more fMATCH_ flags. // Searches through all its siblings as well... // Recursive function, so keep locals to a minimum. DWORD mtFindMatch(HMATCHTREE hmt, PMATCHREC pmr) { MATCHREC mr; DWORD dwRet = 0; if (!pmr) goto end; mr = *pmr; // Structure copy. dwRet = mn_find_match((PMNODE)hmt, &mr); // mn_find_match trashes dwLen and cb fields of pmr, only pv field is valid pmr->pv = mr.pv; end: return dwRet; } #ifdef DEBUG void mtDumpTree(HMATCHTREE hmt) { PMNODE pmn = (PMNODE) hmt; mn_dump_tree(pmn, 0, 0); } #endif // DEBUG PMNODE mn_create_raw_tree(PMATCHREC pmr, DWORD dwcmr) { PMNODE pmnFirst=NULL; for (;dwcmr--;pmr++) { PMNODE pmnTmp = mn_alloc(pmr, pmnFirst); if (!pmnTmp) goto failure; pmnFirst=pmnTmp; } goto end; failure: mn_free_tree(pmnFirst); // NULL is a valid tree to free pmnFirst=NULL; // FALL THROUGH end: return pmnFirst; } PMNODE mn_alloc(PMATCHREC pmr, PMNODE pmnNext) { PMNODE pmn = (pmr && pmr->pb && pmr->dwLen) ? LocalAlloc(LPTR, sizeof(MNODE)) : NULL; if (pmn) { pmn->mr = *pmr; // structure copy. pmn->pmnNext=pmnNext; pmn->pmnCh=NULL; } return pmn; } void mn_free(PMNODE pmn) { if (pmn) { ASSERT(!pmn->pmnCh && !pmn->pmnNext); LocalFree(pmn); } } void mn_free_tree(PMNODE pmn) { if (pmn) { mn_free_tree(pmn->pmnCh); // recurse down mn_free_tree(pmn->pmnNext); // recurse left pmn->pmnNext=pmn->pmnCh=NULL; mn_free(pmn); } } BOOL mn_cook_tree(PMNODE pmn, DWORD dwMin) { // Find smallest string of me and siblings if (dwMin==MAXDWORD) dwMin = mn_find_smallest(pmn); if (!pmn) goto success; // These are all serious problems -- so we assert on failure: if (!dwMin || pmn->pmnCh || !pmn->mr.pb || (pmn->mr.dwLenpb is longer // than the minimum, in which case we also NULL pv, because pv should // be NULL for internal (non-leaf) nodes. if (pmn->mr.dwLen>dwMin) { MATCHREC mr; mr.pb=pmn->mr.pb+dwMin; mr.dwLen=pmn->mr.dwLen-dwMin; mr.pv=pmn->mr.pv; pmn->pmnCh = mn_alloc(&mr, NULL); if (!pmn->pmnCh) goto failure; pmn->mr.pv=NULL; pmn->mr.dwLen=dwMin; } // Add to my children's list by converting those siblings which // share my dwMin-sized prefix into my children. (Obviously) remove // these siblings from the sibling list. { PMNODE pmnTmp = pmn; while(pmnTmp->pmnNext) { PMNODE pmnTmp1 = pmnTmp->pmnNext; if (pmnTmp1->mr.dwLenmr.pb) goto failure; if (!_strnicmp(pmn->mr.pb, pmnTmp1->mr.pb, dwMin)) { // Found a prefix match -- remove from sibling list // and add to child list if non-null pmnTmp->pmnNext = pmnTmp1->pmnNext; pmnTmp1->mr.dwLen-=dwMin; pmnTmp1->mr.pb+=dwMin; pmnTmp1->pmnNext=NULL; // If nothing left to add -- we're at the end of pb -- // (leaf node) so we make pmn->pv non-NULL, and free pmnTmp1 // Otherwise we add it to pmn's child's list. if (!pmnTmp1->mr.dwLen) { if (pmn->mr.pv) {PRINTF0("Warning: overriding pv!\n");} pmn->mr.pv = pmnTmp1->mr.pv; ASSERT(!pmnTmp1->pmnCh && !pmnTmp1->pmnNext); mn_free(pmnTmp1); } else { pmnTmp1->pmnNext = pmn->pmnCh; pmn->pmnCh=pmnTmp1; } } else { pmnTmp = pmnTmp->pmnNext; } if (!pmnTmp) break; } } // recurse down if (!mn_cook_tree(pmn->pmnCh, MAXDWORD)) goto failure; // recurse left if (!mn_cook_tree(pmn->pmnNext, dwMin)) goto failure; // FALL THROUGH success: return TRUE; failure: ASSERT(FALSE); return FALSE; } DWORD mn_find_smallest(PMNODE pmn) { DWORD dw = MAXDWORD; if (pmn) { dw = mn_find_smallest(pmn->pmnNext); if (dw>pmn->mr.dwLen) dw=pmn->mr.dwLen; } return dw; } void mn_dump_tree(PMNODE pmn, UINT uDepth, UINT uWidth) { static char rgchFill[]="----------------------------------------"; LONG lOff = sizeof(rgchFill) - (uDepth+1); if (lOff<0) lOff=0; // if(!pmn) return; PRINTF2("%02lu%s", (unsigned long) uDepth, rgchFill+lOff); if (!pmn) { PRINTF1("NULL(w=%lu)\n", (unsigned long) uWidth); } else { CHAR *pb = (pmn->mr.pb)?pmn->mr.pb:"\"\""; CHAR c = pb[pmn->mr.dwLen]; CHAR *pc2 = (CHAR *) pmn->mr.pv; if (!pc2) pc2=""; pb[pmn->mr.dwLen]=0; PRINTF3("[%s]:%02lu %s\n", pc2, (unsigned long) pmn->mr.dwLen, pb); pb[pmn->mr.dwLen]=c; mn_dump_tree(pmn->pmnCh, uDepth+1,0); // recurse down mn_dump_tree(pmn->pmnNext, uDepth, uWidth+1); // recurse left } } // Returns one or more fMATCH_ flags. // Searches through all its siblings as well... // Recursive function, so keep locals to a minimum. // WARNING: Trashes dwLen and cb fields of pmr. DWORD mn_find_match(PMNODE pmn, PMATCHREC pmr) { DWORD dwRet=0; DWORD dwcbMin; CHAR *pc; #ifdef DEBUG DWORD dwcb; CHAR rgchTmp[256]; DWORD dwcbTmp; #endif // DEBUG if (!pmn || !pmr) goto end; #ifdef DEBUG # define DWCB dwcb # define DBGPSZ rgchTmp DWCB = pmr->dwLen; dwcbTmp = sizeof(DBGPSZ)-1; if (dwcbTmp>DWCB) dwcbTmp=DWCB; // We do this because pb is not null terminated. CopyMemory(DBGPSZ, pmr->pb, dwcbTmp); DBGPSZ[dwcbTmp]=0; #else // !DEBUG # define DBGPSZ "" # define DWCB 0 #endif // !DEBUG PRINTF1("Entering mn_find_match(-, [%s], -)\n", DBGPSZ); // Iterate through siblings, looking for matches and partial matches. // We stop when we find the first match or partial match, because all // the substrings at the same level are unique and have the same length: // so if we found a match we can't find a partial match among the siblings, // and vice versa. Furthermore, if we find a partial match, we don't really // care which (could be more than one) sibling it is a partial match for. // If in the future we we *do* return the node for efficiency purposes // (so that the next call can start where we left off), // we would return the node for the *first* partial match we found. pc = pmr->pb; dwcbMin = pmn->mr.dwLen; if (pmr->dwLendwLen; dwRet = fMATCH_PARTIAL; } else if (pmr->dwLen==dwcbMin) { dwRet = fMATCH_COMPLETE; } else { // pmr->dwLen>pmn->mr.dwU, dwRet==0, so do nothing } // WARNING: we trash pmr here, except for pmr->pv pmr->dwLen-=dwcbMin; pmr->pb+=dwcbMin; while(pmn) { // Fundamental assumption is that all dwLens of siblings are equal. ASSERT((pmn->pmnNext)? pmn->mr.dwLen==pmn->pmnNext->mr.dwLen:TRUE); if (!_strnicmp(pc, pmn->mr.pb, dwcbMin)) { PRINTF3("Match: dwcb=%lu; pmn->mr.dwLen=%lu; pc=[%s]\n", (unsigned long) DWCB, (unsigned long) pmn->mr.dwLen, DBGPSZ); // Note: dwRet is computed just once, out of the while loop. // we were really overloading dwRet then. Now we compute the // true return value. switch(dwRet) { case 0: // i.e., (DWCB > pmn->mr.dwLen) ASSERT(DWCB>pmn->mr.dwLen); // Recurse down. WARNING: pmr is trashed, except for pmr->pv dwRet = mn_find_match(pmn->pmnCh, pmr); break; case fMATCH_PARTIAL: // i.e., (DWCB < pmn->mr.dwLen) ASSERT(DWCBmr.dwLen); break; case fMATCH_COMPLETE: // i.e., (DWCB == pmn->mr.dwLen) ASSERT(DWCB==pmn->mr.dwLen); // Now let's see if we're truly a perfect macth: non-null // pv indicates an actual response terminates in this node. dwRet=0; if (pmn->mr.pv) { pmr->pv = pmn->mr.pv; dwRet = fMATCH_COMPLETE; } // Non-null pmnCh implies there are postfixes, i.e., longer // responses if (pmn->pmnCh) { dwRet |= fMATCH_PARTIAL; } break; default: ASSERT(FALSE); dwRet=0; goto end; } if (dwRet) break; // break out of while loop if we got something } pmn=pmn->pmnNext; } if (!pmn) { dwRet=0; } else { ASSERT(dwRet && !_strnicmp(pc, pmn->mr.pb, dwcbMin)); } PRINTF2("Exiting mn_find_match(-, [%s], -) returing 0x%lx\n", DBGPSZ, (unsigned long) dwRet); end: return dwRet; }