/************************************************************************* * * * QTLIST.C * * * * Copyright (C) Microsoft Corporation 1990-1994 * * All Rights reserved. * * * ************************************************************************** * * * Module Intent * * Tree and list structures handler * * * ************************************************************************** * * * Written By : Binh Nguyen * * Current Owner: Binh Nguyen * * * ************************************************************************** * * * Released by Development: (date) * * * *************************************************************************/ #include #include #ifdef DOS_ONLY #include #include #endif #include #include "common.h" #include "search.h" /************************************************************************* * * INTERNAL GLOBAL FUNCTIONS * All of them should be declared far, unless they are known to be called * in the same segment *************************************************************************/ PUBLIC LPITOPIC PASCAL NEAR TopicNodeAllocate(LPQT); PUBLIC VOID PASCAL NEAR TopicNodeFree (LPQT, _LPQTNODE, LPITOPIC, LPITOPIC); PUBLIC LPIOCC PASCAL NEAR OccNodeAllocate(LPQT); PUBLIC LPITOPIC PASCAL NEAR TopicNodeSearch(LPQT, _LPQTNODE, DWORD); PUBLIC LPIOCC PASCAL NEAR OccNodeSearch(LPQT, LPITOPIC , LPIOCC ); PUBLIC HRESULT PASCAL NEAR OccNodeInsert(LPQT, LPITOPIC, LPIOCC); PUBLIC VOID NEAR FreeTopicList(LPITOPIC lpTopicList); PUBLIC VOID PASCAL NEAR RemoveNode(LPQT, LPV, LPSLINK, LPSLINK, int); PUBLIC int PASCAL NEAR OccCompare(LPIOCC, LPIOCC); /************************************************************************* * @doc API RETRIEVAL * * @func VOID FAR PASCAL | MVQueryFree | * Free all the memory used in the query. WARNING: THERE ARE * 2 MEMORY BLOCKS THAT SHOULD NOT BE FREED, IE. THE TOPIC AND * OCCURRENCE NODES MEMORY BLOCKS. THEY ARE USED AND RELEASED * BY THE HISTLIST ROUTINES * * @parm LPQT | lpQueryTree | * Pointer to query tree * *************************************************************************/ PUBLIC VOID EXPORT_API PASCAL FAR MVQueryFree(_LPQT lpQueryTree) { /* Sanity check */ if (lpQueryTree == NULL) return; /* Free the parser nodes memory block */ BlockFree((LPV)lpQueryTree->lpNodeBlock); /* Free the string memory block */ BlockFree((LPV)lpQueryTree->lpStringBlock); #ifndef SIMILARITY /* Free the string memory block */ BlockFree((LPV)lpQueryTree->lpWordInfoBlock); #endif /* Free query tree */ GlobalLockedStructMemFree((LPV)lpQueryTree); } /************************************************************************* * @doc INTERNAL RETRIEVAL * * @func LPITOPIC PASCAL NEAR | TopicNodeAllocate | * Allocate a new TOPIC_LIST node * * @parm _LPQT | lpQueryTree | * Pointer to query tree * * @rdesc * Pointer to new node if succeeded, NULL otherwise *************************************************************************/ PUBLIC LPITOPIC PASCAL NEAR TopicNodeAllocate(_LPQT lpQueryTree) { LPITOPIC lpNode; // Ptr to an internal Topic node LPSLINK lpTopicFreeList; // Ptr to the Topic free memory pool LPBLK lpBlockHead; if (lpQueryTree->lpTopicFreeList == NULL) { /* Allocate a big block of TOPIC */ if (BlockGrowth(lpBlockHead = lpQueryTree->lpTopicMemBlock) != S_OK) return NULL; lpQueryTree->lpTopicFreeList = (LPSLINK)BlockGetLinkedList(lpBlockHead); } lpTopicFreeList = lpQueryTree->lpTopicFreeList; /* Allocate from the free list */ lpNode = (LPITOPIC)lpTopicFreeList; lpQueryTree->lpTopicFreeList = lpTopicFreeList->pNext; lpNode->pNext = NULL; lpNode->fFlag = 0; return lpNode; } /************************************************************************* * @doc INTERNAL RETRIEVAL * * @func LPIOCC PASCAL NEAR | OccNodeAllocate | * Allocate a new OCCURENCE node * * @parm _LPQT | lpQueryTree | * Pointer to query tree * * @rdesc * Pointer to new node if succeeded, NULL otherwise *************************************************************************/ PUBLIC LPIOCC PASCAL NEAR OccNodeAllocate(_LPQT lpQueryTree) { LPIOCC lpNode; // Pointer to occurence node LPSLINK lpOccFreeList; // Pointer to occurence free list LPBLK lpBlockHead; if (lpQueryTree->lpOccFreeList == NULL) { // Check to see if we are running out of blocks before we do allocation lpBlockHead = lpQueryTree->lpOccMemBlock; if (lpBlockHead->cCurBlockCnt >= lpBlockHead->cMaxBlock) { // OK, we ran out of blocks, let's borrow from the topic list if (((LPBLK)(lpQueryTree->lpTopicMemBlock))->cMaxBlock > ((LPBLK)(lpQueryTree->lpTopicMemBlock))->cCurBlockCnt) { ((LPBLK)(lpQueryTree->lpTopicMemBlock))->cMaxBlock--; lpBlockHead->cMaxBlock++; } } /* Allocate a big block of Occ */ if (BlockGrowth(lpBlockHead) != S_OK) return NULL; lpQueryTree->lpOccFreeList = (LPSLINK)BlockGetLinkedList(lpBlockHead); } lpOccFreeList = lpQueryTree->lpOccFreeList; /* Allocate from the free list */ lpNode = (LPIOCC )lpOccFreeList; lpQueryTree->lpOccFreeList = lpOccFreeList->pNext; lpNode->pNext = NULL; lpNode->fFlag = 0; return lpNode; } /************************************************************************* * @doc INTERNAL RETRIEVAL * * @func VOID PASCAL NEAR | RemoveNode | * This function will unlink a single linked node from a list. * This node may be a doc node or an occurence node. This node * is added to the free pool * * @parm _LPQT | lpQueryTree | * Pointer to query tree's containing globals * * @parm LPV | lpRoot | * Pointer to either a Topic node or an occurence node, depending * on the value of fNodeType * * @parm LPSLINK | lpPrevNode | * Node previous to node to be removed, or NULL * * @parm LPSLINK | lpRemovedNode | * Node to be removed * * @parm int | fNodeType | Tell what kind of node we are dealing with *************************************************************************/ PUBLIC VOID PASCAL NEAR RemoveNode(_LPQT lpQueryTree, LPV lpRoot, LPSLINK lpPrevNode, LPSLINK lpRemovedNode, int fNodeType) { if (lpRoot) { /* The node came from an existing list. We have to look for its position and unlink it */ if (lpPrevNode == NULL) { /* Start from the beginning and look for the specific node */ switch (fNodeType & NODE_TYPE_MASK) { case TOPICLIST_NODE : lpPrevNode = (LPSLINK)(((_LPQTNODE)lpRoot)->lpTopicList); break; case OCCURENCE_NODE : lpPrevNode = (LPSLINK)(((LPITOPIC)lpRoot)->lpOccur); break; } } if (lpPrevNode != lpRemovedNode) { while (lpPrevNode->pNext && lpPrevNode->pNext != lpRemovedNode) lpPrevNode = lpPrevNode->pNext; } if (lpPrevNode == lpRemovedNode) { /* This is the first node, just set the list to NULL */ switch (fNodeType & NODE_TYPE_MASK) { case TOPICLIST_NODE : ((_LPQTNODE)lpRoot)->lpTopicList=(LPITOPIC)lpRemovedNode->pNext; break; case OCCURENCE_NODE : ((LPITOPIC)lpRoot)->lpOccur=(LPIOCC)lpRemovedNode->pNext; break; } } else { /* Unlink the node */ lpPrevNode->pNext = lpRemovedNode->pNext; } } if ((fNodeType & DONT_FREE) == 0) { /* Add the free node to FreeList */ switch (fNodeType & NODE_TYPE_MASK) { case TOPICLIST_NODE : lpRemovedNode->pNext = lpQueryTree->lpTopicFreeList; lpQueryTree->lpTopicFreeList = lpRemovedNode; if (lpQueryTree->lpTopicStartSearch == (LPITOPIC)lpRemovedNode) lpQueryTree->lpTopicStartSearch = NULL; if (lpRoot) ((_LPQTNODE)lpRoot)->cTopic--; break; case OCCURENCE_NODE : lpRemovedNode->pNext = lpQueryTree->lpOccFreeList; lpQueryTree->lpOccFreeList = lpRemovedNode; if (lpQueryTree->lpOccStartSearch == (LPIOCC)lpRemovedNode) lpQueryTree->lpOccStartSearch = NULL; if (lpRoot) ((LPITOPIC)lpRoot)->lcOccur--; break; } } } /************************************************************************* * @doc INTERNAL RETRIEVAL * * @func HRESULT PASCAL NEAR | OccNodeInsert | * Purpose add a Occurrence node into the TopicList's occurrence list. * The function assumes that lpOccStartSearch is pointing to the place * to insert the node (non NULL). This is a single linked list * insertion. * On exit, lpOccStartSearch will be updated * * @parm LPQT | lpQueryTree | * Pointer to query tree struct containing globals * * @parm LPITOPIC | lpTopicList | * Pointer to doclist * * @parm LPIOCC | lpInsertedOcc | * Occ node to be inserted into the list * * @rdesc S_OK, if the node is successfully added to the list, * FAILED otherwise *************************************************************************/ PUBLIC HRESULT PASCAL NEAR OccNodeInsert(_LPQT lpQueryTree, LPITOPIC lpTopicList, LPIOCC lpInsertedOcc) { LPIOCC lpCurOcc; // Starting node for searching LPIOCC lpPrevOcc; int fResult; if (lpTopicList->lpOccur == NULL) { lpTopicList->lpOccur = lpInsertedOcc; } else { /* SORTED VERSION */ lpCurOcc = lpQueryTree->lpOccStartSearch; if (lpCurOcc == NULL || OccCompare (lpCurOcc, lpInsertedOcc) < 0) { /* Out of sequence, start from beginning and look for * appropriate location */ lpCurOcc = lpTopicList->lpOccur; } for (lpPrevOcc = NULL; lpCurOcc; lpCurOcc = lpCurOcc->pNext) { if ((fResult = OccCompare (lpCurOcc, lpInsertedOcc)) <= 0) break; lpPrevOcc = lpCurOcc; } if (fResult == 0) { /* Duplicate data, just free the node */ lpInsertedOcc->pNext = (LPIOCC)lpQueryTree->lpOccFreeList; lpQueryTree->lpOccFreeList = (LPSLINK)lpInsertedOcc; return S_OK; } if (lpPrevOcc == NULL) { /* Insert at the beginning */ lpInsertedOcc->pNext = lpTopicList->lpOccur; lpTopicList->lpOccur = lpInsertedOcc; } else { /* Insert at the middle or end */ lpInsertedOcc->pNext = lpPrevOcc->pNext; lpPrevOcc->pNext = lpInsertedOcc; } } lpTopicList->lcOccur ++; /* Update lpOccStartSearch */ lpQueryTree->lpOccStartSearch = lpInsertedOcc; return S_OK; } /************************************************************************* * @doc INTERNAL RETRIEVAL * * @func HRESULT PASCAL NEAR | TopicNodeInsert | * Purpose add a TopicList node into the query node 's TopicList. The * function assumes that lpTopicStartSearch is pointing to the place * to insert the node (non NULL). This is a single linked list * insertion. * On exit, lpTopicStartSearch will be updated * * @parm LPQT | lpQueryTree | * Pointer to query tree struct containing globals * * @parm _LPQTNODE | lpQtNode | * Pointer to query tree node * * @parm LPITOPIC | lpInsertedTopic | * Topic node to be inserted into the list * * @rdesc S_OK, if the node is successfully added to the list, * FAILED otherwise *************************************************************************/ PUBLIC HRESULT PASCAL NEAR TopicNodeInsert (_LPQT lpQueryTree, _LPQTNODE lpQtNode, LPITOPIC lpInsertedTopic) { LPITOPIC lpCurTopic; // Starting node for searching LPITOPIC lpPrevTopic; if (lpQtNode->lpTopicList == NULL) { lpQtNode->lpTopicList = lpInsertedTopic; } else { lpCurTopic = lpQueryTree->lpTopicStartSearch; if (lpCurTopic == NULL || lpCurTopic->dwTopicId >= lpInsertedTopic->dwTopicId) lpCurTopic = lpQtNode->lpTopicList; for (lpPrevTopic = NULL; lpCurTopic; lpCurTopic = lpCurTopic->pNext) { if (lpCurTopic->dwTopicId >= lpInsertedTopic->dwTopicId) { /* We pass the inserted point */ break; } lpPrevTopic = lpCurTopic; } if (lpPrevTopic == NULL) { /* Insert at the beginning */ lpInsertedTopic->pNext = lpQtNode->lpTopicList; lpQtNode->lpTopicList = lpInsertedTopic; } else { /* Inserted at the middle or the end */ lpInsertedTopic->pNext = lpPrevTopic->pNext; lpPrevTopic->pNext = lpInsertedTopic; } } /* Update lpTopicStartSearch */ lpQueryTree->lpTopicStartSearch = lpInsertedTopic; lpQtNode->cTopic++; return S_OK; } /************************************************************************* * @doc INTERNAL * * @func VOID PASCAL NEAR | TopicNodeFree | * Free a doc node and its occurrences list * * @parm _LPQT | lpQueryTree | * Pointer to query tree struct (for globals) * * @parm _LPQTNODE | lpQtNode | * Pointer to query tree node where the Topic node belongs to * * @parm LPITOPIC | lpTopicList | * Topic node to be free *************************************************************************/ PUBLIC VOID PASCAL NEAR TopicNodeFree (_LPQT lpQueryTree, _LPQTNODE lpQtNode, LPITOPIC lpPrev, LPITOPIC lpTopicList) { register LPSLINK lpTmp; // erinfox - comment out because we pass in as parameter // register LPSLINK lpPrev; /* Free all the occurences nodes */ if (lpTmp = (LPSLINK)lpTopicList->lpOccur) { /* Traverse to the occurence list */ while (lpTmp->pNext) lpTmp = lpTmp->pNext; /* Free the occurrence list */ lpTmp->pNext = lpQueryTree->lpOccFreeList; lpQueryTree->lpOccFreeList = (LPSLINK)lpTopicList->lpOccur; } // we pass in lpPrev so we don't have to traverse the linked list again #if 0 /* Free the doc list */ for (lpPrev = NULL, lpTmp = (LPSLINK)lpQtNode->lpTopicList; lpTmp; lpTmp = lpTmp->pNext) { if (lpTmp == (LPSLINK)lpTopicList) break; lpPrev = lpTmp; } #endif if (lpPrev == NULL) { /* Remove the first node */ lpQtNode->lpTopicList = (LPITOPIC)lpTopicList->pNext; } else { /* Remove middle or end node */ lpPrev->pNext = lpTopicList->pNext; } if (lpQueryTree->lpTopicStartSearch == (LPITOPIC)lpTopicList) lpQueryTree->lpTopicStartSearch = lpPrev; lpTopicList->pNext = (LPITOPIC)lpQueryTree->lpTopicFreeList; lpQueryTree->lpTopicFreeList = (LPSLINK)lpTopicList; lpQtNode->cTopic --; } /************************************************************************* * @doc INTERNAL RETRIEVAL * * @func LPITOPIC PASCAL NEAR | TopicNodeSearch | * This function handles the search for a TopicId node in a query node * The assumptions made are: * - TopicId nodes read from the index file are already sorted * - The series of nodes we are looking for is also sorted (since they * are read from the index file) * Ex: For A and B, all the nodes of A are sorted. As we read in each * node of B, they are also sorted * In this case the search will be fast, since we don't have to start * from the beginning. * An exception happens when the index is sorted with fields, but then * retrieved ignoring field. Everytime when a new field occurs, the new * TopicId may be less than the one in lpTopicStartSearch, which demand the * search to start from the beginning * * @parm _LPQT | lpQueryTree | * Pointer to query tree structure containing globals * * @parm _LPQTNODE | lpQtNode | * The query node we are searching on * * @parm DWORD | dwTopicID | * The TopicId we are looking for * * @rdesc The list of the sorted nodes is a single linked list. * If the node is found: * - The function returns a pointer to that node * - lpQueryTree->lpTopicSearchStart points to the node before * the returned node * If the node is not found, * - The function returns NULL * - lpQueryTree->lpTopicStartSearch will point to the node before * the one that cause failure * *************************************************************************/ PUBLIC LPITOPIC PASCAL NEAR TopicNodeSearch(_LPQT lpQueryTree, _LPQTNODE lpQtNode, DWORD dwTopicID) { register LPITOPIC lpStartSearch; if ((lpStartSearch = lpQueryTree->lpTopicStartSearch) == NULL || (lpStartSearch->dwTopicId > dwTopicID)) { /* We begin the search at the beginning of the TopicList. */ lpStartSearch = lpQtNode->lpTopicList; } for (; lpStartSearch; lpStartSearch = lpStartSearch->pNext) { if (lpStartSearch->dwTopicId == dwTopicID) { /* Update the starting search pointer */ lpQueryTree->lpTopicStartSearch = lpStartSearch; return lpStartSearch; } if (lpStartSearch->dwTopicId > dwTopicID) { /* We pass the wanted node, let's break out of the loop */ break; } /* Update the starting search pointer */ lpQueryTree->lpTopicStartSearch = lpStartSearch; } return NULL; } /************************************************************************* * PUBLIC int PASCAL NEAR OccCompare(LPIOCC lpOccur1, * LPIOCC lpOccur2) * * Description: * Compare two occurrence nodes * * Returned Values: * = 0 : if the nodes are equal * > 0 : if lpOccur2 > lpOccur1 * < 0 : if lpOccur2 < lpOccur1 *************************************************************************/ PUBLIC int PASCAL NEAR OccCompare(LPIOCC lpOccur1, LPIOCC lpOccur2) { int fReturn; if (fReturn = (int)(lpOccur2->dwCount - lpOccur1->dwCount)) return fReturn; if (fReturn = (int)(lpOccur2->cLength - lpOccur1->cLength)) return fReturn; return ((int)(lpOccur2->dwOffset - lpOccur1->dwOffset)); }